diff --git a/web/apps/auth/src/pages/_app.tsx b/web/apps/auth/src/pages/_app.tsx index b405e4cacc..28792966c3 100644 --- a/web/apps/auth/src/pages/_app.tsx +++ b/web/apps/auth/src/pages/_app.tsx @@ -1,9 +1,8 @@ import "@fontsource-variable/inter"; import { CssBaseline } from "@mui/material"; import { ThemeProvider } from "@mui/material/styles"; -import { getData } from "ente-accounts/services/accounts-db"; +import { savedLocalUser } from "ente-accounts/services/accounts-db"; import { accountLogout } from "ente-accounts/services/logout"; -import type { User } from "ente-accounts/services/user"; import { staticAppTitle } from "ente-base/app"; import { CustomHead } from "ente-base/components/Head"; import { @@ -32,8 +31,7 @@ const App: React.FC = ({ Component, pageProps }) => { const { showMiniDialog, miniDialogProps } = useAttributedMiniDialog(); useEffect(() => { - const user = getData("user") as User | undefined | null; - logStartupBanner(user?.id); + logStartupBanner(savedLocalUser()?.id); }, []); const logout = useCallback(() => { diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index 39dc37b57c..baa3f357e9 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -6,8 +6,8 @@ import { useNotification } from "components/utils/hooks-app"; import { getData, isLocalStorageAndIndexedDBMismatch, + savedLocalUser, } from "ente-accounts/services/accounts-db"; -import type { User } from "ente-accounts/services/user"; import { isDesktop, staticAppTitle } from "ente-base/app"; import { CenteredRow } from "ente-base/components/containers"; import { CustomHead } from "ente-base/components/Head"; @@ -68,8 +68,7 @@ const App: React.FC = ({ Component, pageProps }) => { const logout = useCallback(() => void photosLogout(), []); useEffect(() => { - const user = getData("user") as User | undefined | null; - logStartupBanner(user?.id); + logStartupBanner(savedLocalUser()?.id); void isLocalStorageAndIndexedDBMismatch().then((mismatch) => { if (mismatch) { log.error("Logging out (IndexedDB and local storage mismatch)"); diff --git a/web/apps/photos/src/utils/file/index.ts b/web/apps/photos/src/utils/file/index.ts index 07930d19a1..03ccbc90a0 100644 --- a/web/apps/photos/src/utils/file/index.ts +++ b/web/apps/photos/src/utils/file/index.ts @@ -1,5 +1,5 @@ import { getData } from "ente-accounts/services/accounts-db"; -import type { LocalUser, User } from "ente-accounts/services/user"; +import type { LocalUser, PartialLocalUser } from "ente-accounts/services/user"; import { joinPath } from "ente-base/file-name"; import log from "ente-base/log"; import { type Electron } from "ente-base/types/ipc"; @@ -284,7 +284,7 @@ export const getArchivedFiles = (files: EnteFile[]) => { }; export const getUserOwnedFiles = (files: EnteFile[]) => { - const user: User = getData("user"); + const user: PartialLocalUser = getData("user"); if (!user?.id) { throw Error("user missing"); } diff --git a/web/packages/accounts/components/LoginContents.tsx b/web/packages/accounts/components/LoginContents.tsx index 7217573145..f0677daa52 100644 --- a/web/packages/accounts/components/LoginContents.tsx +++ b/web/packages/accounts/components/LoginContents.tsx @@ -1,8 +1,11 @@ import { Input, Stack, TextField, Typography } from "@mui/material"; import { AccountsPageFooter } from "ente-accounts/components/layouts/centered-paper"; -import { saveSRPAttributes } from "ente-accounts/services/accounts-db"; +import { + savePartialLocalUser, + saveSRPAttributes, +} from "ente-accounts/services/accounts-db"; import { getSRPAttributes } from "ente-accounts/services/srp"; -import { savePartialLocalUser, sendOTT } from "ente-accounts/services/user"; +import { sendOTT } from "ente-accounts/services/user"; import { LinkButton } from "ente-base/components/LinkButton"; import { LoadingButton } from "ente-base/components/mui/LoadingButton"; import { isMuseumHTTPError } from "ente-base/http"; diff --git a/web/packages/accounts/components/SignUpContents.tsx b/web/packages/accounts/components/SignUpContents.tsx index 3dad9a3ede..3a04d6e7e2 100644 --- a/web/packages/accounts/components/SignUpContents.tsx +++ b/web/packages/accounts/components/SignUpContents.tsx @@ -16,6 +16,7 @@ import { import { saveJustSignedUp, saveOriginalKeyAttributes, + savePartialLocalUser, stashReferralSource, stashSRPSetupAttributes, } from "ente-accounts/services/accounts-db"; @@ -23,7 +24,6 @@ import { generateSRPSetupAttributes } from "ente-accounts/services/srp"; import { generateAndSaveInteractiveKeyAttributes, generateKeysAndAttributes, - savePartialLocalUser, sendOTT, type GenerateKeysAndAttributesResult, } from "ente-accounts/services/user"; diff --git a/web/packages/accounts/pages/change-password.tsx b/web/packages/accounts/pages/change-password.tsx index 462121a4eb..aea4641c30 100644 --- a/web/packages/accounts/pages/change-password.tsx +++ b/web/packages/accounts/pages/change-password.tsx @@ -5,11 +5,7 @@ import { AccountsPageTitle, } from "ente-accounts/components/layouts/centered-paper"; import { appHomeRoute, stashRedirect } from "ente-accounts/services/redirect"; -import { - changePassword, - localUser, - type LocalUser, -} from "ente-accounts/services/user"; +import { changePassword, type LocalUser } from "ente-accounts/services/user"; import { LinkButton } from "ente-base/components/LinkButton"; import { LoadingIndicator } from "ente-base/components/loaders"; import { deriveKeyInsufficientMemoryErrorMessage } from "ente-base/crypto/types"; @@ -21,12 +17,13 @@ import { NewPasswordForm, type NewPasswordFormProps, } from "../components/NewPasswordForm"; +import { savedLocalUser } from "../services/accounts-db"; /** * A page that allows a user to reset or change their password. */ const Page: React.FC = () => { - const [user, setUser] = useState(); + const [user, setUser] = useState(undefined); const router = useRouter(); @@ -34,7 +31,7 @@ const Page: React.FC = () => { const isReset = router.query.op == "reset"; useEffect(() => { - const user = localUser(); + const user = savedLocalUser(); if (user) { setUser(user); } else { diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index 8579913702..1d91bb89e5 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -45,7 +45,7 @@ import { import { generateAndSaveInteractiveKeyAttributes, type KeyAttributes, - type User, + type PartialLocalUser, } from "ente-accounts/services/user"; import { decryptAndStoreToken } from "ente-accounts/utils/helpers"; import { LinkButton } from "ente-base/components/LinkButton"; @@ -81,7 +81,7 @@ const Page: React.FC = () => { const [srpAttributes, setSrpAttributes] = useState(); const [keyAttributes, setKeyAttributes] = useState(); - const [user, setUser] = useState(); + const [user, setUser] = useState(); const [passkeyVerificationData, setPasskeyVerificationData] = useState< { passkeySessionID: string; url: string } | undefined >(); @@ -127,7 +127,7 @@ const Page: React.FC = () => { useEffect(() => { const main = async () => { - const user: User = getData("user"); + const user: PartialLocalUser = getData("user"); if (!user?.email) { void router.push("/"); return; @@ -280,7 +280,7 @@ const Page: React.FC = () => { await decryptAndStoreToken(keyAttributes, masterKey); try { let srpAttributes = savedSRPAttributes(); - if (!srpAttributes && user) { + if (!srpAttributes && user?.email) { srpAttributes = await getSRPAttributes(user.email); if (srpAttributes) { saveSRPAttributes(srpAttributes); diff --git a/web/packages/accounts/pages/generate.tsx b/web/packages/accounts/pages/generate.tsx index c563bdb245..4e5d38ed1b 100644 --- a/web/packages/accounts/pages/generate.tsx +++ b/web/packages/accounts/pages/generate.tsx @@ -16,7 +16,7 @@ import { generateSRPSetupAttributes, setupSRP, } from "ente-accounts/services/srp"; -import type { User } from "ente-accounts/services/user"; +import type { PartialLocalUser } from "ente-accounts/services/user"; import { generateAndSaveInteractiveKeyAttributes, generateKeysAndAttributes, @@ -42,14 +42,14 @@ import { const Page: React.FC = () => { const { logout, showMiniDialog } = useBaseContext(); - const [user, setUser] = useState(); + const [user, setUser] = useState(); const [openRecoveryKey, setOpenRecoveryKey] = useState(false); const [loading, setLoading] = useState(true); const router = useRouter(); useEffect(() => { - const user: User = getData("user"); + const user: PartialLocalUser = getData("user"); setUser(user); if (!user?.token) { void router.push("/"); @@ -109,7 +109,7 @@ const Page: React.FC = () => { {t("set_password")} diff --git a/web/packages/accounts/pages/recover.tsx b/web/packages/accounts/pages/recover.tsx index 92d07f86d4..65e9b71e10 100644 --- a/web/packages/accounts/pages/recover.tsx +++ b/web/packages/accounts/pages/recover.tsx @@ -9,7 +9,10 @@ import { } from "ente-accounts/services/accounts-db"; import { recoveryKeyFromMnemonic } from "ente-accounts/services/recovery-key"; import { appHomeRoute, stashRedirect } from "ente-accounts/services/redirect"; -import type { KeyAttributes, User } from "ente-accounts/services/user"; +import type { + KeyAttributes, + PartialLocalUser, +} from "ente-accounts/services/user"; import { sendOTT } from "ente-accounts/services/user"; import { decryptAndStoreToken } from "ente-accounts/utils/helpers"; import { LinkButton } from "ente-base/components/LinkButton"; @@ -38,7 +41,7 @@ const Page: React.FC = () => { const router = useRouter(); useEffect(() => { - const user: User = getData("user"); + const user: PartialLocalUser = getData("user"); if (!user?.email) { void router.push("/"); return; diff --git a/web/packages/accounts/pages/two-factor/verify.tsx b/web/packages/accounts/pages/two-factor/verify.tsx index 2c8f39f394..8c32a92b3e 100644 --- a/web/packages/accounts/pages/two-factor/verify.tsx +++ b/web/packages/accounts/pages/two-factor/verify.tsx @@ -4,7 +4,7 @@ import { saveKeyAttributes, setLSUser, } from "ente-accounts/services/accounts-db"; -import type { User } from "ente-accounts/services/user"; +import type { PartialLocalUser } from "ente-accounts/services/user"; import { verifyTwoFactor } from "ente-accounts/services/user"; import { LinkButton } from "ente-base/components/LinkButton"; import { useBaseContext } from "ente-base/context"; @@ -27,7 +27,7 @@ const Page: React.FC = () => { const router = useRouter(); useEffect(() => { - const user: User = getData("user"); + const user: PartialLocalUser = getData("user"); if (!user?.email || !user.twoFactorSessionID) { void router.push("/"); } else if ( diff --git a/web/packages/accounts/pages/verify.tsx b/web/packages/accounts/pages/verify.tsx index d65e2c4d4b..4b873a1e0a 100644 --- a/web/packages/accounts/pages/verify.tsx +++ b/web/packages/accounts/pages/verify.tsx @@ -28,7 +28,7 @@ import { unstashRedirect, } from "ente-accounts/services/redirect"; import { getSRPAttributes, setupSRP } from "ente-accounts/services/srp"; -import type { User } from "ente-accounts/services/user"; +import type { PartialLocalUser } from "ente-accounts/services/user"; import { putUserKeyAttributes, sendOTT, @@ -69,7 +69,7 @@ const Page: React.FC = () => { useEffect(() => { const main = async () => { - const user: User = getData("user"); + const user: PartialLocalUser = getData("user"); const redirect = await redirectionIfNeeded(user); if (redirect) { @@ -253,7 +253,7 @@ export default Page; * * @returns The slug to redirect to, if needed. */ -const redirectionIfNeeded = async (user: User | undefined) => { +const redirectionIfNeeded = async (user: PartialLocalUser | undefined) => { const email = user?.email; if (!email) { return "/"; diff --git a/web/packages/accounts/services/accounts-db.ts b/web/packages/accounts/services/accounts-db.ts index 19893578bd..2aff817739 100644 --- a/web/packages/accounts/services/accounts-db.ts +++ b/web/packages/accounts/services/accounts-db.ts @@ -1,18 +1,5 @@ -import { getKVS, removeKV, setKV } from "ente-base/kv"; -import log from "ente-base/log"; -import { nullToUndefined } from "ente-utils/transform"; -import { z } from "zod/v4"; -import { - RemoteSRPAttributes, - SRPSetupAttributes, - type SRPAttributes, -} from "./srp"; -import { RemoteKeyAttributes, type KeyAttributes } from "./user"; - -export type LocalStorageKey = "user"; - /** - * [Note: Accounts DB] + * @file [Note: Accounts DB] * * The accounts package stores various state both during the login / signup * flow, and post login to identify the logged in user. @@ -29,6 +16,145 @@ export type LocalStorageKey = "user"; * - "originalKeyAttributes" * - "srpAttributes" */ + +import { getKVS, removeKV, setKV } from "ente-base/kv"; +import log from "ente-base/log"; +import { getAuthToken } from "ente-base/token"; +import { nullToUndefined } from "ente-utils/transform"; +import { z } from "zod/v4"; +import { + RemoteSRPAttributes, + SRPSetupAttributes, + type SRPAttributes, +} from "./srp"; +import { + RemoteKeyAttributes, + type KeyAttributes, + type LocalUser, +} from "./user"; + +/** + * The local storage data about the user before login or signup is complete. + * + * [Note: Partial local user] + * + * During login or signup, the user object exists in various partial states in + * local storage. + * + * - Initially, there is no user object in local storage. + * + * - When the user enters their email, the email property of the stored object + * is set, but nothing else. + * + * - After they verify their password, we have two cases: if second factor + * verification is not set, and when it is set. + * + * - If second factor verification is not set, then after verifying their + * password their {@link id} and {@link encryptedToken} will get filled in, + * and {@link isTwoFactorEnabled} will be set to false. + * + * - If they have second factor verification set, then after verifying their + * password {@link isTwoFactorEnabled} and {@link twoFactorSessionID} will + * also get filled in. Once they verify their TOTP based second factor, their + * {@link id} and {@link encryptedToken} will also get filled in. + * + * - As the login or signup sequence completes, a {@link token} obtained from + * the {@link encryptedToken} will be written out, and the + * {@link encryptedToken} cleared since it is not needed anymore. + * + * So while the underlying storage is the same, we offer two APIs for code to + * obtain the user: + * + * - Before login is complete, or when it is unknown if login is complete or + * not, then {@link savedPartialLocalUser} can be used to obtain a + * {@link PartialLocalUser} with all of its properties set to be optional (and + * some additional properties not available in the regular user object). + * + * - When we know that the login has completed, we can use either + * {@link savedLocalUser} (which returns `undefined` if our assumption is + * false) or {@link ensureSavedLocalUser} (which throws if our assumption is + * false) to obtain an object with all the properties expected to be present + * for a locally persisted user set to be required. + */ +export interface PartialLocalUser { + id?: number; + email?: string; + token?: string; + encryptedToken?: string; + isTwoFactorEnabled?: boolean; + twoFactorSessionID?: string; +} + +const PartialLocalUser = z.object({ + id: z.number().nullish().transform(nullToUndefined), + email: z.string().nullish().transform(nullToUndefined), + token: z.string().nullish().transform(nullToUndefined), + encryptedToken: z.string().nullish().transform(nullToUndefined), + isTwoFactorEnabled: z.boolean().nullish().transform(nullToUndefined), + twoFactorSessionID: z.string().nullish().transform(nullToUndefined), +}); + +/** + * Zod schema for the {@link LocalUser} TypeScript type. + * + * The type itself is in `user.ts`. + */ +const LocalUser = z.object({ + id: z.number(), + email: z.string(), + token: z.string(), +}); + +/** + * Return the local storage value of the user's data. + * + * This function is meant to be called during the login or signup sequence. + * After the user is logged in, use {@link savedLocalUser} or + * {@link ensureLocalUser} instead. + * + * Use {@link savePartialLocalUser} to updated the saved value. + */ +export const savedPartialLocalUser = (): PartialLocalUser | undefined => { + const jsonString = localStorage.getItem("user"); + if (!jsonString) return undefined; + return PartialLocalUser.parse(JSON.parse(jsonString)); +}; + +/** + * Save the users data as we accrue it during the signup or login flow. + * + * See: [Note: Partial local user]. + * + * TODO: WARNING: This does not update the KV token. The idea is to gradually + * move over uses of setLSUser to this while explicitly setting the KV token + * where needed. + */ +export const savePartialLocalUser = (partialLocalUser: Partial) => + localStorage.setItem("user", JSON.stringify(partialLocalUser)); + +/** + * Return data about the logged-in user, if someone is indeed logged in. + * Otherwise return `undefined`. + * + * The user's data is stored in the browser's localStorage. Thus, this function + * only works from the main thread, not from web workers since local storage is + * not accessible to web workers. + * + * There is no setter corresponding to this function since this is only a view + * on data saved using {@link savePartialLocalUser}. + * + * See: [Note: Partial local user] for more about the whole shebang. + */ +export const savedLocalUser = (): LocalUser | undefined => { + const jsonString = localStorage.getItem("user"); + if (!jsonString) return undefined; + // We might have some data, but not all of it. So do a non-throwing parse. + const { success, data } = LocalUser.safeParse(JSON.parse(jsonString)); + return success ? data : undefined; +}; + +export type LocalStorageKey = "user"; + export const getData = (key: LocalStorageKey) => { try { if ( @@ -110,16 +236,8 @@ export const migrateKVToken = async (user: unknown) => { * This acts a sanity check on IndexedDB by ensuring that if the user has a * token in local storage, then it should also be present in IndexedDB. */ -export const isLocalStorageAndIndexedDBMismatch = async () => { - const oldLSUser = getData("user"); - return ( - oldLSUser && - typeof oldLSUser == "object" && - "token" in oldLSUser && - typeof oldLSUser.token == "string" && - !(await getKVS("token")) - ); -}; +export const isLocalStorageAndIndexedDBMismatch = async () => + savedPartialLocalUser()?.token && !(await getAuthToken()); /** * Return the user's {@link KeyAttributes} if they are present in local storage. @@ -155,6 +273,9 @@ export const saveKeyAttributes = (keyAttributes: KeyAttributes) => * either freshly generated (if the user signed up on this client) or were * fetched from remote (otherwise). * + * > NOTE: Currently the code does not guarantee that savedOriginalKeyAttributes + * > will always be set when savedKeyAttributes is set. + * * In contrast, the regular key attributes get overwritten by the local only * interactive key attributes for the user's convenience. See the documentation * of {@link generateAndSaveInteractiveKeyAttributes} for more details. diff --git a/web/packages/accounts/services/user.ts b/web/packages/accounts/services/user.ts index eb54e1b94c..abfd6ea5ed 100644 --- a/web/packages/accounts/services/user.ts +++ b/web/packages/accounts/services/user.ts @@ -1,9 +1,11 @@ import { getData, savedKeyAttributes, + savedLocalUser, saveKeyAttributes, saveSRPAttributes, setLSUser, + type PartialLocalUser, } from "ente-accounts/services/accounts-db"; import { generateSRPSetupAttributes, @@ -36,121 +38,51 @@ import { nullToUndefined } from "ente-utils/transform"; import { z } from "zod/v4"; import { getUserRecoveryKey, recoveryKeyFromMnemonic } from "./recovery-key"; -export interface User { - id: number; - email: string; - token: string; - encryptedToken: string; - isTwoFactorEnabled: boolean; - twoFactorSessionID: string; -} +// TODO(RE): Temporary re-export +export type { PartialLocalUser }; /** - * The local storage data about the user after they've logged in. + * The locally persisted data we have about the user after they've logged in. + * + * This type arguably belongs to accounts-db (since that's what persists it and + * its shadow alias, {@link PartialLocalUser}), but since most code that will + * need this will need this after login has completed, and will be using the + * {@link ensureLocalUser} method below, we keep this in the same file to reduce + * the need to import the type from a separate file. */ -const LocalUser = z.object({ +export interface LocalUser { /** * The user's ID. */ - id: z.number(), + id: number; /** - * The user's email. + * The email associated with the user's Ente account. */ - email: z.string(), + email: string; /** * The user's (plaintext) auth token. * * It is used for making API calls on their behalf, by passing this token as * the value of the X-Auth-Token header in the HTTP request. * - * Deprecated, use `getAuthToken()` instead (which fetches it from IDB). + * Usually you shouldn't be needing to access this property; instead use + * {@link getAuthToken()} which is kept in sync with this value, and lives + * in IndexedDB and thus can also be used in web workers. */ - token: z.string(), -}); + token: string; +} /** - * The local storage data about the user after they've logged in. - */ -export type LocalUser = z.infer; - -/** - * The local storage data about the user before login or signup is complete. + * Return the currently logged in {@link LocalUser}, throwing it the user is not + * logged in. * - * [Note: Partial local user] - * - * During login or signup, the user object exists in various partial states in - * local storage. - * - * - Initially, there is no user object in local storage. - * - * - When the user enters their email, the email property of the stored object - * is set, but nothing else. - * - * - After they verify their password, we have two cases: if second factor - * verification is not set, and when it is set. - * - * - If second factor verification is not set, then after verifying their - * password their {@link id} and {@link encryptedToken} will get filled in, - * and {@link isTwoFactorEnabled} will be set to false. - * - * - If they have second factor verification set, then after verifying their - * password {@link isTwoFactorEnabled} and {@link twoFactorSessionID} will - * also get filled in. Once they verify their TOTP based second factor, their - * {@link id} and {@link encryptedToken} will also get filled in. - * - * So while the underlying storage is the same, we offer two APIs for code to - * obtain the user: - * - * - Before login is complete, or when it is unknown if login is complete or - * not, then {@link partialLocalUser} can be used to obtain a - * {@link LocalUser} with all of its properties set to be optional. - * - * - When we know that the login has completed, we can use either - * {@link localUser} (which returns `undefined` if our presumption is false) - * or {@link ensureLocalUser} (which throws if our presumption is false) to - * obtain an object with all the properties expected to be present for a - * locally persisted user set to be required. - */ -export const partialLocalUser = (): Partial | undefined => { - // TODO: duplicate of getData("user") - const s = localStorage.getItem("user"); - if (!s) return undefined; - return LocalUser.partial().parse(JSON.parse(s)); -}; - -/** - * Save the users data as we accrue it during the signup or login flow. - * - * See: [Note: Partial local user]. - * - * TODO: WARNING: This does not update the KV token. The idea is to gradually - * move over uses of setLSUser to this while explicitly setting the KV token - * where needed. - */ -export const savePartialLocalUser = (partialLocalUser: Partial) => - localStorage.setItem("user", JSON.stringify(partialLocalUser)); - -/** - * Return the logged-in user, if someone is indeed logged in. Otherwise return - * `undefined`. - * - * The user's data is stored in the browser's localStorage. Thus, this function - * only works from the main thread, not from web workers (local storage is not - * accessible to web workers). - */ -export const localUser = (): LocalUser | undefined => { - // TODO: duplicate of getData("user") - const s = localStorage.getItem("user"); - if (!s) return undefined; - const { success, data } = LocalUser.safeParse(JSON.parse(s)); - return success ? data : undefined; -}; - -/** - * A wrapper over {@link localUser} with that throws if no one is logged in. + * This is a wrapper over the {@link savedLocalUser} function that throws if no + * one is logged in. A more appropriate name for this function, keeping in line + * with the conventions the other methods follow, would've been + * {@link ensureSavedLocalUser}. The shorter name is for readability. */ export const ensureLocalUser = (): LocalUser => - ensureExpectedLoggedInValue(localUser()); + ensureExpectedLoggedInValue(savedLocalUser()); /** * A function throws an error if a value that is expected to be truthy when the diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index 741ffc39d2..56a2590ff0 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1,4 +1,4 @@ -import type { User } from "ente-accounts/services/user"; +import { type LocalUser } from "ente-accounts/services/user"; import { groupFilesByCollectionID, sortFiles, @@ -108,15 +108,15 @@ export interface GalleryState { /*--< Mostly static state >--*/ /** - * The logged in {@link User}. + * The logged in {@link LocalUser}. * * This is expected to be undefined only for a brief duration until the code * for the initial "mount" runs (If we're not logged in, then the gallery * will redirect the user to an appropriate authentication page). */ - user: User | undefined; + user: LocalUser | undefined; /** - * Family plan related information for the logged in {@link User}. + * Family plan related information for the logged in {@link LocalUser}. */ familyData: FamilyData | undefined; @@ -449,7 +449,7 @@ export interface GalleryState { export type GalleryAction = | { type: "mount"; - user: User; + user: LocalUser; familyData: FamilyData | undefined; collections: Collection[]; collectionFiles: EnteFile[]; @@ -1225,7 +1225,7 @@ const deriveArchivedFileIDs = ( * Compute favorite file IDs from their dependencies. */ const deriveFavoriteFileIDs = ( - user: User, + user: LocalUser, collections: GalleryState["collections"], collectionFiles: GalleryState["collectionFiles"], unsyncedFavoriteUpdates: GalleryState["unsyncedFavoriteUpdates"], @@ -1278,7 +1278,7 @@ const createFileCollectionIDs = (files: EnteFile[]) => */ const deriveNormalCollectionSummaries = ( normalCollections: Collection[], - user: User, + user: LocalUser, trashItems: GalleryState["trashItems"], collectionFiles: GalleryState["collectionFiles"], hiddenFileIDs: GalleryState["hiddenFileIDs"], @@ -1378,7 +1378,7 @@ const pseudoCollectionOptionsForLatestFileAndCount = ( */ const deriveHiddenCollectionSummaries = ( hiddenCollections: Collection[], - user: User, + user: LocalUser, collectionFiles: GalleryState["collectionFiles"], ) => { const hiddenCollectionSummaries = createCollectionSummaries( @@ -1414,7 +1414,7 @@ const deriveUncategorizedCollectionSummaryID = ( PseudoCollectionID.uncategorizedPlaceholder; const createCollectionSummaries = ( - user: User, + user: LocalUser, collections: Collection[], collectionFiles: EnteFile[], ) => { @@ -1968,7 +1968,7 @@ const enqueuePendingSearchSuggestionsIfNeeded = ( * users who have shared a collection with the user. */ const constructUserIDToEmailMap = ( - user: User, + user: LocalUser, collections: GalleryState["collections"], ): Map => { const userIDToEmail = new Map(); @@ -1990,7 +1990,7 @@ const constructUserIDToEmailMap = ( * are trying to share albums with specific users. */ const createShareSuggestionEmails = ( - user: User, + user: LocalUser, familyData: FamilyData | undefined, collections: Collection[], ): string[] => [ diff --git a/web/packages/new/photos/services/settings.ts b/web/packages/new/photos/services/settings.ts index d5ae3bf14d..aedda42384 100644 --- a/web/packages/new/photos/services/settings.ts +++ b/web/packages/new/photos/services/settings.ts @@ -2,7 +2,7 @@ * @file Storage (in-memory, local, remote) and update of various settings. */ -import { partialLocalUser } from "ente-accounts/services/user"; +import { savedPartialLocalUser } from "ente-accounts/services/accounts-db"; import { isDevBuild } from "ente-base/env"; import log from "ente-base/log"; import { updateShouldDisableCFUploadProxy } from "ente-gallery/services/upload"; @@ -195,10 +195,8 @@ const setSettingsSnapshot = (snapshot: Settings) => { */ export const isDevBuildAndUser = () => isDevBuild && isDevUserViaEmail(); -const isDevUserViaEmail = () => { - const user = partialLocalUser(); - return !!user?.email?.endsWith("@ente.io"); -}; +const isDevUserViaEmail = () => + !!savedPartialLocalUser()?.email?.endsWith("@ente.io"); /** * Persist the user's map enabled preference both locally and on remote.