mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-26 02:24:44 +03:00
added internal routing for default to first slate in profile, and added directory to sidebar
This commit is contained in:
parent
c0f918f8c6
commit
42eda96cdb
@ -165,12 +165,12 @@ export const navigation = [
|
|||||||
// pathname: "/_/search",
|
// pathname: "/_/search",
|
||||||
// mainNav: true,
|
// mainNav: true,
|
||||||
// },
|
// },
|
||||||
{
|
// {
|
||||||
id: "NAV_DIRECTORY",
|
// id: "NAV_DIRECTORY",
|
||||||
name: "Directory",
|
// name: "Directory",
|
||||||
pageTitle: "Your Following",
|
// pageTitle: "Your Following",
|
||||||
pathname: "/_/directory",
|
// pathname: "/_/directory",
|
||||||
},
|
// },
|
||||||
slatePage,
|
slatePage,
|
||||||
{
|
{
|
||||||
id: "NAV_API",
|
id: "NAV_API",
|
||||||
|
@ -24,7 +24,7 @@ import SceneSettingsDeveloper from "~/scenes/SceneSettingsDeveloper";
|
|||||||
import SceneAuth from "~/scenes/SceneAuth";
|
import SceneAuth from "~/scenes/SceneAuth";
|
||||||
import SceneSlate from "~/scenes/SceneSlate";
|
import SceneSlate from "~/scenes/SceneSlate";
|
||||||
import SceneActivity from "~/scenes/SceneActivity";
|
import SceneActivity from "~/scenes/SceneActivity";
|
||||||
import SceneDirectory from "~/scenes/SceneDirectory";
|
// import SceneDirectory from "~/scenes/SceneDirectory";
|
||||||
import SceneProfile from "~/scenes/SceneProfile";
|
import SceneProfile from "~/scenes/SceneProfile";
|
||||||
|
|
||||||
// NOTE(jim):
|
// NOTE(jim):
|
||||||
@ -71,7 +71,7 @@ const SCENES = {
|
|||||||
NAV_ERROR: <SceneError />,
|
NAV_ERROR: <SceneError />,
|
||||||
NAV_SIGN_IN: <SceneAuth />,
|
NAV_SIGN_IN: <SceneAuth />,
|
||||||
...(Environment.ACTIVITY_FEATURE_FLAG ? { NAV_ACTIVITY: <SceneActivity /> } : {}),
|
...(Environment.ACTIVITY_FEATURE_FLAG ? { NAV_ACTIVITY: <SceneActivity /> } : {}),
|
||||||
NAV_DIRECTORY: <SceneDirectory />,
|
// NAV_DIRECTORY: <SceneDirectory />,
|
||||||
NAV_PROFILE: <SceneProfile />,
|
NAV_PROFILE: <SceneProfile />,
|
||||||
NAV_DATA: <SceneFilesFolder />,
|
NAV_DATA: <SceneFilesFolder />,
|
||||||
NAV_SLATE: <SceneSlate />,
|
NAV_SLATE: <SceneSlate />,
|
||||||
@ -374,6 +374,11 @@ export default class ApplicationPage extends React.Component {
|
|||||||
data = response.data;
|
data = response.data;
|
||||||
pathname = `/${data.owner.username}/${data.slatename}${search}`;
|
pathname = `/${data.owner.username}/${data.slatename}${search}`;
|
||||||
} else if (page?.id === "NAV_PROFILE") {
|
} else if (page?.id === "NAV_PROFILE") {
|
||||||
|
if (details.id === this.state.viewer?.id) {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
this._handleNavigateTo({ href: "/_/data", redirect: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
let response = await Actions.getSerializedProfile(details);
|
let response = await Actions.getSerializedProfile(details);
|
||||||
if (!response || response.error) {
|
if (!response || response.error) {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
@ -381,6 +386,15 @@ export default class ApplicationPage extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data = response.data;
|
data = response.data;
|
||||||
|
if (data.slates?.length) {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
this._handleNavigateTo({
|
||||||
|
href: `/${data.username}/${data.slates[0].slatename}`,
|
||||||
|
redirect: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pathname = `/${data.username}${search}`;
|
pathname = `/${data.username}${search}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,7 +487,7 @@ export default class ApplicationPage extends React.Component {
|
|||||||
|
|
||||||
const isProfilePage =
|
const isProfilePage =
|
||||||
(page.id === "NAV_SLATE" && this.state.data?.ownerId !== this.state.viewer?.id) ||
|
(page.id === "NAV_SLATE" && this.state.data?.ownerId !== this.state.viewer?.id) ||
|
||||||
(page.id === "NAV_PROFILE" && this.state.data?.id !== this.state.viewer?.id);
|
page.id === "NAV_PROFILE";
|
||||||
|
|
||||||
// if (!this.state.loaded) {
|
// if (!this.state.loaded) {
|
||||||
// return (
|
// return (
|
||||||
@ -505,7 +519,7 @@ export default class ApplicationPage extends React.Component {
|
|||||||
viewer={this.state.viewer}
|
viewer={this.state.viewer}
|
||||||
>
|
>
|
||||||
<Filter
|
<Filter
|
||||||
// isActive={isProfilePage || !!this.state.viewer}
|
isProfilePage={isProfilePage}
|
||||||
isActive={true}
|
isActive={true}
|
||||||
viewer={this.state.viewer}
|
viewer={this.state.viewer}
|
||||||
page={page}
|
page={page}
|
||||||
|
@ -182,20 +182,20 @@ export class ApplicationUserControlsPopup extends React.Component {
|
|||||||
text: (
|
text: (
|
||||||
<div css={STYLES_SECTION_ITEM_HOVER}>
|
<div css={STYLES_SECTION_ITEM_HOVER}>
|
||||||
<Link href={`/$/user/${this.props.viewer.id}`} onAction={this._handleAction}>
|
<Link href={`/$/user/${this.props.viewer.id}`} onAction={this._handleAction}>
|
||||||
Profile
|
Home
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: (
|
|
||||||
<div css={STYLES_SECTION_ITEM_HOVER}>
|
|
||||||
<Link href={"/_/directory"} onAction={this._handleAction}>
|
|
||||||
Directory
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// text: (
|
||||||
|
// <div css={STYLES_SECTION_ITEM_HOVER}>
|
||||||
|
// <Link href={"/_/directory"} onAction={this._handleAction}>
|
||||||
|
// Directory
|
||||||
|
// </Link>
|
||||||
|
// </div>
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -207,8 +207,6 @@ export class ApplicationUserControlsPopup extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
{
|
||||||
text: (
|
text: (
|
||||||
<div css={STYLES_SECTION_ITEM_HOVER}>
|
<div css={STYLES_SECTION_ITEM_HOVER}>
|
||||||
|
@ -49,11 +49,12 @@ const STYLES_FILTERS_GROUP = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FilterButton = ({ children, Icon, isSelected, ...props }) => (
|
const FilterButton = ({ children, Icon, image, isSelected, ...props }) => (
|
||||||
<li>
|
<li>
|
||||||
<Link {...props}>
|
<Link {...props}>
|
||||||
<span as="span" css={[STYLES_FILTER_BUTTON, isSelected && STYLES_FILTER_BUTTON_HIGHLIGHTED]}>
|
<span as="span" css={[STYLES_FILTER_BUTTON, isSelected && STYLES_FILTER_BUTTON_HIGHLIGHTED]}>
|
||||||
<Icon height={16} width={16} style={{ flexShrink: 0 }} />
|
{Icon ? <Icon height={16} width={16} style={{ flexShrink: 0 }} /> : null}
|
||||||
|
{image ? image : null}
|
||||||
<Typography.P2 as="span" nbrOflines={1} style={{ marginLeft: 6 }}>
|
<Typography.P2 as="span" nbrOflines={1} style={{ marginLeft: 6 }}>
|
||||||
{children}
|
{children}
|
||||||
</Typography.P2>
|
</Typography.P2>
|
||||||
@ -120,6 +121,28 @@ function Tags({ viewer, data, onAction, ...props }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Following({ viewer, data, onAction, ...props }) {
|
||||||
|
const [, { hidePopup }] = useFilterContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterSection title="Following" {...props}>
|
||||||
|
{viewer.following.map((user) => (
|
||||||
|
<FilterButton
|
||||||
|
key={user.id}
|
||||||
|
href={`/${user.username}`}
|
||||||
|
isSelected={false}
|
||||||
|
onAction={onAction}
|
||||||
|
// Icon={SVG.ProfileUser}
|
||||||
|
image={<ProfilePhoto user={user} style={{ borderRadius: "8px" }} size={20} />}
|
||||||
|
onClick={hidePopup}
|
||||||
|
>
|
||||||
|
{user.username}
|
||||||
|
</FilterButton>
|
||||||
|
))}
|
||||||
|
</FilterSection>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function Profile({ viewer, data, page, onAction, ...props }) {
|
function Profile({ viewer, data, page, onAction, ...props }) {
|
||||||
if (page.id === "NAV_SLATE") {
|
if (page.id === "NAV_SLATE") {
|
||||||
data = data.owner;
|
data = data.owner;
|
||||||
@ -230,4 +253,4 @@ function ProfileTags({ viewer, data, page, onAction, ...props }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Library, Tags, Profile, ProfileTags };
|
export { Library, Tags, Following, Profile, ProfileTags };
|
||||||
|
@ -66,12 +66,9 @@ const STYLES_SIDEBAR_FILTER_WRAPPER = (theme) => css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function Popup({ viewer, onAction, css, data, page, isMobile, ...props }) {
|
export function Popup({ viewer, onAction, css, data, page, isMobile, isProfilePage, ...props }) {
|
||||||
const [{ popupState }] = useFilterContext();
|
const [{ popupState }] = useFilterContext();
|
||||||
|
|
||||||
const isProfilePage =
|
|
||||||
(page.id === "NAV_SLATE" && data?.ownerId !== viewer?.id) ||
|
|
||||||
(page.id === "NAV_PROFILE" && data?.id !== viewer?.id);
|
|
||||||
if (isProfilePage && !isMobile) {
|
if (isProfilePage && !isMobile) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -102,6 +99,13 @@ export function Popup({ viewer, onAction, css, data, page, isMobile, ...props })
|
|||||||
onAction={onAction}
|
onAction={onAction}
|
||||||
style={{ marginTop: 12 }}
|
style={{ marginTop: 12 }}
|
||||||
/>
|
/>
|
||||||
|
<Filters.Following
|
||||||
|
viewer={viewer}
|
||||||
|
data={data}
|
||||||
|
page={page}
|
||||||
|
onAction={onAction}
|
||||||
|
style={{ marginTop: 12 }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -56,11 +56,8 @@ const STYLES_SIDEBAR_FILTER_WRAPPER = (theme) => css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function Sidebar({ viewer, onAction, data, page, isMobile }) {
|
export function Sidebar({ viewer, onAction, data, page, isMobile, isProfilePage }) {
|
||||||
const [{ sidebarState }] = useFilterContext();
|
const [{ sidebarState }] = useFilterContext();
|
||||||
const isProfilePage =
|
|
||||||
(page.id === "NAV_SLATE" && data?.ownerId !== viewer?.id) ||
|
|
||||||
(page.id === "NAV_PROFILE" && data?.id !== viewer?.id);
|
|
||||||
|
|
||||||
if ((!isProfilePage || isMobile) && (!sidebarState.isVisible || isMobile)) return null;
|
if ((!isProfilePage || isMobile) && (!sidebarState.isVisible || isMobile)) return null;
|
||||||
|
|
||||||
@ -98,6 +95,13 @@ export function Sidebar({ viewer, onAction, data, page, isMobile }) {
|
|||||||
viewer={viewer}
|
viewer={viewer}
|
||||||
style={{ marginTop: 12 }}
|
style={{ marginTop: 12 }}
|
||||||
/>
|
/>
|
||||||
|
<Filters.Following
|
||||||
|
page={page}
|
||||||
|
onAction={onAction}
|
||||||
|
data={data}
|
||||||
|
viewer={viewer}
|
||||||
|
style={{ marginTop: 12 }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -113,7 +113,16 @@ const STYLES_FILTER_CONTENT = (theme) => css`
|
|||||||
${"" /* margin-top: ${theme.sizes.filterNavbar}px; */}
|
${"" /* margin-top: ${theme.sizes.filterNavbar}px; */}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function Filter({ isActive, viewer, onAction, page, data, isMobile, children }) {
|
export default function Filter({
|
||||||
|
isActive,
|
||||||
|
viewer,
|
||||||
|
onAction,
|
||||||
|
page,
|
||||||
|
data,
|
||||||
|
isMobile,
|
||||||
|
children,
|
||||||
|
isProfilePage,
|
||||||
|
}) {
|
||||||
const { results, isSearching } = useSearchStore();
|
const { results, isSearching } = useSearchStore();
|
||||||
const showSearchResult = isSearching && !!results;
|
const showSearchResult = isSearching && !!results;
|
||||||
|
|
||||||
@ -125,10 +134,6 @@ export default function Filter({ isActive, viewer, onAction, page, data, isMobil
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isProfilePage =
|
|
||||||
(page.id === "NAV_SLATE" && data?.ownerId !== viewer?.id) ||
|
|
||||||
(page.id === "NAV_PROFILE" && data?.id !== viewer?.id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider>
|
<Provider>
|
||||||
{isProfilePage && isMobile ? (
|
{isProfilePage && isMobile ? (
|
||||||
@ -152,6 +157,7 @@ export default function Filter({ isActive, viewer, onAction, page, data, isMobil
|
|||||||
data={data}
|
data={data}
|
||||||
page={page}
|
page={page}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
|
isProfilePage={isProfilePage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -175,7 +181,14 @@ export default function Filter({ isActive, viewer, onAction, page, data, isMobil
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div css={STYLES_FILTER_CONTENT}>
|
<div css={STYLES_FILTER_CONTENT}>
|
||||||
<Sidebar viewer={viewer} onAction={onAction} data={data} page={page} isMobile={isMobile} />
|
<Sidebar
|
||||||
|
viewer={viewer}
|
||||||
|
onAction={onAction}
|
||||||
|
data={data}
|
||||||
|
page={page}
|
||||||
|
isMobile={isMobile}
|
||||||
|
isProfilePage={isProfilePage}
|
||||||
|
/>
|
||||||
<div style={{ flexGrow: 1 }}>
|
<div style={{ flexGrow: 1 }}>
|
||||||
{showSearchResult ? (
|
{showSearchResult ? (
|
||||||
<Search.Content viewer={viewer} page={page} onAction={onAction} />
|
<Search.Content viewer={viewer} page={page} onAction={onAction} />
|
||||||
|
@ -1,354 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import * as Actions from "~/common/actions";
|
|
||||||
import * as Constants from "~/common/constants";
|
|
||||||
import * as SVG from "~/common/svg";
|
|
||||||
|
|
||||||
import { css } from "@emotion/react";
|
|
||||||
import { SecondaryTabGroup } from "~/components/core/TabGroup";
|
|
||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
|
||||||
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
|
|
||||||
import { Link } from "~/components/core/Link";
|
|
||||||
|
|
||||||
import ScenePage from "~/components/core/ScenePage";
|
|
||||||
import ScenePageHeader from "~/components/core/ScenePageHeader";
|
|
||||||
import EmptyState from "~/components/core/EmptyState";
|
|
||||||
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
|
||||||
import ProfilePhoto from "~/components/core/ProfilePhoto";
|
|
||||||
|
|
||||||
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_BUTTONS = css`
|
|
||||||
justify-self: end;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-right: 16px;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
@media (max-width: ${Constants.sizes.mobile}px) {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
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_ACTION_BUTTON = css`
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 8px;
|
|
||||||
color: ${Constants.system.blue};
|
|
||||||
font-family: ${Constants.font.medium};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const STYLES_PROFILE_IMAGE = css`
|
|
||||||
background-color: ${Constants.semantic.bgLight};
|
|
||||||
background-size: cover;
|
|
||||||
background-position: 50% 50%;
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
margin-right: 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
position: relative;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const STYLES_STATUS_INDICATOR = css`
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 7px;
|
|
||||||
height: 7px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 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_NAME = css`
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
`;
|
|
||||||
|
|
||||||
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_PROFILE_IMAGE}>
|
|
||||||
<ProfilePhoto user={user} size={24} />
|
|
||||||
{isOnline ? <div css={STYLES_STATUS_INDICATOR} /> : null}
|
|
||||||
</div>
|
|
||||||
<span css={STYLES_NAME}>
|
|
||||||
{user.name || `@${user.username}`}
|
|
||||||
{message ? <span css={STYLES_MESSAGE}>{message}</span> : null}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{button}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const STYLES_COPY_INPUT = css`
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const STYLES_MOBILE_HIDDEN = css`
|
|
||||||
@media (max-width: ${Constants.sizes.mobile}px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const STYLES_MOBILE_ONLY = css`
|
|
||||||
@media (min-width: ${Constants.sizes.mobile}px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default class SceneDirectory extends React.Component {
|
|
||||||
_ref;
|
|
||||||
|
|
||||||
state = {
|
|
||||||
copyValue: "",
|
|
||||||
contextMenu: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
_handleCopy = (e, value) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
this.setState({ copyValue: value }, () => {
|
|
||||||
this._ref.select();
|
|
||||||
document.execCommand("copy");
|
|
||||||
this._handleHide();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
_handleHide = (e) => {
|
|
||||||
this.setState({ contextMenu: null });
|
|
||||||
};
|
|
||||||
|
|
||||||
_handleClick = (e, value) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
if (this.state.contextMenu === value) {
|
|
||||||
this._handleHide();
|
|
||||||
} else {
|
|
||||||
this.setState({ contextMenu: value });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_handleFollow = async (e, id) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
this._handleHide();
|
|
||||||
await Actions.createSubscription({
|
|
||||||
userId: id,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
checkStatus = ({ id }) => {
|
|
||||||
const { activeUsers } = this.props;
|
|
||||||
|
|
||||||
return activeUsers && activeUsers.includes(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let following = this.props.viewer.following.map((relation) => {
|
|
||||||
let button = (
|
|
||||||
<div css={STYLES_ITEM_BOX} onClick={(e) => this._handleClick(e, relation.id)}>
|
|
||||||
<SVG.MoreHorizontal height="24px" />
|
|
||||||
{this.state.contextMenu === relation.id ? (
|
|
||||||
<Boundary
|
|
||||||
captureResize={true}
|
|
||||||
captureScroll={false}
|
|
||||||
enabled
|
|
||||||
onOutsideRectEvent={(e) => this._handleClick(e, relation.id)}
|
|
||||||
>
|
|
||||||
<PopoverNavigation
|
|
||||||
style={{
|
|
||||||
top: "40px",
|
|
||||||
right: "0px",
|
|
||||||
}}
|
|
||||||
navigation={[
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: "Unfollow",
|
|
||||||
onClick: (e) => this._handleFollow(e, relation.id),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Boundary>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Link key={relation.id} href={`/$/user/${relation.id}`} onAction={this.props.onAction}>
|
|
||||||
<UserEntry
|
|
||||||
key={relation.id}
|
|
||||||
user={relation}
|
|
||||||
button={button}
|
|
||||||
checkStatus={this.checkStatus}
|
|
||||||
// onClick={() => {
|
|
||||||
// this.props.onAction({
|
|
||||||
// type: "NAVIGATE",
|
|
||||||
// value: "NAV_PROFILE",
|
|
||||||
// shallow: true,
|
|
||||||
// data: relation,
|
|
||||||
// });
|
|
||||||
// }}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let followers = this.props.viewer.followers.map((relation) => {
|
|
||||||
let button = (
|
|
||||||
<div css={STYLES_ITEM_BOX} onClick={(e) => this._handleClick(e, relation.id)}>
|
|
||||||
<SVG.MoreHorizontal height="24px" />
|
|
||||||
{this.state.contextMenu === relation.id ? (
|
|
||||||
<Boundary
|
|
||||||
captureResize={true}
|
|
||||||
captureScroll={false}
|
|
||||||
enabled
|
|
||||||
onOutsideRectEvent={(e) => this._handleClick(e, relation.id)}
|
|
||||||
>
|
|
||||||
<PopoverNavigation
|
|
||||||
style={{
|
|
||||||
top: "40px",
|
|
||||||
right: "0px",
|
|
||||||
}}
|
|
||||||
navigation={[
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: this.props.viewer.following.some((user) => {
|
|
||||||
return user.id === relation.id;
|
|
||||||
})
|
|
||||||
? "Unfollow"
|
|
||||||
: "Follow",
|
|
||||||
onClick: (e) => this._handleFollow(e, relation.id),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Boundary>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Link key={relation.id} href={`/$/user/${relation.id}`} onAction={this.props.onAction}>
|
|
||||||
<UserEntry
|
|
||||||
key={relation.id}
|
|
||||||
user={relation}
|
|
||||||
button={button}
|
|
||||||
checkStatus={this.checkStatus}
|
|
||||||
// onClick={() => {
|
|
||||||
// this.props.onAction({
|
|
||||||
// type: "NAVIGATE",
|
|
||||||
// value: "NAV_PROFILE",
|
|
||||||
// shallow: true,
|
|
||||||
// data: relation,
|
|
||||||
// });
|
|
||||||
// }}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let tab = this.props.page.params?.tab || "following";
|
|
||||||
return (
|
|
||||||
<WebsitePrototypeWrapper
|
|
||||||
title={`${this.props.page.pageTitle} • Slate`}
|
|
||||||
url={`${Constants.hostname}${this.props.page.pathname}`}
|
|
||||||
>
|
|
||||||
<ScenePage>
|
|
||||||
<ScenePageHeader title="Directory" />
|
|
||||||
<SecondaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
{ title: "Following", value: { tab: "following" } },
|
|
||||||
{ title: "Followers", value: { tab: "followers" } },
|
|
||||||
]}
|
|
||||||
value={tab}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
/>
|
|
||||||
{tab === "following" ? (
|
|
||||||
following && following.length ? (
|
|
||||||
following
|
|
||||||
) : (
|
|
||||||
<EmptyState>
|
|
||||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
|
||||||
You can follow any user on the network to be updated on their new uploads and
|
|
||||||
collections.
|
|
||||||
</EmptyState>
|
|
||||||
)
|
|
||||||
) : null}
|
|
||||||
{tab === "followers" ? (
|
|
||||||
followers && followers.length ? (
|
|
||||||
followers
|
|
||||||
) : (
|
|
||||||
<EmptyState>
|
|
||||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
|
||||||
You don't have any followers yet.
|
|
||||||
</EmptyState>
|
|
||||||
)
|
|
||||||
) : null}
|
|
||||||
<input
|
|
||||||
readOnly
|
|
||||||
ref={(c) => {
|
|
||||||
this._ref = c;
|
|
||||||
}}
|
|
||||||
value={this.state.copyValue}
|
|
||||||
tabIndex="-1"
|
|
||||||
css={STYLES_COPY_INPUT}
|
|
||||||
/>
|
|
||||||
</ScenePage>
|
|
||||||
</WebsitePrototypeWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -147,36 +147,36 @@ export default class SceneProfile extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const viewer = this.props.viewer;
|
const viewer = this.props.viewer;
|
||||||
let user = this.props.data;
|
let user = this.props.data;
|
||||||
if (!user) {
|
// if (!user) {
|
||||||
return (
|
// return (
|
||||||
<WebsitePrototypeWrapper
|
// <WebsitePrototypeWrapper
|
||||||
title={`${this.props.page.pageTitle} • Slate`}
|
// title={`${this.props.page.pageTitle} • Slate`}
|
||||||
url={`${Constants.hostname}${this.props.page.pathname}`}
|
// url={`${Constants.hostname}${this.props.page.pathname}`}
|
||||||
>
|
// >
|
||||||
<ScenePage>
|
// <ScenePage>
|
||||||
<EmptyState>
|
// <EmptyState>
|
||||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
// <SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
||||||
<div>We were unable to locate that user profile</div>
|
// <div>We were unable to locate that user profile</div>
|
||||||
</EmptyState>
|
// </EmptyState>
|
||||||
</ScenePage>
|
// </ScenePage>
|
||||||
</WebsitePrototypeWrapper>
|
// </WebsitePrototypeWrapper>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
if (!user.slates?.length) {
|
// if (!user.slates?.length) {
|
||||||
return (
|
return (
|
||||||
<WebsitePrototypeWrapper
|
<WebsitePrototypeWrapper
|
||||||
title={`${this.props.page.pageTitle} • Slate`}
|
title={`${this.props.page.pageTitle} • Slate`}
|
||||||
url={`${Constants.hostname}${this.props.page.pathname}`}
|
url={`${Constants.hostname}${this.props.page.pathname}`}
|
||||||
>
|
>
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
||||||
<div>This user doesn't have any public content</div>
|
<div>This user doesn't have any public content</div>
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
</ScenePage>
|
</ScenePage>
|
||||||
</WebsitePrototypeWrapper>
|
</WebsitePrototypeWrapper>
|
||||||
);
|
);
|
||||||
}
|
// }
|
||||||
const isOwner = user.id === viewer?.id;
|
const isOwner = user.id === viewer?.id;
|
||||||
let name = user.name || `@${user.username}`;
|
let name = user.name || `@${user.username}`;
|
||||||
let description, title;
|
let description, title;
|
||||||
|
Loading…
Reference in New Issue
Block a user