From 538b931309804e034e288633a9461c2bfdf5b8b7 Mon Sep 17 00:00:00 2001 From: Martina Date: Tue, 30 Nov 2021 13:46:00 -0800 Subject: [PATCH] added profile page --- components/core/EmptyState.js | 4 +- components/core/Filter/Filters.js | 117 ++++++++++++++++++++++++++++- components/core/Filter/Sidebar.js | 18 +++++ components/core/Profile.js | 19 +++++ pages/api/slates/get-serialized.js | 12 ++- pages/api/users/get-serialized.js | 2 +- scenes/SceneProfile.js | 31 +++++++- scenes/SceneSlate.js | 2 +- server.js | 19 +++-- 9 files changed, 211 insertions(+), 13 deletions(-) diff --git a/components/core/EmptyState.js b/components/core/EmptyState.js index ad4f7be0..9ac79a06 100644 --- a/components/core/EmptyState.js +++ b/components/core/EmptyState.js @@ -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; diff --git a/components/core/Filter/Filters.js b/components/core/Filter/Filters.js index c6ab5e9c..38c3d3d1 100644 --- a/components/core/Filter/Filters.js +++ b/components/core/Filter/Filters.js @@ -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 ( + +
+ +
+ + {username} + + {twitterUsername && ( +
+ + {twitterUsername} +
+ )} + {body && ( + + {body} + + )} +
+ {isFollowing ? ( + { + setIsFollowing(false); + handleFollow(); + }} + > + Unfollow + + ) : ( + { + setIsFollowing(true); + handleFollow(); + }} + > + Follow + + )} +
+
+ ); +} + +function ProfileTags({ viewer, data, page, onAction, ...props }) { + const [, { hidePopup }] = useFilterContext(); + + let user = data; + if (page.id === "NAV_SLATE") { + user = data.owner; + } + + return ( + + {user?.slates?.map((slate) => ( + + {slate.slatename} + + ))} + + ); +} + +export { Library, Tags, Profile, ProfileTags }; diff --git a/components/core/Filter/Sidebar.js b/components/core/Filter/Sidebar.js index 23b2ed30..41f601db 100644 --- a/components/core/Filter/Sidebar.js +++ b/components/core/Filter/Sidebar.js @@ -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 ( +
+ + +
+ ); + } + return (
diff --git a/components/core/Profile.js b/components/core/Profile.js index 0d8d1e2f..a118d153 100644 --- a/components/core/Profile.js +++ b/components/core/Profile.js @@ -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 ( +
+ {this.props.data.slates?.length && ( + + )} +
+ ); + return (
diff --git a/pages/api/slates/get-serialized.js b/pages/api/slates/get-serialized.js index 85a754a9..91ec287d 100644 --- a/pages/api/slates/get-serialized.js +++ b/pages/api/slates/get-serialized.js @@ -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", diff --git a/pages/api/users/get-serialized.js b/pages/api/users/get-serialized.js index a9d39a7d..8857f12a 100644 --- a/pages/api/users/get-serialized.js +++ b/pages/api/users/get-serialized.js @@ -42,7 +42,7 @@ export default async (req, res) => { let slates = await Data.getSlatesByUserId({ ownerId: user.id, - includeFiles: true, + // includeFiles: true, publicOnly: true, }); diff --git a/scenes/SceneProfile.js b/scenes/SceneProfile.js index af820f39..d16c784b 100644 --- a/scenes/SceneProfile.js +++ b/scenes/SceneProfile.js @@ -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 { ); } + 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} > - + {user.library?.length ? ( + + ) : ( + This user doesn't have any public content + )} +
+ {/* + /> */} ); diff --git a/scenes/SceneSlate.js b/scenes/SceneSlate.js index c4f45591..4390175f 100644 --- a/scenes/SceneSlate.js +++ b/scenes/SceneSlate.js @@ -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 ( diff --git a/server.js b/server.js index a456902c..0c82ac87 100644 --- a/server.js +++ b/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,