Skip to content

Commit 04a5d32

Browse files
committed
Handle when there is no network and reconnecting
1 parent 6120deb commit 04a5d32

File tree

6 files changed

+520
-398
lines changed

6 files changed

+520
-398
lines changed

src/Auth/SignInModal.tsx

Lines changed: 102 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useContext } from 'react';
1+
import React, { useState, useContext, useEffect } from 'react';
22
import styled from 'styled-components';
33

44
import {
@@ -90,6 +90,22 @@ const SignInAgainButton = styled(Button)`
9090
}
9191
`;
9292

93+
const InfoWrapper = styled.div`
94+
padding: 5px;
95+
height: 100%;
96+
display: flex;
97+
margin: auto;
98+
align-items: center;
99+
justify-content: center;
100+
flex-direction: column;
101+
`;
102+
103+
const InfoMessage = styled.div`
104+
color: #5A5A6F;
105+
font-size: 16px;
106+
font-weight: 600;
107+
`;
108+
93109
const InputWrapper = styled.div`
94110
margin-bottom: 20px;
95111
width: 100%;
@@ -140,11 +156,11 @@ function SignInModal({ onCloseRequest }: SignInModalProps) {
140156
const [email, setEmail] = useState('');
141157
const [error, setError] = useState('');
142158

143-
const auth = useContext(AuthContext);
159+
const authInfo = useContext(AuthContext);
144160

145-
const isLoading = auth.state === AuthState.SigningInUser;
161+
const isLoading = authInfo.state === AuthState.SigningInUser;
146162

147-
const isSignedIn = auth.state === AuthState.UserAndMetadataLoaded;
163+
const isSignedIn = authInfo.state === AuthState.UserAndMetadataLoaded;
148164

149165
function handleEmailInputChange(e: any) {
150166
setEmail(e.target.value);
@@ -166,6 +182,7 @@ function SignInModal({ onCloseRequest }: SignInModalProps) {
166182

167183
try {
168184
await signIn(email);
185+
setError('');
169186
trackSignInFinished();
170187
} catch (error) {
171188
console.error(error.message);
@@ -180,90 +197,116 @@ function SignInModal({ onCloseRequest }: SignInModalProps) {
180197
}
181198

182199
function handleEmailInputOnKeyDown(e: any) {
183-
if (e.key === 'Enter') handleSignInButtonClick();
200+
if (e.key === 'Enter') {
201+
e.preventDefault();
202+
handleSignInButtonClick();
203+
}
184204
}
185205

186206
function handleContinueIntoAppButtonClick() {
187207
trackContinueIntoAppButtonClicked();
188208
onCloseRequest?.();
189209
}
190210

211+
useEffect(() => {
212+
if (authInfo.isReconnecting) {
213+
cancelSignIn();
214+
}
215+
}, [
216+
authInfo.isReconnecting,
217+
]);
218+
191219
return (
192220
<StyledModal
193221
onCloseRequest={handleCloseRequest}
194222
>
195-
{!isSignedIn && !isLoading &&
223+
224+
{authInfo.isReconnecting
225+
&&
226+
<InfoWrapper>
227+
<InfoMessage>Contacting Devbook servers failed.</InfoMessage>
228+
<InfoMessage>Reconnecting...</InfoMessage>
229+
</InfoWrapper>
230+
}
231+
232+
{!authInfo.isReconnecting &&
196233
<>
197-
<Title>
198-
Sign in with your email
234+
{!isSignedIn
235+
&& !isLoading
236+
&&
237+
<>
238+
<Title>
239+
Sign in with your email
199240
</Title>
200241

201-
<Description>
202-
Click on the sign-in button to receive an email with a sign-in link.
242+
<Description>
243+
Click on the sign-in button to receive an email with a sign-in link.
203244
</Description>
204245

205-
<InputWrapper>
206-
<InputTitle>EMAIL</InputTitle>
207-
<EmailInput
208-
autoFocus
209-
placeholder="[email protected]"
210-
value={email}
211-
onChange={handleEmailInputChange}
212-
onKeyDown={handleEmailInputOnKeyDown}
213-
/>
214-
</InputWrapper>
215-
216-
{error &&
217-
<Error>
218-
{error}
219-
</Error>
220-
}
221-
<SignInButton
222-
onClick={handleSignInButtonClick}
223-
>
224-
Sign in to Devbook
246+
<InputWrapper>
247+
<InputTitle>EMAIL</InputTitle>
248+
<EmailInput
249+
autoFocus
250+
placeholder="[email protected]"
251+
value={email}
252+
onChange={handleEmailInputChange}
253+
onKeyDown={handleEmailInputOnKeyDown}
254+
/>
255+
</InputWrapper>
256+
257+
{error &&
258+
<Error>
259+
{error}
260+
</Error>
261+
}
262+
<SignInButton
263+
onClick={handleSignInButtonClick}
264+
>
265+
Sign in to Devbook
225266
</SignInButton>
226-
</>
227-
}
267+
</>
268+
}
228269

229-
{!isSignedIn
230-
&& isLoading
231-
&& !error
232-
&&
233-
<>
234-
<Title>
235-
Check your email
270+
{!isSignedIn
271+
&& isLoading
272+
&& !error
273+
&&
274+
<>
275+
<Title>
276+
Check your email
236277
</Title>
237278

238-
<Description>
239-
We just sent an email with the sign-in link to the following email address:
279+
<Description>
280+
We just sent an email with the sign-in link to the following email address:
240281
<br />
241-
<strong>{email}</strong>
242-
</Description>
282+
<strong>{email}</strong>
283+
</Description>
243284

244-
<Description>
245-
Click on the link and you'll be signed-in in a few seconds...
285+
<Description>
286+
Click on the link and you'll be signed-in in a few seconds...
246287
</Description>
247288

248-
<Loader />
289+
<Loader />
249290

250-
<SignInAgainButton
251-
onClick={handleSignInAgainButtonClick}
252-
>
253-
Sign in again
291+
<SignInAgainButton
292+
onClick={handleSignInAgainButtonClick}
293+
>
294+
Sign in again
254295
</SignInAgainButton>
255-
</>
256-
}
296+
</>
297+
}
257298

258-
{isSignedIn &&
259-
<>
260-
<Title>
261-
You are signed in!
299+
{isSignedIn &&
300+
<>
301+
<Title>
302+
You are signed in!
262303
</Title>
263-
<CheckIcon />
264-
<ContinueIntoAppButton onClick={handleContinueIntoAppButtonClick}>
265-
Continue into the app
304+
<CheckIcon />
305+
<ContinueIntoAppButton onClick={handleContinueIntoAppButtonClick}>
306+
Continue into the app
266307
</ContinueIntoAppButton>
308+
</>
309+
}
267310
</>
268311
}
269312
</StyledModal>

src/Auth/index.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@ import {
1616
} from 'mainCommunication';
1717
import timeout from 'utils/timeout';
1818

19+
axios.interceptors.response.use((value) => {
20+
setIsReconnecting(false);
21+
return Promise.resolve(value);
22+
}, (error) => {
23+
if (error.response) {
24+
setIsReconnecting(false);
25+
return Promise.reject(error);
26+
}
27+
28+
if (error.request) {
29+
setIsReconnecting(true);
30+
console.log('Reconnecting');
31+
return timeout(1000).then(() => axios.request(error.config));
32+
}
33+
return Promise.reject(error);
34+
});
35+
1936
export enum AuthError {
2037
// The error when the looking for a valid stored user failed - probably because of the network connection.
2138
// User is no signed in and no metadata are present.
@@ -69,14 +86,14 @@ type SigningInUserAuthInfo = { state: AuthState.SigningInUser }
6986
type SigningOutUserAuthInfo = { state: AuthState.SigningOutUser, user?: User }
7087
type UserAndMetadataLoadedAuthInfo = { state: AuthState.UserAndMetadataLoaded, user: User };
7188

72-
export type AuthInfo = FailedSigningOutAuthInfo
89+
export type AuthInfo = (FailedSigningOutAuthInfo
7390
| FailedSigningInAuthInfo
7491
| NoUserAuthInfo
7592
| FailedLookingForStoredUserAuthInfo
7693
| SigningInUserAuthInfo
7794
| UserAndMetadataLoadedAuthInfo
7895
| LookingForStoredUserAuthInfo
79-
| SigningOutUserAuthInfo;
96+
| SigningOutUserAuthInfo) & { isReconnecting?: boolean }
8097

8198
export type { MagicUserMetadata };
8299

@@ -113,6 +130,13 @@ function deleteRefreshToken() {
113130
return electronStore.delete(refreshTokenStoreName);
114131
}
115132

133+
function setIsReconnecting(isReconnecting: boolean) {
134+
updateAuth({
135+
...auth,
136+
isReconnecting,
137+
});
138+
}
139+
116140
function changeAnalyticsUserAndSaveEmail(auth: AuthInfo) {
117141
if (auth.state === AuthState.UserAndMetadataLoaded) {
118142
const email = auth?.user?.email || undefined;
@@ -281,6 +305,7 @@ export async function refreshAuth() {
281305
updateAuth({ state: AuthState.UserAndMetadataLoaded, user });
282306
setRefreshToken(refreshToken);
283307
} catch (error) {
308+
deleteRefreshToken();
284309
updateAuth({ state: AuthState.NoUser, error: AuthError.FailedLookingForStoredUser });
285310
}
286311
}

src/Home/SearchHeaderPanel/SearchInput.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, {
22
useState,
33
useEffect,
44
useCallback,
5+
useContext,
56
} from 'react';
67
import styled from 'styled-components';
78

@@ -12,6 +13,7 @@ import useDebounce from 'hooks/useDebounce';
1213
import { DocSource } from 'search/docs';
1314
import Loader from 'components/Loader';
1415
import Hotkey from 'Home/HotkeysPanel/Hotkey';
16+
import { AuthContext } from 'Auth';
1517

1618
const Input = styled.input`
1719
padding: 10px 15px;
@@ -100,6 +102,8 @@ function SearchInput({
100102
searchMode,
101103
onInputFocusChange,
102104
}: SearchInputProps) {
105+
const authInfo = useContext(AuthContext);
106+
103107
const [value, setValue] = useState('');
104108
const [lastValue, setLastValue] = useState('');
105109
const [lastDocSource, setLastDocSource] = useState<DocSource>();
@@ -231,7 +235,11 @@ function SearchInput({
231235
onBlur={() => onInputFocusChange(false)}
232236
onKeyDown={handleInputKeyDown}
233237
/>
234-
{isLoading && <StyledLoader />}
238+
{isLoading
239+
&& !authInfo.isReconnecting
240+
&&
241+
< StyledLoader />
242+
}
235243

236244
{searchMode === SearchMode.OnEnterPress &&
237245
<HotkeyWrapper

src/Home/SearchHeaderPanel/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import electron, {
1414
postponeUpdate,
1515
togglePinMode,
1616
} from 'mainCommunication';
17-
import Loader from 'components/Loader';
1817
import { PreferencesPage } from 'Preferences';
1918
import { IPCMessage } from 'mainCommunication/ipc';
2019

0 commit comments

Comments
 (0)