add relogin flow after user session expired.

This commit is contained in:
ammarahm-ed
2021-05-25 22:14:06 +05:00
parent fde5938b67
commit c5debd3e94
11 changed files with 214 additions and 44 deletions

View File

@@ -87,9 +87,15 @@ async function reconnectSSE(connection) {
let user = await db.user.getUser();
if (user && state.isConnected && state.isInternetReachable) {
await doInBackground(async () => {
await db.connectSSE();
let res = await doInBackground(async () => {
try {
await db.connectSSE();
return true;
} catch (e) {
return e.message;
}
});
if (res !== true) throw new Error(res);
}
} catch (e) {}
}
@@ -110,6 +116,7 @@ export const AppRootEvents = React.memo(
EV.subscribe(EVENTS.databaseSyncRequested, partialSync);
EV.subscribe(EVENTS.userLoggedOut, onLogout);
EV.subscribe(EVENTS.userEmailConfirmed, onEmailVerified);
EV.subscribe(EVENTS.userSessionExpired, onSessionExpired);
EV.subscribe(EVENTS.userCheckStatus, PremiumService.onUserStatusCheck);
EV.subscribe(EVENTS.userSubscriptionUpdated, onAccountStatusChange);
EV.subscribe(EVENTS.noteRemoved, onNoteRemoved);
@@ -119,6 +126,7 @@ export const AppRootEvents = React.memo(
);
return () => {
eUnSubscribeEvent('userLoggedIn', setCurrentUser);
EV.unsubscribe(EVENTS.userSessionExpired, onSessionExpired);
EV.unsubscribe(EVENTS.appRefreshRequested, onSyncComplete);
EV.unsubscribe(EVENTS.databaseSyncRequested, partialSync);
EV.unsubscribe(EVENTS.userLoggedOut, onLogout);
@@ -135,6 +143,18 @@ export const AppRootEvents = React.memo(
};
}, []);
const onSessionExpired = async () => {
await Storage.write('loginSessionHasExpired', 'expired');
return new Promise((res, rej) => {
eSendEvent(eOpenLoginDialog, 4);
let onsuccess = () => {
res(true);
eUnSubscribeEvent('reLoginSuccess', onsuccess);
};
eSubscribeEvent('reLoginSuccess', onsuccess);
});
};
const onNoteRemoved = async id => {
try {
await db.notes.remove(id);
@@ -251,12 +271,24 @@ export const AppRootEvents = React.memo(
const partialSync = async () => {
try {
dispatch({type: Actions.SYNCING, syncing: true});
await doInBackground(async () => {
await db.sync(false);
let res = await doInBackground(async () => {
try {
await db.sync(false);
return true;
} catch (e) {
return e.message;
}
});
if (res !== true) throw new Error(res);
dispatch({type: Actions.LAST_SYNC, lastSync: await db.lastSynced()});
} catch (e) {
dispatch({type: Actions.SYNCING, syncing: false});
ToastEvent.show({
heading: 'Sync failed',
message: e.message,
context: context,
});
} finally {
dispatch({type: Actions.SYNCING, syncing: false});
}
@@ -305,9 +337,16 @@ export const AppRootEvents = React.memo(
await PremiumService.setPremiumStatus();
attachIAPListeners();
await Sync.run();
await doInBackground(async () => {
user = await db.user.fetchUser(true);
let res = await doInBackground(async () => {
try {
user = await db.user.fetchUser();
return true;
} catch (e) {
return e.message;
}
});
if (res !== true) throw new Error(res);
if (!user.isEmailConfirmed) {
setEmailVerifyMessage(dispatch);
return;
@@ -436,7 +475,7 @@ export const AppRootEvents = React.memo(
let user = await db.user.getUser();
if (user && !user.isEmailConfirmed) {
try {
let user = await db.user.fetchUser(true);
let user = await db.user.fetchUser();
if (user.isEmailConfirmed) {
onEmailVerified(dispatch);
}

View File

@@ -659,7 +659,7 @@ export const ActionSheetComponent = ({
<TouchableOpacity
activeOpacity={0.9}
testID={notesnook.ids.dialogs.actionsheet.sync}
onPress={async () => await Sync.run()}
onPress={async () => await Sync.run("local")}
style={{
borderColor: colors.accent,
paddingHorizontal: 5,

View File

@@ -14,6 +14,7 @@ const DialogButtons = ({
negativeTitle = 'Cancel',
loading,
doneText,
positiveType
}) => {
const [state] = useTracked();
const {colors} = state;
@@ -59,7 +60,7 @@ const DialogButtons = ({
style={{
marginLeft: 10,
}}
type="transparent"
type={positiveType || "transparent"}
title={positiveTitle}
/>
)}

View File

@@ -20,6 +20,7 @@ import {clearMessage, setEmailVerifyMessage} from '../../services/Message';
import PremiumService from '../../services/PremiumService';
import Sync from '../../services/Sync';
import {getElevation} from '../../utils';
import {hexToRGBA} from '../../utils/ColorUtils';
import {db} from '../../utils/DB';
import {
eCloseProgressDialog,
@@ -31,9 +32,13 @@ import {
import {openLinkInBrowser} from '../../utils/functions';
import {MMKV} from '../../utils/mmkv';
import {SIZE} from '../../utils/SizeUtils';
import Storage from '../../utils/storage';
import {sleep} from '../../utils/TimeUtils';
import {ActionIcon} from '../ActionIcon';
import BaseDialog from '../Dialog/base-dialog';
import DialogButtons from '../Dialog/dialog-buttons';
import DialogContainer from '../Dialog/dialog-container';
import DialogHeader from '../Dialog/dialog-header';
import Input from '../Input';
import {Header} from '../SimpleList/header';
import Heading from '../Typography/Heading';
@@ -44,6 +49,7 @@ const MODES = {
signup: 1,
forgotPassword: 2,
changePassword: 3,
sessionExpired: 4,
};
let email = '';
@@ -52,6 +58,15 @@ let password = '';
let confirmPassword;
let oldPassword;
function getEmail() {
return email.replace(/(.{2})(.*)(?=@)/, function (gp1, gp2, gp3) {
for (let i = 0; i < gp3.length; i++) {
gp2 += '*';
}
return gp2;
});
}
const LoginDialog = () => {
const [state, dispatch] = useTracked();
const colors = state.colors;
@@ -62,6 +77,7 @@ const LoginDialog = () => {
const [mode, setMode] = useState(MODES.login);
const [error, setError] = useState(false);
const [focused, setFocused] = useState(false);
const [confirm, setConfirm] = useState(false);
const insets = useSafeAreaInsets();
const _email = useRef();
@@ -140,19 +156,42 @@ const LoginDialog = () => {
showLoader: true,
buttonAlt: null,
},
{
headerButton: 'Session expired',
headerButtonFunc: () => {},
button: 'Login',
buttonFunc: () => loginUser(),
headerParagraph: '',
showForgotButton: true,
loading: 'Please wait while we log in and sync your data.',
showLoader: true,
buttonAlt: 'Logout & delete data',
buttonAltFunc: () => {
setConfirm(true);
},
},
];
const current = MODE_DATA[mode];
useEffect(() => {
MMKV.getItem('loginSessionHasExpired').then(r => {
if (r === 'expired') {
open(MODES.sessionExpired);
}
});
eSubscribeEvent(eOpenLoginDialog, open);
return () => {
eUnSubscribeEvent(eOpenLoginDialog, open);
};
}, []);
function open(mode) {
async function open(mode) {
setMode(mode ? mode : MODES.login);
if (mode === MODES.sessionExpired) {
let user = await db.user.getUser();
email = user.email;
}
setStatus(null);
setVisible(true);
}
@@ -191,7 +230,8 @@ const LoginDialog = () => {
setStatus('Logging in');
let user;
try {
await db.user.login(email.toLowerCase(), password, true);
console.log(email, password);
await db.user.login(email.toLowerCase(), password);
user = await db.user.getUser();
if (!user) throw new Error('Email or password incorrect!');
setStatus('Syncing Your Data');
@@ -205,13 +245,18 @@ const LoginDialog = () => {
context: 'local',
});
close();
await sleep(300);
eSendEvent('userLoggedIn', true);
eSendEvent(eOpenProgressDialog, {
title: 'Syncing your data',
paragraph: 'Please wait while we sync all your data.',
noProgress: false,
});
if (MODES.sessionExpired !== mode) {
await sleep(300);
eSendEvent('userLoggedIn', true);
eSendEvent(eOpenProgressDialog, {
title: 'Syncing your data',
paragraph: 'Please wait while we sync all your data.',
noProgress: false,
});
} else {
await MMKV.removeItem('loginSessionHasExpired');
eSendEvent('reLoginSuccess');
}
} catch (e) {
setLoading(false);
setStatus(null);
@@ -294,7 +339,7 @@ const LoginDialog = () => {
}
};
const sendEmail = async () => {
const sendEmail = async nostatus => {
if (!email || error) {
ToastEvent.show({
heading: 'Account email is required.',
@@ -311,7 +356,7 @@ const LoginDialog = () => {
) {
throw new Error('Please wait before requesting another email');
}
setStatus('Password Recovery Email Sent!');
!nostatus && setStatus('Password Recovery Email Sent!');
await db.user.recoverAccount(email);
await MMKV.setItem('lastRecoveryEmailTime', JSON.stringify(Date.now()));
} catch (e) {
@@ -365,14 +410,49 @@ const LoginDialog = () => {
animated={true}
animationType={DDS.isTab ? 'fade' : 'slide'}
statusBarTranslucent={true}
onRequestClose={close}
onRequestClose={MODES.sessionExpired !== mode && close}
visible={true}
onShow={() => {
setTimeout(() => {
if (MODES.sessionExpired === mode) {
_pass.current?.focus();
return;
}
_email.current?.focus();
}, 150);
}}
transparent={true}>
{confirm && (
<BaseDialog
onRequestClose={() => {
setConfirm(false);
}}
visible>
<DialogContainer>
<DialogHeader
title="Logout & delete data"
paragraph="All synced and unsynced data on this device will be removed. Do you want to proceed?"
paragraphColor="red"
/>
<DialogButtons
negativeTitle="Cancel"
onPressNegative={() => {
setConfirm(false);
}}
positiveType="error"
positiveTitle="Logout"
onPressPositive={async () => {
await db.user.logout();
await BiometricService.resetCredentials();
await Storage.write('introCompleted', 'true');
setConfirm(false);
close();
}}
/>
</DialogContainer>
</BaseDialog>
)}
{status ? (
<BaseDialog
visible={true}
@@ -475,6 +555,7 @@ const LoginDialog = () => {
name="close"
size={SIZE.xxxl}
onPress={() => {
if (MODES.sessionExpired === mode) return;
close();
}}
customStyle={{
@@ -489,6 +570,7 @@ const LoginDialog = () => {
name="arrow-left"
size={SIZE.xxxl}
onPress={() => {
if (MODES.sessionExpired === mode) return;
close();
}}
customStyle={{
@@ -512,6 +594,33 @@ const LoginDialog = () => {
onPress={mode !== MODES.changePassword && current.headerButtonFunc}
paragraph={mode !== MODES.changePassword && current.headerParagraph}
/>
{mode === MODES.sessionExpired && (
<View
style={{
width: '100%',
paddingHorizontal: 12,
paddingVertical: 12,
marginTop: 10,
flexDirection: 'row',
alignItems: 'center',
flexShrink: 1,
backgroundColor: hexToRGBA(colors.red, 0.2),
}}>
<Icon
size={20}
style={{marginRight: 10}}
name="information"
color={colors.errorText}
/>
<Paragraph
style={{flexWrap: 'wrap', flex: 1}}
color={colors.errorText}>
Please login to your account to access your notes on this device
and sync them.
</Paragraph>
</View>
)}
{mode === MODES.signup && (
<View
style={{
@@ -557,6 +666,8 @@ const LoginDialog = () => {
onErrorCheck={r => {
setError(r);
}}
loading={MODES.sessionExpired === mode}
defaultValue={MODES.sessionExpired === mode ? getEmail() : null}
onFocusInput={onChangeFocus}
returnKeyLabel="Next"
returnKeyType="next"
@@ -629,18 +740,23 @@ const LoginDialog = () => {
</>
)}
{mode === MODES.login && (
<TouchableOpacity
onPress={() => {
setMode(MODES.forgotPassword);
}}
style={{
alignSelf: 'flex-end',
marginTop: 2.5,
}}>
<Paragraph color={colors.accent}>Forgot password?</Paragraph>
</TouchableOpacity>
)}
{mode === MODES.login ||
(mode === MODES.sessionExpired && (
<TouchableOpacity
onPress={() => {
if (MODES.sessionExpired === mode) {
sendEmail(true);
return;
}
setMode(MODES.forgotPassword);
}}
style={{
alignSelf: 'flex-end',
marginTop: 2.5,
}}>
<Paragraph color={colors.accent}>Forgot password?</Paragraph>
</TouchableOpacity>
))}
<Seperator />
{mode !== MODES.signup && mode !== MODES.changePassword ? null : (
<>
@@ -733,7 +849,7 @@ const LoginDialog = () => {
title={current.buttonAlt}
onPress={current.buttonAltFunc}
width="100%"
type="shade"
type={MODES.sessionExpired === mode ? 'error' : 'shade'}
fontSize={SIZE.md}
style={{
marginTop: 10,

View File

@@ -80,7 +80,7 @@ export const UserSection = () => {
{user && (
<TouchableOpacity
activeOpacity={0.8}
onPress={async ()=>await Sync.run('local')}
onPress={async ()=>await Sync.run()}
style={{
flexDirection: 'row',
alignItems: 'center',

View File

@@ -251,7 +251,7 @@ const MergeEditor = () => {
updateEvent({type: Actions.NOTES});
updateEvent({type: Actions.FAVORITES});
close();
doInBackground(async () => {Sync.run()});
await Sync.run();
};
const show = async (item) => {

View File

@@ -79,7 +79,7 @@ const SimpleList = ({
}, [listData, deviceMode, loading]);
const _onRefresh = async () => {
await Sync.run('local');
await Sync.run();
if (refreshCallback) {
refreshCallback();
}

View File

@@ -33,6 +33,7 @@ export const Toast = ({context = 'global'}) => {
const showToastFunc = async (data) => {
if (data.context !== context) return;
if (toastMessages.findIndex((m) => m.message === data.message) >= 0) {
console.log('returning from here')
return;
}
toastMessages.push(data);

View File

@@ -12,9 +12,17 @@ const run = async (context = 'global') => {
});
try {
await doInBackground(async () => {
await db.sync();
let res = await doInBackground(async () => {
try {
await db.sync();
return true;
} catch (e) {
return e.message;
}
});
console.log(res);
if (res !== true) throw new Error(res);
console.log('here too');
ToastEvent.show({
heading: 'Sync complete',
type: 'success',

View File

@@ -10,6 +10,7 @@ let RNFetchBlob;
async function read(key, isArray = false) {
let data = await MMKV.getItem(key);
if (!data) return null;
data = JSON.parse(data);
return data;
}
@@ -170,7 +171,10 @@ async function checkAndCreateDir(path) {
}
async function hash(password, email) {
return await Sodium.hashPassword(password, email);
console.log(password,email,'calling');
let result = await Sodium.hashPassword(password, email);
console.log(result,'result');
return result
}
/*
function compress(data) {

View File

@@ -5,7 +5,7 @@ import {updateEvent} from '../components/DialogManager/recievers';
import {dummyRef} from '../components/DummyText';
import {Actions} from '../provider/Actions';
import {defaultState} from '../provider/DefaultState';
import {eSendEvent} from '../services/EventManager';
import {eSendEvent, ToastEvent} from '../services/EventManager';
import {MMKV} from './mmkv';
import {tabBarRef} from './Refs';
import {SIZE} from './SizeUtils';
@@ -192,14 +192,15 @@ export async function doInBackground(cb) {
if (Platform.OS === 'ios') {
let bgTaskId;
bgTaskId = await beginBackgroundTask();
await cb();
let res = await cb();
await endBackgroundTask(bgTaskId);
return res;
} else {
return new Promise(async (res, rej) => {
await BackgroundService.start(async () => {
await cb();
let result = await cb();
await BackgroundService.stop();
res('done');
res(result);
}, bgTaskOptions);
});
}