Combine
This commit is contained in:
@@ -675,7 +675,7 @@ const DebugSection: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: Legacy synchronous check, use the one for feature-flags.ts instead.
|
||||
// TODO: Legacy synchronous check, use the one from remote-store.ts instead.
|
||||
const isInternalUserViaEmailCheck = () => {
|
||||
const userEmail = getData(LS_KEYS.USER)?.email;
|
||||
if (!userEmail) return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { accountLogout } from "@/accounts/services/logout";
|
||||
import log from "@/base/log";
|
||||
import DownloadManager from "@/new/photos/services/download";
|
||||
import { clearFeatureFlagSessionState } from "@/new/photos/services/feature-flags";
|
||||
import { clearFeatureFlagSessionState } from "@/new/photos/services/remote-store";
|
||||
import { logoutML, terminateMLWorker } from "@/new/photos/services/ml";
|
||||
import { logoutSearch } from "@/new/photos/services/search";
|
||||
import exportService from "./export";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { triggerFeatureFlagsFetchIfNeeded } from "@/new/photos/services/feature-flags";
|
||||
import { triggerFeatureFlagsFetchIfNeeded } from "@/new/photos/services/remote-store";
|
||||
import { isMLSupported, mlStatusSync, mlSync } from "@/new/photos/services/ml";
|
||||
import { searchDataSync } from "@/new/photos/services/search";
|
||||
import { syncMapEnabled } from "services/userService";
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
import { authenticatedRequestHeaders, ensureOk } from "@/base/http";
|
||||
import { localUser } from "@/base/local-user";
|
||||
import log from "@/base/log";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import { nullToUndefined } from "@/utils/transform";
|
||||
import { z } from "zod";
|
||||
|
||||
let _fetchTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
let _haveFetched = false;
|
||||
|
||||
/**
|
||||
* Fetch feature flags (potentially user specific) from remote and save them in
|
||||
* local storage for subsequent lookup.
|
||||
*
|
||||
* It fetches only once per session, and so is safe to call as arbitrarily many
|
||||
* times. Remember to call {@link clearFeatureFlagSessionState} on logout to
|
||||
* clear any in memory state so that these can be fetched again on the
|
||||
* subsequent login.
|
||||
*
|
||||
* [Note: Feature Flags]
|
||||
*
|
||||
* The workflow with feature flags is:
|
||||
*
|
||||
* 1. On app start feature flags are fetched once and saved in local storage. If
|
||||
* this fetch fails, we try again periodically (on every "sync") until
|
||||
* success.
|
||||
*
|
||||
* 2. Attempts to access any individual feature flage (e.g.
|
||||
* {@link isInternalUser}) returns the corresponding value from local storage
|
||||
* (substituting a default if needed).
|
||||
*
|
||||
* 3. However, if perchance the fetch-on-app-start hasn't completed yet (or had
|
||||
* failed), then a new fetch is tried. If even this fetch fails, we return
|
||||
* the default. Otherwise the now fetched result is saved to local storage
|
||||
* and the corresponding value returned.
|
||||
*/
|
||||
export const triggerFeatureFlagsFetchIfNeeded = () => {
|
||||
if (_haveFetched) return;
|
||||
if (_fetchTimeout) return;
|
||||
// Not critical, so fetch these after some delay.
|
||||
_fetchTimeout = setTimeout(() => {
|
||||
_fetchTimeout = undefined;
|
||||
void fetchAndSaveFeatureFlags().then(() => {
|
||||
_haveFetched = true;
|
||||
});
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
export const clearFeatureFlagSessionState = () => {
|
||||
if (_fetchTimeout) {
|
||||
clearTimeout(_fetchTimeout);
|
||||
_fetchTimeout = undefined;
|
||||
}
|
||||
_haveFetched = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch feature flags (potentially user specific) from remote and save them in
|
||||
* local storage for subsequent lookup.
|
||||
*/
|
||||
const fetchAndSaveFeatureFlags = () =>
|
||||
fetchFeatureFlags()
|
||||
.then((res) => res.text())
|
||||
.then(saveFlagJSONString);
|
||||
|
||||
const fetchFeatureFlags = async () => {
|
||||
const res = await fetch(await apiURL("/remote-store/feature-flags"), {
|
||||
headers: await authenticatedRequestHeaders(),
|
||||
});
|
||||
ensureOk(res);
|
||||
return res;
|
||||
};
|
||||
|
||||
const saveFlagJSONString = (s: string) =>
|
||||
localStorage.setItem("remoteFeatureFlags", s);
|
||||
|
||||
const remoteFeatureFlags = () => {
|
||||
const s = localStorage.getItem("remoteFeatureFlags");
|
||||
if (!s) return undefined;
|
||||
return FeatureFlags.parse(JSON.parse(s));
|
||||
};
|
||||
|
||||
const FeatureFlags = z.object({
|
||||
internalUser: z.boolean().nullish().transform(nullToUndefined),
|
||||
betaUser: z.boolean().nullish().transform(nullToUndefined),
|
||||
});
|
||||
|
||||
type FeatureFlags = z.infer<typeof FeatureFlags>;
|
||||
|
||||
const remoteFeatureFlagsFetchingIfNeeded = async () => {
|
||||
let ff = remoteFeatureFlags();
|
||||
if (!ff) {
|
||||
try {
|
||||
await fetchAndSaveFeatureFlags();
|
||||
} catch (e) {
|
||||
log.warn("Ignoring error when fetching feature flags", e);
|
||||
}
|
||||
ff = remoteFeatureFlags();
|
||||
}
|
||||
return ff;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `true` if the current user is marked as an "internal" user.
|
||||
*
|
||||
* 1. Emails that end in `@ente.io` are considered as internal users.
|
||||
* 2. If the "internalUser" remote feature flag is set, the user is internal.
|
||||
* 3. Otherwise false.
|
||||
*
|
||||
* See also: [Note: Feature Flags].
|
||||
*/
|
||||
export const isInternalUser = async () => {
|
||||
const user = localUser();
|
||||
if (user?.email.endsWith("@ente.io")) return true;
|
||||
|
||||
const flags = await remoteFeatureFlagsFetchingIfNeeded();
|
||||
return flags?.internalUser ?? false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `true` if the current user is marked as a "beta" user.
|
||||
*
|
||||
* See also: [Note: Feature Flags].
|
||||
*/
|
||||
export const isBetaUser = async () => {
|
||||
const flags = await remoteFeatureFlagsFetchingIfNeeded();
|
||||
return flags?.betaUser ?? false;
|
||||
};
|
||||
@@ -41,9 +41,9 @@ import type { CLIPMatches } from "./worker-types";
|
||||
/**
|
||||
* Internal state of the ML subsystem.
|
||||
*
|
||||
* This are essentially cached values used by the functions of this module.
|
||||
* These are essentially cached values used by the functions of this module.
|
||||
*
|
||||
* This should be cleared on logout.
|
||||
* They will be cleared on logout.
|
||||
*/
|
||||
class MLState {
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,132 @@
|
||||
import { authenticatedRequestHeaders, ensureOk } from "@/base/http";
|
||||
import { localUser } from "@/base/local-user";
|
||||
import log from "@/base/log";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import { nullToUndefined } from "@/utils/transform";
|
||||
import { z } from "zod";
|
||||
|
||||
let _fetchTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
let _haveFetched = false;
|
||||
|
||||
/**
|
||||
* Fetch feature flags (potentially user specific) from remote and save them in
|
||||
* local storage for subsequent lookup.
|
||||
*
|
||||
* It fetches only once per session, and so is safe to call as arbitrarily many
|
||||
* times. Remember to call {@link clearFeatureFlagSessionState} on logout to
|
||||
* clear any in memory state so that these can be fetched again on the
|
||||
* subsequent login.
|
||||
*
|
||||
* [Note: Feature Flags]
|
||||
*
|
||||
* The workflow with feature flags is:
|
||||
*
|
||||
* 1. On app start feature flags are fetched once and saved in local storage. If
|
||||
* this fetch fails, we try again periodically (on every "sync") until
|
||||
* success.
|
||||
*
|
||||
* 2. Attempts to access any individual feature flage (e.g.
|
||||
* {@link isInternalUser}) returns the corresponding value from local storage
|
||||
* (substituting a default if needed).
|
||||
*
|
||||
* 3. However, if perchance the fetch-on-app-start hasn't completed yet (or had
|
||||
* failed), then a new fetch is tried. If even this fetch fails, we return
|
||||
* the default. Otherwise the now fetched result is saved to local storage
|
||||
* and the corresponding value returned.
|
||||
*/
|
||||
export const triggerFeatureFlagsFetchIfNeeded = () => {
|
||||
if (_haveFetched) return;
|
||||
if (_fetchTimeout) return;
|
||||
// Not critical, so fetch these after some delay.
|
||||
_fetchTimeout = setTimeout(() => {
|
||||
_fetchTimeout = undefined;
|
||||
void fetchAndSaveFeatureFlags().then(() => {
|
||||
_haveFetched = true;
|
||||
});
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
export const clearFeatureFlagSessionState = () => {
|
||||
if (_fetchTimeout) {
|
||||
clearTimeout(_fetchTimeout);
|
||||
_fetchTimeout = undefined;
|
||||
}
|
||||
_haveFetched = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch feature flags (potentially user specific) from remote and save them in
|
||||
* local storage for subsequent lookup.
|
||||
*/
|
||||
const fetchAndSaveFeatureFlags = () =>
|
||||
fetchFeatureFlags()
|
||||
.then((res) => res.text())
|
||||
.then(saveFlagJSONString);
|
||||
|
||||
const fetchFeatureFlags = async () => {
|
||||
const res = await fetch(await apiURL("/remote-store/feature-flags"), {
|
||||
headers: await authenticatedRequestHeaders(),
|
||||
});
|
||||
ensureOk(res);
|
||||
return res;
|
||||
};
|
||||
|
||||
const saveFlagJSONString = (s: string) =>
|
||||
localStorage.setItem("remoteFeatureFlags", s);
|
||||
|
||||
const remoteFeatureFlags = () => {
|
||||
const s = localStorage.getItem("remoteFeatureFlags");
|
||||
if (!s) return undefined;
|
||||
return FeatureFlags.parse(JSON.parse(s));
|
||||
};
|
||||
|
||||
const FeatureFlags = z.object({
|
||||
internalUser: z.boolean().nullish().transform(nullToUndefined),
|
||||
betaUser: z.boolean().nullish().transform(nullToUndefined),
|
||||
});
|
||||
|
||||
type FeatureFlags = z.infer<typeof FeatureFlags>;
|
||||
|
||||
const remoteFeatureFlagsFetchingIfNeeded = async () => {
|
||||
let ff = remoteFeatureFlags();
|
||||
if (!ff) {
|
||||
try {
|
||||
await fetchAndSaveFeatureFlags();
|
||||
} catch (e) {
|
||||
log.warn("Ignoring error when fetching feature flags", e);
|
||||
}
|
||||
ff = remoteFeatureFlags();
|
||||
}
|
||||
return ff;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `true` if the current user is marked as an "internal" user.
|
||||
*
|
||||
* 1. Emails that end in `@ente.io` are considered as internal users.
|
||||
* 2. If the "internalUser" remote feature flag is set, the user is internal.
|
||||
* 3. Otherwise false.
|
||||
*
|
||||
* See also: [Note: Feature Flags].
|
||||
*/
|
||||
export const isInternalUser = async () => {
|
||||
const user = localUser();
|
||||
if (user?.email.endsWith("@ente.io")) return true;
|
||||
|
||||
const flags = await remoteFeatureFlagsFetchingIfNeeded();
|
||||
return flags?.internalUser ?? false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `true` if the current user is marked as a "beta" user.
|
||||
*
|
||||
* See also: [Note: Feature Flags].
|
||||
*/
|
||||
export const isBetaUser = async () => {
|
||||
const flags = await remoteFeatureFlagsFetchingIfNeeded();
|
||||
return flags?.betaUser ?? false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the value for the given {@link key} from remote store.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user