mobile: fix layout shift on app launch

This commit is contained in:
Ammar Ahmed
2025-11-03 12:17:58 +05:00
parent c46919b727
commit ee82ee7a0d
5 changed files with 204 additions and 185 deletions

View File

@@ -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();

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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) => <Update version={version} fwdRef={ref} />
});
},
data: {},
icon: "update",
type: "normal"
});
export function setUpdateAvailableMessage(version) {
useMessageStore.getState().setMessage(updateAvailableMessage(version));
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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) => <Update version={version} fwdRef={ref} />
});
},
data: {},
icon: "update",
type: "normal",
id: "app-update"
}) as Message;
export function setUpdateAvailableMessage(
version: GithubVersionInfo | CheckVersionResponse
) {
useMessageStore.getState().setMessage(updateAvailableMessage(version));
}

View File

@@ -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<MessageStore>((set, get) => ({
onPress: () => null,
data: {},
icon: "account-outline",
type: ""
type: "normal",
id: "none"
},
setMessage: (message) => {
set({ message: { ...message } });

View File

@@ -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<GithubVersionInfo | null> => {
const url = `https://api.github.com/repos/streetwriters/notesnook/releases`;
let res;
try {