Skip to content

Commit 102df37

Browse files
authored
Merge branch 'master' into sandbox-downloader
2 parents e32050e + 0208bd6 commit 102df37

File tree

101 files changed

+1583
-1460
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+1583
-1460
lines changed

packages/app/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"@babel/plugin-transform-destructuring": "^7.5.0",
7575
"@babel/preset-env": "^7.5.5",
7676
"@codesandbox/executors": "^0.1.0",
77-
"@codesandbox/template-icons": "^1.0.2",
77+
"@codesandbox/template-icons": "^1.1.0",
7878
"@emmetio/codemirror-plugin": "^0.3.5",
7979
"@sentry/webpack-plugin": "^1.8.0",
8080
"@styled-system/css": "^5.0.23",
@@ -158,9 +158,9 @@
158158
"normalizr": "^3.2.3",
159159
"onigasm": "^2.2.1",
160160
"ot": "^0.0.15",
161-
"overmind": "^21.0.0-1575912749783",
161+
"overmind": "^21.0.0-1576231149763",
162162
"overmind-devtools": "^19.0.0",
163-
"overmind-react": "^22.0.0-1575912749783",
163+
"overmind-react": "^22.0.0-1576231149763",
164164
"phoenix": "^1.4.11",
165165
"postcss": "^6.0.9",
166166
"postcss-selector-parser": "^2.2.3",
@@ -252,6 +252,7 @@
252252
"@types/phoenix": "^1.4.0",
253253
"@types/prop-types": "^15.7.0",
254254
"@types/react": "^16.8.12",
255+
"@types/react-color": "^2.17.3",
255256
"@types/react-dom": "^16.8.3",
256257
"@types/react-helmet": "^5.0.11",
257258
"@types/react-icons": "2.2.7",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export { useInterval } from './useInterval';
12
export { useScript } from './useScript';
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useEffect, useRef } from 'react';
2+
3+
const noop = () => undefined;
4+
5+
export const useInterval = (callback: () => void, delay: number | null) => {
6+
const savedCallback = useRef<() => void>();
7+
8+
useEffect(() => {
9+
savedCallback.current = callback;
10+
}, [callback]);
11+
12+
useEffect(() => {
13+
if (delay !== null) {
14+
const tick = () => {
15+
if (savedCallback?.current) {
16+
savedCallback.current();
17+
}
18+
};
19+
20+
const interval = setInterval(tick, delay);
21+
22+
return () => clearInterval(interval);
23+
}
24+
25+
return noop;
26+
}, [delay]);
27+
};

packages/app/src/app/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ import { createOvermind } from 'overmind';
2525
import { Provider as ActualOvermindProvider } from 'overmind-react';
2626
import React from 'react';
2727
import { ApolloProvider } from 'react-apollo';
28+
import { DndProvider } from 'react-dnd';
2829
import { render } from 'react-dom';
2930
import { Router } from 'react-router-dom';
3031
import { ThemeProvider } from 'styled-components';
31-
import { DndProvider } from 'react-dnd';
3232

3333
import { config } from './overmind';
3434
import { Provider as OvermindProvider } from './overmind/Provider';

packages/app/src/app/overmind/effects/api/index.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import {
1919
UploadedFilesInfo,
2020
UserSandbox,
2121
} from '@codesandbox/common/lib/types';
22-
import { client } from 'app/graphql/client';
2322
import { LIST_PERSONAL_TEMPLATES } from 'app/components/CreateNewSandbox/queries';
23+
import { client } from 'app/graphql/client';
2424

