Skip to content

Commit d94a86e

Browse files
fix: runtime and typechecks for potentially undefined properties
1 parent ed48726 commit d94a86e

File tree

4 files changed

+77
-31
lines changed

4 files changed

+77
-31
lines changed

packages/app/src/app/overmind/namespaces/dashboard/internalActions.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ export const deleteSandboxesFromState = (
151151
repoSandbox.sandboxes = newSandboxes;
152152
}
153153
});
154+
} else if (type === 'DELETED') {
155+
const newSandboxes = sandboxStructure[type].filter(
156+
sandbox => !ids.includes(sandbox.id)
157+
);
158+
if (newSandboxes.length !== sandboxStructure[type].length) {
159+
dashboard.sandboxes[type] = newSandboxes;
160+
}
154161
} else if (type !== 'RECENT_BRANCHES') {
155162
const newSandboxes = sandboxStructure[type].filter(sandboxFilter);
156163
if (newSandboxes.length !== sandboxStructure[type].length) {

packages/app/src/app/pages/Dashboard/Components/Selection/ContextMenus/MultiItemMenu.tsx

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,20 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
5858
const exportItems = () => {
5959
const ids = [
6060
...sandboxes
61-
.filter(sandbox => !sandbox.sandbox.permissions.preventSandboxExport)
61+
.filter(
62+
s =>
63+
'permissions' in s.sandbox &&
64+
!s.sandbox.permissions.preventSandboxExport
65+
)
6266
.map(sandbox => sandbox.sandbox.id),
6367
...templates.map(template => template.sandbox.id),
6468
];
6569

6670
actions.dashboard.downloadSandboxes(ids);
6771

6872
const skippedSandboxes = sandboxes.filter(
69-
sandbox => sandbox.sandbox.permissions.preventSandboxExport
73+
s =>
74+
'permissions' in s.sandbox && s.sandbox.permissions.preventSandboxExport
7075
);
7176

7277
if (skippedSandboxes.length) {
@@ -97,7 +102,7 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
97102
sandboxIds: [...sandboxes, ...templates].map(s => s.sandbox.id),
98103
preventSandboxLeaving: Boolean(
99104
[...sandboxes, ...templates].find(
100-
s => s.sandbox.permissions.preventSandboxLeaving
105+
s => 'permissions' in s.sandbox && s.sandbox.permissions.preventSandboxLeaving
101106
)
102107
),
103108
});
@@ -149,7 +154,7 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
149154
const FROZEN_ITEMS = isInDrafts
150155
? []
151156
: [
152-
sandboxes.some(s => !s.sandbox.isFrozen) && {
157+
sandboxes.some(s => 'isFrozen' in s.sandbox && !s.sandbox.isFrozen) && {
153158
label: 'Protect',
154159
fn: () => {
155160
actions.dashboard.changeSandboxesFrozen({
@@ -158,7 +163,7 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
158163
});
159164
},
160165
},
161-
sandboxes.some(s => s.sandbox.isFrozen) && {
166+
sandboxes.some(s => 'isFrozen' in s.sandbox && s.sandbox.isFrozen) && {
162167
label: 'Remove protection',
163168
fn: () => {
164169
actions.dashboard.changeSandboxesFrozen({
@@ -171,7 +176,11 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
171176

172177
const PROTECTED_SANDBOXES_ITEMS = isAdmin
173178
? [
174-
sandboxes.some(s => !s.sandbox.permissions.preventSandboxLeaving) && {
179+
sandboxes.some(
180+
s =>
181+
'permissions' in s.sandbox &&
182+
s.sandbox.permissions.preventSandboxLeaving
183+
) && {
175184
label: 'Prevent leaving workspace',
176185
fn: () => {
177186
actions.dashboard.setPreventSandboxesLeavingWorkspace({
@@ -180,7 +189,11 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
180189
});
181190
},
182191
},
183-
sandboxes.some(s => s.sandbox.permissions.preventSandboxLeaving) && {
192+
sandboxes.some(
193+
s =>
194+
'permissions' in s.sandbox &&
195+
s.sandbox.permissions.preventSandboxLeaving
196+
) && {
184197
label: 'Allow leaving workspace',
185198
fn: () => {
186199
actions.dashboard.setPreventSandboxesLeavingWorkspace({
@@ -189,7 +202,11 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
189202
});
190203
},
191204
},
192-
sandboxes.some(s => !s.sandbox.permissions.preventSandboxExport) &&
205+
sandboxes.some(
206+
s =>
207+
'permissions' in s.sandbox &&
208+
s.sandbox.permissions.preventSandboxExport
209+
) &&
193210
sandboxes.every(s => !s.sandbox.isV2) && {
194211
label: 'Prevent export as .zip',
195212
fn: () => {
@@ -199,7 +216,11 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
199216
});
200217
},
201218
},
202-
sandboxes.some(s => s.sandbox.permissions.preventSandboxExport) &&
219+
sandboxes.some(
220+
s =>
221+
'permissions' in s.sandbox &&
222+
s.sandbox.permissions.preventSandboxExport
223+
) &&
203224
sandboxes.every(s => !s.sandbox.isV2) && {
204225
label: 'Allow export as .zip',
205226
fn: () => {
@@ -213,8 +234,13 @@ export const MultiMenu = ({ selectedItems, page }: IMultiMenuProps) => {
213234
: [];
214235

215236
const EXPORT =
216-
sandboxes.some(s => !s.sandbox.permissions.preventSandboxExport) &&
217-
sandboxes.every(s => !s.sandbox.isV2)
237+
sandboxes.some(
238+
s =>
239+
!(
240+
'permissions' in s.sandbox &&
241+
s.sandbox.permissions.preventSandboxExport
242+
)
243+
) && sandboxes.every(s => !s.sandbox.isV2)
218244
? [{ label: 'Download', fn: exportItems }]
219245
: [];
220246

packages/app/src/app/pages/Dashboard/Components/Selection/ContextMenus/SandboxMenu.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
3030
browser: { copyToClipboard },
3131
} = useEffects();
3232
const { sandbox } = item;
33-
const isTemplate = !!sandbox.customTemplate;
33+
const isTemplate = 'customTemplate' in sandbox && !!sandbox.customTemplate;
3434

3535
const { visible, setVisibility, position } = React.useContext(Context);
3636
const history = useHistory();
@@ -45,7 +45,7 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
4545
const restrictedFork = isFrozen;
4646

4747
const isInActiveTeam = React.useMemo(() => {
48-
if (item.sandbox.teamId === activeTeam) {
48+
if ('teamId' in item.sandbox && item.sandbox.teamId === activeTeam) {
4949
return true;
5050
}
5151

@@ -82,7 +82,7 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
8282
}
8383

8484
const preventSandboxExport =
85-
!hasWriteAccess || sandbox.permissions.preventSandboxExport;
85+
!hasWriteAccess || ('permissions' in sandbox && sandbox.permissions.preventSandboxExport);
8686

8787
// TODO(@CompuIves): refactor this to an array
8888

@@ -147,8 +147,8 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
147147
openInNewWindow: true,
148148
redirectAfterFork: true,
149149
body: {
150-
privacy: sandbox.privacy as 2 | 1 | 0,
151-
collectionId: sandbox.draft ? undefined : sandbox.collection.id,
150+
privacy: 'privacy' in sandbox ? sandbox.privacy as 2 | 1 | 0 : undefined,
151+
collectionId: 'draft' in sandbox && sandbox.draft ? undefined : 'collection' in sandbox && sandbox.collection.id,
152152
},
153153
});
154154
}}
@@ -164,7 +164,7 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
164164
actions.modals.moveSandboxModal.open({
165165
sandboxIds: [item.sandbox.id],
166166
preventSandboxLeaving:
167-
item.sandbox.permissions.preventSandboxLeaving,
167+
'permissions' in item.sandbox && item.sandbox.permissions.preventSandboxLeaving,
168168
});
169169
}}
170170
>
@@ -194,7 +194,7 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
194194
</Tooltip>
195195
)}
196196

197-
{hasWriteAccess ? (
197+
{hasWriteAccess && 'privacy' in sandbox ? (
198198
<>
199199
<Menu.Divider />
200200
{sandbox.privacy !== 0 && (
@@ -244,7 +244,7 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
244244
)}
245245
{hasWriteAccess &&
246246
!isTemplate &&
247-
(sandbox.isFrozen ? (
247+
('isFrozen' in sandbox && sandbox.isFrozen ? (
248248
<MenuItem
249249
onSelect={() => {
250250
actions.dashboard.changeSandboxesFrozen({
@@ -304,7 +304,7 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
304304
))}
305305
{isPro &&
306306
hasAdminAccess &&
307-
(sandbox.permissions.preventSandboxLeaving ? (
307+
('permissions' in sandbox && sandbox.permissions.preventSandboxLeaving ? (
308308
<MenuItem
309309
onSelect={() => {
310310
actions.dashboard.setPreventSandboxesLeavingWorkspace({
@@ -330,7 +330,7 @@ export const SandboxMenu: React.FC<SandboxMenuProps> = ({
330330
{!sandbox.isV2 &&
331331
isPro &&
332332
hasAdminAccess &&
333-
(sandbox.permissions.preventSandboxExport ? (
333+
('permissions' in sandbox && sandbox.permissions.preventSandboxExport ? (
334334
<MenuItem
335335
onSelect={() => {
336336
actions.dashboard.setPreventSandboxesExport({
@@ -394,7 +394,7 @@ const getFolderUrl = (
394394
if (item.type === 'template') return dashboard.templates(activeTeamId);
395395

396396
const path = item.sandbox.collection?.path;
397-
if (path == null || (!item.sandbox.teamId && path === '/')) {
397+
if (path == null || ('teamId' in item.sandbox && !item.sandbox.teamId && path === '/')) {
398398
return dashboard.drafts(activeTeamId);
399399
}
400400

packages/app/src/app/pages/Dashboard/Components/Selection/DragPreview.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,30 @@ export const DragPreview: React.FC<DragPreviewProps> = React.memo(
7676

7777
const sandbox = dashboardEntry.sandbox;
7878

79-
let screenshotUrl = sandbox.screenshotUrl;
80-
// We set a fallback thumbnail in the API which is used for
81-
// both old and new dashboard, we can move this logic to the
82-
// backend when we deprecate the old dashboard
83-
if (
84-
screenshotUrl === 'https://codesandbox.io/static/img/banner.png'
85-
) {
86-
screenshotUrl = '/static/img/default-sandbox-thumbnail.png';
79+
let screenshotUrl;
80+
81+
// 'screenshotUrl' is not present in:
82+
// - deleted sandboxes
83+
// TODO: Decide if we really want deleted sandboxes to be draggable.
84+
if ('screenshotUrl' in sandbox) {
85+
screenshotUrl = sandbox.screenshotUrl;
86+
// We set a fallback thumbnail in the API which is used for
87+
// both old and new dashboard, we can move this logic to the
88+
// backend when we deprecate the old dashboard
89+
if (
90+
screenshotUrl === 'https://codesandbox.io/static/img/banner.png'
91+
) {
92+
screenshotUrl = '/static/img/default-sandbox-thumbnail.png';
93+
}
8794
}
8895

89-
const TemplateIcon = getTemplateIcon(sandbox);
96+
let TemplateIcon: React.ComponentType<{ width: string; height: string }> | undefined;
97+
98+
// 'source' is not present in:
99+
// - deleted sandboxes
100+
if ('source' in sandbox) {
101+
TemplateIcon = getTemplateIcon(sandbox);
102+
}
90103

91104
return {
92105
type: 'sandbox',

0 commit comments

Comments
 (0)