mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-25 18:13:10 +03:00
added carousel to profiles and fixed not updating bug
This commit is contained in:
parent
7f4f6a4b04
commit
c4301e74b8
@ -52,7 +52,6 @@ import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
||||
|
||||
import { GlobalModal } from "~/components/system/components/GlobalModal";
|
||||
import { OnboardingModal } from "~/components/core/OnboardingModal";
|
||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
||||
import { SearchModal } from "~/components/core/SearchModal";
|
||||
import { Alert } from "~/components/core/Alert";
|
||||
import { announcements } from "~/components/core/OnboardingModal";
|
||||
@ -690,15 +689,6 @@ export default class ApplicationPage extends React.Component {
|
||||
>
|
||||
{scene}
|
||||
</ApplicationLayout>
|
||||
<GlobalCarousel
|
||||
onUpdateViewer={this._handleUpdateViewer}
|
||||
resources={this.props.resources}
|
||||
viewer={this.state.viewer}
|
||||
current={this.state.data}
|
||||
slates={this.state.viewer.slates}
|
||||
onAction={this._handleAction}
|
||||
mobile={this.props.mobile}
|
||||
/>
|
||||
<GlobalModal />
|
||||
<SearchModal
|
||||
viewer={this.state.viewer}
|
||||
|
@ -82,6 +82,7 @@ const STYLES_DISMISS_BOX = css`
|
||||
`;
|
||||
|
||||
const STYLES_META = css`
|
||||
text-align: start;
|
||||
padding: 14px 0px 8px 0px;
|
||||
overflow-wrap: break-word;
|
||||
`;
|
||||
@ -343,6 +344,7 @@ export default class CarouselSidebarData extends React.Component {
|
||||
};
|
||||
|
||||
_handleDelete = (cid) => {
|
||||
if (!this.props.isOwner) return;
|
||||
const message = `Are you sure you want to delete this? It will be deleted from your slates as well`;
|
||||
if (!window.confirm(message)) {
|
||||
return;
|
||||
@ -384,7 +386,6 @@ export default class CarouselSidebarData extends React.Component {
|
||||
const isVisible = this.state.inPublicSlates || this.state.isPublic;
|
||||
let selected = this.state.selected;
|
||||
if (this.state.inPublicSlates) {
|
||||
console.log("in public slates");
|
||||
const slateIds = Object.entries(this.state.selected)
|
||||
.filter((entry) => entry[1])
|
||||
.map((entry) => entry[0]);
|
||||
@ -410,7 +411,6 @@ export default class CarouselSidebarData extends React.Component {
|
||||
public: !isVisible,
|
||||
},
|
||||
});
|
||||
console.log(response);
|
||||
if (isVisible) {
|
||||
this.setState({ inPublicSlates: false, isPublic: false, selected });
|
||||
} else {
|
||||
@ -445,7 +445,10 @@ export default class CarouselSidebarData extends React.Component {
|
||||
/>
|
||||
</Boundary>
|
||||
) : (
|
||||
<span css={STYLES_META_TITLE} target="_blank" onClick={this._handleEditFilename}>
|
||||
<span
|
||||
css={STYLES_META_TITLE}
|
||||
onClick={this.props.external ? () => {} : this._handleEditFilename}
|
||||
>
|
||||
{this.state.name}
|
||||
</span>
|
||||
)}
|
||||
@ -462,37 +465,45 @@ export default class CarouselSidebarData extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
<div css={STYLES_ACTIONS}>
|
||||
<div css={STYLES_ACTION} onClick={() => this._handleCopy(cid, "cidCopying")}>
|
||||
<SVG.CopyAndPaste height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>
|
||||
{this.state.loading === "cidCopying" ? "Copied!" : "Copy file CID"}
|
||||
</span>
|
||||
</div>
|
||||
{this.props.isOwner ? (
|
||||
<div css={STYLES_ACTION} onClick={() => this._handleCopy(cid, "cidCopying")}>
|
||||
<SVG.CopyAndPaste height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>
|
||||
{this.state.loading === "cidCopying" ? "Copied!" : "Copy file CID"}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
<div css={STYLES_ACTION} onClick={() => this._handleCopy(url, "gatewayUrlCopying")}>
|
||||
<SVG.Data height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>
|
||||
{this.state.loading === "gatewayUrlCopying" ? "Copied!" : "Copy gateway URL"}
|
||||
{this.state.loading === "gatewayUrlCopying" ? "Copied!" : "Copy external URL"}
|
||||
</span>
|
||||
</div>
|
||||
<div css={STYLES_ACTION} onClick={this._handleDownload}>
|
||||
<SVG.Download height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>Download</span>
|
||||
</div>
|
||||
<div css={STYLES_ACTION} onClick={() => this._handleDelete(cid)}>
|
||||
<SVG.Trash height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>Delete</span>
|
||||
</div>
|
||||
{this.props.isOwner ? (
|
||||
<div css={STYLES_ACTION} onClick={() => this._handleDelete(cid)}>
|
||||
<SVG.Trash height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>Delete</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div css={STYLES_SECTION_HEADER}>Connected Slates</div>
|
||||
<SlatePicker
|
||||
dark
|
||||
slates={this.props.slates}
|
||||
onCreateSlate={this._handleCreateSlate}
|
||||
selectedColor={Constants.system.white}
|
||||
files={[this.props.data]}
|
||||
selected={this.state.selected}
|
||||
onAdd={this._handleAdd}
|
||||
/>
|
||||
{this.props.external ? null : (
|
||||
<React.Fragment>
|
||||
<div css={STYLES_SECTION_HEADER}>Connected Slates</div>
|
||||
<SlatePicker
|
||||
dark
|
||||
slates={this.props.slates}
|
||||
onCreateSlate={this._handleCreateSlate}
|
||||
selectedColor={Constants.system.white}
|
||||
files={[this.props.data]}
|
||||
selected={this.state.selected}
|
||||
onAdd={this._handleAdd}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{type && Validations.isPreviewableImage(type) ? null : (
|
||||
<div>
|
||||
<System.P css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||
@ -534,28 +545,32 @@ export default class CarouselSidebarData extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||
Visibility
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
margin: "16px 0 16px 0",
|
||||
}}
|
||||
>
|
||||
<div style={{ color: Constants.system.darkGray, lineHeight: "1.5" }}>
|
||||
{isVisible ? "Everyone" : "Link only"}
|
||||
</div>
|
||||
<Toggle dark active={isVisible} onChange={this._handleToggleVisibility} />
|
||||
</div>
|
||||
<div style={{ color: Constants.system.darkGray, marginTop: 8 }}>
|
||||
{isVisible
|
||||
? "This file is currently visible to everyone and searchable within Slate through public slates."
|
||||
: "This file is currently not visible to others unless they have the link."}
|
||||
</div>
|
||||
{this.props.isOwner ? (
|
||||
<React.Fragment>
|
||||
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||
Visibility
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
margin: "16px 0 16px 0",
|
||||
}}
|
||||
>
|
||||
<div style={{ color: Constants.system.darkGray, lineHeight: "1.5" }}>
|
||||
{isVisible ? "Everyone" : "Link only"}
|
||||
</div>
|
||||
<Toggle dark active={isVisible} onChange={this._handleToggleVisibility} />
|
||||
</div>
|
||||
<div style={{ color: Constants.system.darkGray, marginTop: 8 }}>
|
||||
{isVisible
|
||||
? "This file is currently visible to everyone and searchable within Slate through public slates."
|
||||
: "This file is currently not visible to others unless they have the link."}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
<input
|
||||
css={STYLES_HIDDEN}
|
||||
ref={(c) => {
|
||||
|
@ -4,7 +4,14 @@ import * as Strings from "~/common/strings";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as Actions from "~/common/actions";
|
||||
|
||||
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";
|
||||
//FOR TARA: try and group imports by style (import *, import { Whatever }, import Whatever). Just a neatness preference
|
||||
|
||||
import ProcessedText from "~/components/core/ProcessedText";
|
||||
import SlatePreviewBlocks from "~/components/core/SlatePreviewBlock";
|
||||
@ -12,12 +19,6 @@ import CTATransition from "~/components/core/CTATransition";
|
||||
import DataView from "~/components/core/DataView";
|
||||
import EmptyState from "~/components/core/EmptyState";
|
||||
|
||||
import { SceneUtils } from "three";
|
||||
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";
|
||||
|
||||
const STYLES_PROFILE_BACKGROUND = css`
|
||||
background-color: ${Constants.system.white};
|
||||
width: 100%;
|
||||
@ -60,7 +61,8 @@ const STYLES_INFO = css`
|
||||
`;
|
||||
|
||||
const STYLES_PROFILE_IMAGE = css`
|
||||
background-color: ${Constants.system.white};
|
||||
background-color: ${Constants.system.foreground};
|
||||
${"" /* FOR TARA: I like to put a background color that stands out from the background so that in the case that the background image isn't found, it doesn't just show blank. It at least shows a square there */}
|
||||
background-size: cover;
|
||||
background-position: 50% 50%;
|
||||
width: 120px;
|
||||
@ -108,8 +110,8 @@ const STYLES_STATS = css`
|
||||
`;
|
||||
|
||||
const STYLES_STAT = css`
|
||||
margin-right: 8px;
|
||||
width: 112px;
|
||||
margin: 0px 12px;
|
||||
${"" /* width: 112px; */}
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
@ -233,70 +235,53 @@ export default class Profile extends React.Component {
|
||||
peerTab: 0,
|
||||
copyValue: "",
|
||||
contextMenu: null,
|
||||
followingSlates: [],
|
||||
publicSlates: [],
|
||||
publicFiles: [],
|
||||
pseudoPrivateFiles: [],
|
||||
};
|
||||
|
||||
_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();
|
||||
if (this.state.contextMenu === value) {
|
||||
this._handleHide();
|
||||
} else {
|
||||
this.setState({ contextMenu: value });
|
||||
}
|
||||
};
|
||||
|
||||
_handleFollow = async (e, id) => {
|
||||
this._handleHide();
|
||||
e.stopPropagation();
|
||||
await Actions.createSubscription({
|
||||
userId: id,
|
||||
});
|
||||
isFollowing: this.props.external
|
||||
? false
|
||||
: !!this.props.viewer.subscriptions.filter((entry) => {
|
||||
return entry.target_user_id === this.props.data.id;
|
||||
}).length,
|
||||
//FOR TARA: here's an example of using the !! where it's needed. I want isFollowing to be true (if a subscription to this user was found) or false, not 0 or 1, so I use a !! to convert the number to a boolean
|
||||
};
|
||||
|
||||
componentDidMount = async () => {
|
||||
await this.filterByVisibility();
|
||||
await this.fetchUsername();
|
||||
};
|
||||
|
||||
componentDidUpdate = async () => {
|
||||
if (this.props.viewer?.library[0].children.length !== this.lastLength) {
|
||||
if (
|
||||
this.lastLength != null &&
|
||||
this.props.creator?.library[0].children.length !== this.lastLength
|
||||
) {
|
||||
//FOR TARA: I just added the "this.lastLength != null" so that we don't get the double-call here where component did mount calls filterbyvisiblity,
|
||||
//then filterbyvisibility changes the library length to not null,
|
||||
//then componentdidupdate sees that the length has changed from null to some number, then calls filterbyvisibility again
|
||||
//FOR TARA: I also made it this.props.creator rather than this.props.viewer. Not a big deal, but this is b/c if you are viewing someone else's profile and your OWN library changes, you shouldn't have to do filterbyvisibility again b/c it won't affect anything
|
||||
this.filterByVisibility();
|
||||
}
|
||||
};
|
||||
|
||||
fetchUsername = async () => {
|
||||
let followingSlates = [];
|
||||
let subscriptions = this.props.creator.subscriptions ? this.props.creator.subscriptions : null;
|
||||
for (let subscription of subscriptions) {
|
||||
if (subscription.slate != null) {
|
||||
followingSlates.push(subscription.slate);
|
||||
}
|
||||
}
|
||||
//FOR TARA: you don't need to do actions.getserialized profile to get the username of the slate owner here. it was already accessible through subscription.slate.username, and you don't need to pass it in
|
||||
//Without that call, it makes more sense to put this inside the render method so I moved this there now. You can delete this function
|
||||
// fetchUsername = async () => {
|
||||
// let followingSlates = [];
|
||||
// let subscriptions = this.props.creator.subscriptions ? this.props.creator.subscriptions : [];
|
||||
// for (let subscription of subscriptions) {
|
||||
// if (subscription.slate != null) {
|
||||
// followingSlates.push(subscription.slate);
|
||||
// }
|
||||
// }
|
||||
|
||||
for (let followingSlate of followingSlates) {
|
||||
let id = { id: followingSlate.data.ownerId };
|
||||
let slateOwner = await Actions.getSerializedProfile(id);
|
||||
followingSlate.username = slateOwner.data?.username;
|
||||
}
|
||||
// for (let followingSlate of followingSlates) {
|
||||
// let id = { id: followingSlate.data.ownerId };
|
||||
// let slateOwner = await Actions.getSerializedProfile(id); //martina: do we need to do serialized? or just normal? Or some other way of getting teh username that's more efficient?
|
||||
// followingSlate.username = slateOwner.data?.username;
|
||||
// }
|
||||
|
||||
this.setState({ followingSlates: followingSlates });
|
||||
};
|
||||
// this.setState({ followingSlates: followingSlates });
|
||||
// };
|
||||
|
||||
filterByVisibility = async () => {
|
||||
let publicFiles = [];
|
||||
@ -334,24 +319,53 @@ export default class Profile extends React.Component {
|
||||
publicFiles: publicFiles,
|
||||
pseudoPrivateFiles: pseudoPrivateFiles,
|
||||
});
|
||||
this.lastLength = this.props.viewer?.library[0].children.length;
|
||||
this.lastLength = this.props.creator?.library[0].children.length;
|
||||
};
|
||||
|
||||
_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();
|
||||
if (this.state.contextMenu === value) {
|
||||
this._handleHide();
|
||||
} else {
|
||||
this.setState({ contextMenu: value });
|
||||
}
|
||||
};
|
||||
|
||||
_handleFollow = async (e, id) => {
|
||||
this._handleHide();
|
||||
e.stopPropagation();
|
||||
await Actions.createSubscription({
|
||||
userId: id,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const external = !this.props.onAction;
|
||||
let data = this.props.creator ? this.props.creator : this.props.data;
|
||||
let exploreSlates = this.props.exploreSlates;
|
||||
let subscriptions = this.props.isOwner
|
||||
? this.props.viewer.subscriptions
|
||||
: this.props.creator.subscriptions
|
||||
? this.props.creator.subscriptions
|
||||
: null;
|
||||
let subscribers = this.props.isOwner
|
||||
? this.props.viewer.subscribers
|
||||
: this.props.creator.subscribers
|
||||
? this.props.creator.subscribers
|
||||
: null;
|
||||
let isOwner = this.props.creator.username === this.props.viewer?.username;
|
||||
let data = this.props.creator ? this.props.creator : this.props.data;
|
||||
//FOR TARA: i made a chnage in scene profile so that if it is your own profile, creator is equivalent to viewer, so there is no longer a need to check for that here
|
||||
//FOR TARA: also, it's always safer to make the "default" for an array an empty array, not null. Because if you try iterate over null, it will error.
|
||||
let subscriptions = this.props.creator.subscriptions || [];
|
||||
let subscribers = this.props.creator.subscribers || [];
|
||||
|
||||
//FOR TARA: this is where I moved the "fetchUsernames" function to
|
||||
let followingSlates = subscriptions
|
||||
.filter((relation) => {
|
||||
return !!relation.target_slate_id;
|
||||
})
|
||||
.map((relation) => relation.slate);
|
||||
|
||||
let following = subscriptions
|
||||
.filter((relation) => {
|
||||
@ -415,7 +429,7 @@ export default class Profile extends React.Component {
|
||||
<UserEntry
|
||||
key={relation.id}
|
||||
user={relation.user}
|
||||
button={button}
|
||||
button={this.props.external ? null : button}
|
||||
onClick={() => {
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
@ -486,7 +500,7 @@ export default class Profile extends React.Component {
|
||||
<UserEntry
|
||||
key={relation.id}
|
||||
user={relation.owner}
|
||||
button={button}
|
||||
button={this.props.external ? null : button} //FOR TARA: clicking on a following/follower person errors if you are in the external view. I recommend making it check if this.props.external, in which case do a link instead (wrap it with an <a href={urlHere}> tag). See SlatePreviewBlocks for an example if you need one
|
||||
onClick={() => {
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
@ -499,12 +513,37 @@ export default class Profile extends React.Component {
|
||||
);
|
||||
});
|
||||
|
||||
let total = 0;
|
||||
for (let slate of data.slates) {
|
||||
total += slate.data.objects.length;
|
||||
}
|
||||
//FOR TARA: I wanted to introduce you to the "reduce" function. It basically walks through all the items in an array and reduces it down to one "summary" value
|
||||
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
|
||||
//This allows you to quickly take the sum of the lengths of the objects in each slate for example, or get the maximum value in an array
|
||||
//It isn't necessary to use since you can usually use a for loop instead, but good to know in case you encounter it later
|
||||
// let total = 0;
|
||||
// for (let slate of data.slates) {
|
||||
// total += slate.data.objects.length;
|
||||
// }
|
||||
let total = data.slates.reduce((total, slate) => {
|
||||
return total + slate.data?.objects?.length || 0; //FOR TARA: should use optional chaining and add a default value of 0 in case there are some slates where slate.data or slate.data.objects is null
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<GlobalCarousel
|
||||
carouselType="data"
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
resources={this.props.resources}
|
||||
viewer={this.props.viewer}
|
||||
objects={
|
||||
this.state.fileTab === 0
|
||||
? this.props.creator.library[0].children
|
||||
: this.state.fileTab === 1
|
||||
? this.state.publicFiles
|
||||
: this.state.pseudoPrivateFiles
|
||||
}
|
||||
isOwner={isOwner}
|
||||
onAction={this.props.onAction}
|
||||
mobile={this.props.mobile}
|
||||
external={this.props.external}
|
||||
/>
|
||||
<div css={STYLES_PROFILE_BACKGROUND}>
|
||||
<div css={STYLES_PROFILE_INFO}>
|
||||
<div
|
||||
@ -513,7 +552,29 @@ export default class Profile extends React.Component {
|
||||
/>
|
||||
<div css={STYLES_INFO}>
|
||||
<div css={STYLES_NAME}>{Strings.getPresentationName(data)}</div>
|
||||
<div css={STYLES_BUTTON}>{this.props.buttons}</div>
|
||||
{!isOwner && !this.props.external && (
|
||||
<div css={STYLES_BUTTON}>
|
||||
{this.state.isFollowing ? (
|
||||
<ButtonSecondary
|
||||
onClick={(e) => {
|
||||
this.setState({ isFollowing: false });
|
||||
this._handleFollow(e, this.props.creator.id);
|
||||
}}
|
||||
>
|
||||
Unfollow
|
||||
</ButtonSecondary>
|
||||
) : (
|
||||
<ButtonPrimary
|
||||
onClick={(e) => {
|
||||
this.setState({ isFollowing: true });
|
||||
this._handleFollow(e, this.props.creator.id);
|
||||
}}
|
||||
>
|
||||
Follow
|
||||
</ButtonPrimary>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{data.data.body ? (
|
||||
<div css={STYLES_DESCRIPTION}>
|
||||
<ProcessedText text={data.data.body} />
|
||||
@ -522,14 +583,13 @@ export default class Profile extends React.Component {
|
||||
<div css={STYLES_STATS}>
|
||||
<div css={STYLES_STAT}>
|
||||
<div style={{ fontFamily: `${Constants.font.text}` }}>
|
||||
{total}{" "}
|
||||
<span style={{ color: `${Constants.system.darkGray}` }}>Public data</span>
|
||||
{total} <span style={{ color: `${Constants.system.darkGray}` }}>Files</span>
|
||||
</div>
|
||||
</div>
|
||||
<div css={STYLES_STAT}>
|
||||
<div style={{ fontFamily: `${Constants.font.text}` }}>
|
||||
{data.slates.length}{" "}
|
||||
<span style={{ color: `${Constants.system.darkGray}` }}>Public slates</span>
|
||||
<span style={{ color: `${Constants.system.darkGray}` }}>Slates</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -558,7 +618,7 @@ export default class Profile extends React.Component {
|
||||
<div style={{ display: `flex` }}>
|
||||
{isOwner && (
|
||||
<SecondaryTabGroup
|
||||
tabs={["All files", "Everyone can see", "Link access only"]}
|
||||
tabs={["All files", "Everyone can view", "Link access only"]}
|
||||
value={this.state.fileTab}
|
||||
onChange={(value) => this.setState({ fileTab: value })}
|
||||
style={{ margin: "0 0 24px 0" }}
|
||||
@ -574,6 +634,8 @@ export default class Profile extends React.Component {
|
||||
style={{ margin: "0 0 24px 0", justifyContent: "flex-end" }}
|
||||
/>
|
||||
</div>
|
||||
{/* FOR TARA: I want you to see how you can compress these following lines 625-713. There is a lot of repeated code here and you don't need to be restating DataView and EmptyState so many times.
|
||||
You could just change what is passed into items for DataView (or even make a variable named items at the render method, assign it to the right thing, then just pass it into items) and what is displayed in the text for EmptyState */}
|
||||
{isOwner ? (
|
||||
<div>
|
||||
{this.state.fileTab === 0 ? (
|
||||
@ -655,7 +717,9 @@ export default class Profile extends React.Component {
|
||||
) : (
|
||||
<EmptyState>
|
||||
<FileTypeGroup />
|
||||
<div style={{ marginTop: 24 }}>Drag and drop files into Slate to upload</div>
|
||||
<div style={{ marginTop: 24 }}>
|
||||
This user does not have any public files yet
|
||||
</div>
|
||||
</EmptyState>
|
||||
)}
|
||||
</div>
|
||||
@ -665,7 +729,7 @@ export default class Profile extends React.Component {
|
||||
{this.state.tab === 1 ? (
|
||||
<div>
|
||||
<SecondaryTabGroup
|
||||
tabs={["Public Slates", "Following Slates"]}
|
||||
tabs={["Slates", "Following"]}
|
||||
value={this.state.slateTab}
|
||||
onChange={(value) => this.setState({ slateTab: value })}
|
||||
style={{ margin: "0 0 24px 0" }}
|
||||
@ -675,7 +739,7 @@ export default class Profile extends React.Component {
|
||||
{this.state.publicSlates && this.state.publicSlates.length ? (
|
||||
<SlatePreviewBlocks
|
||||
isOwner={this.props.isOwner}
|
||||
external={this.props.onAction ? false : true}
|
||||
external={this.props.external}
|
||||
slates={this.state.publicSlates}
|
||||
username={data.username}
|
||||
onAction={this.props.onAction}
|
||||
@ -683,24 +747,24 @@ export default class Profile extends React.Component {
|
||||
) : (
|
||||
<EmptyState>
|
||||
<SVG.Slate height="24px" style={{ marginBottom: 24 }} />
|
||||
There aren't any public slates yet.
|
||||
This user does not have any public slates yet
|
||||
</EmptyState>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
{this.state.slateTab === 1 ? (
|
||||
<div>
|
||||
{this.state.followingSlates && this.state.followingSlates.length ? (
|
||||
{followingSlates?.length ? ( //FOR TARA: with the optional chaining (see the link I sent you), you can replace "followingSlates && followingSlates.length" with just followingSlates?.length
|
||||
<SlatePreviewBlocks
|
||||
isOwner={false}
|
||||
external={this.props.onAction ? false : true}
|
||||
slates={this.state.followingSlates}
|
||||
external={this.props.external}
|
||||
slates={followingSlates}
|
||||
onAction={this.props.onAction}
|
||||
/>
|
||||
) : (
|
||||
<EmptyState>
|
||||
<SVG.Slate height="24px" style={{ marginBottom: 24 }} />
|
||||
There aren't any following slates yet.
|
||||
This user is not following any slates yet
|
||||
</EmptyState>
|
||||
)}
|
||||
</div>
|
||||
@ -722,7 +786,7 @@ export default class Profile extends React.Component {
|
||||
) : (
|
||||
<EmptyState>
|
||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
||||
There isn't any following yet.
|
||||
This user is not following anyone yet
|
||||
</EmptyState>
|
||||
)}
|
||||
</div>
|
||||
@ -734,7 +798,7 @@ export default class Profile extends React.Component {
|
||||
) : (
|
||||
<EmptyState>
|
||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
||||
There aren't any followers yet.
|
||||
This user does not have any followers yet
|
||||
</EmptyState>
|
||||
)}
|
||||
</div>
|
||||
|
@ -10,6 +10,7 @@ import * as Events from "~/common/custom-events";
|
||||
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
|
||||
import CTATransition from "~/components/core/CTATransition";
|
||||
|
||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
||||
import { CheckBox } from "~/components/system/components/CheckBox";
|
||||
import { css } from "@emotion/react";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
@ -1105,6 +1106,17 @@ export class SlateLayout extends React.Component {
|
||||
let unit = this.state.unit;
|
||||
return (
|
||||
<div>
|
||||
<GlobalCarousel
|
||||
carouselType="slate"
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
viewer={this.props.viewer}
|
||||
objects={this.state.items}
|
||||
current={this.props.current}
|
||||
onAction={this.props.onAction}
|
||||
mobile={this.props.mobile}
|
||||
isOwner={this.props.isOwner}
|
||||
external={this.props.external}
|
||||
/>
|
||||
{this.props.isOwner ? (
|
||||
this.state.editing ? (
|
||||
<div
|
||||
|
@ -11,6 +11,7 @@ import { Alert } from "~/components/core/Alert";
|
||||
import CarouselSidebarSlate from "~/components/core/CarouselSidebarSlate";
|
||||
import CarouselSidebarData from "~/components/core/CarouselSidebarData";
|
||||
import SlateMediaObject from "~/components/core/SlateMediaObject";
|
||||
import { CompressedPixelFormat } from "three";
|
||||
|
||||
const STYLES_ROOT = css`
|
||||
position: fixed;
|
||||
@ -124,6 +125,12 @@ export class GlobalCarousel extends React.Component {
|
||||
window.removeEventListener("slate-global-close-carousel", this._handleClose);
|
||||
};
|
||||
|
||||
componentDidUpdate = (prevProps) => {
|
||||
if (this.state.index >= (this.props.objects?.length || 0)) {
|
||||
this._handleClose();
|
||||
}
|
||||
};
|
||||
|
||||
_handleKeyDown = (e) => {
|
||||
const inputs = document.querySelectorAll("input");
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
@ -155,20 +162,16 @@ export class GlobalCarousel extends React.Component {
|
||||
};
|
||||
|
||||
_handleOpen = (e) => {
|
||||
let carouselType =
|
||||
!this.props.current ||
|
||||
(this.props.current &&
|
||||
(this.props.current.decorator === "FOLDER" || this.props.current.decorator === "ACTIVITY"))
|
||||
? "data"
|
||||
: "slate";
|
||||
if (e.detail.index < 0 || e.detail.index >= this.props.objects.length) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
carouselType: carouselType,
|
||||
visible: true,
|
||||
index: e.detail.index || 0,
|
||||
baseURL: window.location.pathname,
|
||||
});
|
||||
if (carouselType === "slate" && this.props.current.data?.objects) {
|
||||
const data = this.props.current.data.objects[e.detail.index];
|
||||
if (this.props.carouselType === "slate") {
|
||||
const data = this.props.objects[e.detail.index];
|
||||
window.history.replaceState(
|
||||
{ ...window.history.state, cid: data.cid },
|
||||
null,
|
||||
@ -193,31 +196,13 @@ export class GlobalCarousel extends React.Component {
|
||||
|
||||
_handleNext = () => {
|
||||
let index = this.state.index + 1;
|
||||
if (
|
||||
this.state.carouselType === "slate" &&
|
||||
this.props.current.data &&
|
||||
this.props.current.data.objects
|
||||
) {
|
||||
if (index >= this.props.current.data.objects.length) {
|
||||
index = 0;
|
||||
}
|
||||
} else if (
|
||||
this.state.carouselType === "data" &&
|
||||
this.props.viewer &&
|
||||
this.props.viewer.library
|
||||
) {
|
||||
if (index >= this.props.viewer.library[0].children.length) {
|
||||
index = 0;
|
||||
}
|
||||
if (index >= this.props.objects.length) {
|
||||
index = 0;
|
||||
}
|
||||
this.setState({ index });
|
||||
|
||||
if (
|
||||
this.state.carouselType === "slate" &&
|
||||
this.state.baseURL &&
|
||||
this.props.current.data?.objects
|
||||
) {
|
||||
const data = this.props.current.data.objects[index];
|
||||
if (this.props.carouselType === "slate" && this.state.baseURL) {
|
||||
const data = this.props.objects[index];
|
||||
window.history.replaceState(
|
||||
{ ...window.history.state, cid: data.cid },
|
||||
"",
|
||||
@ -229,28 +214,12 @@ export class GlobalCarousel extends React.Component {
|
||||
_handlePrevious = () => {
|
||||
let index = this.state.index - 1;
|
||||
if (index < 0) {
|
||||
if (
|
||||
this.state.carouselType === "slate" &&
|
||||
this.props.current.data &&
|
||||
this.props.current.data.objects
|
||||
) {
|
||||
index = this.props.current.data.objects.length - 1;
|
||||
} else if (
|
||||
this.state.carouselType === "data" &&
|
||||
this.props.viewer &&
|
||||
this.props.viewer.library
|
||||
) {
|
||||
index = this.props.viewer.library[0].children.length - 1;
|
||||
}
|
||||
index = this.props.objects.length - 1;
|
||||
}
|
||||
this.setState({ index });
|
||||
|
||||
if (
|
||||
this.state.carouselType === "slate" &&
|
||||
this.state.baseURL &&
|
||||
this.props.current.data?.objects
|
||||
) {
|
||||
const data = this.props.current.data.objects[index];
|
||||
if (this.props.carouselType === "slate" && this.state.baseURL) {
|
||||
const data = this.props.objects[index];
|
||||
window.history.replaceState(
|
||||
{ ...window.history.state, cid: data.cid },
|
||||
"",
|
||||
@ -260,9 +229,9 @@ export class GlobalCarousel extends React.Component {
|
||||
};
|
||||
|
||||
_handleSave = async (details, index) => {
|
||||
if (this.state.carouselType === "slate") {
|
||||
if (this.props.viewer.id !== this.props.current.data.ownerId || this.props.external) return;
|
||||
let objects = this.props.current.data.objects;
|
||||
let objects = this.props.objects;
|
||||
if (!this.props.isOwner || this.props.external) return;
|
||||
if (this.props.carouselType === "slate") {
|
||||
objects[index] = { ...objects[index], ...details };
|
||||
const response = await Actions.updateSlate({
|
||||
id: this.props.current.id,
|
||||
@ -270,9 +239,7 @@ export class GlobalCarousel extends React.Component {
|
||||
});
|
||||
Events.hasError(response);
|
||||
}
|
||||
if (this.state.carouselType === "data") {
|
||||
if (this.props.external) return;
|
||||
let objects = this.props.viewer.library[0].children;
|
||||
if (this.props.carouselType === "data") {
|
||||
objects[index] = { ...objects[index], ...details };
|
||||
const response = await Actions.updateData({
|
||||
id: this.props.viewer.id,
|
||||
@ -283,35 +250,16 @@ export class GlobalCarousel extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.visible || !this.state.carouselType || this.state.index < 0) {
|
||||
if (!this.state.visible || !this.props.carouselType || this.state.index < 0) {
|
||||
return null;
|
||||
}
|
||||
let data;
|
||||
let isOwner;
|
||||
let data = this.props.objects[this.state.index];
|
||||
let isOwner = this.props.isOwner;
|
||||
let isRepost;
|
||||
if (
|
||||
this.state.carouselType === "slate" &&
|
||||
this.props.current?.data?.objects?.length &&
|
||||
this.state.index < this.props.current.data.objects.length
|
||||
) {
|
||||
data = this.props.current.data.objects[this.state.index];
|
||||
data.cid = Strings.urlToCid(data.url);
|
||||
if (this.props.carouselType === "slate") {
|
||||
isRepost = this.props.external ? false : this.props.current.data.ownerId !== data.ownerId;
|
||||
isOwner = this.props.external
|
||||
? false
|
||||
: this.props.viewer.id === this.props.current.data.ownerId;
|
||||
} else if (
|
||||
this.state.carouselType === "data" &&
|
||||
this.props.viewer?.library[0]?.children?.length &&
|
||||
this.state.index < this.props.viewer.library[0].children.length
|
||||
) {
|
||||
data = this.props.viewer.library[0].children[this.state.index];
|
||||
} else if (this.props.carouselType === "data") {
|
||||
data.url = Strings.getCIDGatewayURL(data.cid);
|
||||
isOwner = this.props.external ? false : true;
|
||||
}
|
||||
if (!data) {
|
||||
this._handleClose();
|
||||
return null;
|
||||
}
|
||||
let slide = <SlateMediaObject data={data} />;
|
||||
return (
|
||||
@ -383,14 +331,14 @@ export class GlobalCarousel extends React.Component {
|
||||
</span>
|
||||
</div>
|
||||
<span css={STYLES_MOBILE_HIDDEN}>
|
||||
{this.state.carouselType === "data" ? (
|
||||
{this.props.carouselType === "data" ? (
|
||||
<CarouselSidebarData
|
||||
viewer={this.props.viewer}
|
||||
display={this.state.showSidebar ? "block" : "none"}
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
onClose={this._handleClose}
|
||||
key={data.id}
|
||||
slates={this.props.slates}
|
||||
slates={this.props.viewer?.slates || []}
|
||||
onAction={this.props.onAction}
|
||||
resources={this.props.resources}
|
||||
data={data}
|
||||
@ -407,7 +355,7 @@ export class GlobalCarousel extends React.Component {
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
current={this.props.current}
|
||||
key={data.id}
|
||||
slates={this.props.slates}
|
||||
slates={this.props.viewer?.slates || []}
|
||||
onClose={this._handleClose}
|
||||
onAction={this.props.onAction}
|
||||
data={data}
|
||||
@ -416,7 +364,6 @@ export class GlobalCarousel extends React.Component {
|
||||
isOwner={isOwner}
|
||||
isRepost={isRepost}
|
||||
index={this.state.index}
|
||||
external={this.props.external}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
|
@ -35,7 +35,6 @@ export default class ProfilePage extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
console.log(this.props.creator);
|
||||
const title = this.props.creator ? `${this.props.creator.username}` : "404";
|
||||
const url = `https://slate.host/${title}`;
|
||||
const description = this.props.creator.data.body;
|
||||
@ -52,7 +51,7 @@ export default class ProfilePage extends React.Component {
|
||||
<WebsitePrototypeWrapper title={title} description={description} url={url} image={image}>
|
||||
<WebsitePrototypeHeader />
|
||||
<div css={STYLES_ROOT}>
|
||||
<Profile {...this.props} buttons={buttons} />
|
||||
<Profile {...this.props} buttons={buttons} external />
|
||||
</div>
|
||||
{this.state.visible && (
|
||||
<div>
|
||||
|
@ -13,7 +13,6 @@ import { ViewAllButton } from "~/components/core/ViewAll";
|
||||
import { SlateLayout } from "~/components/core/SlateLayout";
|
||||
import { SlateLayoutMobile } from "~/components/core/SlateLayoutMobile";
|
||||
import { GlobalModal } from "~/components/system/components/GlobalModal";
|
||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
||||
|
||||
import ProcessedText from "~/components/core/ProcessedText";
|
||||
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
||||
@ -317,6 +316,7 @@ export default class SlatePage extends React.Component {
|
||||
items={objects}
|
||||
fileNames={layouts && layouts.ver === "2.0" ? layouts.fileNames : false}
|
||||
onSelect={this._handleSelect}
|
||||
external
|
||||
/>
|
||||
) : (
|
||||
<SlateLayout
|
||||
@ -335,7 +335,6 @@ export default class SlatePage extends React.Component {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<GlobalCarousel external current={this.props.slate} mobile={this.props.mobile} />
|
||||
<GlobalModal />
|
||||
{this.state.visible && (
|
||||
<div>
|
||||
|
@ -4,6 +4,7 @@ import * as SVG from "~/common/svg";
|
||||
import { ButtonPrimary } from "~/components/system/components/Buttons";
|
||||
import { FileTypeGroup } from "~/components/core/FileTypeIcon";
|
||||
import { PrimaryTabGroup, SecondaryTabGroup } from "~/components/core/TabGroup";
|
||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
||||
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import DataView from "~/components/core/DataView";
|
||||
@ -45,6 +46,16 @@ export default class SceneFilesFolder extends React.Component {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<GlobalCarousel
|
||||
carouselType="data"
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
resources={this.props.resources}
|
||||
viewer={this.props.viewer}
|
||||
objects={this.props.viewer?.library[0]?.children || []}
|
||||
onAction={this.props.onAction}
|
||||
mobile={this.props.mobile}
|
||||
isOwner={true}
|
||||
/>
|
||||
<DataMeter
|
||||
stats={this.props.viewer.stats}
|
||||
style={{ marginBottom: 64 }}
|
||||
|
@ -3,19 +3,12 @@ import * as Actions from "~/common/actions";
|
||||
import * as SVG from "~/common/svg";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import Profile from "~/components/core/Profile";
|
||||
import EmptyState from "~/components/core/EmptyState";
|
||||
|
||||
const STYLES_BUTTONS = css`
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const STYLES_LOADER = css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -75,7 +68,7 @@ export default class SceneProfile extends React.Component {
|
||||
window.history.replaceState(window.history.state, "A slate user", `/${targetUser.username}`);
|
||||
|
||||
this.props.onUpdateData({ data: targetUser });
|
||||
this.setState({ profile: targetUser });
|
||||
this.setState({ profile: targetUser }); //martina: keep this here? or use the data itself rather than using this?
|
||||
};
|
||||
|
||||
render() {
|
||||
@ -98,58 +91,15 @@ export default class SceneProfile extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
return <ProfilePage {...this.props} data={this.state.profile} />;
|
||||
}
|
||||
}
|
||||
|
||||
class ProfilePage extends React.Component {
|
||||
state = {
|
||||
isFollowing: !!this.props.viewer.subscriptions.filter((entry) => {
|
||||
return entry.target_user_id === this.props.data.id;
|
||||
}).length,
|
||||
};
|
||||
|
||||
componentDidUpdate = (prevProps) => {
|
||||
if (
|
||||
this.props.data.id !== prevProps.data.id ||
|
||||
this.props.viewer.subscriptions !== prevProps.viewer.subscriptions
|
||||
) {
|
||||
this.setState({
|
||||
isFollowing: !!this.props.viewer.subscriptions.filter((entry) => {
|
||||
return entry.target_user_id === this.props.data.id;
|
||||
}).length,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_handleFollow = async () => {
|
||||
this.setState({ isFollowing: !this.state.isFollowing });
|
||||
await Actions.createSubscription({
|
||||
userId: this.props.data.id,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
let buttons = (
|
||||
<div css={STYLES_BUTTONS}>
|
||||
{this.state.isFollowing ? (
|
||||
<ButtonSecondary onClick={this._handleFollow}>Unfollow</ButtonSecondary>
|
||||
) : (
|
||||
<ButtonPrimary onClick={this._handleFollow}>Follow</ButtonPrimary>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
//FOR TARA: this is the change that'll allow it to update "automatically" if you're viewing your own profile and make changes
|
||||
//if you are viewing your own profile (aka this.state.profile.id === this.props.viewer.id), it'll set it to viewer directly, rather than a stale copy of viewer
|
||||
return (
|
||||
<ScenePage style={{ padding: `0` }}>
|
||||
<Profile
|
||||
{...this.props}
|
||||
onAction={this.props.onAction}
|
||||
creator={this.props.data}
|
||||
sceneId={this.props.sceneId}
|
||||
buttons={this.props.viewer.username === this.props.data.username ? null : buttons}
|
||||
isOwner={this.props.viewer.username === this.props.data.username}
|
||||
/>
|
||||
</ScenePage>
|
||||
<Profile
|
||||
{...this.props}
|
||||
creator={
|
||||
this.state.profile.id === this.props.viewer.id ? this.props.viewer : this.state.profile
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user