2525
import {
2626
transformDirectory,
@@ -116,10 +116,12 @@ export default {
116116
)
117117
.then(transformModule);
118118
},
119-
saveModules(sandboxId: string, modules: Module[]) {
120-
return api.put(`/sandboxes/${sandboxId}/modules/mupdate`, {
121-
modules,
122-
});
119+
saveModules(sandboxId: string, modules: Module[]): Promise<Module[]> {
120+
return api
121+
.put<IModuleAPIResponse[]>(`/sandboxes/${sandboxId}/modules/mupdate`, {
122+
modules,
123+
})
124+
.then(modulesResult => modulesResult.map(transformModule));
123125
},
124126
getGitChanges(sandboxId: string): Promise<GitChanges> {
125127
return api.get(`/sandboxes/${sandboxId}/git/diff`);

packages/app/src/app/overmind/factories.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { IDerive, IState, json } from 'overmind';
33

44
import { AsyncAction } from '.';
55

6+
/*
7+
Ensures that we have loaded the app with the initial user
8+
and settings
9+
*/
610
export const withLoadApp = <T>(
711
continueAction?: AsyncAction<T>
812
): AsyncAction<T> => async (context, value) => {
@@ -18,6 +22,7 @@ export const withLoadApp = <T>(
1822

1923
state.isAuthenticating = true;
2024
state.jwt = effects.jwt.get() || null;
25+
2126
effects.connection.addListener(actions.connectionChanged);
2227
actions.internal.setStoredSettings();
2328
effects.keybindingManager.set(

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

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const signIn: AsyncAction<{ useExtraScopes?: boolean }> = async (
3636
effects.live.connect();
3737
actions.userNotifications.internal.initialize(); // Seemed a bit different originally?
3838
actions.refetchSandboxInfo();
39+
state.isAuthenticating = false;
3940
} catch (error) {
4041
actions.internal.handleError({
4142
message: 'Could not authenticate with Github',
@@ -339,6 +340,46 @@ export const closeTabByIndex: Action<number> = ({ state }, tabIndex) => {
339340
state.editor.tabs.splice(tabIndex, 1);
340341
};
341342

343+
export const getErrorMessage: Action<{ error: ApiError | Error }, string> = (
344+
context,
345+
{ error }
346+
) => {
347+
const isGenericError = !('response' in error) || error.response.status >= 500;
348+
349+
if (isGenericError) {
350+
return error.message;
351+
}
352+
353+
const { response } = error as ApiError;
354+
/*
355+
Update error message with what is coming from the server
356+
*/
357+
const result = response.data;
358+
359+
if (result) {
360+
if (typeof result === 'string') {
361+
return result;
362+
}
363+
if ('errors' in result) {
364+
const errors = values(result.errors)[0];
365+
const fields = Object.keys(result.errors);
366+
if (Array.isArray(errors)) {
367+
if (errors[0]) {
368+
return `${fields[0]}: ${errors[0]}`; // eslint-disable-line no-param-reassign,prefer-destructuring
369+
}
370+
} else {
371+
return errors; // eslint-disable-line no-param-reassign
372+
}
373+
} else if (result.error) {
374+
return result.error; // eslint-disable-line no-param-reassign
375+
} else if (response.status === 413) {
376+
return 'File too large, upload limit is 5MB.';
377+
}
378+
}
379+
380+
return error.message;
381+
};
382+
342383
export const handleError: Action<{
343384
/*
344385
The message that will show as title of the notification
@@ -385,30 +426,7 @@ export const handleError: Action<{
385426
return;
386427
}
387428

388-
/*
389-
Update error message with what is coming from the server
390-
*/
391-
const result = response.data;
392-
393-
if (result) {
394-
if (typeof result === 'string') {
395-
error.message = result;
396-
} else if ('errors' in result) {
397-
const errors = values(result.errors)[0];
398-
const fields = Object.keys(result.errors);
399-
if (Array.isArray(errors)) {
400-
if (errors[0]) {
401-
error.message = `${fields[0]}: ${errors[0]}`; // eslint-disable-line no-param-reassign,prefer-destructuring
402-
}
403-
} else {
404-
error.message = errors; // eslint-disable-line no-param-reassign
405-
}
406-
} else if (result.error) {
407-
error.message = result.error; // eslint-disable-line no-param-reassign
408-
} else if (response.status === 413) {
409-
error.message = 'File too large, upload limit is 5MB.';
410-
}
411-
}
429+
error.message = actions.internal.getErrorMessage({ error });
412430

413431
const notificationActions = {
414432
primary: [],
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export const forkFrozenModal = {
22
result: 'fork' as 'fork' | 'cancel' | 'unfreeze',
33
};
4+
5+
export const authenticateUserModal = {};

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@ export const orderByChanged: Action<{ orderBy: OrderBy }> = (
3030
state.dashboard.orderBy = orderBy;
3131
};
3232

33-
export const blacklistedTemplateAdded: Action<{ template: string }> = (
33+
export const blacklistedTemplateAdded: Action<string> = (
3434
{ state },
35-
{ template }
35+
template
3636
) => {
3737
state.dashboard.filters.blacklistedTemplates.push(template);
3838
};
3939

40-
export const blacklistedTemplateRemoved: Action<{ template: string }> = (
40+
export const blacklistedTemplateRemoved: Action<string> = (
4141
{ state },
42-
{ template }
42+
template
4343
) => {
4444
state.dashboard.filters.blacklistedTemplates = state.dashboard.filters.blacklistedTemplates.filter(
4545
currentTemplate => currentTemplate !== template
@@ -50,9 +50,9 @@ export const blacklistedTemplatesCleared: Action = ({ state }) => {
5050
state.dashboard.filters.blacklistedTemplates = [];
5151
};
5252

53-
export const blacklistedTemplatesChanged: Action<{ templates: string[] }> = (
53+
export const blacklistedTemplatesChanged: Action<string[]> = (
5454
{ state },
55-
{ templates }
55+
templates
5656
) => {
5757
state.dashboard.filters.blacklistedTemplates = templates;
5858
};

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

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,6 @@ export const sandboxChanged: AsyncAction<{ id: string }> = withLoadApp<{
8787
state.editor.isLoading = !hasExistingSandbox;
8888
state.editor.notFound = false;
8989

90-
// Only reset changed modules if sandbox wasn't in memory, otherwise a fork
91-
// can mark real changed modules as unchanged
92-
state.editor.changedModuleShortids = [];
93-
9490
try {
9591
const sandbox = await effects.api.getSandbox(newId);
9692

@@ -232,17 +228,42 @@ export const codeChanged: Action<{
232228
export const saveClicked: AsyncAction = withOwnedSandbox(
233229
async ({ state, effects, actions }) => {
234230
const sandbox = state.editor.currentSandbox;
235-
const currentlyChangedModuleShortids = state.editor.changedModuleShortids.slice();
236231

237232
try {
238233
const changedModules = sandbox.modules.filter(module =>
239234
state.editor.changedModuleShortids.includes(module.shortid)
240235
);
241236

242-
state.editor.changedModuleShortids = [];
237+
const updatedModules = await effects.api.saveModules(
238+
sandbox.id,
239+
changedModules
240+
);
241+
242+
updatedModules.forEach(updatedModule => {
243+
const module = sandbox.modules.find(
244+
moduleItem => moduleItem.shortid === updatedModule.shortid
245+
);
243246

244-
await effects.api.saveModules(sandbox.id, changedModules);
245-
effects.moduleRecover.clearSandbox(sandbox.id);
247+
if (module) {
248+
module.insertedAt = updatedModule.insertedAt;
249+
module.updatedAt = updatedModule.updatedAt;
250+
251+
module.savedCode =
252+
updatedModule.code === module.code ? null : updatedModule.code;
253+
254+
effects.vscode.sandboxFsSync.writeFile(
255+
state.editor.modulesByPath,
256+
module
257+
);
258+
effects.moduleRecover.remove(sandbox.id, module);
259+
} else {
260+
// We might not have the module, as it was created by the server. In
261+
// this case we put it in. There is an edge case here where the user
262+
// might delete the module while it is being updated, but it will very
263+
// likely not happen
264+
sandbox.modules.push(updatedModule);
265+
}
266+
});
246267

247268
if (
248269
state.editor.currentSandbox.originalGit &&
@@ -253,13 +274,6 @@ export const saveClicked: AsyncAction = withOwnedSandbox(
253274

254275
effects.preview.executeCodeImmediately();
255276
} catch (error) {
256-
// Put back any unsaved modules taking into account that you
257-
// might have changed some modules waiting for saving
258-
currentlyChangedModuleShortids.forEach(moduleShortid => {
259-
if (!state.editor.changedModuleShortids.includes(moduleShortid)) {
260-
state.editor.changedModuleShortids.push(moduleShortid);
261-
}
262-
});
263277
actions.internal.handleError({
264278
message: 'There was a problem with saving the files, please try again',
265279
error,
@@ -292,6 +306,7 @@ export const forkSandboxClicked: AsyncAction = async ({
292306
}) => {
293307
if (
294308
state.editor.currentSandbox.owned &&
309+
!state.editor.currentSandbox.customTemplate &&
295310
!effects.browser.confirm('Do you want to fork your own sandbox?')
296311
) {
297312
return;
@@ -510,11 +525,6 @@ export const discardModuleChanges: Action<{
510525

511526
module.updatedAt = new Date().toString();
512527
effects.vscode.revertModule(module);
513-
514-
state.editor.changedModuleShortids.splice(
515-
state.editor.changedModuleShortids.indexOf(moduleShortid),
516-
1
517-
);
518528
};
519529

520530
export const fetchEnvironmentVariables: AsyncAction = async ({

0 commit comments

Comments
 (0)