Merge pull request #201 from filecoin-project/@martinalong/style-fixes

Added subscribing to slates
This commit is contained in:
martinalong 2020-09-02 16:38:55 -07:00 committed by GitHub
commit 373b839a70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 237 additions and 207 deletions

View File

@ -735,20 +735,3 @@ export const NoImage = (props) => (
<path d="M15.28 15.28C14.9481 15.765 14.5134 16.171 14.0068 16.469C13.5002 16.7669 12.9342 16.9496 12.3489 17.004C11.7637 17.0584 11.1737 16.9831 10.6209 16.7836C10.0681 16.5841 9.56601 16.2652 9.15042 15.8496C8.73483 15.434 8.41593 14.9319 8.2164 14.3791C8.01688 13.8263 7.94163 13.2363 7.99601 12.6511C8.05039 12.0658 8.23306 11.4998 8.53103 10.9932C8.829 10.4866 9.23495 10.0519 9.72 9.72M21 21H3C2.46957 21 1.96086 20.7893 1.58579 20.4142C1.21071 20.0391 1 19.5304 1 19V8C1 7.46957 1.21071 6.96086 1.58579 6.58579C1.96086 6.21071 2.46957 6 3 6H6L21 21ZM9 3H15L17 6H21C21.5304 6 22.0391 6.21071 22.4142 6.58579C22.7893 6.96086 23 7.46957 23 8V17.34L9 3Z" />
</svg>
);
export const Lock = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
height={props.height}
style={props.style}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M19 11H5C3.89543 11 3 11.8954 3 13V20C3 21.1046 3.89543 22 5 22H19C20.1046 22 21 21.1046 21 20V13C21 11.8954 20.1046 11 19 11Z" />
<path d="M7 11V7C7 5.67392 7.52678 4.40215 8.46447 3.46447C9.40215 2.52678 10.6739 2 12 2C13.3261 2 14.5979 2.52678 15.5355 3.46447C16.4732 4.40215 17 5.67392 17 7V11" />
</svg>
);

View File

