import * as React from "react"; import * as Constants from "~/common/constants"; import * as Strings from "~/common/strings"; import * as SVG from "~/common/svg"; import * as Actions from "~/common/actions"; import * as Utilities from "~/common/utilities"; import * as Events from "~/common/custom-events"; import * as Window from "~/common/window"; import { useState } from "react"; import { Link } from "~/components/core/Link"; import { GlobalCarousel } from "~/components/system/components/GlobalCarousel"; import { css } from "@emotion/react"; import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons"; import { TabGroup, SecondaryTabGroup } from "~/components/core/TabGroup"; import { Boundary } from "~/components/system/components/fragments/Boundary"; import { PopoverNavigation } from "~/components/system/components/PopoverNavigation"; import { FileTypeGroup } from "~/components/core/FileTypeIcon"; import { LoaderSpinner } from "~/components/system/components/Loaders"; import ProcessedText from "~/components/core/ProcessedText"; import SlatePreviewBlocks from "~/components/core/SlatePreviewBlock"; import CTATransition from "~/components/core/CTATransition"; import DataView from "~/components/core/DataView"; import EmptyState from "~/components/core/EmptyState"; const STYLES_PROFILE_BACKGROUND = css` background-color: ${Constants.system.white}; width: 100%; padding: 104px 56px 24px 56px; @media (max-width: ${Constants.sizes.mobile}px) { padding: 80px 24px 16px 24px; } `; const STYLES_PROFILE = css` width: 100%; padding: 0px 56px 80px 56px; overflow-wrap: break-word; white-space: pre-wrap; @media (max-width: ${Constants.sizes.mobile}px) { padding: 0px 24px 16px 24px; } `; const STYLES_PROFILE_INFO = css` line-height: 1.3; width: 50%; max-width: 800px; overflow-wrap: break-word; white-space: pre-wrap; margin: 0 auto; @media (max-width: ${Constants.sizes.tablet}px) { width: 100%; max-width: 100%; } `; const STYLES_INFO = css` display: block; width: 100%; text-align: center; margin-bottom: 48px; overflow-wrap: break-word; white-space: pre-wrap; `; const STYLES_PROFILE_IMAGE = css` background-color: ${Constants.system.foreground}; background-size: cover; background-position: 50% 50%; width: 120px; height: 120px; flex-shrink: 0; border-radius: 4px; margin: 0 auto; position: relative; @media (max-width: ${Constants.sizes.mobile}px) { width: 64px; height: 64px; } `; const STYLES_STATUS_INDICATOR = css` position: absolute; bottom: 0; right: 0; width: 12px; height: 12px; border-radius: 50%; border: 2px solid ${Constants.system.active}; background-color: ${Constants.system.active}; `; const STYLES_NAME = css` font-size: ${Constants.typescale.lvl4}; font-family: ${Constants.font.semiBold}; max-width: 100%; font-weight: 400; margin: 16px auto; overflow-wrap: break-word; white-space: pre-wrap; color: ${Constants.system.black}; @media (max-width: ${Constants.sizes.mobile}px) { margin-bottom: 8px; } `; const STYLES_DESCRIPTION = css` font-size: ${Constants.typescale.lvl0}; color: ${Constants.system.darkGray}; max-width: 100%; overflow-wrap: break-word; white-space: pre-wrap; ul, ol { white-space: normal; } @media (max-width: ${Constants.sizes.mobile}px) { margin-top: 24px; } `; const STYLES_STATS = css` font-size: ${Constants.typescale.lvl0}; margin: 16px auto; display: flex; justify-content: center; color: ${Constants.system.grayBlack}; `; const STYLES_STAT = css` margin: 0px 12px; ${"" /* width: 112px; */} flex-shrink: 0; `; const STYLES_EXPLORE = css` font-size: ${Constants.typescale.lvl1}; font-family: ${Constants.font.text}; font-weight: 400; margin: 64px auto 64px auto; width: 120px; padding-top: 16px; border-top: 1px solid ${Constants.system.black}; `; const STYLES_BUTTON = css` margin-bottom: 32px; @media (max-width: ${Constants.sizes.mobile}px) { margin-bottom: 16px; } `; const STYLES_ITEM_BOX = css` position: relative; justify-self: end; display: flex; align-items: center; justify-content: center; padding: 8px; margin-right: 16px; color: ${Constants.system.darkGray}; @media (max-width: ${Constants.sizes.mobile}px) { margin-right: 8px; } `; const STYLES_USER_ENTRY = css` display: grid; grid-template-columns: auto 1fr; align-items: center; font-size: ${Constants.typescale.lvl1}; cursor: pointer; ${"" /* border: 1px solid ${Constants.system.lightBorder}; */} border-radius: 4px; margin-bottom: 8px; background-color: ${Constants.system.white}; `; const STYLES_USER = css` display: grid; grid-template-columns: auto 1fr; align-items: center; margin: 16px; color: ${Constants.system.brand}; font-family: ${Constants.font.medium}; font-size: ${Constants.typescale.lvl1}; @media (max-width: ${Constants.sizes.mobile}px) { margin: 12px 16px; } `; const STYLES_DIRECTORY_PROFILE_IMAGE = css` background-color: ${Constants.system.foreground}; background-size: cover; background-position: 50% 50%; height: 24px; width: 24px; margin-right: 16px; border-radius: 4px; position: relative; `; const STYLES_DIRECTORY_STATUS_INDICATOR = css` position: absolute; bottom: 0; right: 0; width: 7px; height: 7px; border-radius: 50%; border: 1.2px solid ${Constants.system.active}; background-color: ${Constants.system.active}; `; const STYLES_MESSAGE = css` color: ${Constants.system.black}; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @media (max-width: 1000px) { display: none; } `; const STYLES_DIRECTORY_NAME = css` overflow: hidden; white-space: nowrap; text-overflow: ellipsis; `; // const STYLES_COPY_INPUT = css` // pointer-events: none; // position: absolute; // opacity: 0; // `; function UserEntry({ user, button, onClick, message, checkStatus }) { const isOnline = checkStatus({ id: user.id }); return (
{isOnline &&
}
{user.data.name || `@${user.username}`} {message ? {message} : null}
{button}
); } function FilesPage({ library, isOwner, isMobile, viewer, onAction, resources, page, tab = "grid", }) { return (
{isMobile ? null : ( , value: { tab: "grid", subtab: "files" }, }, { title: , value: { tab: "table", subtab: "files" }, }, ]} value={tab} onAction={onAction} style={{ margin: "0 0 24px 0" }} /> )} {library.length ? ( ) : (
This user does not have any public files yet
)}
); } function CollectionsPage({ user, viewer, fetched, subscriptions, tab = "collections", isOwner, onAction, }) { let slates = []; if (tab === "collections") { slates = user.slates ? isOwner ? user.slates.filter((slate) => slate.isPublic === true) : user.slates : slates; } else { slates = subscriptions; } slates = slates || []; return (
{slates?.length ? ( ) : ( {tab === "collections" || fetched ? ( {tab === "collections" ? `This user does not have any public collections yet` : `This user is not following any collections yet`} ) : ( )} )}
); } function PeersPage({ checkStatus, viewer, following, followers, fetched, tab = "following", onAction, onLoginModal, }) { const [selectedUser, setSelectedUser] = useState(false); const selectUser = (e, id) => { e.stopPropagation(); e.preventDefault(); if (!id || selectedUser === id) { setSelectedUser(null); } else { setSelectedUser(id); } }; const followUser = async (e, id) => { e.stopPropagation(); e.preventDefault(); selectUser(e, null); if (!viewer) { onLoginModal(); return; } await Actions.createSubscription({ userId: id, }); }; let peers = tab === "following" ? following : followers; peers = peers.map((relation) => { const following = !!( viewer && viewer.following.some((subscription) => { return subscription.id === relation.id; }).length ); let button = !viewer || relation.id !== viewer?.id ? (
selectUser(e, relation.id)}> {selectedUser === relation.id ? ( selectUser(e)} > followUser(e, relation.id), }, ], ]} /> ) : null}
) : null; return ( ); }); return (
{peers?.length ? ( peers ) : ( {fetched ? ( {tab === "following" ? `This user is not following anyone yet` : `This user does not have any followers yet`} ) : ( )} )}
); } export default class Profile extends React.Component { _ref = null; state = { contextMenu: null, subscriptions: [], followers: [], following: [], isFollowing: this.props.external || this.props.user.id === this.props.viewer?.id ? false : !!this.props.viewer?.following.some((entry) => { return entry.id === this.props.user.id; }), fetched: false, }; componentDidMount = () => { this.fetchSocial(); }; componentDidUpdate = (prevProps) => { if (!this.state.fetched && this.props.page.params !== prevProps.page.params) { this.fetchSocial(); } }; fetchSocial = async () => { if (this.state.fetched) return; if (this.props.page.params?.subtab !== "peers" && this.props.page.params?.tab !== "subscribed") return; let following, followers, subscriptions; if (this.props.user.id === this.props.viewer?.id) { following = this.props.viewer?.following; followers = this.props.viewer?.followers; subscriptions = this.props.viewer?.subscriptions; } else { const query = { id: this.props.user.id }; let response = await Actions.getSocial(query); if (Events.hasError(response)) { return; } following = response.following; followers = response.followers; subscriptions = response.subscriptions; } this.setState({ following: following, followers: followers, subscriptions: subscriptions, fetched: true, }); }; _handleHide = (e) => { this.setState({ contextMenu: null }); }; // _handleClick = (e, value) => { // e.stopPropagation(); // if (this.state.contextMenu === value) { // this._handleHide(); // } else { // this.setState({ contextMenu: value }); // } // }; _handleFollow = async (e, id) => { if (this.props.external) { this._handleLoginModal(); return; } this._handleHide(); e.stopPropagation(); await Actions.createSubscription({ userId: id, }); }; _handleLoginModal = (e) => { if (e) { e.preventDefault(); e.stopPropagation(); } Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} }); }; checkStatus = ({ id }) => { const { activeUsers } = this.props; return activeUsers && activeUsers.includes(id); }; render() { let subtab = this.props.page.params?.subtab ? this.props.page.params?.subtab : this.props.page.params?.cid ? "files" : "collections"; let tab = this.props.page.params?.tab; let library = this.props.user.library; let isOwner = this.props.isOwner; let user = this.props.user; const showStatusIndicator = this.props.isAuthenticated; return (
{showStatusIndicator && this.checkStatus({ id: user.id }) && (
)}
{Strings.getPresentationName(user)}
{!isOwner && (
{this.state.isFollowing ? ( { this.setState({ isFollowing: false }); this._handleFollow(e, this.props.user.id); }} > Unfollow ) : ( { this.setState({ isFollowing: true }); this._handleFollow(e, this.props.user.id); }} > Follow )}
)} {user.data.body ? (
) : null}
{library.length}{" "} Files
{user.slates?.length || 0}{" "} Collections
{subtab === "files" ? : null} {subtab === "collections" ? ( ) : null} {subtab === "peers" ? ( ) : null}
); } }