mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 23:19:40 +01:00
add relogin flow after user session expired.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -79,7 +79,7 @@ const SimpleList = ({
|
||||
}, [listData, deviceMode, loading]);
|
||||
|
||||
const _onRefresh = async () => {
|
||||
await Sync.run('local');
|
||||
await Sync.run();
|
||||
if (refreshCallback) {
|
||||
refreshCallback();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user