diff --git a/apps/mobile/app/hooks/use-app-events.tsx b/apps/mobile/app/hooks/use-app-events.tsx index 1ee50a689..c92bff51b 100644 --- a/apps/mobile/app/hooks/use-app-events.tsx +++ b/apps/mobile/app/hooks/use-app-events.tsx @@ -341,24 +341,32 @@ const onSubscriptionError = async (error: RNIap.PurchaseError) => { const SodiumEventEmitter = new NativeEventEmitter(NativeModules.Sodium); +const setAppMessage = async () => { + await useMessageStore.getState().setAnnouncement(); + if (await checkAppUpdateAvailable()) return; + const user = await db.user.getUser(); + if (!user) { + setLoginMessage(); + return; + } + if (!user?.isEmailConfirmed) setEmailVerifyMessage(); + if (await checkForRateAppRequest()) return; + if ( + user?.isEmailConfirmed && + !SettingsService.get().recoveryKeySaved && + !useMessageStore.getState().message?.visible + ) { + setRecoveryKeyMessage(); + } +}; + const doAppLoadActions = async () => { if (SettingsService.get().sessionExpired) { eSendEvent(eLoginSessionExpired); return; } - - await useMessageStore.getState().setAnnouncement(); - notifee.setBadgeCount(0); - - if (!(await db.user.getUser())) { - setLoginMessage(); - return; - } - if (NewFeature.present()) return; - if (await checkAppUpdateAvailable()) return; - if (await checkForRateAppRequest()) return; if (SettingsService.get().introCompleted) { useMessageStore.subscribe((state) => { const dialogs = state.dialogs; @@ -401,7 +409,7 @@ const checkForRateAppRequest = async () => { !useMessageStore.getState().message?.visible ) { setRateAppMessage(); - return false; + return true; } return false; }; @@ -440,6 +448,7 @@ const initializeDatabase = async (password?: string) => { if (IsDatabaseMigrationRequired()) return; if (db.isInitialized) { + await setAppMessage(); useSettingStore.getState().setAppLoading(false); Notifications.setupReminders(true); if (SettingsService.get().notifNotes) { @@ -584,8 +593,6 @@ export const useAppEvents = () => { syncedOnLaunch.current = true; return; } - - clearMessage(); subscribeToPurchaseListeners(); if (!isLogin) { user = await db.user.fetchUser(); @@ -596,6 +603,11 @@ export const useAppEvents = () => { }); } + if (useMessageStore.getState().message.id === "log-in") { + clearMessage(); + setAppMessage(); + } + await PremiumService.setPremiumStatus(); if (user?.isEmailConfirmed && !isUserEmailConfirmed) { setTimeout(() => { @@ -609,16 +621,6 @@ export const useAppEvents = () => { ToastManager.error(e as Error, "Error updating user", "global"); } - user = await db.user.getUser(); - if ( - user?.isEmailConfirmed && - !SettingsService.get().recoveryKeySaved && - !useMessageStore.getState().message?.visible - ) { - setRecoveryKeyMessage(); - } - if (!user?.isEmailConfirmed) setEmailVerifyMessage(); - syncedOnLaunch.current = true; if (!isLogin) { checkAutoBackup(); diff --git a/apps/mobile/app/services/message.js b/apps/mobile/app/services/message.js deleted file mode 100644 index 0a47e71fb..000000000 --- a/apps/mobile/app/services/message.js +++ /dev/null @@ -1,157 +0,0 @@ -/* -This file is part of the Notesnook project (https://notesnook.com/) - -Copyright (C) 2023 Streetwriters (Private) Limited - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -import { strings } from "@notesnook/intl"; -import React from "react"; -import { Platform } from "react-native"; -import { AuthMode } from "../components/auth/common"; -import { Update } from "../components/sheets/update"; -import { verifyUser } from "../screens/settings/functions"; -import { useMessageStore } from "../stores/use-message-store"; -import { eOpenRateDialog, eOpenRecoveryKeyDialog } from "../utils/events"; -import { eSendEvent, presentSheet } from "./event-manager"; -import Navigation from "./navigation"; -import PremiumService from "./premium"; -import SettingsService from "./settings"; - -const rateAppMessage = { - visible: true, - message: strings.rateAppMessage(), - actionText: strings.rateAppActionText(Platform.OS), - onPress: () => { - eSendEvent(eOpenRateDialog); - }, - data: {}, - icon: "star", - type: "normal" -}; - -export function setRateAppMessage() { - useMessageStore.getState().setMessage(rateAppMessage); -} - -const recoveryKeyMessage = { - visible: true, - message: strings.recoveryKeyMessage(), - actionText: strings.recoveryKeyMessageActionText(), - onPress: () => { - verifyUser( - null, - () => { - eSendEvent(eOpenRecoveryKeyDialog); - }, - false, - async () => { - SettingsService.set({ - recoveryKeySaved: true - }); - clearMessage(); - }, - "Cancel" - ); - }, - data: {}, - icon: "key", - type: "normal" -}; - -export function setRecoveryKeyMessage() { - useMessageStore.getState().setMessage(recoveryKeyMessage); -} - -const loginMessage = { - visible: true, - message: strings.loginMessage(), - actionText: strings.loginMessageActionText(), - onPress: () => { - Navigation.navigate("Auth", { - mode: AuthMode.login - }); - }, - data: {}, - icon: "account-outline", - type: "normal" -}; - -export function setLoginMessage() { - useMessageStore.getState().setMessage(loginMessage); -} - -const emailMessage = { - visible: true, - message: strings.syncDisabled(), - actionText: strings.syncDisabledActionText(), - onPress: () => { - PremiumService.showVerifyEmailDialog(); - }, - data: {}, - icon: "email", - type: "error" -}; - -export function setEmailVerifyMessage() { - useMessageStore.getState().setMessage(emailMessage); -} - -const noMessage = { - visible: false, - message: "", - actionText: "", - onPress: null, - data: {}, - icon: "account-outline" -}; - -export function clearMessage() { - useMessageStore.getState().setMessage(noMessage); -} - -const autoBackupsOff = { - visible: true, - message: strings.autoBackupsOffMessage(), - actionText: strings.autoBackupsOffActionText(), - onPress: () => { - clearMessage(); - }, - data: {}, - icon: "backup-restore", - type: "error" -}; - -export function setAutobackOffMessage() { - useMessageStore.getState().setMessage(autoBackupsOff); -} - -const updateAvailableMessage = (version) => ({ - visible: true, - message: strings.newUpdateMessage(), - actionText: strings.newUpdateActionText(), - onPress: () => { - presentSheet({ - component: (ref) => - }); - }, - data: {}, - icon: "update", - type: "normal" -}); - -export function setUpdateAvailableMessage(version) { - useMessageStore.getState().setMessage(updateAvailableMessage(version)); -} diff --git a/apps/mobile/app/services/message.tsx b/apps/mobile/app/services/message.tsx new file mode 100644 index 000000000..966d67025 --- /dev/null +++ b/apps/mobile/app/services/message.tsx @@ -0,0 +1,155 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { strings } from "@notesnook/intl"; +import { Platform } from "react-native"; +import { AuthMode } from "../components/auth/common"; +import { verifyUser } from "../screens/settings/functions"; +import { + Message, + MessageId, + useMessageStore +} from "../stores/use-message-store"; +import { eOpenRateDialog, eOpenRecoveryKeyDialog } from "../utils/events"; +import { eSendEvent, presentSheet } from "./event-manager"; +import Navigation from "./navigation"; +import PremiumService from "./premium"; +import SettingsService from "./settings"; +import { Update } from "../components/sheets/update"; +import { GithubVersionInfo } from "../utils/github-version"; +import { CheckVersionResponse } from "react-native-check-version"; + +const APP_MESSAGES: Message[] = [ + { + visible: true, + message: strings.rateAppMessage(), + actionText: strings.rateAppActionText(Platform.OS), + onPress: () => { + eSendEvent(eOpenRateDialog); + }, + data: {}, + icon: "star", + type: "normal", + id: "rate-app" + }, + { + visible: true, + message: strings.recoveryKeyMessage(), + actionText: strings.recoveryKeyMessageActionText(), + onPress: () => { + verifyUser( + null, + () => { + eSendEvent(eOpenRecoveryKeyDialog); + }, + false, + async () => { + SettingsService.set({ + recoveryKeySaved: true + }); + clearMessage(); + }, + "Cancel" + ); + }, + data: {}, + icon: "key", + type: "normal", + id: "recovery-key" + }, + { + visible: true, + message: strings.loginMessage(), + actionText: strings.loginMessageActionText(), + onPress: () => { + Navigation.navigate("Auth", { + mode: AuthMode.login + }); + }, + data: {}, + icon: "account-outline", + type: "normal", + id: "log-in" + }, + { + visible: true, + message: strings.syncDisabled(), + actionText: strings.syncDisabledActionText(), + onPress: () => { + PremiumService.showVerifyEmailDialog(); + }, + data: {}, + icon: "email", + type: "error", + id: "confirm-email" + } +]; + +function showMessageById(id: MessageId) { + useMessageStore + .getState() + .setMessage(APP_MESSAGES.find((m) => m.id === id) as Message); +} + +export function setRateAppMessage() { + showMessageById("rate-app"); +} + +export function setRecoveryKeyMessage() { + showMessageById("recovery-key"); +} + +export function setLoginMessage() { + showMessageById("log-in"); +} + +export function setEmailVerifyMessage() { + showMessageById("confirm-email"); +} + +export function clearMessage() { + useMessageStore.getState().setMessage({ + ...useMessageStore.getState().message, + visible: false + }); +} + +const updateAvailableMessage = ( + version: GithubVersionInfo | CheckVersionResponse +) => + ({ + visible: true, + message: strings.newUpdateMessage(), + actionText: strings.newUpdateActionText(), + onPress: () => { + presentSheet({ + component: (ref) => + }); + }, + data: {}, + icon: "update", + type: "normal", + id: "app-update" + }) as Message; + +export function setUpdateAvailableMessage( + version: GithubVersionInfo | CheckVersionResponse +) { + useMessageStore.getState().setMessage(updateAvailableMessage(version)); +} diff --git a/apps/mobile/app/stores/use-message-store.ts b/apps/mobile/app/stores/use-message-store.ts index 47a31588c..6ffd9c5b6 100644 --- a/apps/mobile/app/stores/use-message-store.ts +++ b/apps/mobile/app/stores/use-message-store.ts @@ -33,6 +33,14 @@ export interface MessageStore { remove: (id: string) => void; } +export type MessageId = + | "rate-app" + | "log-in" + | "recovery-key" + | "confirm-email" + | "app-update" + | "none"; + export type Message = { visible: boolean; message: string | null; @@ -40,7 +48,8 @@ export type Message = { onPress: () => void; data: object; icon: string; - type?: string; + type: "error" | "normal"; + id: MessageId; }; export type Action = { @@ -97,7 +106,8 @@ export const useMessageStore = create((set, get) => ({ onPress: () => null, data: {}, icon: "account-outline", - type: "" + type: "normal", + id: "none" }, setMessage: (message) => { set({ message: { ...message } }); diff --git a/apps/mobile/app/utils/github-version.ts b/apps/mobile/app/utils/github-version.ts index 37478b4eb..33cd6024c 100644 --- a/apps/mobile/app/utils/github-version.ts +++ b/apps/mobile/app/utils/github-version.ts @@ -42,8 +42,17 @@ export interface GithubRelease { reactions: unknown; discussion_url: string; } - -export const getGithubVersion = async () => { +export type GithubVersionInfo = { + version: string | null; + releasedAt: string; + notes: string; + body: string; + url: string; + lastChecked: string; + needsUpdate: boolean; + current: string; +}; +export const getGithubVersion = async (): Promise => { const url = `https://api.github.com/repos/streetwriters/notesnook/releases`; let res; try {