Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / components / containers / app / errorRefresh.ts
blob188afc4b3d0620178e61680ef39d39bd3e9a661f
1 import { deleteVersionCookies } from '@proton/components/helpers/versionCookie';
2 import { getApiError } from '@proton/shared/lib/api/helpers/apiErrorHelper';
3 import { wait } from '@proton/shared/lib/helpers/promise';
4 import noop from '@proton/utils/noop';
6 import { setCurrentRetries } from '../../helpers/earlyAccessDesynchronization';
8 const API_ERROR_KEY = 'API_ERROR_REFRESH';
9 const EARLY_ACCESS_KEY = 'EARLY_ACCESS_RESET';
11 export const clearAutomaticErrorRefresh = () => {
12     sessionStorage.removeItem(API_ERROR_KEY);
13     sessionStorage.removeItem(EARLY_ACCESS_KEY);
16 const handleEarlyAccessRefresh = () => {
17     const oldValue = Number(sessionStorage.getItem(EARLY_ACCESS_KEY));
18     // If it's the first time, we try a simple reload.
19     if (!oldValue) {
20         sessionStorage.setItem(EARLY_ACCESS_KEY, '1');
21         window.location.reload();
22         return true;
23     }
24     // If this error happens a second time, we force delete everything regarding version cookie and attempt another reload.
25     if (oldValue === 1) {
26         sessionStorage.setItem(EARLY_ACCESS_KEY, '2');
27         deleteVersionCookies();
28         setCurrentRetries(2);
29         window.location.reload();
30         return true;
31     }
32     return false;
35 const handleApiErrorRefresh = () => {
36     if (!sessionStorage.getItem(API_ERROR_KEY)) {
37         sessionStorage.setItem(API_ERROR_KEY, 'true');
38         window.location.reload();
39         return true;
40     }
41     return false;
44 const handleError = (error: any) => {
45     const apiError = getApiError(error);
46     // If there is no API error message or it's a chunk loading error, assume it's a resource loading error.
47     if (!apiError.message || error?.message?.includes('chunk')) {
48         if (handleEarlyAccessRefresh()) {
49             return true;
50         }
51     } else if (apiError.message) {
52         if (handleApiErrorRefresh()) {
53             return true;
54         }
55     }
56     return false;
59 export const wrapUnloadError = <T>(promise: Promise<T>): Promise<T> => {
60     // In Firefox, navigation events cancel ongoing network requests. This triggers the error handler and error
61     // screen to be displayed. We set up a 'beforeunload' listener to detect unload to not unnecessarily
62     // show cancelled network requests errors as fatal errors, and keep showing the loader screen instead.
63     let unloaded = false;
65     const handleUnload = () => {
66         unloaded = true;
67     };
69     const listen = () => {
70         window.addEventListener('beforeunload', handleUnload);
71         return () => {
72             window.removeEventListener('beforeunload', handleUnload);
73         };
74     };
76     const unlisten = listen();
78     return promise
79         .then((result) => {
80             clearAutomaticErrorRefresh();
81             unlisten();
82             return result;
83         })
84         .catch(async (e) => {
85             // Wait an arbitrary amount of time to ensure the unload listener is first.
86             await wait(3000);
87             unlisten();
88             // Never resolve the promise if the browser has unloaded or if we're refreshing
89             if (unloaded || handleError(e)) {
90                 return new Promise(noop);
91             }
92             throw e;
93         });