Skip to content

Commit b005252

Browse files
authored
WIP Profiles: likes + pages (#4802)
1 parent 8ebd090 commit b005252

File tree

8 files changed

+301
-134
lines changed

8 files changed

+301
-134
lines changed

packages/app/src/app/pages/Profile2/AllSandboxes.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import css from '@styled-system/css';
1111
import { useOvermind } from 'app/overmind';
1212
import { SandboxCard, SkeletonCard } from './SandboxCard';
13+
import { SANDBOXES_PER_PAGE } from './constants';
1314

1415
export const AllSandboxes = ({ menuControls }) => {
1516
const {
@@ -108,7 +109,7 @@ export const AllSandboxes = ({ menuControls }) => {
108109
}}
109110
>
110111
{isLoadingSandboxes
111-
? Array(15)
112+
? Array(SANDBOXES_PER_PAGE)
112113
.fill(true)
113114
.map((_, index) => (
114115
// eslint-disable-next-line
@@ -127,7 +128,6 @@ export const AllSandboxes = ({ menuControls }) => {
127128
);
128129
};
129130

130-
const SANDBOXES_PER_PAGE = 15;
131131
const Pagination = () => {
132132
const {
133133
actions: {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import React from 'react';
2+
import { useOvermind } from 'app/overmind';
3+
import { sandboxUrl } from '@codesandbox/common/lib/utils/url-generator';
4+
import { useLocation } from 'react-router-dom';
5+
import { Menu } from '@codesandbox/components';
6+
7+
export const ContextMenu = ({
8+
visible,
9+
setVisibility,
10+
position,
11+
sandboxId,
12+
}) => {
13+
const {
14+
actions: {
15+
editor: { forkExternalSandbox },
16+
profile: {
17+
addFeaturedSandboxes,
18+
removeFeaturedSandboxes,
19+
changeSandboxPrivacy,
20+
deleteSandboxClicked,
21+
},
22+
},
23+
state: {
24+
user: loggedInUser,
25+
profile: { current: user },
26+
},
27+
} = useOvermind();
28+
const location = useLocation();
29+
30+
if (!visible) return null;
31+
32+
const myProfile = loggedInUser?.username === user.username;
33+
const likesPage = location.pathname === '/likes';
34+
35+
const isFeatured = user.featuredSandboxes
36+
.map(sandbox => sandbox.id)
37+
.includes(sandboxId);
38+
39+
return (
40+
<Menu.ContextMenu
41+
visible={visible}
42+
setVisibility={setVisibility}
43+
position={position}
44+
>
45+
{myProfile && !likesPage && (
46+
<>
47+
{isFeatured ? (
48+
<Menu.Item onSelect={() => removeFeaturedSandboxes({ sandboxId })}>
49+
Unpin sandbox
50+
</Menu.Item>
51+
) : (
52+
<Menu.Item onSelect={() => addFeaturedSandboxes({ sandboxId })}>
53+
Pin sandbox
54+
</Menu.Item>
55+
)}
56+
<Menu.Divider />
57+
</>
58+
)}
59+
<Menu.Item
60+
onSelect={() => {
61+
window.location.href = sandboxUrl({ id: sandboxId });
62+
}}
63+
>
64+
Open sandbox
65+
</Menu.Item>
66+
<Menu.Item
67+
onSelect={() => {
68+
forkExternalSandbox({ sandboxId, openInNewWindow: true });
69+
}}
70+
>
71+
Fork sandbox
72+
</Menu.Item>
73+
{myProfile && !likesPage && !isFeatured && (
74+
<>
75+
<Menu.Divider />
76+
<Menu.Item
77+
onSelect={() => changeSandboxPrivacy({ sandboxId, privacy: 1 })}
78+
>
79+
Make sandbox unlisted
80+
</Menu.Item>
81+
<Menu.Item
82+
onSelect={() => changeSandboxPrivacy({ sandboxId, privacy: 2 })}
83+
>
84+
Make sandbox private
85+
</Menu.Item>
86+
<Menu.Divider />
87+
<Menu.Item onSelect={() => deleteSandboxClicked(sandboxId)}>
88+
Delete sandbox
89+
</Menu.Item>
90+
</>
91+
)}
92+
</Menu.ContextMenu>
93+
);
94+
};

packages/app/src/app/pages/Profile2/Header.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react';
22
import { useOvermind } from 'app/overmind';
3+
import { useHistory, useLocation } from 'react-router-dom';
34
import LogoIcon from '@codesandbox/common/lib/components/Logo';
45
import { UserMenu } from 'app/pages/common/UserMenu';
5-
66
import { Stack, Input, Button, Link, Icon } from '@codesandbox/components';
77
import css from '@styled-system/css';
88

@@ -20,6 +20,10 @@ export const Header: React.FC = () => {
2020
},
2121
} = useOvermind();
2222

23+
const history = useHistory();
24+
const location = useLocation();
25+
if (!location.search) searchQueryChanged('');
26+
2327
return (
2428
<Stack
2529
as="header"
@@ -63,8 +67,22 @@ export const Header: React.FC = () => {
6367
paddingLeft: 7,
6468
width: [0, 360, 480],
6569
})}
66-
defaultValue={searchQuery}
67-
onChange={event => searchQueryChanged(event.target.value)}
70+
value={searchQuery}
71+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
72+
const query = event.target.value;
73+
searchQueryChanged(query);
74+
75+
if (!query.length) {
76+
history.push('');
77+
return;
78+
}
79+
80+
if (history.location.pathname === '/search') {
81+
history.replace('/search?query=' + query);
82+
} else {
83+
history.push('/search?query=' + query);
84+
}
85+
}}
6886
/>
6987
</Stack>
7088

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import React from 'react';
2+
import { Grid, Column, Stack, Text, IconButton } from '@codesandbox/components';
3+
import css from '@styled-system/css';
4+
import { useOvermind } from 'app/overmind';
5+
import { SandboxCard, SkeletonCard } from './SandboxCard';
6+
import { SANDBOXES_PER_PAGE } from './constants';
7+
8+
export const LikedSandboxes = ({ menuControls }) => {
9+
const {
10+
actions: {
11+
profile: { likedSandboxesPageChanged },
12+
},
13+
state: {
14+
profile: {
15+
current: { username },
16+
isLoadingSandboxes,
17+
currentLikedSandboxesPage,
18+
likedSandboxes,
19+
},
20+
},
21+
} = useOvermind();
22+
23+
// explicitly call it on first page render
24+
React.useEffect(() => {
25+
if (currentLikedSandboxesPage === 1) likedSandboxesPageChanged(1);
26+
}, [currentLikedSandboxesPage, likedSandboxesPageChanged]);
27+
28+
const sandboxes = (
29+
(likedSandboxes[username] &&
30+
likedSandboxes[username][currentLikedSandboxesPage]) ||
31+
[]
32+
)
33+
// only show public sandboxes on profile
34+
.filter(sandbox => sandbox.privacy === 0);
35+
36+
return (
37+
<Stack as="section" direction="vertical" gap={6}>
38+
<Stack justify="space-between" align="center">
39+
<Text size={7} weight="bold">
40+
Liked Sandboxes
41+
</Text>
42+
</Stack>
43+
44+
<Grid
45+
rowGap={6}
46+
columnGap={6}
47+
css={{
48+
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
49+
}}
50+
>
51+
{isLoadingSandboxes
52+
? Array(SANDBOXES_PER_PAGE)
53+
.fill(true)
54+
.map((_, index) => (
55+
// eslint-disable-next-line
56+
<Column key={index}>
57+
<SkeletonCard />
58+
</Column>
59+
))
60+
: sandboxes.map((sandbox, index) => (
61+
<Column key={sandbox.id}>
62+
<SandboxCard sandbox={sandbox} menuControls={menuControls} />
63+
</Column>
64+
))}
65+
</Grid>
66+
<Pagination />
67+
</Stack>
68+
);
69+
};
70+
71+
const Pagination = () => {
72+
const {
73+
actions: {
74+
profile: { likedSandboxesPageChanged },
75+
},
76+
state: {
77+
profile: {
78+
currentLikedSandboxesPage,
79+
current: { givenLikeCount },
80+
},
81+
},
82+
} = useOvermind();
83+
84+
const numberOfPages = Math.ceil(givenLikeCount / SANDBOXES_PER_PAGE);
85+
86+
return (
87+
<nav role="navigation" aria-label="Pagination Navigation">
88+
<Stack
89+
as="ul"
90+
gap={4}
91+
justify="center"
92+
align="center"
93+
css={css({ marginX: 0, marginY: 10, listStyle: 'none' })}
94+
>
95+
<li>
96+
<IconButton
97+
name="backArrow"
98+
title="Previous page"
99+
onClick={() =>
100+
likedSandboxesPageChanged(currentLikedSandboxesPage - 1)
101+
}
102+
disabled={currentLikedSandboxesPage === 1}
103+
/>
104+
</li>
105+
<li>
106+
<IconButton
107+
name="backArrow"
108+
title="Next page"
109+
style={{ transform: 'scaleX(-1)' }}
110+
onClick={() =>
111+
likedSandboxesPageChanged(currentLikedSandboxesPage + 1)
112+
}
113+
disabled={currentLikedSandboxesPage === numberOfPages}
114+
/>
115+
</li>
116+
</Stack>
117+
</nav>
118+
);
119+
};

packages/app/src/app/pages/Profile2/ProfileCard.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22
import { motion } from 'framer-motion';
3+
import { useOvermind } from 'app/overmind';
4+
import { Link as RouterLink } from 'react-router-dom';
35
import {
46
Stack,
57
Avatar,
@@ -13,7 +15,6 @@ import {
1315
} from '@codesandbox/components';
1416
import { TeamAvatar } from 'app/components/TeamAvatar';
1517
import css from '@styled-system/css';
16-
import { useOvermind } from 'app/overmind';
1718

1819
export const ProfileCard = ({ defaultEditing = false }) => {
1920
const {
@@ -99,13 +100,15 @@ export const ProfileCard = ({ defaultEditing = false }) => {
99100
<Stack direction="vertical" gap={3}>
100101
<Stack gap={2} align="center">
101102
<Icon name="box" />
102-
<Text size={3}>
103+
<Link as={RouterLink} to="/" size={3}>
103104
{user.sandboxCount + user.templateCount} Sandboxes
104-
</Text>
105+
</Link>
105106
</Stack>
106107
<Stack gap={2} align="center">
107108
<Icon name="heart" />
108-
<Text size={3}>{user.receivedLikeCount} Likes</Text>
109+
<Link as={RouterLink} to="/likes" size={3}>
110+
{user.givenLikeCount} Likes
111+
</Link>
109112
</Stack>
110113
</Stack>
111114
)}

packages/app/src/app/pages/Profile2/SandboxCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const SandboxCard = ({
9090
name="more"
9191
size={9}
9292
title="Sandbox actions"
93-
onClick={event => onContextMenu(event, sandbox.id)}
93+
onClick={event => onContextMenu(event, sandbox)}
9494
/>
9595
</Stack>
9696
</Stack>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const SANDBOXES_PER_PAGE = 15;

0 commit comments

Comments
 (0)