From b0052520f650ca702537d8f07b2db0e5ab6390e2 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Thu, 3 Sep 2020 09:33:58 +0200 Subject: [PATCH] WIP Profiles: likes + pages (#4802) --- .../src/app/pages/Profile2/AllSandboxes.tsx | 4 +- .../src/app/pages/Profile2/ContextMenu.tsx | 94 +++++++++ .../app/src/app/pages/Profile2/Header.tsx | 24 ++- .../src/app/pages/Profile2/LikedSandboxes.tsx | 119 ++++++++++++ .../src/app/pages/Profile2/ProfileCard.tsx | 11 +- .../src/app/pages/Profile2/SandboxCard.tsx | 2 +- .../app/src/app/pages/Profile2/constants.ts | 1 + packages/app/src/app/pages/Profile2/index.tsx | 180 ++++++------------ 8 files changed, 301 insertions(+), 134 deletions(-) create mode 100644 packages/app/src/app/pages/Profile2/ContextMenu.tsx create mode 100644 packages/app/src/app/pages/Profile2/LikedSandboxes.tsx create mode 100644 packages/app/src/app/pages/Profile2/constants.ts diff --git a/packages/app/src/app/pages/Profile2/AllSandboxes.tsx b/packages/app/src/app/pages/Profile2/AllSandboxes.tsx index 5f5b1e01fac..026fa95aac3 100644 --- a/packages/app/src/app/pages/Profile2/AllSandboxes.tsx +++ b/packages/app/src/app/pages/Profile2/AllSandboxes.tsx @@ -10,6 +10,7 @@ import { import css from '@styled-system/css'; import { useOvermind } from 'app/overmind'; import { SandboxCard, SkeletonCard } from './SandboxCard'; +import { SANDBOXES_PER_PAGE } from './constants'; export const AllSandboxes = ({ menuControls }) => { const { @@ -108,7 +109,7 @@ export const AllSandboxes = ({ menuControls }) => { }} > {isLoadingSandboxes - ? Array(15) + ? Array(SANDBOXES_PER_PAGE) .fill(true) .map((_, index) => ( // eslint-disable-next-line @@ -127,7 +128,6 @@ export const AllSandboxes = ({ menuControls }) => { ); }; -const SANDBOXES_PER_PAGE = 15; const Pagination = () => { const { actions: { diff --git a/packages/app/src/app/pages/Profile2/ContextMenu.tsx b/packages/app/src/app/pages/Profile2/ContextMenu.tsx new file mode 100644 index 00000000000..98e9c9f310b --- /dev/null +++ b/packages/app/src/app/pages/Profile2/ContextMenu.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { useOvermind } from 'app/overmind'; +import { sandboxUrl } from '@codesandbox/common/lib/utils/url-generator'; +import { useLocation } from 'react-router-dom'; +import { Menu } from '@codesandbox/components'; + +export const ContextMenu = ({ + visible, + setVisibility, + position, + sandboxId, +}) => { + const { + actions: { + editor: { forkExternalSandbox }, + profile: { + addFeaturedSandboxes, + removeFeaturedSandboxes, + changeSandboxPrivacy, + deleteSandboxClicked, + }, + }, + state: { + user: loggedInUser, + profile: { current: user }, + }, + } = useOvermind(); + const location = useLocation(); + + if (!visible) return null; + + const myProfile = loggedInUser?.username === user.username; + const likesPage = location.pathname === '/likes'; + + const isFeatured = user.featuredSandboxes + .map(sandbox => sandbox.id) + .includes(sandboxId); + + return ( + + {myProfile && !likesPage && ( + <> + {isFeatured ? ( + removeFeaturedSandboxes({ sandboxId })}> + Unpin sandbox + + ) : ( + addFeaturedSandboxes({ sandboxId })}> + Pin sandbox + + )} + + + )} + { + window.location.href = sandboxUrl({ id: sandboxId }); + }} + > + Open sandbox + + { + forkExternalSandbox({ sandboxId, openInNewWindow: true }); + }} + > + Fork sandbox + + {myProfile && !likesPage && !isFeatured && ( + <> + + changeSandboxPrivacy({ sandboxId, privacy: 1 })} + > + Make sandbox unlisted + + changeSandboxPrivacy({ sandboxId, privacy: 2 })} + > + Make sandbox private + + + deleteSandboxClicked(sandboxId)}> + Delete sandbox + + + )} + + ); +}; diff --git a/packages/app/src/app/pages/Profile2/Header.tsx b/packages/app/src/app/pages/Profile2/Header.tsx index 5918e263cb5..61ded2c8e43 100644 --- a/packages/app/src/app/pages/Profile2/Header.tsx +++ b/packages/app/src/app/pages/Profile2/Header.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { useOvermind } from 'app/overmind'; +import { useHistory, useLocation } from 'react-router-dom'; import LogoIcon from '@codesandbox/common/lib/components/Logo'; import { UserMenu } from 'app/pages/common/UserMenu'; - import { Stack, Input, Button, Link, Icon } from '@codesandbox/components'; import css from '@styled-system/css'; @@ -20,6 +20,10 @@ export const Header: React.FC = () => { }, } = useOvermind(); + const history = useHistory(); + const location = useLocation(); + if (!location.search) searchQueryChanged(''); + return ( { paddingLeft: 7, width: [0, 360, 480], })} - defaultValue={searchQuery} - onChange={event => searchQueryChanged(event.target.value)} + value={searchQuery} + onChange={(event: React.ChangeEvent) => { + const query = event.target.value; + searchQueryChanged(query); + + if (!query.length) { + history.push(''); + return; + } + + if (history.location.pathname === '/search') { + history.replace('/search?query=' + query); + } else { + history.push('/search?query=' + query); + } + }} /> diff --git a/packages/app/src/app/pages/Profile2/LikedSandboxes.tsx b/packages/app/src/app/pages/Profile2/LikedSandboxes.tsx new file mode 100644 index 00000000000..4a3cb766ca4 --- /dev/null +++ b/packages/app/src/app/pages/Profile2/LikedSandboxes.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { Grid, Column, Stack, Text, IconButton } from '@codesandbox/components'; +import css from '@styled-system/css'; +import { useOvermind } from 'app/overmind'; +import { SandboxCard, SkeletonCard } from './SandboxCard'; +import { SANDBOXES_PER_PAGE } from './constants'; + +export const LikedSandboxes = ({ menuControls }) => { + const { + actions: { + profile: { likedSandboxesPageChanged }, + }, + state: { + profile: { + current: { username }, + isLoadingSandboxes, + currentLikedSandboxesPage, + likedSandboxes, + }, + }, + } = useOvermind(); + + // explicitly call it on first page render + React.useEffect(() => { + if (currentLikedSandboxesPage === 1) likedSandboxesPageChanged(1); + }, [currentLikedSandboxesPage, likedSandboxesPageChanged]); + + const sandboxes = ( + (likedSandboxes[username] && + likedSandboxes[username][currentLikedSandboxesPage]) || + [] + ) + // only show public sandboxes on profile + .filter(sandbox => sandbox.privacy === 0); + + return ( + + + + Liked Sandboxes + + + + + {isLoadingSandboxes + ? Array(SANDBOXES_PER_PAGE) + .fill(true) + .map((_, index) => ( + // eslint-disable-next-line + + + + )) + : sandboxes.map((sandbox, index) => ( + + + + ))} + + + + ); +}; + +const Pagination = () => { + const { + actions: { + profile: { likedSandboxesPageChanged }, + }, + state: { + profile: { + currentLikedSandboxesPage, + current: { givenLikeCount }, + }, + }, + } = useOvermind(); + + const numberOfPages = Math.ceil(givenLikeCount / SANDBOXES_PER_PAGE); + + return ( + + ); +}; diff --git a/packages/app/src/app/pages/Profile2/ProfileCard.tsx b/packages/app/src/app/pages/Profile2/ProfileCard.tsx index 8ad812068e8..9079c033025 100644 --- a/packages/app/src/app/pages/Profile2/ProfileCard.tsx +++ b/packages/app/src/app/pages/Profile2/ProfileCard.tsx @@ -1,5 +1,7 @@ import React from 'react'; import { motion } from 'framer-motion'; +import { useOvermind } from 'app/overmind'; +import { Link as RouterLink } from 'react-router-dom'; import { Stack, Avatar, @@ -13,7 +15,6 @@ import { } from '@codesandbox/components'; import { TeamAvatar } from 'app/components/TeamAvatar'; import css from '@styled-system/css'; -import { useOvermind } from 'app/overmind'; export const ProfileCard = ({ defaultEditing = false }) => { const { @@ -99,13 +100,15 @@ export const ProfileCard = ({ defaultEditing = false }) => { - + {user.sandboxCount + user.templateCount} Sandboxes - + - {user.receivedLikeCount} Likes + + {user.givenLikeCount} Likes + )} diff --git a/packages/app/src/app/pages/Profile2/SandboxCard.tsx b/packages/app/src/app/pages/Profile2/SandboxCard.tsx index d62634a3b37..6189dac8baf 100644 --- a/packages/app/src/app/pages/Profile2/SandboxCard.tsx +++ b/packages/app/src/app/pages/Profile2/SandboxCard.tsx @@ -90,7 +90,7 @@ export const SandboxCard = ({ name="more" size={9} title="Sandbox actions" - onClick={event => onContextMenu(event, sandbox.id)} + onClick={event => onContextMenu(event, sandbox)} /> diff --git a/packages/app/src/app/pages/Profile2/constants.ts b/packages/app/src/app/pages/Profile2/constants.ts new file mode 100644 index 00000000000..710ccc51fa1 --- /dev/null +++ b/packages/app/src/app/pages/Profile2/constants.ts @@ -0,0 +1 @@ +export const SANDBOXES_PER_PAGE = 15; diff --git a/packages/app/src/app/pages/Profile2/index.tsx b/packages/app/src/app/pages/Profile2/index.tsx index 0a45f9958b1..bc8cb6183fa 100644 --- a/packages/app/src/app/pages/Profile2/index.tsx +++ b/packages/app/src/app/pages/Profile2/index.tsx @@ -14,9 +14,9 @@ import React from 'react'; import { useOvermind } from 'app/overmind'; -import { sandboxUrl } from '@codesandbox/common/lib/utils/url-generator'; -import { ThemeProvider, Stack, Menu, Element } from '@codesandbox/components'; +import { ThemeProvider, Stack, Element } from '@codesandbox/components'; import css from '@styled-system/css'; +import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import { DndProvider } from 'react-dnd'; import Backend from 'react-dnd-html5-backend'; import { Header } from './Header'; @@ -25,6 +25,8 @@ import { ShowcaseSandbox } from './ShowcaseSandbox'; import { PinnedSandboxes } from './PinnedSandboxes'; import { AllSandboxes } from './AllSandboxes'; import { SearchedSandboxes } from './SearchedSandboxes'; +import { LikedSandboxes } from './LikedSandboxes'; +import { ContextMenu } from './ContextMenu'; export const Profile = props => { const { username } = props.match.params; @@ -34,7 +36,7 @@ export const Profile = props => { profile: { profileMounted }, }, state: { - profile: { current: user, searchQuery }, + profile: { current: user }, }, } = useOvermind(); @@ -66,134 +68,64 @@ export const Profile = props => { return ( - -
- + - - - - - - {searchQuery ? ( - - ) : ( - <> - - + + + + + + + + + + + + - - - )} - - + + + + + + + + + + + + + - - + + ); }; - -const ContextMenu = ({ visible, setVisibility, position, sandboxId }) => { - const { - actions: { - editor: { forkExternalSandbox }, - profile: { - addFeaturedSandboxes, - removeFeaturedSandboxes, - changeSandboxPrivacy, - deleteSandboxClicked, - }, - }, - state: { - user: loggedInUser, - profile: { current: user }, - }, - } = useOvermind(); - - const myProfile = loggedInUser?.username === user.username; - - const isFeatured = user.featuredSandboxes - .map(sandbox => sandbox.id) - .includes(sandboxId); - - return ( - - {myProfile && ( - <> - {isFeatured ? ( - removeFeaturedSandboxes({ sandboxId })}> - Unpin sandbox - - ) : ( - addFeaturedSandboxes({ sandboxId })}> - Pin sandbox - - )} - - - )} - { - location.href = sandboxUrl({ id: sandboxId }); - }} - > - Open sandbox - - { - forkExternalSandbox({ sandboxId, openInNewWindow: true }); - }} - > - Fork sandbox - - {myProfile && !isFeatured && ( - <> - - changeSandboxPrivacy({ sandboxId, privacy: 1 })} - > - Make sandbox unlisted - - changeSandboxPrivacy({ sandboxId, privacy: 2 })} - > - Make sandbox private - - - deleteSandboxClicked(sandboxId)}> - Delete sandbox - - - )} - - ); -};