From e4124f28d1c7bd4d17d2a42f9f853ce762a5d568 Mon Sep 17 00:00:00 2001 From: Martina Date: Wed, 20 Jan 2021 13:50:29 -0800 Subject: [PATCH] reworked profile public files --- common/strings.js | 2 +- common/user-behaviors.js | 5 + common/utilities.js | 24 ++ components/core/CarouselSidebarData.js | 2 +- components/core/Profile.js | 478 ++++++++++--------------- components/core/SlateLayout.js | 103 +++--- pages/_/profile.js | 2 +- pages/_/slate.js | 1 + scenes/SceneProfile.js | 2 + scenes/SceneSlate.js | 1 + 10 files changed, 285 insertions(+), 335 deletions(-) create mode 100644 common/utilities.js diff --git a/common/strings.js b/common/strings.js index 6fc7e856..252eb8a7 100644 --- a/common/strings.js +++ b/common/strings.js @@ -176,7 +176,7 @@ export const bytesToSize = (bytes, decimals = 2) => { const k = 1024; const dm = decimals < 0 ? 0 : decimals; - const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); diff --git a/common/user-behaviors.js b/common/user-behaviors.js index 500db3ef..fe253b76 100644 --- a/common/user-behaviors.js +++ b/common/user-behaviors.js @@ -10,8 +10,13 @@ import * as Events from "~/common/custom-events"; import Cookies from "universal-cookie"; import JSZip from "jszip"; + import { saveAs } from "file-saver"; +//NOTE(martina): this file is for utility *API-calling* functions +//For non API related utility functions, see common/utilities.js +//And for uploading related utility functions, see common/file-utilities.js + const cookies = new Cookies(); export const authenticate = async (state) => { diff --git a/common/utilities.js b/common/utilities.js new file mode 100644 index 00000000..dfe99039 --- /dev/null +++ b/common/utilities.js @@ -0,0 +1,24 @@ +//NOTE(martina): this file is for utility functions that do not involve API calls +//For API related utility functions, see common/user-behaviors.js +//And for uploading related utility functions, see common/file-utilities.js + +export const getPublicAndPrivateFiles = ({ viewer }) => { + let publicFileIds = []; + for (let slate of viewer.slates) { + if (slate.data.public) { + publicFileIds.push(...slate.data.objects.map((obj) => obj.id)); + } + } + + let publicFiles = []; + let privateFiles = []; + let library = viewer.library[0]?.children || []; + for (let file of library) { + if (file.public || publicFileIds.includes(file.id)) { + publicFiles.push(file); + } else { + privateFiles.push(file); + } + } + return { publicFiles, privateFiles }; +}; diff --git a/components/core/CarouselSidebarData.js b/components/core/CarouselSidebarData.js index bcf0b887..158a65cd 100644 --- a/components/core/CarouselSidebarData.js +++ b/components/core/CarouselSidebarData.js @@ -489,7 +489,7 @@ export default class CarouselSidebarData extends React.Component {
this._handleCopy(url, "gatewayUrlCopying")}> - {this.state.loading === "gatewayUrlCopying" ? "Copied!" : "Copy external URL"} + {this.state.loading === "gatewayUrlCopying" ? "Copied!" : "Copy file URL"}
diff --git a/components/core/Profile.js b/components/core/Profile.js index 50b6cb93..8bf21528 100644 --- a/components/core/Profile.js +++ b/components/core/Profile.js @@ -3,6 +3,7 @@ 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 { GlobalCarousel } from "~/components/system/components/GlobalCarousel"; import { css } from "@emotion/react"; @@ -235,19 +236,15 @@ function UserEntry({ user, button, onClick, message, external, url }) { export default class Profile extends React.Component { _ref = null; - lastLength = null; state = { tab: 1, view: 0, - fileTab: 0, slateTab: 0, peerTab: 0, copyValue: "", contextMenu: null, - publicSlates: [], publicFiles: [], - pseudoPrivateFiles: [], isFollowing: this.props.external ? false : !!this.props.viewer.subscriptions.filter((entry) => { @@ -255,70 +252,19 @@ export default class Profile extends React.Component { }).length, }; - componentDidMount = async () => { - await this.filterByVisibility(); + componentDidMount = () => { + this.filterByVisibility(); }; - componentDidUpdate = async (prevProps) => { - if (this.props.creator?.id !== prevProps.creator?.id) { - this.setState({ - tab: 1, - view: 0, - fileTab: 0, - slateTab: 0, - peerTab: 0, - isFollowing: this.props.external - ? false - : !!this.props.viewer.subscriptions.filter((entry) => { - return entry.target_user_id === this.props.creator.id; - }).length, - }); - } - if ( - this.lastLength != null && - this.props.creator?.library[0].children.length !== this.lastLength - ) { - this.filterByVisibility(); - } - }; - - filterByVisibility = async () => { + filterByVisibility = () => { let publicFiles = []; - let pseudoPrivateFiles = []; - let files = this.props.creator.library[0].children; - let publicSlates = - this.props.creator.username === this.props.viewer?.username - ? this.props.creator.slates.filter((slate) => { - return slate.data.public === true; - }) - : this.props.creator.slates; - - let publicSlateFiles = publicSlates - .reduce((acc, curr) => { - return acc.concat(curr.data.objects); - }, []) - .reduce((acc, curr) => { - return acc.concat(curr.cid); - }, []) - .reduce((acc, curr) => { - if (acc.indexOf(curr) === -1) { - acc.push(curr); - } - return acc; - }, []); - for (let file of files) { - if (file.public === true || publicSlateFiles.indexOf(file.cid) != -1) { - publicFiles.push(file); - } else { - pseudoPrivateFiles.push(file); - } + if (this.props.isOwner) { + const res = Utilities.getPublicAndPrivateFiles({ viewer: this.props.creator }); + publicFiles = res.publicFiles; + } else { + publicFiles = this.props.creator.library[0].children; } - this.setState({ - publicSlates: publicSlates, - publicFiles: publicFiles, - pseudoPrivateFiles: pseudoPrivateFiles, - }); - this.lastLength = this.props.creator?.library[0].children.length; + this.setState({ publicFiles: publicFiles }); }; _handleCopy = (e, value) => { @@ -352,182 +298,182 @@ export default class Profile extends React.Component { }; render() { - let isOwner = this.props.creator.username === this.props.viewer?.username; + let isOwner = this.props.isOwner; let creator = this.props.creator; + let username = this.state.slateTab === 0 ? creator.username : null; let subscriptions = this.props.creator.subscriptions || []; let subscribers = this.props.creator.subscribers || []; - let dataItems = !isOwner - ? this.state.publicFiles - : this.state.fileTab === 0 - ? this.props.creator.library[0].children - : this.state.fileTab === 1 - ? this.state.publicFiles - : this.state.pseudoPrivateFiles; let exploreSlates = this.props.exploreSlates; - let followingSlates = subscriptions - .filter((relation) => { - return !!relation.target_slate_id; - }) - .map((relation) => relation.slate); + let slates = []; + if (this.state.tab === 1) { + if (this.state.slateTab === 0) { + slates = isOwner + ? creator.slates.filter((slate) => slate.data.public === true) + : creator.slates; + } else { + slates = subscriptions + .filter((relation) => { + return !!relation.target_slate_id; + }) + .map((relation) => relation.slate); + } + } - let slates = - this.state.slateTab === 0 - ? this.state.publicSlates - : followingSlates?.length - ? followingSlates - : null; - let username = this.state.slateTab === 0 ? creator.username : null; - - let following = subscriptions - .filter((relation) => { - return !!relation.target_user_id; - }) - .map((relation) => { - let button = ( -
this._handleClick(e, relation.id)}> - - {this.state.contextMenu === relation.id ? ( - this._handleClick(e, relation.id)} - > - {relation.target_user_id === this.props.viewer?.id ? ( - - this._handleCopy(e, `https://slate.host/${relation.user.username}`), - }, - ]} - /> - ) : ( - - this._handleCopy(e, `https://slate.host/${relation.user.username}`), - }, - { - text: this.props.viewer?.subscriptions.filter((subscription) => { - return subscription.target_user_id === relation.target_user_id; - }).length - ? "Unfollow" - : "Follow", - onClick: this.props.viewer - ? (e) => this._handleFollow(e, relation.target_user_id) - : () => this.setState({ visible: true }), - }, - ]} - /> - )} - - ) : null} -
- ); - return ( - { - this.props.onAction({ - type: "NAVIGATE", - value: this.props.sceneId, - scene: "PROFILE", - data: relation.user, - }); - }} - external={this.props.external} - url={`/${relation.user.username}`} - /> - ); - }); - - let followers = subscribers.map((relation) => { - let button = ( -
this._handleClick(e, relation.id)}> - - {this.state.contextMenu === relation.id ? ( - this._handleClick(e, relation.id)} - > - {relation.owner_user_id === this.props.viewer?.id ? ( - - this._handleCopy(e, `https://slate.host/${relation.owner.username}`), - }, - ]} - /> - ) : ( - - this._handleCopy(e, `https://slate.host/${relation.owner.username}`), - }, - { - text: this.props.viewer?.subscriptions.filter((subscription) => { - return subscription.target_user_id === relation.owner_user_id; - }).length - ? "Unfollow" - : "Follow", - onClick: this.props.viewer - ? (e) => this._handleFollow(e, relation.owner_user_id) - : () => this.setState({ visible: true }), - }, - ]} - /> - )} - - ) : null} -
- ); - return ( - { - this.props.onAction({ - type: "NAVIGATE", - value: this.props.sceneId, - scene: "PROFILE", - data: relation.owner, - }); - }} - external={this.props.external} - url={`https://slate.host/${relation.owner.username}`} - /> - ); - }); + let peers = []; + if (this.state.tab === 2) { + if (this.state.peerTab === 0) { + peers = subscriptions + .filter((relation) => { + return !!relation.target_user_id; + }) + .map((relation) => { + let button = ( +
this._handleClick(e, relation.id)}> + + {this.state.contextMenu === relation.id ? ( + this._handleClick(e, relation.id)} + > + {relation.target_user_id === this.props.viewer?.id ? ( + + this._handleCopy(e, `https://slate.host/${relation.user.username}`), + }, + ]} + /> + ) : ( + + this._handleCopy(e, `https://slate.host/${relation.user.username}`), + }, + { + text: this.props.viewer?.subscriptions.filter((subscription) => { + return subscription.target_user_id === relation.target_user_id; + }).length + ? "Unfollow" + : "Follow", + onClick: this.props.viewer + ? (e) => this._handleFollow(e, relation.target_user_id) + : () => this.setState({ visible: true }), + }, + ]} + /> + )} + + ) : null} +
+ ); + return ( + { + this.props.onAction({ + type: "NAVIGATE", + value: this.props.sceneId, + scene: "PROFILE", + data: relation.user, + }); + }} + external={this.props.external} + url={`/${relation.user.username}`} + /> + ); + }); + } else { + peers = subscribers.map((relation) => { + let button = ( +
this._handleClick(e, relation.id)}> + + {this.state.contextMenu === relation.id ? ( + this._handleClick(e, relation.id)} + > + {relation.owner_user_id === this.props.viewer?.id ? ( + + this._handleCopy(e, `https://slate.host/${relation.owner.username}`), + }, + ]} + /> + ) : ( + + this._handleCopy(e, `https://slate.host/${relation.owner.username}`), + }, + { + text: this.props.viewer?.subscriptions.filter((subscription) => { + return subscription.target_user_id === relation.owner_user_id; + }).length + ? "Unfollow" + : "Follow", + onClick: this.props.viewer + ? (e) => this._handleFollow(e, relation.owner_user_id) + : () => this.setState({ visible: true }), + }, + ]} + /> + )} + + ) : null} +
+ ); + return ( + { + this.props.onAction({ + type: "NAVIGATE", + value: this.props.sceneId, + scene: "PROFILE", + data: relation.owner, + }); + }} + external={this.props.external} + url={`https://slate.host/${relation.owner.username}`} + /> + ); + }); + } + } let total = creator.slates.reduce((total, slate) => { return total + slate.data?.objects?.length || 0; @@ -540,14 +486,8 @@ export default class Profile extends React.Component { 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} + objects={this.state.publicFiles} + isOwner={false} onAction={this.props.onAction} mobile={this.props.mobile} external={this.props.external} @@ -624,14 +564,6 @@ export default class Profile extends React.Component { {this.state.tab === 0 ? (
- {isOwner && ( - this.setState({ fileTab: value })} - style={{ margin: "0 0 24px 0" }} - /> - )} , @@ -642,23 +574,19 @@ export default class Profile extends React.Component { style={{ margin: "0 0 24px 0", justifyContent: "flex-end" }} />
- {!!dataItems.length ? ( + {this.state.publicFiles.length ? ( ) : ( -
- {isOwner - ? `Drag and drop files into Slate to upload` - : `This user does not have any public files yet`} -
+
This user does not have any public files yet
)}
@@ -710,30 +638,18 @@ export default class Profile extends React.Component { onChange={(value) => this.setState({ peerTab: value })} style={{ margin: "0 0 24px 0" }} /> - {this.state.peerTab === 0 ? ( -
- {following?.length ? ( - following - ) : ( - - - This user is not following anyone yet - - )} -
- ) : null} - {this.state.peerTab === 1 ? ( -
- {followers?.length ? ( - followers - ) : ( - - - This user does not have any followers yet - - )} -
- ) : null} +
+ {peers?.length ? ( + peers + ) : ( + + + {this.state.peerTab === 0 + ? "This user is not following anyone yet" + : "This user does not have any followers yet"} + + )} +
{ diff --git a/components/core/SlateLayout.js b/components/core/SlateLayout.js index 2d34474c..594d2f3f 100644 --- a/components/core/SlateLayout.js +++ b/components/core/SlateLayout.js @@ -374,57 +374,58 @@ export class SlateLayout extends React.Component { }; componentDidUpdate = async (prevProps) => { - if (prevProps.slateId !== this.props.slateId) { - //NOTE(martina): to handle when you navigate between two slates, so it registers the change properly - await this.setState({ show: false }); - let defaultLayout = this.props.layout ? this.props.defaultLayout : true; - let fileNames = this.props.fileNames; - let layout; - if (this.props.layout) { - layout = await this.repairLayout(this.props.items, { - defaultLayout, - fileNames, - layout: this.props.layout, - }); - if (layout) { - this.props.onSaveLayout( - { - ver: "2.0", - fileNames, - defaultLayout, - layout, - }, - true - ); - } else { - layout = this.props.layout; - } - } else { - layout = generateLayout(this.props.items); - await this.setState({ layout, items: this.props.items }); - layout = await this.calculateLayout(layout); - this.props.onSaveLayout( - { - ver: "2.0", - fileNames, - defaultLayout, - layout, - }, - true - ); - } - await this.setState({ - items: this.props.items, - layout, - prevLayouts: [], - zIndexMax: layout && layout.length ? Math.max(...layout.map((pos) => pos.z)) + 1 : 1, - fileNames, - defaultLayout, - editing: false, - show: true, - }); - this.calculateContainer(); - } else if (prevProps.items.length !== this.props.items.length) { + // if (prevProps.slateId !== this.props.slateId) { + // //NOTE(martina): to handle when you navigate between two slates, so it registers the change properly + // await this.setState({ show: false }); + // let defaultLayout = this.props.layout ? this.props.defaultLayout : true; + // let fileNames = this.props.fileNames; + // let layout; + // if (this.props.layout) { + // layout = await this.repairLayout(this.props.items, { + // defaultLayout, + // fileNames, + // layout: this.props.layout, + // }); + // if (layout) { + // this.props.onSaveLayout( + // { + // ver: "2.0", + // fileNames, + // defaultLayout, + // layout, + // }, + // true + // ); + // } else { + // layout = this.props.layout; + // } + // } else { + // layout = generateLayout(this.props.items); + // await this.setState({ layout, items: this.props.items }); + // layout = await this.calculateLayout(layout); + // this.props.onSaveLayout( + // { + // ver: "2.0", + // fileNames, + // defaultLayout, + // layout, + // }, + // true + // ); + // } + // await this.setState({ + // items: this.props.items, + // layout, + // prevLayouts: [], + // zIndexMax: layout && layout.length ? Math.max(...layout.map((pos) => pos.z)) + 1 : 1, + // fileNames, + // defaultLayout, + // editing: false, + // show: true, + // }); + // this.calculateContainer(); + // } + if (prevProps.items.length !== this.props.items.length) { //NOTE(martina): to handle when items are added / deleted from the slate, and recalculate the layout //NOTE(martina): if there is a case that allows simultaneous add / delete (aka modify but same length), this will not work. //would need to replace it with event listener + custom events diff --git a/pages/_/profile.js b/pages/_/profile.js index f10b84b5..372c123e 100644 --- a/pages/_/profile.js +++ b/pages/_/profile.js @@ -51,7 +51,7 @@ export default class ProfilePage extends React.Component {
- +
{this.state.visible && (
diff --git a/pages/_/slate.js b/pages/_/slate.js index 23483a6f..47dea714 100644 --- a/pages/_/slate.js +++ b/pages/_/slate.js @@ -322,6 +322,7 @@ export default class SlatePage extends React.Component { ); } diff --git a/scenes/SceneSlate.js b/scenes/SceneSlate.js index ea00504d..e1ca97b0 100644 --- a/scenes/SceneSlate.js +++ b/scenes/SceneSlate.js @@ -363,6 +363,7 @@ class SlatePage extends React.Component { ) : (