Files
notesnook/apps/mobile/src/components/auth/sessionexpired.js

259 lines
7.3 KiB
JavaScript
Raw Normal View History

2022-02-07 14:44:48 +05:00
import React, { useEffect, useRef, useState } from 'react';
import { Modal, View } from 'react-native';
2022-04-24 05:59:14 +05:00
import { useThemeStore } from '../../stores/use-theme-store';
import { useUserStore } from '../../stores/use-user-store';
2022-02-28 23:25:18 +05:00
import BiometricService from '../../services/biometrics';
2022-02-07 14:44:48 +05:00
import {
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent,
presentSheet,
ToastEvent
2022-02-28 23:25:18 +05:00
} from '../../services/event-manager';
import { clearMessage } from '../../services/message';
import PremiumService from '../../services/premium';
import Sync from '../../services/sync';
2022-02-07 14:44:48 +05:00
import { db } from '../../utils/database';
2022-02-28 13:48:59 +05:00
import { MMKV } from '../../utils/database/mmkv';
import { SIZE } from '../../utils/size';
import { sleep } from '../../utils/time';
import { IconButton } from '../ui/icon-button';
import { Button } from '../ui/button';
import { Dialog } from '../dialog';
import { presentDialog } from '../dialog/functions';
import Input from '../ui/input';
import { Toast } from '../toast';
import Heading from '../ui/typography/heading';
import Paragraph from '../ui/typography/paragraph';
2022-03-03 15:09:30 +05:00
import SettingsService from '../../services/settings';
2022-03-18 14:40:15 +05:00
import TwoFactorVerification from './two-factor';
import SheetProvider from '../sheet-provider';
import { Progress } from '../sheets/progress';
2022-02-07 14:44:48 +05:00
function getEmail(email) {
if (!email) return null;
return email.replace(/(.{2})(.*)(?=@)/, function (gp1, gp2, gp3) {
for (let i = 0; i < gp3.length; i++) {
gp2 += '*';
}
return gp2;
});
}
export const SessionExpired = () => {
2022-02-28 23:25:18 +05:00
const colors = useThemeStore(state => state.colors);
2022-02-07 14:44:48 +05:00
const email = useRef();
const emailInputRef = useRef();
const passwordInputRef = useRef();
const password = useRef();
const [visible, setVisible] = useState(false);
const [focused, setFocused] = useState(false);
const setUser = useUserStore(state => state.setUser);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const logout = async () => {
try {
await db.user.logout();
await BiometricService.resetCredentials();
2022-03-03 15:09:30 +05:00
await SettingsService.set({
introCompleted: true
});
2022-02-07 14:44:48 +05:00
setVisible(false);
} catch (e) {
ToastEvent.show({
heading: e.message,
type: 'error',
context: 'local'
});
}
};
useEffect(() => {
eSubscribeEvent('session_expired', open);
return () => {
eUnSubscribeEvent('session_expired', open);
setFocused(false);
};
}, [visible]);
const open = async () => {
try {
console.log('REQUESTING NEW TOKEN');
let res = await db.user.tokenManager.getToken();
if (!res) throw new Error('no token found');
if (db.user.tokenManager._isTokenExpired(res)) throw new Error('token expired');
if (!(await Sync.run())) throw new Error('e');
2022-03-03 18:03:03 +05:00
await SettingsService.set({
sessionExpired: false
});
2022-02-07 14:44:48 +05:00
setVisible(false);
} catch (e) {
console.log(e);
let user = await db.user.getUser();
if (!user) return;
email.current = user.email;
setVisible(true);
}
};
2022-03-18 14:40:15 +05:00
const login = async (mfa, callback) => {
2022-02-07 14:44:48 +05:00
if (error) return;
setLoading(true);
let user;
try {
2022-03-18 14:40:15 +05:00
if (mfa) {
await db.user.mfaLogin(email.current.toLowerCase(), password.current, mfa);
} else {
await db.user.login(email.current.toLowerCase(), password.current);
}
callback && callback(true);
setVisible(false);
2022-02-07 14:44:48 +05:00
user = await db.user.getUser();
if (!user) throw new Error('Email or password incorrect!');
PremiumService.setPremiumStatus();
setUser(user);
clearMessage();
ToastEvent.show({
heading: 'Login successful',
message: `Logged in as ${user.email}`,
type: 'success',
context: 'global'
});
2022-03-03 18:03:03 +05:00
await SettingsService.set({
2022-03-03 22:31:23 +05:00
sessionExpired: false,
2022-07-09 09:45:16 +05:00
userEmailConfirmed: user?.isEmailConfirmed
2022-03-03 18:03:03 +05:00
});
2022-02-07 14:44:48 +05:00
eSendEvent('userLoggedIn', true);
await sleep(500);
Progress.present();
2022-03-23 17:49:01 +05:00
setLoading(false);
2022-02-07 14:44:48 +05:00
} catch (e) {
2022-03-23 17:49:01 +05:00
callback && callback(false);
if (e.message === 'Multifactor authentication required.') {
2022-03-18 14:40:15 +05:00
TwoFactorVerification.present(async mfa => {
if (mfa) {
2022-03-23 17:49:01 +05:00
console.log(mfa);
2022-03-18 14:40:15 +05:00
await login(mfa);
2022-03-23 17:49:01 +05:00
} else {
setLoading(false);
2022-03-18 14:40:15 +05:00
}
}, e.data);
} else {
console.log(e.stack);
setLoading(false);
ToastEvent.show({
heading: user ? 'Failed to sync' : 'Login failed',
message: e.message,
type: 'error',
context: 'local'
});
}
2022-02-07 14:44:48 +05:00
}
};
return (
visible && (
<Modal
onShow={async () => {
await sleep(300);
passwordInputRef.current?.focus();
setFocused(true);
}}
visible={true}
>
2022-03-18 14:40:15 +05:00
<SheetProvider context="two_factor_verify" />
2022-02-07 14:44:48 +05:00
<View
style={{
width: focused ? '100%' : '99.9%',
padding: 12,
justifyContent: 'center',
flex: 1
}}
>
<View
style={{
justifyContent: 'center',
alignItems: 'center',
width: '100%',
marginBottom: 20,
borderRadius: 10,
paddingVertical: 20
}}
>
2022-02-28 13:48:59 +05:00
<IconButton
2022-02-07 14:44:48 +05:00
customStyle={{
width: 60,
height: 60
}}
name="alert"
color={colors.errorText}
size={50}
/>
<Heading size={SIZE.xxxl} color={colors.heading}>
Session expired
</Heading>
<Paragraph
style={{
textAlign: 'center'
}}
>
Your session on this device has expired. Please enter password for{' '}
{getEmail(email.current)} to continue.
</Paragraph>
</View>
<Input
fwdRef={passwordInputRef}
onChangeText={value => {
password.current = value;
}}
returnKeyLabel="Next"
returnKeyType="next"
secureTextEntry
2022-02-28 13:48:59 +05:00
autoComplete="password"
2022-02-07 14:44:48 +05:00
autoCapitalize="none"
autoCorrect={false}
placeholder="Password"
onSubmit={() => login()}
2022-02-07 14:44:48 +05:00
/>
<Button
style={{
marginTop: 10,
width: '100%'
}}
loading={loading}
onPress={() => login()}
2022-02-07 14:44:48 +05:00
type="accent"
title={loading ? null : 'Login'}
/>
<Button
style={{
marginTop: 10,
width: '100%'
}}
onPress={() => {
presentDialog({
context: 'session_expiry',
title: 'Logout',
paragraph:
'Are you sure you want to logout from this device? Any unsynced changes will be lost.',
positiveText: 'Logout',
positiveType: 'errorShade',
positivePress: logout
});
}}
type="errorShade"
title={loading ? null : 'Logout from this device'}
/>
</View>
<Toast context="local" />
<Dialog context="session_expiry" />
</Modal>
)
);
};