mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-26 13:45:30 +03:00
added profile page
This commit is contained in:
parent
b2a5670a56
commit
538b931309
@ -5,8 +5,8 @@ import { css } from "@emotion/react";
|
||||
|
||||
const STYLES_EMPTY_STATE = css`
|
||||
width: 100%;
|
||||
height: 328px;
|
||||
border: 1px solid ${Constants.semantic.borderGrayLight};
|
||||
height: 50vh;
|
||||
color: ${Constants.semantic.textGray};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -2,10 +2,14 @@ import * as React from "react";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as Styles from "~/common/styles";
|
||||
import * as Typography from "~/components/system/components/Typography";
|
||||
import * as Utilities from "~/common/utilities";
|
||||
|
||||
import ProfilePhoto from "~/components/core/ProfilePhoto";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { useFilterContext } from "~/components/core/Filter/Provider";
|
||||
import { Link } from "~/components/core/Link";
|
||||
import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons";
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Shared components between filters
|
||||
@ -115,4 +119,115 @@ function Tags({ viewer, data, onAction, ...props }) {
|
||||
);
|
||||
}
|
||||
|
||||
export { Library, Tags };
|
||||
function Profile({ viewer, data, page, onAction, ...props }) {
|
||||
if (page.id === "NAV_SLATE") {
|
||||
data = data.owner;
|
||||
}
|
||||
|
||||
const [, { hidePopup }] = useFilterContext();
|
||||
const isAuthenticated = !!viewer;
|
||||
const isOwner = viewer?.id === data.id;
|
||||
|
||||
const [isFollowing, setIsFollowing] = React.useState(() =>
|
||||
isOwner
|
||||
? false
|
||||
: !!viewer?.following.some((entry) => {
|
||||
return entry.id === data.id;
|
||||
})
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
let updatedIsFollowing = isOwner
|
||||
? false
|
||||
: !!viewer?.following.some((entry) => {
|
||||
return entry.id === data.id;
|
||||
});
|
||||
setIsFollowing(updatedIsFollowing);
|
||||
}, [viewer?.following, data?.id]);
|
||||
|
||||
const handleFollow = async () => {
|
||||
if (!isAuthenticated) {
|
||||
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||
return;
|
||||
}
|
||||
await Actions.createSubscription({
|
||||
userId: data.id,
|
||||
});
|
||||
};
|
||||
|
||||
const username = Utilities.getUserDisplayName(data);
|
||||
let { twitterUsername, body } = data;
|
||||
|
||||
return (
|
||||
<FilterSection style={{ marginBottom: "24px" }}>
|
||||
<div css={Styles.VERTICAL_CONTAINER_CENTERED} style={{ gap: "12px" }}>
|
||||
<ProfilePhoto user={data} style={{ borderRadius: "20px" }} size={80} />
|
||||
<div css={Styles.VERTICAL_CONTAINER_CENTERED} style={{ gap: "4px" }}>
|
||||
<Typography.H4 color="textGrayDark" style={{ marginTop: 10 }}>
|
||||
{username}
|
||||
</Typography.H4>
|
||||
{twitterUsername && (
|
||||
<div css={Styles.HORIZONTAL_CONTAINER_CENTERED} style={{ gap: "4px" }}>
|
||||
<SVG.Twitter style={{ width: "16px" }} />
|
||||
<Typography.P2 color="textGrayDark">{twitterUsername}</Typography.P2>
|
||||
</div>
|
||||
)}
|
||||
{body && (
|
||||
<Typography.P2 color="textGrayDark" nbrOflines={4} style={{ textAlign: "center" }}>
|
||||
{body}
|
||||
</Typography.P2>
|
||||
)}
|
||||
</div>
|
||||
{isFollowing ? (
|
||||
<ButtonSecondary
|
||||
full
|
||||
onClick={() => {
|
||||
setIsFollowing(false);
|
||||
handleFollow();
|
||||
}}
|
||||
>
|
||||
Unfollow
|
||||
</ButtonSecondary>
|
||||
) : (
|
||||
<ButtonPrimary
|
||||
full
|
||||
onClick={() => {
|
||||
setIsFollowing(true);
|
||||
handleFollow();
|
||||
}}
|
||||
>
|
||||
Follow
|
||||
</ButtonPrimary>
|
||||
)}
|
||||
</div>
|
||||
</FilterSection>
|
||||
);
|
||||
}
|
||||
|
||||
function ProfileTags({ viewer, data, page, onAction, ...props }) {
|
||||
const [, { hidePopup }] = useFilterContext();
|
||||
|
||||
let user = data;
|
||||
if (page.id === "NAV_SLATE") {
|
||||
user = data.owner;
|
||||
}
|
||||
|
||||
return (
|
||||
<FilterSection {...props}>
|
||||
{user?.slates?.map((slate) => (
|
||||
<FilterButton
|
||||
key={slate.id}
|
||||
href={`/$/slate/${slate.id}`}
|
||||
isSelected={slate.id === data?.id}
|
||||
onAction={onAction}
|
||||
Icon={slate.isPublic ? SVG.Hash : SVG.SecurityLock}
|
||||
onClick={hidePopup}
|
||||
>
|
||||
{slate.slatename}
|
||||
</FilterButton>
|
||||
))}
|
||||
</FilterSection>
|
||||
);
|
||||
}
|
||||
|
||||
export { Library, Tags, Profile, ProfileTags };
|
||||
|
@ -61,6 +61,24 @@ export function Sidebar({ viewer, onAction, data, page, isMobile }) {
|
||||
|
||||
if (!sidebarState.isVisible || isMobile) return null;
|
||||
|
||||
if (
|
||||
(page.id === "NAV_SLATE" && data?.ownerId !== viewer?.id) ||
|
||||
(page.id === "NAV_PROFILE" && data?.id !== viewer?.id)
|
||||
) {
|
||||
return (
|
||||
<div css={STYLES_SIDEBAR_FILTER_WRAPPER}>
|
||||
<Filters.Profile page={page} data={data} viewer={viewer} onAction={onAction} />
|
||||
<Filters.ProfileTags
|
||||
page={page}
|
||||
onAction={onAction}
|
||||
data={data}
|
||||
viewer={viewer}
|
||||
style={{ marginTop: 12 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div css={STYLES_SIDEBAR_FILTER_WRAPPER}>
|
||||
<Filters.Library page={page} onAction={onAction} />
|
||||
|
@ -12,6 +12,7 @@ import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/B
|
||||
import { SecondaryTabGroup } from "~/components/core/TabGroup";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
|
||||
import DataView from "~/components/core/DataView";
|
||||
import ProcessedText from "~/components/core/ProcessedText";
|
||||
import EmptyState from "~/components/core/EmptyState";
|
||||
import ProfilePhoto from "~/components/core/ProfilePhoto";
|
||||
@ -300,6 +301,24 @@ export default class Profile extends React.Component {
|
||||
|
||||
const showStatusIndicator = this.props.isAuthenticated;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{this.props.data.slates?.length && (
|
||||
<DataView
|
||||
key="scene-files-folder"
|
||||
type="collection"
|
||||
collection={this.props.data.slates[0]}
|
||||
onAction={this.props.onAction}
|
||||
viewer={this.props.viewer}
|
||||
items={this.props.data.slates[0].objects}
|
||||
view={"grid"}
|
||||
isOwner={isOwner}
|
||||
page={this.props.page}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div css={STYLES_PROFILE_BACKGROUND}>
|
||||
|
@ -42,7 +42,17 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
slate.user = owner;
|
||||
let slates = await Data.getSlatesByUserId({
|
||||
ownerId: owner.id,
|
||||
// includeFiles: true,
|
||||
publicOnly: true,
|
||||
});
|
||||
|
||||
if (slates && !slates.error) {
|
||||
owner.slates = slates;
|
||||
}
|
||||
|
||||
slate.owner = owner;
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_GET_SERIALIZED_SLATE",
|
||||
|
@ -42,7 +42,7 @@ export default async (req, res) => {
|
||||
|
||||
let slates = await Data.getSlatesByUserId({
|
||||
ownerId: user.id,
|
||||
includeFiles: true,
|
||||
// includeFiles: true,
|
||||
publicOnly: true,
|
||||
});
|
||||
|
||||
|
@ -9,6 +9,7 @@ import * as SVG from "~/common/svg";
|
||||
import { css } from "@emotion/react";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
|
||||
import DataView from "~/components/core/DataView";
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import Profile from "~/components/core/Profile";
|
||||
import EmptyState from "~/components/core/EmptyState";
|
||||
@ -22,6 +23,15 @@ const STYLES_LOADER = css`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const STYLES_DATAVIEWER_WRAPPER = (theme) => css`
|
||||
width: 100%;
|
||||
min-height: calc(100vh - ${theme.sizes.filterNavbar}px);
|
||||
padding: calc(20px + ${theme.sizes.filterNavbar}px) 24px 44px;
|
||||
@media (max-width: ${theme.sizes.mobile}px) {
|
||||
padding: calc(31px + ${theme.sizes.filterNavbar}px) 16px 44px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default class SceneProfile extends React.Component {
|
||||
state = {
|
||||
notFound: false,
|
||||
@ -135,6 +145,7 @@ export default class SceneProfile extends React.Component {
|
||||
// };
|
||||
|
||||
render() {
|
||||
const viewer = this.props.viewer;
|
||||
let user = this.props.data;
|
||||
if (!user) {
|
||||
return (
|
||||
@ -151,6 +162,7 @@ export default class SceneProfile extends React.Component {
|
||||
</WebsitePrototypeWrapper>
|
||||
);
|
||||
}
|
||||
const isOwner = user.id === viewer.id;
|
||||
let name = user.name || `@${user.username}`;
|
||||
let description, title;
|
||||
const image = user.photo;
|
||||
@ -172,13 +184,28 @@ export default class SceneProfile extends React.Component {
|
||||
url={`${Constants.hostname}${this.props.page.pathname}`}
|
||||
image={image}
|
||||
>
|
||||
<Profile
|
||||
<div css={STYLES_DATAVIEWER_WRAPPER}>
|
||||
{user.library?.length ? (
|
||||
<DataView
|
||||
key="scene-files-folder"
|
||||
isOwner={this.props.viewer.id === this.props.user.id}
|
||||
items={user.library}
|
||||
onAction={this.props.onAction}
|
||||
viewer={this.props.viewer}
|
||||
page={this.props.page}
|
||||
view="grid"
|
||||
/>
|
||||
) : (
|
||||
<EmptyState>This user doesn't have any public content</EmptyState>
|
||||
)}
|
||||
</div>
|
||||
{/* <Profile
|
||||
{...this.props}
|
||||
user={user}
|
||||
isOwner={this.props.viewer ? user.id === this.props.viewer.id : false}
|
||||
isAuthenticated={this.props.viewer !== null}
|
||||
key={user.id}
|
||||
/>
|
||||
/> */}
|
||||
</WebsitePrototypeWrapper>
|
||||
);
|
||||
|
||||
|
@ -411,7 +411,7 @@ class SlatePage extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { user, name, objects, body, isPublic, ownerId } = this.props.data;
|
||||
const { owner: user, name, objects, body, isPublic, ownerId } = this.props.data;
|
||||
const isOwner = this.props.viewer ? ownerId === this.props.viewer.id : false;
|
||||
|
||||
return (
|
||||
|
19
server.js
19
server.js
@ -292,7 +292,7 @@ app.prepare().then(async () => {
|
||||
|
||||
const slates = await Data.getSlatesByUserId({
|
||||
ownerId: user.id,
|
||||
includeFiles: true,
|
||||
// includeFiles: true,
|
||||
publicOnly: true,
|
||||
});
|
||||
|
||||
@ -401,20 +401,29 @@ app.prepare().then(async () => {
|
||||
return res.redirect("/_/404");
|
||||
}
|
||||
|
||||
const user = await Data.getUserById({
|
||||
const owner = await Data.getUserById({
|
||||
id: slate.ownerId,
|
||||
sanitize: true,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
if (!owner) {
|
||||
return res.redirect("/_/404");
|
||||
}
|
||||
|
||||
if (user.error) {
|
||||
if (owner.error) {
|
||||
return res.redirect("/_/404");
|
||||
}
|
||||
|
||||
slate.user = user;
|
||||
let slates = await Data.getSlatesByUserId({
|
||||
ownerId: owner.id,
|
||||
publicOnly: true,
|
||||
});
|
||||
|
||||
if (slates && !slates.error) {
|
||||
owner.slates = slates;
|
||||
}
|
||||
|
||||
slate.owner = owner;
|
||||
|
||||
return app.render(req, res, "/_", {
|
||||
viewer,
|
||||
|
Loading…
Reference in New Issue
Block a user