diff --git a/apps/mobile/AppRootEvents.js b/apps/mobile/AppRootEvents.js index 4d153e7da..9f2e70339 100644 --- a/apps/mobile/AppRootEvents.js +++ b/apps/mobile/AppRootEvents.js @@ -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); } diff --git a/apps/mobile/src/components/ActionSheetComponent/index.js b/apps/mobile/src/components/ActionSheetComponent/index.js index 9ce3a6f9a..8791e4e84 100644 --- a/apps/mobile/src/components/ActionSheetComponent/index.js +++ b/apps/mobile/src/components/ActionSheetComponent/index.js @@ -659,7 +659,7 @@ export const ActionSheetComponent = ({ await Sync.run()} + onPress={async () => await Sync.run("local")} style={{ borderColor: colors.accent, paddingHorizontal: 5, diff --git a/apps/mobile/src/components/Dialog/DialogButtons.js b/apps/mobile/src/components/Dialog/DialogButtons.js index c3653f76f..6671eb622 100644 --- a/apps/mobile/src/components/Dialog/DialogButtons.js +++ b/apps/mobile/src/components/Dialog/DialogButtons.js @@ -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} /> )} diff --git a/apps/mobile/src/components/LoginDialog/index.js b/apps/mobile/src/components/LoginDialog/index.js index 8554202a7..ab6b8cc44 100644 --- a/apps/mobile/src/components/LoginDialog/index.js +++ b/apps/mobile/src/components/LoginDialog/index.js @@ -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 && ( + { + setConfirm(false); + }} + visible> + + + { + setConfirm(false); + }} + positiveType="error" + positiveTitle="Logout" + onPressPositive={async () => { + await db.user.logout(); + await BiometricService.resetCredentials(); + await Storage.write('introCompleted', 'true'); + setConfirm(false); + close(); + }} + /> + + + )} + {status ? ( { 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 && ( + + + + Please login to your account to access your notes on this device + and sync them. + + + )} + {mode === MODES.signup && ( { 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 && ( - { - setMode(MODES.forgotPassword); - }} - style={{ - alignSelf: 'flex-end', - marginTop: 2.5, - }}> - Forgot password? - - )} + {mode === MODES.login || + (mode === MODES.sessionExpired && ( + { + if (MODES.sessionExpired === mode) { + sendEmail(true); + return; + } + setMode(MODES.forgotPassword); + }} + style={{ + alignSelf: 'flex-end', + marginTop: 2.5, + }}> + Forgot password? + + ))} {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, diff --git a/apps/mobile/src/components/Menu/UserSection.js b/apps/mobile/src/components/Menu/UserSection.js index aa9ede894..c07e99e80 100644 --- a/apps/mobile/src/components/Menu/UserSection.js +++ b/apps/mobile/src/components/Menu/UserSection.js @@ -80,7 +80,7 @@ export const UserSection = () => { {user && ( await Sync.run('local')} + onPress={async ()=>await Sync.run()} style={{ flexDirection: 'row', alignItems: 'center', diff --git a/apps/mobile/src/components/MergeEditor/index.js b/apps/mobile/src/components/MergeEditor/index.js index d2759d428..c44081717 100644 --- a/apps/mobile/src/components/MergeEditor/index.js +++ b/apps/mobile/src/components/MergeEditor/index.js @@ -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) => { diff --git a/apps/mobile/src/components/SimpleList/index.js b/apps/mobile/src/components/SimpleList/index.js index dea9fc509..fe50c4163 100644 --- a/apps/mobile/src/components/SimpleList/index.js +++ b/apps/mobile/src/components/SimpleList/index.js @@ -79,7 +79,7 @@ const SimpleList = ({ }, [listData, deviceMode, loading]); const _onRefresh = async () => { - await Sync.run('local'); + await Sync.run(); if (refreshCallback) { refreshCallback(); } diff --git a/apps/mobile/src/components/Toast/index.js b/apps/mobile/src/components/Toast/index.js index d17d1970f..5cd7b0bd4 100644 --- a/apps/mobile/src/components/Toast/index.js +++ b/apps/mobile/src/components/Toast/index.js @@ -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); diff --git a/apps/mobile/src/services/Sync.js b/apps/mobile/src/services/Sync.js index 4a27fe7e1..1866d1a29 100644 --- a/apps/mobile/src/services/Sync.js +++ b/apps/mobile/src/services/Sync.js @@ -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', diff --git a/apps/mobile/src/utils/Storage.js b/apps/mobile/src/utils/Storage.js index 964c9779a..b1f48710a 100644 --- a/apps/mobile/src/utils/Storage.js +++ b/apps/mobile/src/utils/Storage.js @@ -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) { diff --git a/apps/mobile/src/utils/index.js b/apps/mobile/src/utils/index.js index e959228e4..d65df12ba 100755 --- a/apps/mobile/src/utils/index.js +++ b/apps/mobile/src/utils/index.js @@ -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); }); }