Skip to content

Commit 4189e09

Browse files
siddharthkpCompuIves
authored andcommitted
Privacy popup (codesandbox#3205)
* Revert "Revert "Privacy popup" (codesandbox#3204)" This reverts commit c73d174. * custom caret * Make updating privacy optimistic * Add tracking of updating privacy of a sandbox * Add tracking for opening the tooltip * add popup for non owners as well * track if popup is opened by owner or not
1 parent 509e6f1 commit 4189e09

File tree

10 files changed

+281
-20
lines changed

10 files changed

+281
-20
lines changed

packages/app/src/app/overmind/namespaces/workspace/actions.ts

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import slugify from '@codesandbox/common/lib/utils/slugify';
44
import { Action, AsyncAction } from 'app/overmind';
55
import { withOwnedSandbox } from 'app/overmind/factories';
66
import getItems from 'app/overmind/utils/items';
7+
import track from '@codesandbox/common/lib/utils/analytics';
78

89
export const valueChanged: Action<{
910
field: string;
@@ -203,25 +204,41 @@ export const sandboxDeleted: AsyncAction = async ({
203204
effects.router.redirectToSandboxWizard();
204205
};
205206

206-
export const sandboxPrivacyChanged: AsyncAction<0 | 1 | 2> = async (
207-
{ actions, effects, state },
208-
privacy
209-
) => {
207+
export const sandboxPrivacyChanged: AsyncAction<{
208+
privacy: 0 | 1 | 2;
209+
source?: string;
210+
}> = async ({ actions, effects, state }, { privacy, source = 'generic' }) => {
211+
track('Sandbox - Update Privacy', {
212+
privacy,
213+
source,
214+
});
215+
210216
const oldPrivacy = state.editor.currentSandbox.privacy;
211-
const sandbox = await effects.api.updatePrivacy(
212-
state.editor.currentSandbox.id,
213-
privacy
214-
);
215-
state.editor.currentSandbox.previewSecret = sandbox.previewSecret;
216217
state.editor.currentSandbox.privacy = privacy;
217218

218-
if (
219-
getTemplate(state.editor.currentSandbox.template).isServer &&
220-
((oldPrivacy !== 2 && privacy === 2) || (oldPrivacy === 2 && privacy !== 2))
221-
) {
222-
// Privacy changed from private to unlisted/public or other way around, restart
223-
// the sandbox to notify containers
224-
actions.server.restartContainer();
219+
try {
220+
const sandbox = await effects.api.updatePrivacy(
221+
state.editor.currentSandbox.id,
222+
privacy
223+
);
224+
state.editor.currentSandbox.previewSecret = sandbox.previewSecret;
225+
state.editor.currentSandbox.privacy = privacy;
226+
227+
if (
228+
getTemplate(state.editor.currentSandbox.template).isServer &&
229+
((oldPrivacy !== 2 && privacy === 2) ||
230+
(oldPrivacy === 2 && privacy !== 2))
231+
) {
232+
// Privacy changed from private to unlisted/public or other way around, restart
233+
// the sandbox to notify containers
234+
actions.server.restartContainer();
235+
}
236+
} catch (error) {
237+
state.editor.currentSandbox.privacy = oldPrivacy;
238+
actions.internal.handleError({
239+
message: "We weren't able to update the sandbox privacy",
240+
error,
241+
});
225242
}
226243
};
227244

packages/app/src/app/pages/Dashboard/Content/SandboxGrid/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ class SandboxGridComponent extends React.Component<
196196
};
197197

198198
setSandboxesPrivacy = (privacy: number) => {
199+
track('Sandbox - Update Privacy', {
200+
privacy,
201+
source: 'dashboard',
202+
});
199203
setSandboxesPrivacy(this.props.selectedSandboxes, privacy);
200204
};
201205

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import styled, {
2+
StyledComponent,
3+
StyledComponentInnerOtherProps,
4+
} from 'styled-components';
5+
import css from '@styled-system/css';
6+
7+
export const Element = styled.div<{
8+
margin?: number;
9+
marginX?: number;
10+
marginBottom?: number;
11+
}>(props =>
12+
css({
13+
margin: props.margin || null,
14+
marginX: props.marginX || null,
15+
marginBottom: props.marginBottom || null,
16+
})
17+
);
18+
19+
export const Container = styled(Element)(
20+
css({
21+
'.tippy-content': {
22+
width: '200px',
23+
backgroundColor: 'grays.500',
24+
border: '1px solid',
25+
borderColor: 'grays.600',
26+
padding: 4,
27+
borderRadius: 'medium',
28+
whiteSpace: 'normal',
29+
},
30+
})
31+
);
32+
33+
export const Text = styled(Element)<{
34+
color?: string;
35+
size?: string;
36+
align?: string;
37+
}>(props =>
38+
css({
39+
color: props.color || 'white',
40+
fontSize: props.size || 'inherit',
41+
textAlign: props.align || 'left',
42+
})
43+
);
44+
45+
export const Link = styled.a(
46+
css({
47+
color: 'blues.300',
48+
textDecoration: 'none',
49+
})
50+
);
51+
52+
const caret =
53+
'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMTAgMjQiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjNzU3NTc1Ij48cGF0aCBkPSJtNS4wMDAwNiAxNy0zLjAwMDA2LTRoNnoiLz48cGF0aCBkPSJtNC45OTk5NCA3IDMuMDAwMDYgNGgtNnoiLz48L2c+PC9zdmc+';
54+
55+
export const Select = styled(Element).attrs({ as: 'select' })(({ theme }) =>
56+
css({
57+
appearance: 'none',
58+
backgroundImage: `url(${caret})`,
59+
backgroundRepeat: 'no-repeat',
60+
backgroundPosition: '96% 50%',
61+
width: '100%',
62+
backgroundColor: 'grays.800',
63+
color: 'white',
64+
border: '1px solid',
65+
borderColor: 'grays.600',
66+
borderRadius: 'medium',
67+
paddingX: 2,
68+
height: 24,
69+
boxSizing: 'border-box',
70+
fontFamily: 'body',
71+
transition: 'backgroundColor',
72+
transitionDuration: theme.speeds[2],
73+
':hover': {
74+
backgroundColor: 'grays.700',
75+
},
76+
':disabled': {
77+
opacity: 0.5,
78+
cursor: 'not-allowed',
79+
':hover': {
80+
backgroundColor: 'grays.800',
81+
},
82+
},
83+
})
84+
) as StyledComponent<
85+
'select',
86+
any,
87+
StyledComponentInnerOtherProps<typeof Element>
88+
>;

packages/app/src/app/pages/Sandbox/Editor/Header/PrivacyTooltip/icons.tsx

Lines changed: 48 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React, { useEffect } from 'react';
2+
import { useOvermind } from 'app/overmind';
3+
import { ThemeProvider } from 'styled-components';
4+
import Tooltip from '@codesandbox/common/lib/components/Tooltip';
5+
import theme from '@codesandbox/common/lib/design-language/theme';
6+
import track from '@codesandbox/common/lib/utils/analytics';
7+
import { Container, Text, Link, Select } from './elements';
8+
import * as Icons from './icons';
9+
10+
export const PrivacyTooltip = () => {
11+
const {
12+
actions: {
13+
workspace: { sandboxPrivacyChanged },
14+
},
15+
state: {
16+
user,
17+
editor: {
18+
currentSandbox: { owned, privacy },
19+
},
20+
},
21+
} = useOvermind();
22+
23+
useEffect(() => {
24+
track('Open Sandbox Privacy Tooltip', { owned });
25+
}, [owned]);
26+
27+
const config = {
28+
0: {
29+
description: 'Everyone can see this Sandbox',
30+
icon: <Icons.Public />,
31+
},
32+
1: {
33+
description:
34+
'Only people with a private link are able to see this Sandbox',
35+
icon: <Icons.Unlisted />,
36+
},
37+
2: {
38+
description: 'Only you can see this Sandbox.',
39+
icon: <Icons.Private />,
40+
},
41+
};
42+
43+
const onChange = event => {
44+
const value = event.target.value;
45+
sandboxPrivacyChanged({
46+
privacy: Number(value) as 0 | 1 | 2,
47+
source: 'tooltip',
48+
});
49+
};
50+
51+
return (
52+
<ThemeProvider theme={theme}>
53+
<Container>
54+
<Tooltip
55+
delay={0}
56+
interactive
57+
content={
58+
<>
59+
<Text size="3" marginBottom={4}>
60+
{owned ? (
61+
<>
62+
{user && user.subscription ? (
63+
'Adjust privacy settings.'
64+
) : (
65+
<>
66+
You can change privacy of a sandbox as a Pro.
67+
<br />
68+
<Link href="/pricing">Upgrade to Pro</Link>
69+
</>
70+
)}
71+
</>
72+
) : (
73+
'The author has set privacy to'
74+
)}
75+
</Text>
76+
77+
<Select
78+
marginBottom={2}
79+
value={privacy}
80+
onChange={onChange}
81+
disabled={!user || !user.subscription || !owned}
82+
>
83+
<option value={0}>Public</option>
84+
<option value={1}>Unlisted</option>
85+
<option value={2}>Private</option>
86+
</Select>
87+
<Text size="2" color="grays.300">
88+
{config[privacy].description}
89+
</Text>
90+
</>
91+
}
92+
>
93+
{config[privacy].icon}
94+
</Tooltip>
95+
</Container>
96+
</ThemeProvider>
97+
);
98+
};

packages/app/src/app/pages/Sandbox/Editor/Header/SandboxName/SandboxName.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import React, {
1111
useState,
1212
} from 'react';
1313
import { useSpring, animated } from 'react-spring';
14-
1514
import { useOvermind } from 'app/overmind';
15+
import { PrivacyTooltip } from '../PrivacyTooltip';
1616

1717
import {
1818
Container,
@@ -109,7 +109,6 @@ export const SandboxName: FunctionComponent = () => {
109109
</Folder>
110110
</animated.div>
111111
)}
112-
113112
{updatingName ? (
114113
<Form onSubmit={submitNameChange}>
115114
<NameInput
@@ -140,6 +139,8 @@ export const SandboxName: FunctionComponent = () => {
140139
</Name>
141140
)}
142141

142+
{!updatingName ? <PrivacyTooltip /> : null}
143+
143144
{currentSandbox.customTemplate ? (
144145
<Tooltip
145146
content={

packages/app/src/app/pages/Sandbox/Editor/Header/SandboxName/elements.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export const Name = styled.span<{ owned?: boolean }>`
6262
${({ theme, owned }) => css`
6363
color: ${theme.light ? 'black' : 'white'};
6464
margin-left: 0.25rem;
65+
margin-right: ${owned ? 0 : '0.5rem'};
6566
cursor: ${owned ? 'pointer' : 'initial'};
6667
text-overflow: ellipsis;
6768
appearance: ${owned ? 'none' : 'initial'};

packages/app/src/app/pages/Sandbox/Editor/Header/elements.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export const Centered = styled.div`
4747
display: flex;
4848
justify-content: center;
4949
z-index: 0; /* So pointer events on left and right continue */
50+
z-index: 10; /* override: this probably breaks something, not sure what */
5051
margin: 0 3rem;
5152
`;
5253

packages/app/src/app/pages/Sandbox/Editor/Workspace/Project/Privacy/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ export const Privacy: FunctionComponent<Props> = ({ editable }) => {
3535
onChange={({
3636
target: { value },
3737
}: ChangeEvent<HTMLSelectElement>) =>
38-
sandboxPrivacyChanged(Number(value) as 0 | 1 | 2)
38+
sandboxPrivacyChanged({
39+
privacy: Number(value) as 0 | 1 | 2,
40+
source: 'sidebar',
41+
})
3942
}
4043
value={privacy}
4144
>

packages/app/src/app/pages/Sandbox/Editor/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ const ContentSplit: React.FC = () => {
9494
right: 0,
9595
bottom: bottomOffset,
9696
height: statusBar ? 'auto' : 'calc(100% - 3.5rem)',
97-
zIndex: 999999,
97+
zIndex: 9,
9898
}}
9999
>
100100
<SplitPane

0 commit comments

Comments
 (0)