@ -481,9 +481,11 @@ export default class ApplicationPage extends React.Component {
// NOTE(jim): Authenticated.
const navigation = NavigationData.generate(this.state.viewer);
console.log(navigation);
const next = this.state.history[this.state.currentIndex];
const current = NavigationData.getCurrentById(navigation, next.id);
console.log(this.state.history);
console.log(current);
const navigationElement = (
<ApplicationNavigation

View File

@ -2,6 +2,7 @@ import * as React from "react";
import * as Constants from "~/common/constants";
import { css } from "@emotion/react";
import { ProcessedText } from "~/components/system/components/Typography";
import SlatePreviewBlock from "~/components/core/SlatePreviewBlock";
@ -23,7 +24,18 @@ const STYLES_PROFILE_IMAGE = css`
const STYLES_NAME = css`
font-size: ${Constants.typescale.lvl3};
margin: 16px 0px;
width: 100%;
max-width: 420px;
margin: 0 auto;
padding: 0 24px 0 24px;
`;
const STYLES_DESCRIPTION = css`
font-size: ${Constants.typescale.lvl1};
width: 100%;
max-width: 420px;
margin: 0 auto;
padding: 0 24px 0 24px;
`;
const STYLES_LINK = css`
@ -40,11 +52,22 @@ export default class Profile extends React.Component {
css={STYLES_PROFILE_IMAGE}
style={{ backgroundImage: `url('${data.data.photo}')` }}
/>
<div css={STYLES_NAME}>{data.username}</div>
<br />
<div css={STYLES_NAME}>{data.data.name || data.username}</div>
<br />
{data.data.body ? (
<React.Fragment>
<div css={STYLES_DESCRIPTION}>
<ProcessedText text={data.data.body} />
</div>
<br />
</React.Fragment>
) : null}
<br />
{this.props.buttons}
<br />
{data.slates && data.slates.length ? (
<div style={{ width: "100%" }}>
<div>
{data.slates.map((slate) => {
const url = `/${data.username}/${slate.slatename}`;
if (this.props.onAction) {

View File

@ -64,7 +64,7 @@ const STYLES_BLOCK = css`
const STYLES_TITLE_LINE = css`
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-columns: auto auto 1fr;
align-items: center;
font-size: ${Constants.typescale.lvl1};
margin-bottom: 16px;
@ -85,6 +85,14 @@ const STYLES_COPY_INPUT = css`
opacity: 0;
`;
const STYLES_TAG = css`
margin-left: 24px;
padding: 4px 8px;
border-radius: 4px;
background-color: ${Constants.system.gray};
color: ${Constants.system.white};
`;
export default class SlatePreviewBlock extends Component {
_ref;
@ -108,10 +116,12 @@ export default class SlatePreviewBlock extends Component {
<strong style={{ fontSize: Constants.typescale.lvl2 }}>
{this.props.slate.data.name}
</strong>
{this.props.editing && !this.props.slate.data.public ? (
<div style={{ marginLeft: "24px" }}>
<SVG.Lock height="16px" />
</div>
{this.props.editing ? (
this.props.slate.data.public ? (
<div css={STYLES_TAG}>Public</div>
) : (
<div css={STYLES_TAG}>Private</div>
)
) : null}
{this.props.editing ? (
<div style={{ justifySelf: "end" }}>

View File

@ -0,0 +1,52 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import { css } from "@emotion/react";
const STYLES_TAB_GROUP = css`
margin: 36px 0px 24px 0px;
padding: 0 0 0 2px;
display: flex;
align-items: flex-start;
flex-direction: row;
box-sizing: border-box;
font-family: ${Constants.font.text};
width: 100%;
`;
const STYLES_TAB = css`
padding: 8px 8px 8px 0px;
margin-right: 24px;
cursor: pointer;
display: inline-block;
font-size: ${Constants.typescale.lvl1};
user-select: none;
@media (max-width: ${Constants.sizes.mobile}px) {
margin-right: 12px;
}
`;
export class TabGroup extends React.Component {
render() {
return (
<div css={STYLES_TAB_GROUP}>
{this.props.tabs.map((tab, i) => (
<div
css={STYLES_TAB}
key={tab}
style={{
color:
this.props.value === i
? Constants.system.pitchBlack
: Constants.system.gray,
}}
onClick={() => this.props.onChange(i)}
>
{tab}
</div>
))}
</div>
);
}
}

View File

@ -28,12 +28,19 @@ export default class SidebarCreateSlate extends React.Component {
name: this.state.name,
});
console.log(response);
if (response && response.error) {
// TODO(jim): Error task.
alert(response.decorator);
}
this.setState({ loading: false });
this.props.onAction({
type: "NAVIGATE",
value: response.slate.id,
data: response.slate,
});
};
_handleCancel = () => {

View File

@ -37,7 +37,7 @@ export default class ProfilePage extends React.Component {
>
<div css={STYLES_ROOT}>
<WebsitePrototypeHeader />
<div style={{ marginTop: "80px" }}>
<div style={{ margin: "80px 24px 0px 24px" }}>
<Profile {...this.props} />
</div>
<WebsitePrototypeFooter />

View File

@ -5,6 +5,7 @@ import * as Constants from "~/common/constants";
import * as SVG from "~/components/system/svg";
import { css } from "@emotion/react";
import { TabGroup } from "~/components/core/TabGroup";
import ScenePage from "~/components/core/ScenePage";
import ScenePageHeader from "~/components/core/ScenePageHeader";
@ -80,7 +81,7 @@ function UserEntry({ user, button, onClick }) {
export default class SceneDirectory extends React.Component {
state = {
loading: false,
tab: "requests",
tab: 0,
viewer: this.props.viewer,
};
@ -239,50 +240,15 @@ export default class SceneDirectory extends React.Component {
/>
);
});
return (
<ScenePage>
<ScenePageHeader title="Directory" />
<div css={STYLES_TAB_GROUP}>
<div
css={STYLES_TAB}
style={{
color:
this.state.tab === "requests"
? Constants.system.pitchBlack
: Constants.system.gray,
}}
onClick={() => this.setState({ tab: "requests" })}
>
Requests
</div>
<div
css={STYLES_TAB}
style={{
color:
this.state.tab === "peers"
? Constants.system.pitchBlack
: Constants.system.gray,
}}
onClick={() => this.setState({ tab: "peers" })}
>
Trusted
</div>
<div
css={STYLES_TAB}
style={{
marginRight: "0px",
color:
this.state.tab === "following"
? Constants.system.pitchBlack
: Constants.system.gray,
}}
onClick={() => this.setState({ tab: "following" })}
>
Following
</div>
</div>
{this.state.tab === "requests" ? (
<TabGroup
tabs={["Requests", "Trusted", "Following"]}
value={this.state.tab}
onChange={(value) => this.setState({ tab: value })}
/>
{this.state.tab === 0 ? (
requests.length ? (
requests
) : (
@ -292,7 +258,7 @@ export default class SceneDirectory extends React.Component {
</EmptyState>
)
) : null}
{this.state.tab === "peers" ? (
{this.state.tab === 1 ? (
trusted.length ? (
trusted
) : (
@ -302,7 +268,7 @@ export default class SceneDirectory extends React.Component {
</EmptyState>
)
) : null}
{this.state.tab === "following" ? (
{this.state.tab === 2 ? (
following.length ? (
following
) : (

View File

@ -5,7 +5,10 @@ import * as Constants from "~/common/constants";
import * as SVG from "~/components/system/svg";
import { css } from "@emotion/react";
import { ButtonPrimary } from "~/components/system/components/Buttons";
import {
ButtonPrimary,
ButtonSecondary,
} from "~/components/system/components/Buttons";
import ScenePage from "~/components/core/ScenePage";
import Profile from "~/components/core/Profile";
@ -19,18 +22,6 @@ const BUTTON_STYLES = {
minHeight: "30px",
};
const BUTTON_SECONDARY_STYLE = {
...BUTTON_STYLES,
backgroundColor: Constants.system.white,
color: Constants.system.brand,
};
const BUTTON_PRIMARY_STYLE = {
...BUTTON_STYLES,
backgroundColor: Constants.system.brand,
color: Constants.system.white,
};
const STATUS_BUTTON_MAP = {
trusted: "Remove peer",
untrusted: "Add peer",
@ -151,37 +142,48 @@ export default class SceneProfile extends React.Component {
render() {
let buttons = (
<div>
<ButtonPrimary
style={
this.state.followStatus
? BUTTON_SECONDARY_STYLE
: BUTTON_PRIMARY_STYLE
}
onClick={this._handleFollow}
>
{this.state.followStatus ? "Unfollow" : "Follow"}
</ButtonPrimary>
<ButtonPrimary
style={
this.state.trustStatus === "untrusted" ||
this.state.trustStatus === "received"
? BUTTON_PRIMARY_STYLE
: BUTTON_SECONDARY_STYLE
}
onClick={this._handleTrust}
>
{STATUS_BUTTON_MAP[this.state.trustStatus]}
</ButtonPrimary>
{this.state.isTrusted ? (
{this.state.followStatus ? (
<ButtonSecondary
style={{ margin: "0px 8px" }}
onClick={this._handleFollow}
>
Unfollow
</ButtonSecondary>
) : (
<ButtonPrimary
style={{ margin: "0px 8px" }}
onClick={this._handleFollow}
>
Follow
</ButtonPrimary>
)}
{this.state.trustStatus === "untrusted" ||
this.state.trustStatus === "received" ? (
<ButtonPrimary
style={{ margin: "0px 8px" }}
onClick={this._handleTrust}
>
{STATUS_BUTTON_MAP[this.state.trustStatus]}
</ButtonPrimary>
) : (
<ButtonSecondary
style={{ margin: "0px 8px" }}
onClick={this._handleTrust}
>
{STATUS_BUTTON_MAP[this.state.trustStatus]}
</ButtonSecondary>
)}
{/* {this.state.trustStatus === "trusted" ? (
<ButtonPrimary style={BUTTON_STYLE} onClick={this._handleSendMoney}>
Send Money
</ButtonPrimary>
) : null}
) : null} */}
</div>
);
return (
<ScenePage style={{ padding: `88px 24px 128px 24px` }}>
<ScenePage>
<Profile
{...this.props}
onAction={this.props.onAction}
creator={this.props.data}
sceneId={this.props.sceneId}

View File

@ -5,6 +5,7 @@ import * as Constants from "~/common/constants";
import * as SVG from "~/components/system/svg";
import { css } from "@emotion/react";
import { ProcessedText } from "~/components/system/components/Typography";
import ScenePage from "~/components/core/ScenePage";
import ScenePageHeader from "~/components/core/ScenePageHeader";
@ -60,7 +61,8 @@ export default class SceneSlate extends React.Component {
componentDidUpdate(prevProps) {
const updated =
this.props.current.updated_at !== prevProps.current.updated_at;
this.props.current.updated_at !== prevProps.current.updated_at ||
this.props.data.slatename !== prevProps.data.slatename;
if (!updated) {
return;
@ -86,6 +88,30 @@ export default class SceneSlate extends React.Component {
});
}
_handleUpdate = async (e) => {
let response = await this.props.onRehydrate();
if (!response || response.error) {
alert("TODO: error fetching authenticated viewer");
return null;
}
let viewer = response.data;
this.setState({
following: !!viewer.subscriptions.filter((subscription) => {
return subscription.target_slate_id === this.props.data.id;
}).length,
});
};
_handleFollow = async () => {
let response = await Actions.createSubscription({
slateId: this.props.data.id,
});
console.log(response);
// await this._handleUpdate();
};
_handleChangeLayout = async (layout, layouts) => {
this.setState({ layouts, saving: "SAVING" });
await this._handleSave(null, null, layouts);
@ -278,7 +304,7 @@ export default class SceneSlate extends React.Component {
body = "A slate.",
name,
} = this.state;
console.log(this.props);
return (
<ScenePage style={{ padding: `88px 24px 128px 24px` }}>
<ScenePageHeader
@ -322,10 +348,18 @@ export default class SceneSlate extends React.Component {
<SVG.Settings height="16px" />
</CircleButtonGray>
</React.Fragment>
) : null
) : (
<div onClick={this._handleFollow}>
{!!this.props.viewer.subscriptions.filter((subscription) => {
return subscription.target_slate_id === this.props.data.id;
}).length
? "Unfollow"
: "Follow"}
</div>
)
}
>
{body}
<ProcessedText text={body} />
</ScenePageHeader>
<Slate
key={slatename}

View File

@ -4,6 +4,7 @@ import * as System from "~/components/system";
import * as SVG from "~/components/system/svg";
import { css } from "@emotion/react";
import { TabGroup } from "~/components/core/TabGroup";
import ScenePage from "~/components/core/ScenePage";
import ScenePageHeader from "~/components/core/ScenePageHeader";
@ -17,40 +18,6 @@ const STYLES_NUMBER = css`
font-weight: 400;
`;
const STYLES_ACTIONS = css`
z-index: ${Constants.zindex.navigation};
bottom: 16px;
right: 8px;
position: absolute;
flex-direction: column;
display: flex;
`;
const STYLES_ACTION_BUTTON = css`
font-family: ${Constants.font.code};
font-size: 10px;
text-transform: uppercase;
user-select: none;
height: 32px;
padding: 0 16px 0 16px;
border-radius: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
z-index: ${Constants.zindex.modal};
background: ${Constants.system.pitchBlack};
transition: 200ms ease all;
color: ${Constants.system.white};
cursor: pointer;
margin: auto;
margin: 4px 16px 4px 16px;
flex-shrink: 0;
text-decoration: none;
:hover {
background-color: ${Constants.system.black};
}
`;
const STYLES_TAB = css`
padding: 8px 8px 8px 0px;
margin-right: 24px;
@ -64,16 +31,10 @@ const STYLES_TAB = css`
}
`;
const STYLES_TAB_GROUP = css`
${"" /* border-bottom: 1px solid ${Constants.system.gray}; */}
margin: 36px 0px 24px 0px;
padding: 0 0 0 2px;
`;
// TODO(jim): Slates design.
export default class SceneSlates extends React.Component {
state = {
tab: "my",
tab: 0,
};
_handleAdd = () => {
@ -89,14 +50,31 @@ export default class SceneSlates extends React.Component {
const slates = this.props.viewer.slates.map((each) => {
return {
...each,
url: `https://slate.host/${this.props.viewer.username}/${
each.slatename
}`,
url: `https://slate.host/${this.props.viewer.username}/${each.slatename}`,
public: each.data.public,
objects: <span css={STYLES_NUMBER}>{each.data.objects.length}</span>,
};
});
let subscriptions = this.props.viewer.subscriptions
.filter((each) => {
return !!each.target_slate_id;
})
.map((relation) => (
<div
key={relation.slate.id}
onClick={() =>
this.props.onAction({
type: "NAVIGATE",
value: "V1_NAVIGATION_SLATE",
data: relation.slate,
})
}
>
<SlatePreviewBlock slate={relation.slate} />
</div>
));
// TODO(jim): Refactor later.
const slateButtons = [
{ name: "Create slate", type: "SIDEBAR", value: "SIDEBAR_CREATE_SLATE" },
@ -107,7 +85,7 @@ export default class SceneSlates extends React.Component {
<ScenePageHeader
title="Slates"
actions={
this.state.tab === "my" ? (
this.state.tab === 0 ? (
<CircleButtonGray
onMouseUp={this._handleAdd}
onTouchEnd={this._handleAdd}
@ -118,34 +96,14 @@ export default class SceneSlates extends React.Component {
) : null
}
/>
<div css={STYLES_TAB_GROUP}>
<div
css={STYLES_TAB}
style={{
color:
this.state.tab === "my"
? Constants.system.pitchBlack
: Constants.system.gray,
}}
onClick={() => this.setState({ tab: "my" })}
>
My Slates
</div>
<div
css={STYLES_TAB}
style={{
color:
this.state.tab === "following"
? Constants.system.pitchBlack
: Constants.system.gray,
}}
onClick={() => this.setState({ tab: "following" })}
>
Following
</div>
</div>
{this.state.tab === "my"
? this.props.data.children.map((slate) => (
<TabGroup
tabs={["My Slates", "Following"]}
value={this.state.tab}
onChange={(value) => this.setState({ tab: value })}
/>
{this.state.tab === 0 ? (
this.props.data.children.length ? (
this.props.data.children.map((slate) => (
<div
key={slate.id}
onClick={() =>
@ -159,30 +117,23 @@ export default class SceneSlates extends React.Component {
<SlatePreviewBlock slate={slate} editing />
</div>
))
: null}
{this.state.tab === "following" ? (
<EmptyState style={{ marginTop: 88 }}>
This feature is coming soon.
</EmptyState>
) : (
<EmptyState style={{ marginTop: 88 }}>
You have no slates yet! Create a new slate by clicking the plus
button
</EmptyState>
)
) : null}
{this.state.tab === 1 ? (
subscriptions.length ? (
subscriptions
) : (
<EmptyState style={{ marginTop: 88 }}>
You aren't following any slates yet! Get started by following any
slates you encounter that you want to be updated on
</EmptyState>
)
) : null}
{/* this.props.viewer.subscriptions
.filter((each) => {
return !!each.target_slate_id;
})
.map((relation) => (
<div
key={relation.slate.id}
onClick={() =>
this.props.onAction({
type: "NAVIGATE",
value: "V1_NAVIGATION_SLATE",
data: relation.slate,
})
}
>
<SlatePreviewBlock slate={relation.slate} />
</div>
)) */}
</ScenePage>
);
}