Conv
This commit is contained in:
@@ -15,11 +15,9 @@ import { Sidebar } from "components/Sidebar";
|
||||
import { Upload } from "components/Upload";
|
||||
import { sessionExpiredDialogAttributes } from "ente-accounts/components/utils/dialog";
|
||||
import {
|
||||
getAndClearIsFirstLogin,
|
||||
getAndClearJustSignedUp,
|
||||
getData,
|
||||
isFirstLogin,
|
||||
justSignedUp,
|
||||
setIsFirstLogin,
|
||||
setJustSignedUp,
|
||||
} from "ente-accounts/services/accounts-db";
|
||||
import { stashRedirect } from "ente-accounts/services/redirect";
|
||||
import { isSessionInvalid } from "ente-accounts/services/session";
|
||||
@@ -299,16 +297,27 @@ const Page: React.FC = () => {
|
||||
|
||||
// We are logged in and everything looks fine. Proceed with page
|
||||
// load initialization.
|
||||
initSettings();
|
||||
|
||||
// One time inits.
|
||||
preloadImage("/images/subscription-card-background");
|
||||
initSettings();
|
||||
await initUserDetailsOrTriggerPull();
|
||||
setupSelectAllKeyBoardShortcutHandler();
|
||||
|
||||
// Show the initial state while the rest of the sequence proceeds.
|
||||
dispatch({ type: "showAll" });
|
||||
setIsFirstLoad(isFirstLogin());
|
||||
if (justSignedUp()) {
|
||||
|
||||
// If this is the user's first login on this client, then show them
|
||||
// a message informing the that the initial load might take time.
|
||||
setIsFirstLoad(getAndClearIsFirstLogin());
|
||||
|
||||
// If the user created a new account on this client, show them the
|
||||
// plan options.
|
||||
if (getAndClearJustSignedUp()) {
|
||||
showPlanSelector();
|
||||
}
|
||||
setIsFirstLogin(false);
|
||||
|
||||
// Initialize the reducer.
|
||||
const user = getData("user");
|
||||
// TODO: Pass entire snapshot to reducer?
|
||||
const familyData = userDetailsSnapshot()?.familyData;
|
||||
@@ -320,13 +329,19 @@ const Page: React.FC = () => {
|
||||
collectionFiles: await savedCollectionFiles(),
|
||||
trashItems: await savedTrashItems(),
|
||||
});
|
||||
|
||||
// Fetch data from remote.
|
||||
await remotePull();
|
||||
|
||||
// Clear the first load message if needed.
|
||||
setIsFirstLoad(false);
|
||||
setJustSignedUp(false);
|
||||
|
||||
// Start the interval that does a periodic pull.
|
||||
syncIntervalID = setInterval(
|
||||
() => remotePull({ silent: true }),
|
||||
5 * 60 * 1000 /* 5 minutes */,
|
||||
);
|
||||
|
||||
if (electron) {
|
||||
electron.onMainWindowFocus(() => remotePull({ silent: true }));
|
||||
if (await shouldShowWhatsNew(electron)) showWhatsNew();
|
||||
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
saveJustSignedUp,
|
||||
setData,
|
||||
setJustSignedUp,
|
||||
setLocalReferralSource,
|
||||
} from "ente-accounts/services/accounts-db";
|
||||
import {
|
||||
@@ -154,7 +154,7 @@ export const SignUpContents: React.FC<SignUpContentsProps> = ({
|
||||
);
|
||||
await saveMasterKeyInSessionAndSafeStore(masterKey);
|
||||
|
||||
setJustSignedUp(true);
|
||||
saveJustSignedUp();
|
||||
void router.push("/verify");
|
||||
} catch (e) {
|
||||
log.error("Signup failed", e);
|
||||
|
||||
@@ -17,9 +17,9 @@ import {
|
||||
import {
|
||||
getData,
|
||||
getToken,
|
||||
isFirstLogin,
|
||||
savedIsFirstLogin,
|
||||
saveIsFirstLogin,
|
||||
setData,
|
||||
setIsFirstLogin,
|
||||
setLSUser,
|
||||
} from "ente-accounts/services/accounts-db";
|
||||
import {
|
||||
@@ -98,7 +98,7 @@ const Page: React.FC = () => {
|
||||
setData("srpAttributes", session.updatedSRPAttributes);
|
||||
// Set a flag that causes new interactive key attributes to
|
||||
// be generated.
|
||||
setIsFirstLogin(true);
|
||||
saveIsFirstLogin();
|
||||
// This should be a rare occurrence, instead of building the
|
||||
// scaffolding to update all the in-memory state, just
|
||||
// reload everything.
|
||||
@@ -191,7 +191,7 @@ const Page: React.FC = () => {
|
||||
await userVerificationResultAfterResolvingSecondFactorChoice(
|
||||
await verifySRP(srpAttributes!, kek),
|
||||
);
|
||||
setIsFirstLogin(true);
|
||||
saveIsFirstLogin();
|
||||
|
||||
if (passkeySessionID) {
|
||||
await stashKeyEncryptionKeyInSessionStore(kek);
|
||||
@@ -246,7 +246,7 @@ const Page: React.FC = () => {
|
||||
const handleVerifyMasterPassword: VerifyMasterPasswordFormProps["onVerify"] =
|
||||
(key, kek, keyAttributes, password) => {
|
||||
void (async () => {
|
||||
const updatedKeyAttributes = isFirstLogin()
|
||||
const updatedKeyAttributes = savedIsFirstLogin()
|
||||
? await generateAndSaveInteractiveKeyAttributes(
|
||||
password,
|
||||
keyAttributes,
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
import { RecoveryKey } from "ente-accounts/components/RecoveryKey";
|
||||
import {
|
||||
getData,
|
||||
justSignedUp,
|
||||
setJustSignedUp,
|
||||
savedJustSignedUp,
|
||||
saveJustSignedUp,
|
||||
} from "ente-accounts/services/accounts-db";
|
||||
import { appHomeRoute } from "ente-accounts/services/redirect";
|
||||
import {
|
||||
@@ -54,7 +54,7 @@ const Page: React.FC = () => {
|
||||
if (!user?.token) {
|
||||
void router.push("/");
|
||||
} else if (haveCredentialsInSession()) {
|
||||
if (justSignedUp()) {
|
||||
if (savedJustSignedUp()) {
|
||||
setOpenRecoveryKey(true);
|
||||
setLoading(false);
|
||||
} else {
|
||||
@@ -82,7 +82,7 @@ const Page: React.FC = () => {
|
||||
masterKey,
|
||||
);
|
||||
await saveMasterKeyInSessionAndSafeStore(masterKey);
|
||||
setJustSignedUp(true);
|
||||
saveJustSignedUp();
|
||||
setOpenRecoveryKey(true);
|
||||
} catch (e) {
|
||||
log.error("failed to generate password", e);
|
||||
|
||||
@@ -10,8 +10,8 @@ import { useSecondFactorChoiceIfNeeded } from "ente-accounts/components/utils/se
|
||||
import {
|
||||
getData,
|
||||
getLocalReferralSource,
|
||||
saveIsFirstLogin,
|
||||
setData,
|
||||
setIsFirstLogin,
|
||||
setLSUser,
|
||||
} from "ente-accounts/services/accounts-db";
|
||||
import {
|
||||
@@ -107,12 +107,7 @@ const Page: React.FC = () => {
|
||||
isTwoFactorEnabled: true,
|
||||
isTwoFactorPasskeysEnabled: true,
|
||||
});
|
||||
// TODO: This is not the first login though if they already have
|
||||
// 2FA. Does this flag mean first login on this device?
|
||||
//
|
||||
// Update: This flag causes the interactive encryption key to be
|
||||
// generated, so it has a functional impact we need.
|
||||
setIsFirstLogin(true);
|
||||
saveIsFirstLogin();
|
||||
const url = passkeyVerificationRedirectURL(
|
||||
accountsUrl!,
|
||||
passkeySessionID,
|
||||
@@ -125,7 +120,7 @@ const Page: React.FC = () => {
|
||||
twoFactorSessionID,
|
||||
isTwoFactorEnabled: true,
|
||||
});
|
||||
setIsFirstLogin(true);
|
||||
saveIsFirstLogin();
|
||||
void router.push("/two-factor/verify");
|
||||
} else {
|
||||
await setLSUser({
|
||||
@@ -147,7 +142,7 @@ const Page: React.FC = () => {
|
||||
}
|
||||
await unstashAndUseSRPSetupAttributes(setupSRP);
|
||||
}
|
||||
setIsFirstLogin(true);
|
||||
saveIsFirstLogin();
|
||||
const redirectURL = unstashRedirect();
|
||||
if (keyAttributes?.encryptedKey) {
|
||||
clearSessionStorage();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
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 { RemoteKeyAttributes, type KeyAttributes } from "./user";
|
||||
|
||||
export type LocalStorageKey =
|
||||
| "user"
|
||||
// See also savedKeyAttributes.
|
||||
@@ -15,12 +16,6 @@ export type LocalStorageKey =
|
||||
| "srpAttributes"
|
||||
| "referralSource";
|
||||
|
||||
export const setData = (key: LocalStorageKey, value: object) =>
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
|
||||
export const removeData = (key: LocalStorageKey) =>
|
||||
localStorage.removeItem(key);
|
||||
|
||||
/**
|
||||
* [Note: Accounts DB]
|
||||
*
|
||||
@@ -55,6 +50,9 @@ export const getData = (key: LocalStorageKey) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const setData = (key: LocalStorageKey, value: object) =>
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
|
||||
// TODO: Migrate this to `local-user.ts`, with (a) more precise optionality
|
||||
// indication of the constituent fields, (b) moving any fields that need to be
|
||||
// accessed from web workers to KV DB.
|
||||
@@ -153,17 +151,88 @@ export const getToken = (): string => {
|
||||
return token;
|
||||
};
|
||||
|
||||
export const isFirstLogin = () => getData("isFirstLogin")?.status ?? false;
|
||||
const LocalIsFirstLogin = z.object({
|
||||
status: z.boolean().nullish().transform(nullToUndefined),
|
||||
});
|
||||
|
||||
export function setIsFirstLogin(status: boolean) {
|
||||
setData("isFirstLogin", { status });
|
||||
}
|
||||
/**
|
||||
* Return `true` if it is the user's first login on this client.
|
||||
*
|
||||
* The {@link savedIsFirstLogin} flag is saved in local storage (using
|
||||
* {@link saveIsFirstLogin}) if we determine during the login flow that it a
|
||||
* fresh login on this client. If so, we
|
||||
*
|
||||
* - Generate interactive key attributes for them, and
|
||||
*
|
||||
* - Display them a special indicator post login to notify them that the first
|
||||
* load might take extra time. At this point, we also clear the flag (the read
|
||||
* and clear is done by the same {@link getAndClearIsFirstLogin} function).
|
||||
*/
|
||||
export const savedIsFirstLogin = () => {
|
||||
const jsonString = localStorage.getItem("isFirstLogin");
|
||||
if (!jsonString) return false;
|
||||
return LocalIsFirstLogin.parse(JSON.parse(jsonString)).status ?? false;
|
||||
};
|
||||
|
||||
export const justSignedUp = () => getData("justSignedUp")?.status ?? false;
|
||||
/**
|
||||
* Save a flag in local storage to indicate that this is the user's first login
|
||||
* on this client.
|
||||
*
|
||||
* This is the setter corresponding to {@link savedIsFirstLogin}.
|
||||
*/
|
||||
export const saveIsFirstLogin = () => {
|
||||
localStorage.setItem("isFirstLogin", JSON.stringify({ status: true }));
|
||||
};
|
||||
|
||||
export function setJustSignedUp(status: boolean) {
|
||||
setData("justSignedUp", { status });
|
||||
}
|
||||
/**
|
||||
* Get the saved value of the local storage flag that indicates that this is the
|
||||
* user' first login on this client. Also remove the flag after reading.
|
||||
*
|
||||
* The flag can be set by using {@link saveIsFirstLogin}, and can be read
|
||||
* without clearing it by using {@link savedIsFirstLogin}.
|
||||
*/
|
||||
export const getAndClearIsFirstLogin = () => {
|
||||
const result = savedIsFirstLogin();
|
||||
localStorage.removeItem("isFirstLogin");
|
||||
return result;
|
||||
};
|
||||
|
||||
const LocalJustSignedUp = z.object({
|
||||
status: z.boolean().nullish().transform(nullToUndefined),
|
||||
});
|
||||
|
||||
/**
|
||||
* Return `true` if the user created a new account on this client during the
|
||||
* current (in-progress) or just completed login / signup sequence.
|
||||
*/
|
||||
export const savedJustSignedUp = () => {
|
||||
const jsonString = localStorage.getItem("justSignedUp");
|
||||
if (!jsonString) return false;
|
||||
return LocalJustSignedUp.parse(JSON.parse(jsonString)).status ?? false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Save a flag in local storage to indicate that the user signed up for a
|
||||
* new Ente account during the current login / signup sequence.
|
||||
*
|
||||
* This is the setter corresponding to {@link savedJustSignedUp}.
|
||||
*/
|
||||
export const saveJustSignedUp = () => {
|
||||
localStorage.setItem("justSignedUp", JSON.stringify({ status: true }));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the saved value of the local storage flag that indicates that the user just
|
||||
* signed up. Also remove the flag from local storage after reading.
|
||||
*
|
||||
* The flag can be set by using {@link saveJustSignedUp}, and can be read
|
||||
* without clearing it by using {@link savedJustSignedUp}.
|
||||
*/
|
||||
export const getAndClearJustSignedUp = () => {
|
||||
const result = savedJustSignedUp();
|
||||
localStorage.removeItem("justSignedUp");
|
||||
return result;
|
||||
};
|
||||
|
||||
export function getLocalReferralSource() {
|
||||
return getData("referralSource")?.source;
|
||||
|
||||
Reference in New Issue
Block a user