Skip to content

Commit 566eb3c

Browse files
committed
Merge branch 'master' into prototype-common-sync
2 parents 70d3617 + a4513fb commit 566eb3c

File tree

48 files changed

+2586
-635
lines changed

Some content is hidden

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

48 files changed

+2586
-635
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,10 @@ type ModalName =
7474
| 'netlifyLogs'
7575
| 'newSandbox'
7676
| 'preferences'
77-
| 'share'
7877
| 'searchDependencies'
78+
| 'share'
7979
| 'signInForTemplates'
8080
| 'userSurvey';
81-
8281
export const modalOpened: Action<{ modal: ModalName; message?: string }> = (
8382
{ state, effects },
8483
{ modal, message }

packages/app/src/app/overmind/effects/analytics.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import track, {
22
identify,
3+
logError,
34
setAnonymousId,
45
setUserId,
56
} from '@codesandbox/common/lib/utils/analytics';
@@ -16,6 +17,7 @@ export default (() => {
1617
trackedEvents[event] = true;
1718
track(event, data);
1819
},
20+
logError,
1921
identify,
2022
setAnonymousId,
2123
setUserId,

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

Lines changed: 31 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
import { Module } from '@codesandbox/common/lib/types';
12
/* eslint-disable camelcase */
2-
import axios, { AxiosResponse, AxiosError, AxiosRequestConfig } from 'axios';
3-
import { logError } from '@codesandbox/common/lib/utils/analytics';
4-
import { values } from 'lodash-es';
3+
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
54
import { camelizeKeys, decamelizeKeys } from 'humps';
6-
import { Module } from '@codesandbox/common/lib/types';
75

86
export const API_ROOT = '/api/v1';
97

10-
type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> };
8+
export type ApiError = AxiosError<
9+
{ errors: string[] } | { error: string } | any
10+
>;
1111

1212
export type Params = {
1313
[key: string]: string;
@@ -32,36 +32,17 @@ export type ApiConfig = {
3232
[path: string]: Module;
3333
};
3434
getParsedConfigurations: () => any;
35-
onError: (error: string) => void;
35+
onError: (error: ApiError) => void;
3636
};
3737

38-
export default (config: {
39-
provideJwtToken: () => string;
40-
onError: (error: string) => void;
41-
}) => {
38+
export default (config: ApiConfig) => {
4239
const createHeaders = (jwt: string) =>
4340
jwt
4441
? {
4542
Authorization: `Bearer ${jwt}`,
4643
}
4744
: {};
4845

49-
const showError = error => {
50-
config.onError(error.message);
51-
error.apiMessage = error.message; // eslint-disable-line no-param-reassign
52-
};
53-
54-
const handleError = error => {
55-
const newError = convertError(error);
56-
try {
57-
showError(newError);
58-
} catch (e) {
59-
console.error(e);
60-
}
61-
62-
throw newError;
63-
};
64-
6546
const api: Api = {
6647
get(path, params, options) {
6748
return axios
@@ -70,31 +51,43 @@ export default (config: {
7051
headers: createHeaders(config.provideJwtToken()),
7152
})
7253
.then(response => handleResponse(response, options))
73-
.catch(e => handleError(e));
54+
.catch(e => {
55+
config.onError(e);
56+
return Promise.reject(e);
57+
});
7458
},
7559
post(path, body, options) {
7660
return axios
7761
.post(API_ROOT + path, decamelizeKeys(body), {
7862
headers: createHeaders(config.provideJwtToken()),
7963
})
8064
.then(response => handleResponse(response, options))
81-
.catch(e => handleError(e));
65+
.catch(e => {
66+
config.onError(e);
67+
return Promise.reject(e);
68+
});
8269
},
8370
patch(path, body, options) {
8471
return axios
8572
.patch(API_ROOT + path, decamelizeKeys(body), {
8673
headers: createHeaders(config.provideJwtToken()),
8774
})
8875
.then(response => handleResponse(response, options))
89-
.catch(e => handleError(e));
76+
.catch(e => {
77+
config.onError(e);
78+
return Promise.reject(e);
79+
});
9080
},
9181
put(path, body, options) {
9282
return axios
9383
.put(API_ROOT + path, decamelizeKeys(body), {
9484
headers: createHeaders(config.provideJwtToken()),
9585
})
9686
.then(response => handleResponse(response, options))
97-
.catch(e => handleError(e));
87+
.catch(e => {
88+
config.onError(e);
89+
return Promise.reject(e);
90+
});
9891
},
9992
delete(path, params, options) {
10093
return axios
@@ -103,7 +96,10 @@ export default (config: {
10396
headers: createHeaders(config.provideJwtToken()),
10497
})
10598
.then(response => handleResponse(response, options))
106-
.catch(e => handleError(e));
99+
.catch(e => {
100+
config.onError(e);
101+
return Promise.reject(e);
102+
});
107103
},
108104
request(requestConfig, options) {
109105
return axios
@@ -115,49 +111,16 @@ export default (config: {
115111
})
116112
)
117113
.then(response => handleResponse(response, options))
118-
.catch(e => handleError(e));
114+
.catch(e => {
115+
config.onError(e);
116+
return Promise.reject(e);
117+
});
119118
},
120119
};
121120

122121
return api;
123122
};
124123

125-
function convertError(error: AxiosError) {
126-
const { response } = error;
127-
128-
if (!response || response.status >= 500) {
129-
logError(error);
130-
}
131-
132-
if (response && response.data) {
133-
if (response.data.errors) {
134-
const errors = values(response.data.errors)[0];
135-
if (Array.isArray(errors)) {
136-
if (errors[0]) {
137-
error.message = errors[0]; // eslint-disable-line no-param-reassign,prefer-destructuring
138-
}
139-
} else {
140-
error.message = errors; // eslint-disable-line no-param-reassign
141-
}
142-
} else if (response.data.error) {
143-
const { error_code, message, ...data } = response.data.error as {
144-
message: string;
145-
error_code: string;
146-
[k: string]: any;
147-
};
148-
// @ts-ignore
149-
error.error_code = error_code; // eslint-disable-line no-param-reassign
150-
error.message = message; // eslint-disable-line no-param-reassign
151-
// @ts-ignore
152-
error.data = data; // eslint-disable-line no-param-reassign
153-
} else if (response.status === 413) {
154-
return 'File too large, upload limit is 5MB.';
155-
}
156-
}
157-
158-
return error;
159-
}
160-
161124
export function handleResponse(
162125
response: AxiosResponse,
163126
{ shouldCamelize = true } = {}

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

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { Contributor } from '@codesandbox/common/lib/types';
22
import { IDerive, IState, json } from 'overmind';
3+
import { AxiosError } from 'axios';
34

5+
import { notificationState } from '@codesandbox/common/lib/utils/notifications';
6+
import { NotificationStatus } from '@codesandbox/notifications';
47
import { AsyncAction } from '.';
58

69
export const withLoadApp = <T>(
@@ -38,11 +41,31 @@ export const withLoadApp = <T>(
3841
actions.userNotifications.internal.initialize();
3942
effects.api.preloadTemplates();
4043
} catch (error) {
41-
effects.notificationToast.error(
42-
'Your session seems to be expired, please log in again...'
43-
);
44-
effects.jwt.reset();
45-
effects.analytics.setAnonymousId();
44+
if (error.isAxiosError && (error as AxiosError).response.status === 401) {
45+
// Reset existing sign in info
46+
effects.jwt.reset();
47+
effects.analytics.setAnonymousId();
48+
49+
// Allow user to sign in again in notification
50+
notificationState.addNotification({
51+
message: 'Your session seems to be expired, please log in again...',
52+
status: NotificationStatus.ERROR,
53+
actions: {
54+
primary: [
55+
{
56+
label: 'Sign in',
57+
run: () => {
58+
actions.signInClicked({ useExtraScopes: false });
59+
},
60+
},
61+
],
62+
},
63+
});
64+
} else {
65+
effects.notificationToast.error(
66+
"We weren't able to sign you in, this could be due to a flaky connection or something on our server. Please try again in a minute."
67+
);
68+
}
4669
}
4770
} else {
4871
effects.jwt.reset();
@@ -77,28 +100,30 @@ export const withOwnedSandbox = <T>(
77100
): AsyncAction<T> => async (context, payload) => {
78101
const { state, actions } = context;
79102

80-
if (!state.editor.currentSandbox.owned) {
81-
if (state.editor.isForkingSandbox) {
82-
return cancelAction(context, payload);
83-
}
84-
85-
await actions.editor.internal.forkSandbox({
86-
sandboxId: state.editor.currentId,
87-
});
88-
} else if (
89-
state.editor.currentSandbox.isFrozen &&
90-
state.editor.sessionFrozen
91-
) {
92-
const modalResponse = await actions.modals.forkFrozenModal.open();
103+
if (state.editor.currentSandbox) {
104+
if (!state.editor.currentSandbox.owned) {
105+
if (state.editor.isForkingSandbox) {
106+
return cancelAction(context, payload);
107+
}
93108

94-
if (modalResponse === 'fork') {
95109
await actions.editor.internal.forkSandbox({
96110
sandboxId: state.editor.currentId,
97111
});
98-
} else if (modalResponse === 'unfreeze') {
99-
state.editor.sessionFrozen = false;
100-
} else if (modalResponse === 'cancel') {
101-
return cancelAction(context, payload);
112+
} else if (
113+
state.editor.currentSandbox.isFrozen &&
114+
state.editor.sessionFrozen
115+
) {
116+
const modalResponse = await actions.modals.forkFrozenModal.open();
117+
118+
if (modalResponse === 'fork') {
119+
await actions.editor.internal.forkSandbox({
120+
sandboxId: state.editor.currentId,
121+
});
122+
} else if (modalResponse === 'unfreeze') {
123+
state.editor.sessionFrozen = false;
124+
} else if (modalResponse === 'cancel') {
125+
return cancelAction(context, payload);
126+
}
102127
}
103128
}
104129

0 commit comments

Comments
 (0)