mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-27 10:52:41 +03:00
feat(profile): remove peers and files
This commit is contained in:
parent
2c9e7c4f00
commit
23fb81d013
@ -3,25 +3,16 @@ import * as Constants from "~/common/constants";
|
|||||||
import * as Strings from "~/common/strings";
|
import * as Strings from "~/common/strings";
|
||||||
import * as SVG from "~/common/svg";
|
import * as SVG from "~/common/svg";
|
||||||
import * as Actions from "~/common/actions";
|
import * as Actions from "~/common/actions";
|
||||||
import * as Utilities from "~/common/utilities";
|
|
||||||
import * as Events from "~/common/custom-events";
|
import * as Events from "~/common/custom-events";
|
||||||
import * as Window from "~/common/window";
|
|
||||||
import * as Styles from "~/common/styles";
|
import * as Styles from "~/common/styles";
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import { Link } from "~/components/core/Link";
|
import { Link } from "~/components/core/Link";
|
||||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons";
|
import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons";
|
||||||
import { TabGroup, SecondaryTabGroup } from "~/components/core/TabGroup";
|
import { 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 { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||||
|
|
||||||
import ProcessedText from "~/components/core/ProcessedText";
|
import ProcessedText from "~/components/core/ProcessedText";
|
||||||
import SlatePreviewBlocks from "~/components/core/SlatePreviewBlock";
|
|
||||||
import DataView from "~/components/core/DataView";
|
|
||||||
import EmptyState from "~/components/core/EmptyState";
|
import EmptyState from "~/components/core/EmptyState";
|
||||||
import ProfilePhoto from "~/components/core/ProfilePhoto";
|
import ProfilePhoto from "~/components/core/ProfilePhoto";
|
||||||
import CollectionPreviewBlock from "~/components/core/CollectionPreviewBlock";
|
import CollectionPreviewBlock from "~/components/core/CollectionPreviewBlock";
|
||||||
@ -139,16 +130,6 @@ const STYLES_STAT = css`
|
|||||||
flex-shrink: 0;
|
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`
|
const STYLES_BUTTON = css`
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
|
|
||||||
@ -157,164 +138,12 @@ const STYLES_BUTTON = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
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.grayLight2};
|
|
||||||
|
|
||||||
@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.semantic.borderGrayLight};
|
|
||||||
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.blue};
|
|
||||||
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.semantic.bgLight};
|
|
||||||
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.green};
|
|
||||||
background-color: ${Constants.system.green};
|
|
||||||
`;
|
|
||||||
|
|
||||||
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`
|
// const STYLES_COPY_INPUT = css`
|
||||||
// pointer-events: none;
|
// pointer-events: none;
|
||||||
// position: absolute;
|
// position: absolute;
|
||||||
// opacity: 0;
|
// opacity: 0;
|
||||||
// `;
|
// `;
|
||||||
|
|
||||||
function UserEntry({ user, button, onClick, message, checkStatus }) {
|
|
||||||
const isOnline = checkStatus({ id: user.id });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={user.username} css={STYLES_USER_ENTRY}>
|
|
||||||
<div css={STYLES_USER} onClick={onClick}>
|
|
||||||
<div css={STYLES_DIRECTORY_PROFILE_IMAGE}>
|
|
||||||
<ProfilePhoto user={user} size={24} />
|
|
||||||
{isOnline && <div css={STYLES_DIRECTORY_STATUS_INDICATOR} />}
|
|
||||||
</div>
|
|
||||||
<span css={STYLES_DIRECTORY_NAME}>
|
|
||||||
{user.data.name || `@${user.username}`}
|
|
||||||
{message ? <span css={STYLES_MESSAGE}>{message}</span> : null}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{button}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function FilesPage({
|
|
||||||
library,
|
|
||||||
user,
|
|
||||||
isOwner,
|
|
||||||
isMobile,
|
|
||||||
viewer,
|
|
||||||
onAction,
|
|
||||||
resources,
|
|
||||||
page,
|
|
||||||
tab = "grid",
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{isMobile ? null : (
|
|
||||||
<SecondaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
{
|
|
||||||
title: <SVG.GridView height="24px" style={{ display: "block" }} />,
|
|
||||||
value: { tab: "grid", subtab: "files" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: <SVG.TableView height="24px" style={{ display: "block" }} />,
|
|
||||||
value: { tab: "table", subtab: "files" },
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
value={tab}
|
|
||||||
onAction={onAction}
|
|
||||||
style={{ margin: "0 0 24px 0" }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{library.length ? (
|
|
||||||
<DataView
|
|
||||||
key="scene-profile"
|
|
||||||
user={user}
|
|
||||||
onAction={onAction}
|
|
||||||
viewer={viewer}
|
|
||||||
isOwner={isOwner}
|
|
||||||
items={library}
|
|
||||||
view={tab}
|
|
||||||
resources={resources}
|
|
||||||
page={page}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<EmptyState>
|
|
||||||
<FileTypeGroup />
|
|
||||||
<div style={{ marginTop: 24 }}>This user does not have any public files yet</div>
|
|
||||||
</EmptyState>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CollectionsPage({
|
function CollectionsPage({
|
||||||
user,
|
user,
|
||||||
viewer,
|
viewer,
|
||||||
@ -339,8 +168,8 @@ function CollectionsPage({
|
|||||||
<div>
|
<div>
|
||||||
<SecondaryTabGroup
|
<SecondaryTabGroup
|
||||||
tabs={[
|
tabs={[
|
||||||
{ title: "Collections", value: { tab: "collections", subtab: "collections" } },
|
{ title: "Collections", value: { tab: "collections" } },
|
||||||
{ title: "Subscribed", value: { tab: "subscribed", subtab: "collections" } },
|
{ title: "Subscribed", value: { tab: "subscribed" } },
|
||||||
]}
|
]}
|
||||||
value={tab}
|
value={tab}
|
||||||
onAction={onAction}
|
onAction={onAction}
|
||||||
@ -377,125 +206,12 @@ function CollectionsPage({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ? (
|
|
||||||
<div css={STYLES_ITEM_BOX} onClick={(e) => selectUser(e, relation.id)}>
|
|
||||||
<SVG.MoreHorizontal height="24px" />
|
|
||||||
{selectedUser === relation.id ? (
|
|
||||||
<Boundary
|
|
||||||
captureResize={true}
|
|
||||||
captureScroll={false}
|
|
||||||
enabled
|
|
||||||
onOutsideRectEvent={(e) => selectUser(e)}
|
|
||||||
>
|
|
||||||
<PopoverNavigation
|
|
||||||
style={{
|
|
||||||
top: "40px",
|
|
||||||
right: "0px",
|
|
||||||
}}
|
|
||||||
navigation={[
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: following ? "Unfollow" : "Follow",
|
|
||||||
onClick: (e) => followUser(e, relation.id),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Boundary>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link key={relation.id} href={`/$/user/${relation.id}`} onAction={onAction}>
|
|
||||||
<UserEntry key={relation.id} user={relation} button={button} checkStatus={checkStatus} />
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SecondaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
{ title: "Following", value: { tab: "following", subtab: "peers" } },
|
|
||||||
{ title: "Followers", value: { tab: "followers", subtab: "peers" } },
|
|
||||||
]}
|
|
||||||
value={tab}
|
|
||||||
onAction={onAction}
|
|
||||||
style={{ margin: "0 0 24px 0" }}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
{peers?.length ? (
|
|
||||||
peers
|
|
||||||
) : (
|
|
||||||
<EmptyState>
|
|
||||||
{fetched ? (
|
|
||||||
<React.Fragment>
|
|
||||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
|
||||||
{tab === "following"
|
|
||||||
? `This user is not following anyone yet`
|
|
||||||
: `This user does not have any followers yet`}
|
|
||||||
</React.Fragment>
|
|
||||||
) : (
|
|
||||||
<LoaderSpinner style={{ height: 24, width: 24 }} />
|
|
||||||
)}
|
|
||||||
</EmptyState>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Profile extends React.Component {
|
export default class Profile extends React.Component {
|
||||||
_ref = null;
|
_ref = null;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
contextMenu: null,
|
contextMenu: null,
|
||||||
subscriptions: [],
|
subscriptions: [],
|
||||||
followers: [],
|
|
||||||
following: [],
|
|
||||||
isFollowing:
|
isFollowing:
|
||||||
this.props.external || this.props.user.id === this.props.viewer?.id
|
this.props.external || this.props.user.id === this.props.viewer?.id
|
||||||
? false
|
? false
|
||||||
@ -506,10 +222,6 @@ export default class Profile extends React.Component {
|
|||||||
index: -1,
|
index: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount = () => {
|
|
||||||
this.fetchSocial();
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidUpdate = (prevProps) => {
|
componentDidUpdate = (prevProps) => {
|
||||||
if (!this.state.fetched && this.props.page.params !== prevProps.page.params) {
|
if (!this.state.fetched && this.props.page.params !== prevProps.page.params) {
|
||||||
this.fetchSocial();
|
this.fetchSocial();
|
||||||
@ -518,12 +230,9 @@ export default class Profile extends React.Component {
|
|||||||
|
|
||||||
fetchSocial = async () => {
|
fetchSocial = async () => {
|
||||||
if (this.state.fetched) return;
|
if (this.state.fetched) return;
|
||||||
if (this.props.page.params?.subtab !== "peers" && this.props.page.params?.tab !== "subscribed")
|
if (this.props.page.params?.tab !== "subscribed") return;
|
||||||
return;
|
let subscriptions;
|
||||||
let following, followers, subscriptions;
|
|
||||||
if (this.props.user.id === this.props.viewer?.id) {
|
if (this.props.user.id === this.props.viewer?.id) {
|
||||||
following = this.props.viewer?.following;
|
|
||||||
followers = this.props.viewer?.followers;
|
|
||||||
subscriptions = this.props.viewer?.subscriptions;
|
subscriptions = this.props.viewer?.subscriptions;
|
||||||
} else {
|
} else {
|
||||||
const query = { id: this.props.user.id };
|
const query = { id: this.props.user.id };
|
||||||
@ -531,19 +240,15 @@ export default class Profile extends React.Component {
|
|||||||
if (Events.hasError(response)) {
|
if (Events.hasError(response)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
following = response.following;
|
|
||||||
followers = response.followers;
|
|
||||||
subscriptions = response.subscriptions;
|
subscriptions = response.subscriptions;
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
following: following,
|
|
||||||
followers: followers,
|
|
||||||
subscriptions: subscriptions,
|
subscriptions: subscriptions,
|
||||||
fetched: true,
|
fetched: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleHide = (e) => {
|
_handleHide = () => {
|
||||||
this.setState({ contextMenu: null });
|
this.setState({ contextMenu: null });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -582,33 +287,14 @@ export default class Profile extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
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 tab = this.props.page.params?.tab;
|
||||||
let library = this.props.user.library;
|
let { user, isOwner } = this.props;
|
||||||
let isOwner = this.props.isOwner;
|
let { fileCount } = user;
|
||||||
let user = this.props.user;
|
|
||||||
|
|
||||||
const showStatusIndicator = this.props.isAuthenticated;
|
const showStatusIndicator = this.props.isAuthenticated;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<GlobalCarousel
|
|
||||||
carouselType="PROFILE"
|
|
||||||
resources={this.props.resources}
|
|
||||||
viewer={this.props.viewer}
|
|
||||||
objects={library}
|
|
||||||
isOwner={this.props.isOwner}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
isMobile={this.props.isMobile}
|
|
||||||
external={this.props.external}
|
|
||||||
params={this.props.page.params}
|
|
||||||
index={this.state.index}
|
|
||||||
onChange={(index) => this.setState({ index })}
|
|
||||||
/>
|
|
||||||
<div css={STYLES_PROFILE_BACKGROUND}>
|
<div css={STYLES_PROFILE_BACKGROUND}>
|
||||||
<div css={STYLES_PROFILE_INFO}>
|
<div css={STYLES_PROFILE_INFO}>
|
||||||
<div css={STYLES_PROFILE_IMAGE}>
|
<div css={STYLES_PROFILE_IMAGE}>
|
||||||
@ -650,7 +336,7 @@ export default class Profile extends React.Component {
|
|||||||
<div css={STYLES_STATS}>
|
<div css={STYLES_STATS}>
|
||||||
<div css={STYLES_STAT}>
|
<div css={STYLES_STAT}>
|
||||||
<div style={{ fontFamily: `${Constants.font.text}` }}>
|
<div style={{ fontFamily: `${Constants.font.text}` }}>
|
||||||
{library.length}{" "}
|
{fileCount}{" "}
|
||||||
<span style={{ color: `${Constants.system.grayLight2}` }}>Files</span>
|
<span style={{ color: `${Constants.system.grayLight2}` }}>Files</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -665,39 +351,12 @@ export default class Profile extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div css={STYLES_PROFILE}>
|
<div css={STYLES_PROFILE}>
|
||||||
<TabGroup
|
<CollectionsPage
|
||||||
tabs={[
|
{...this.props}
|
||||||
{ title: "Files", value: { subtab: "files" } },
|
tab={tab}
|
||||||
{ title: "Collections", value: { subtab: "collections" } },
|
fetched={this.state.fetched}
|
||||||
{ title: "Peers", value: { subtab: "peers" } },
|
subscriptions={this.state.subscriptions}
|
||||||
]}
|
|
||||||
value={subtab}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
style={{ marginTop: 0, marginBottom: 32 }}
|
|
||||||
itemStyle={{ margin: "0px 16px" }}
|
|
||||||
/>
|
/>
|
||||||
{subtab === "files" ? (
|
|
||||||
<FilesPage {...this.props} user={user} library={library} tab={tab} />
|
|
||||||
) : null}
|
|
||||||
{subtab === "collections" ? (
|
|
||||||
<CollectionsPage
|
|
||||||
{...this.props}
|
|
||||||
tab={tab}
|
|
||||||
fetched={this.state.fetched}
|
|
||||||
subscriptions={this.state.subscriptions}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{subtab === "peers" ? (
|
|
||||||
<PeersPage
|
|
||||||
{...this.props}
|
|
||||||
tab={tab}
|
|
||||||
onLoginModal={this._handleLoginModal}
|
|
||||||
checkStatus={this.checkStatus}
|
|
||||||
following={this.state.following}
|
|
||||||
followers={this.state.followers}
|
|
||||||
fetched={this.state.fetched}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user