From 495eaec4b202ace8febe1de5585b26d1abd81e6f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 09:54:37 +0530 Subject: [PATCH 01/20] Remove unused branch --- .../services/machineLearning/readerService.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/web/apps/photos/src/services/machineLearning/readerService.ts b/web/apps/photos/src/services/machineLearning/readerService.ts index 6ad4c80e81..e517eee317 100644 --- a/web/apps/photos/src/services/machineLearning/readerService.ts +++ b/web/apps/photos/src/services/machineLearning/readerService.ts @@ -1,7 +1,6 @@ import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import log from "@/next/log"; -import PQueue from "p-queue"; import DownloadManager from "services/download"; import { getLocalFiles } from "services/fileService"; import { Dimensions } from "services/ml/geom"; @@ -110,18 +109,13 @@ async function getImageBlobBitmap(blob: Blob): Promise { return await createImageBitmap(blob); } -async function getOriginalFile(file: EnteFile, queue?: PQueue) { - let fileStream; - if (queue) { - fileStream = await queue.add(() => DownloadManager.getFile(file)); - } else { - fileStream = await DownloadManager.getFile(file); - } +async function getOriginalFile(file: EnteFile) { + const fileStream = await DownloadManager.getFile(file); return new Response(fileStream).blob(); } -async function getOriginalConvertedFile(file: EnteFile, queue?: PQueue) { - const fileBlob = await getOriginalFile(file, queue); +async function getOriginalConvertedFile(file: EnteFile) { + const fileBlob = await getOriginalFile(file); if (file.metadata.fileType === FILE_TYPE.IMAGE) { return await getRenderableImage(file.metadata.title, fileBlob); } else { @@ -133,8 +127,8 @@ async function getOriginalConvertedFile(file: EnteFile, queue?: PQueue) { } } -export async function getOriginalImageBitmap(file: EnteFile, queue?: PQueue) { - const fileBlob = await getOriginalConvertedFile(file, queue); +export async function getOriginalImageBitmap(file: EnteFile) { + const fileBlob = await getOriginalConvertedFile(file); log.info("[MLService] Got file: ", file.id.toString()); return getImageBlobBitmap(fileBlob); } From 34a3a8700e708d9eb8c7db32c86d0761d5188585 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 10:05:02 +0530 Subject: [PATCH 02/20] Inline --- .../services/machineLearning/faceService.ts | 4 +-- .../services/machineLearning/peopleService.ts | 4 +-- .../services/machineLearning/readerService.ts | 25 ++++++------------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/web/apps/photos/src/services/machineLearning/faceService.ts b/web/apps/photos/src/services/machineLearning/faceService.ts index b7805b3360..e1c71ba742 100644 --- a/web/apps/photos/src/services/machineLearning/faceService.ts +++ b/web/apps/photos/src/services/machineLearning/faceService.ts @@ -11,9 +11,9 @@ import { } from "services/ml/types"; import { imageBitmapToBlob, warpAffineFloat32List } from "utils/image"; import ReaderService, { + fetchImageBitmap, getFaceId, getLocalFile, - getOriginalImageBitmap, } from "./readerService"; class FaceService { @@ -296,7 +296,7 @@ class FaceService { } const file = await getLocalFile(personFace.fileId); - const imageBitmap = await getOriginalImageBitmap(file); + const imageBitmap = await fetchImageBitmap(file); return await this.saveFaceCrop(imageBitmap, personFace, syncContext); } } diff --git a/web/apps/photos/src/services/machineLearning/peopleService.ts b/web/apps/photos/src/services/machineLearning/peopleService.ts index f7d5cf38a1..b26153f622 100644 --- a/web/apps/photos/src/services/machineLearning/peopleService.ts +++ b/web/apps/photos/src/services/machineLearning/peopleService.ts @@ -2,7 +2,7 @@ import log from "@/next/log"; import mlIDbStorage from "services/ml/db"; import { Face, MLSyncContext, Person } from "services/ml/types"; import FaceService, { isDifferentOrOld } from "./faceService"; -import { getLocalFile, getOriginalImageBitmap } from "./readerService"; +import { fetchImageBitmap, getLocalFile } from "./readerService"; class PeopleService { async syncPeopleIndex(syncContext: MLSyncContext) { @@ -58,7 +58,7 @@ class PeopleService { if (personFace && !personFace.crop?.cacheKey) { const file = await getLocalFile(personFace.fileId); - const imageBitmap = await getOriginalImageBitmap(file); + const imageBitmap = await fetchImageBitmap(file); await FaceService.saveFaceCrop( imageBitmap, personFace, diff --git a/web/apps/photos/src/services/machineLearning/readerService.ts b/web/apps/photos/src/services/machineLearning/readerService.ts index e517eee317..6660cf8667 100644 --- a/web/apps/photos/src/services/machineLearning/readerService.ts +++ b/web/apps/photos/src/services/machineLearning/readerService.ts @@ -40,7 +40,7 @@ class ReaderService { fileContext.enteFile.metadata.fileType, ) ) { - fileContext.imageBitmap = await getOriginalImageBitmap( + fileContext.imageBitmap = await fetchImageBitmap( fileContext.enteFile, ); } else { @@ -105,17 +105,12 @@ export function getFaceId(detectedFace: DetectedFace, imageDims: Dimensions) { return faceID; } -async function getImageBlobBitmap(blob: Blob): Promise { - return await createImageBitmap(blob); -} +export const fetchImageBitmap = async (file: EnteFile) => + fetchRenderableBlob(file).then(createImageBitmap); -async function getOriginalFile(file: EnteFile) { +async function fetchRenderableBlob(file: EnteFile) { const fileStream = await DownloadManager.getFile(file); - return new Response(fileStream).blob(); -} - -async function getOriginalConvertedFile(file: EnteFile) { - const fileBlob = await getOriginalFile(file); + const fileBlob = await new Response(fileStream).blob(); if (file.metadata.fileType === FILE_TYPE.IMAGE) { return await getRenderableImage(file.metadata.title, fileBlob); } else { @@ -127,17 +122,11 @@ async function getOriginalConvertedFile(file: EnteFile) { } } -export async function getOriginalImageBitmap(file: EnteFile) { - const fileBlob = await getOriginalConvertedFile(file); - log.info("[MLService] Got file: ", file.id.toString()); - return getImageBlobBitmap(fileBlob); -} - export async function getThumbnailImageBitmap(file: EnteFile) { const thumb = await DownloadManager.getThumbnail(file); log.info("[MLService] Got thumbnail: ", file.id.toString()); - return getImageBlobBitmap(new Blob([thumb])); + return createImageBitmap(new Blob([thumb])); } export async function getLocalFileImageBitmap( @@ -146,5 +135,5 @@ export async function getLocalFileImageBitmap( ) { let fileBlob = localFile as Blob; fileBlob = await getRenderableImage(enteFile.metadata.title, fileBlob); - return getImageBlobBitmap(fileBlob); + return createImageBitmap(fileBlob); } From 8eb3170067959bb6a63fe6942bf9975c14c23081 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 10:08:28 +0530 Subject: [PATCH 03/20] Flatten --- .../src/services/machineLearning/mlWorkManager.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts index 700d358e04..f5dde1c083 100644 --- a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts +++ b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts @@ -25,19 +25,20 @@ export interface JobConfig { backoffMultiplier: number; } -export interface JobResult { +export interface MLSyncJobResult { shouldBackoff: boolean; + mlSyncResult: MLSyncResult; } -export class SimpleJob { +export class MLSyncJob { private config: JobConfig; - private runCallback: () => Promise; + private runCallback: () => Promise; private state: JobState; private stopped: boolean; private intervalSec: number; private nextTimeoutId: ReturnType; - constructor(config: JobConfig, runCallback: () => Promise) { + constructor(config: JobConfig, runCallback: () => Promise) { this.config = config; this.runCallback = runCallback; this.state = "NotScheduled"; @@ -109,12 +110,6 @@ export class SimpleJob { } } -export interface MLSyncJobResult extends JobResult { - mlSyncResult: MLSyncResult; -} - -export class MLSyncJob extends SimpleJob {} - class MLWorkManager { private mlSyncJob: MLSyncJob; private syncJobWorker: ComlinkWorker; From eaefdb56a0acdfc27110e2b135ac7d54bb2ab70a Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 10:28:00 +0530 Subject: [PATCH 04/20] Move --- .../machineLearning/machineLearningService.ts | 24 ------------------- .../services/machineLearning/mlWorkManager.ts | 16 +++++++------ web/apps/photos/src/services/ml/db.ts | 10 +++++--- 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/web/apps/photos/src/services/machineLearning/machineLearningService.ts b/web/apps/photos/src/services/machineLearning/machineLearningService.ts index 4ac17dbb8c..d89fdebc94 100644 --- a/web/apps/photos/src/services/machineLearning/machineLearningService.ts +++ b/web/apps/photos/src/services/machineLearning/machineLearningService.ts @@ -14,7 +14,6 @@ import { getLocalFiles } from "services/fileService"; import mlIDbStorage, { ML_SEARCH_CONFIG_NAME, ML_SYNC_CONFIG_NAME, - ML_SYNC_JOB_CONFIG_NAME, } from "services/ml/db"; import { BlurDetectionMethod, @@ -48,19 +47,11 @@ import dbscanClusteringService from "./dbscanClusteringService"; import FaceService from "./faceService"; import hdbscanClusteringService from "./hdbscanClusteringService"; import laplacianBlurDetectionService from "./laplacianBlurDetectionService"; -import type { JobConfig } from "./mlWorkManager"; import mobileFaceNetEmbeddingService from "./mobileFaceNetEmbeddingService"; import PeopleService from "./peopleService"; import ReaderService from "./readerService"; import yoloFaceDetectionService from "./yoloFaceDetectionService"; -export const DEFAULT_ML_SYNC_JOB_CONFIG: JobConfig = { - intervalSec: 5, - // TODO: finalize this after seeing effects on and from machine sleep - maxItervalSec: 960, - backoffMultiplier: 2, -}; - export const DEFAULT_ML_SYNC_CONFIG: MLSyncConfig = { batchSize: 200, imageSource: "Original", @@ -108,13 +99,6 @@ export const DEFAULT_ML_SEARCH_CONFIG: MLSearchConfig = { export const MAX_ML_SYNC_ERROR_COUNT = 1; -export async function getMLSyncJobConfig() { - return mlIDbStorage.getConfig( - ML_SYNC_JOB_CONFIG_NAME, - DEFAULT_ML_SYNC_JOB_CONFIG, - ); -} - export async function getMLSyncConfig() { return mlIDbStorage.getConfig(ML_SYNC_CONFIG_NAME, DEFAULT_ML_SYNC_CONFIG); } @@ -131,14 +115,6 @@ export async function getMLSearchConfig() { return DEFAULT_ML_SEARCH_CONFIG; } -export async function updateMLSyncJobConfig(newConfig: JobConfig) { - return mlIDbStorage.putConfig(ML_SYNC_JOB_CONFIG_NAME, newConfig); -} - -export async function updateMLSyncConfig(newConfig: MLSyncConfig) { - return mlIDbStorage.putConfig(ML_SYNC_CONFIG_NAME, newConfig); -} - export async function updateMLSearchConfig(newConfig: MLSearchConfig) { return mlIDbStorage.putConfig(ML_SEARCH_CONFIG_NAME, newConfig); } diff --git a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts index f5dde1c083..7dbc7e25dc 100644 --- a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts +++ b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts @@ -5,7 +5,6 @@ import { eventBus, Events } from "@ente/shared/events"; import { getToken, getUserID } from "@ente/shared/storage/localStorage/helpers"; import debounce from "debounce"; import PQueue from "p-queue"; -import { getMLSyncJobConfig } from "services/machineLearning/machineLearningService"; import mlIDbStorage from "services/ml/db"; import { MLSyncResult } from "services/ml/types"; import { EnteFile } from "types/file"; @@ -38,8 +37,14 @@ export class MLSyncJob { private intervalSec: number; private nextTimeoutId: ReturnType; - constructor(config: JobConfig, runCallback: () => Promise) { - this.config = config; + constructor(runCallback: () => Promise) { + this.config = { + intervalSec: 5, + // TODO: finalize this after seeing effects on and from machine sleep + maxItervalSec: 960, + backoffMultiplier: 2, + }; + this.runCallback = runCallback; this.state = "NotScheduled"; this.stopped = true; @@ -339,11 +344,8 @@ class MLWorkManager { log.info("User not logged in, not starting ml sync job"); return; } - const mlSyncJobConfig = await getMLSyncJobConfig(); if (!this.mlSyncJob) { - this.mlSyncJob = new MLSyncJob(mlSyncJobConfig, () => - this.runMLSyncJob(), - ); + this.mlSyncJob = new MLSyncJob(() => this.runMLSyncJob()); } this.mlSyncJob.start(); } catch (e) { diff --git a/web/apps/photos/src/services/ml/db.ts b/web/apps/photos/src/services/ml/db.ts index 90b2f4aa07..c55d38f2be 100644 --- a/web/apps/photos/src/services/ml/db.ts +++ b/web/apps/photos/src/services/ml/db.ts @@ -12,7 +12,6 @@ import isElectron from "is-electron"; import { DEFAULT_ML_SEARCH_CONFIG, DEFAULT_ML_SYNC_CONFIG, - DEFAULT_ML_SYNC_JOB_CONFIG, MAX_ML_SYNC_ERROR_COUNT, } from "services/machineLearning/machineLearningService"; import { Face, MLLibraryData, MlFileData, Person } from "services/ml/types"; @@ -27,7 +26,6 @@ export interface IndexStatus { interface Config {} -export const ML_SYNC_JOB_CONFIG_NAME = "ml-sync-job"; export const ML_SYNC_CONFIG_NAME = "ml-sync"; export const ML_SEARCH_CONFIG_NAME = "ml-search"; @@ -136,12 +134,14 @@ class MLIDbStorage { // TODO: update configs if version is updated in defaults db.createObjectStore("configs"); + /* await tx .objectStore("configs") .add( DEFAULT_ML_SYNC_JOB_CONFIG, - ML_SYNC_JOB_CONFIG_NAME, + "ml-sync-job", ); + */ await tx .objectStore("configs") .add(DEFAULT_ML_SYNC_CONFIG, ML_SYNC_CONFIG_NAME); @@ -163,6 +163,10 @@ class MLIDbStorage { .objectStore("configs") .delete(ML_SEARCH_CONFIG_NAME); + await tx + .objectStore("configs") + .delete("ml-sync-job"); + await tx .objectStore("configs") .add( From c2fe134c923491e92d486278cf1c0163245de3c3 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 10:30:00 +0530 Subject: [PATCH 05/20] Inline --- .../services/machineLearning/mlWorkManager.ts | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts index 7dbc7e25dc..baefd893bd 100644 --- a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts +++ b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts @@ -20,7 +20,6 @@ export type JobState = "Scheduled" | "Running" | "NotScheduled"; export interface JobConfig { intervalSec: number; - maxItervalSec: number; backoffMultiplier: number; } @@ -30,7 +29,6 @@ export interface MLSyncJobResult { } export class MLSyncJob { - private config: JobConfig; private runCallback: () => Promise; private state: JobState; private stopped: boolean; @@ -38,21 +36,14 @@ export class MLSyncJob { private nextTimeoutId: ReturnType; constructor(runCallback: () => Promise) { - this.config = { - intervalSec: 5, - // TODO: finalize this after seeing effects on and from machine sleep - maxItervalSec: 960, - backoffMultiplier: 2, - }; - this.runCallback = runCallback; this.state = "NotScheduled"; this.stopped = true; - this.intervalSec = this.config.intervalSec; + this.resetInterval(); } public resetInterval() { - this.intervalSec = this.config.intervalSec; + this.intervalSec = 5; } public start() { @@ -85,10 +76,7 @@ export class MLSyncJob { try { const jobResult = await this.runCallback(); if (jobResult && jobResult.shouldBackoff) { - this.intervalSec = Math.min( - this.config.maxItervalSec, - this.intervalSec * this.config.backoffMultiplier, - ); + this.intervalSec = Math.min(960, this.intervalSec * 2); } else { this.resetInterval(); } From d78628b66a56e852847cd407f0c45c2ccd3bc658 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 10:49:50 +0530 Subject: [PATCH 06/20] Move --- .../src/services/machineLearning/mlWorkManager.ts | 9 ++++----- web/apps/photos/src/services/ml/face.ts | 8 ++++++++ .../ml.worker.ts => services/ml/face.worker.ts} | 0 .../photos/src/utils/comlink/ComlinkMLWorker.ts | 13 ------------- 4 files changed, 12 insertions(+), 18 deletions(-) create mode 100644 web/apps/photos/src/services/ml/face.ts rename web/apps/photos/src/{worker/ml.worker.ts => services/ml/face.worker.ts} (100%) delete mode 100644 web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts diff --git a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts index baefd893bd..3712042798 100644 --- a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts +++ b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts @@ -6,12 +6,11 @@ import { getToken, getUserID } from "@ente/shared/storage/localStorage/helpers"; import debounce from "debounce"; import PQueue from "p-queue"; import mlIDbStorage from "services/ml/db"; +import { createFaceComlinkWorker } from "services/ml/face"; +import type { DedicatedMLWorker } from "services/ml/face.worker"; import { MLSyncResult } from "services/ml/types"; import { EnteFile } from "types/file"; -import { getDedicatedMLWorker } from "utils/comlink/ComlinkMLWorker"; -import { DedicatedMLWorker } from "worker/ml.worker"; import { logQueueStats } from "./machineLearningService"; - const LIVE_SYNC_IDLE_DEBOUNCE_SEC = 30; const LIVE_SYNC_QUEUE_TIMEOUT_SEC = 300; const LOCAL_FILES_UPDATED_DEBOUNCE_SEC = 30; @@ -226,7 +225,7 @@ class MLWorkManager { // Live Sync private async getLiveSyncWorker() { if (!this.liveSyncWorker) { - this.liveSyncWorker = getDedicatedMLWorker("ml-sync-live"); + this.liveSyncWorker = createFaceComlinkWorker("ml-sync-live"); } return await this.liveSyncWorker.remote; @@ -274,7 +273,7 @@ class MLWorkManager { // Sync Job private async getSyncJobWorker() { if (!this.syncJobWorker) { - this.syncJobWorker = getDedicatedMLWorker("ml-sync-job"); + this.syncJobWorker = createFaceComlinkWorker("ml-sync-job"); } return await this.syncJobWorker.remote; diff --git a/web/apps/photos/src/services/ml/face.ts b/web/apps/photos/src/services/ml/face.ts new file mode 100644 index 0000000000..7eb6980e0f --- /dev/null +++ b/web/apps/photos/src/services/ml/face.ts @@ -0,0 +1,8 @@ +import { ComlinkWorker } from "@/next/worker/comlink-worker"; +import type { DedicatedMLWorker } from "services/ml/face.worker"; + +const createFaceWebWorker = () => + new Worker(new URL("face.worker.ts", import.meta.url)); + +export const createFaceComlinkWorker = (name: string) => + new ComlinkWorker(name, createFaceWebWorker()); diff --git a/web/apps/photos/src/worker/ml.worker.ts b/web/apps/photos/src/services/ml/face.worker.ts similarity index 100% rename from web/apps/photos/src/worker/ml.worker.ts rename to web/apps/photos/src/services/ml/face.worker.ts diff --git a/web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts b/web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts deleted file mode 100644 index f312a2c5c0..0000000000 --- a/web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { haveWindow } from "@/next/env"; -import { ComlinkWorker } from "@/next/worker/comlink-worker"; -import { type DedicatedMLWorker } from "worker/ml.worker"; - -export const getDedicatedMLWorker = (name: string) => { - if (haveWindow()) { - const cryptoComlinkWorker = new ComlinkWorker( - name ?? "ente-ml-worker", - new Worker(new URL("worker/ml.worker.ts", import.meta.url)), - ); - return cryptoComlinkWorker; - } -}; From 76a09b1473782303f291cc5704e0adfa8769bc10 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 10:53:45 +0530 Subject: [PATCH 07/20] Clean --- .../src/services/machineLearning/mlWorkManager.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts index 3712042798..cacb6d2376 100644 --- a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts +++ b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts @@ -11,6 +11,7 @@ import type { DedicatedMLWorker } from "services/ml/face.worker"; import { MLSyncResult } from "services/ml/types"; import { EnteFile } from "types/file"; import { logQueueStats } from "./machineLearningService"; + const LIVE_SYNC_IDLE_DEBOUNCE_SEC = 30; const LIVE_SYNC_QUEUE_TIMEOUT_SEC = 300; const LOCAL_FILES_UPDATED_DEBOUNCE_SEC = 30; @@ -174,16 +175,6 @@ class MLWorkManager { } } - // Handlers - private async appStartHandler() { - log.info("appStartHandler"); - try { - this.startSyncJob(); - } catch (e) { - log.error("Failed in ML appStart Handler", e); - } - } - private async logoutHandler() { log.info("logoutHandler"); try { From 0bcc6e3f3fcecad8dfbe6debf8e31def4b82a6b8 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 11:44:00 +0530 Subject: [PATCH 08/20] Redo logout --- web/apps/auth/src/components/Navbar.tsx | 5 +- web/apps/auth/src/pages/_app.tsx | 8 +++ .../src/components/DeleteAccountModal.tsx | 6 +- .../src/components/Sidebar/ExitSection.tsx | 10 ++- web/apps/photos/src/pages/_app.tsx | 8 +++ web/apps/photos/src/pages/gallery/index.tsx | 29 ++++++-- .../photos/src/pages/shared-albums/index.tsx | 3 +- web/apps/photos/src/services/clip-service.ts | 5 +- web/apps/photos/src/services/logout.ts | 38 +++++++++++ web/apps/photos/src/services/userService.ts | 13 ---- web/packages/accounts/api/user.ts | 2 +- web/packages/accounts/pages/credentials.tsx | 2 +- web/packages/accounts/pages/generate.tsx | 2 +- .../accounts/pages/two-factor/recover.tsx | 2 +- .../accounts/pages/two-factor/verify.tsx | 2 +- web/packages/accounts/pages/verify.tsx | 2 +- web/packages/accounts/services/logout.ts | 50 ++++++++++++++ web/packages/accounts/services/user.ts | 67 ------------------- web/packages/next/types/ipc.ts | 4 +- .../{localForage/index.ts => localForage.ts} | 4 ++ .../shared/storage/localForage/helpers.ts | 5 -- 21 files changed, 151 insertions(+), 116 deletions(-) create mode 100644 web/apps/photos/src/services/logout.ts create mode 100644 web/packages/accounts/services/logout.ts delete mode 100644 web/packages/accounts/services/user.ts rename web/packages/shared/storage/{localForage/index.ts => localForage.ts} (76%) delete mode 100644 web/packages/shared/storage/localForage/helpers.ts diff --git a/web/apps/auth/src/components/Navbar.tsx b/web/apps/auth/src/components/Navbar.tsx index 293d7fc16a..87614d6433 100644 --- a/web/apps/auth/src/components/Navbar.tsx +++ b/web/apps/auth/src/components/Navbar.tsx @@ -1,4 +1,3 @@ -import { logoutUser } from "@ente/accounts/services/user"; import { HorizontalFlex } from "@ente/shared/components/Container"; import { EnteLogo } from "@ente/shared/components/EnteLogo"; import NavbarBase from "@ente/shared/components/Navbar/base"; @@ -11,7 +10,7 @@ import { AppContext } from "pages/_app"; import React from "react"; export default function AuthNavbar() { - const { isMobile } = React.useContext(AppContext); + const { isMobile, logout } = React.useContext(AppContext); return ( @@ -25,7 +24,7 @@ export default function AuthNavbar() { } - onClick={logoutUser} + onClick={logout} > {t("LOGOUT")} diff --git a/web/apps/auth/src/pages/_app.tsx b/web/apps/auth/src/pages/_app.tsx index a5aa55f98d..1ce3f3a5f2 100644 --- a/web/apps/auth/src/pages/_app.tsx +++ b/web/apps/auth/src/pages/_app.tsx @@ -4,6 +4,7 @@ import { logStartupBanner, logUnhandledErrorsAndRejections, } from "@/next/log-web"; +import { accountLogout } from "@ente/accounts/services/logout"; import { APPS, APP_TITLES, @@ -44,6 +45,7 @@ type AppContextType = { setThemeColor: SetTheme; somethingWentWrong: () => void; setDialogBoxAttributesV2: SetDialogBoxAttributesV2; + logout: () => void; }; export const AppContext = createContext(null); @@ -128,6 +130,11 @@ export default function App({ Component, pageProps }: AppProps) { content: t("UNKNOWN_ERROR"), }); + const logout = async () => { + await accountLogout(); + router.push(PAGES.ROOT); + }; + const title = isI18nReady ? t("TITLE", { context: APPS.AUTH }) : APP_TITLES.get(APPS.AUTH); @@ -162,6 +169,7 @@ export default function App({ Component, pageProps }: AppProps) { setThemeColor, somethingWentWrong, setDialogBoxAttributesV2, + logout, }} > {(loading || !isI18nReady) && ( diff --git a/web/apps/photos/src/components/DeleteAccountModal.tsx b/web/apps/photos/src/components/DeleteAccountModal.tsx index 744fbf3129..d6eb3a0373 100644 --- a/web/apps/photos/src/components/DeleteAccountModal.tsx +++ b/web/apps/photos/src/components/DeleteAccountModal.tsx @@ -1,5 +1,4 @@ import log from "@/next/log"; -import { logoutUser } from "@ente/accounts/services/user"; import DialogBoxV2 from "@ente/shared/components/DialogBoxV2"; import EnteButton from "@ente/shared/components/EnteButton"; import { DELETE_ACCOUNT_EMAIL } from "@ente/shared/constants/urls"; @@ -43,7 +42,8 @@ const getReasonOptions = (): DropdownOption[] => { }; const DeleteAccountModal = ({ open, onClose }: Iprops) => { - const { setDialogBoxAttributesV2, isMobile } = useContext(AppContext); + const { setDialogBoxAttributesV2, isMobile, logout } = + useContext(AppContext); const { authenticateUser } = useContext(GalleryContext); const [loading, setLoading] = useState(false); const deleteAccountChallenge = useRef(); @@ -145,7 +145,7 @@ const DeleteAccountModal = ({ open, onClose }: Iprops) => { ); const { reason, feedback } = reasonAndFeedbackRef.current; await deleteAccount(decryptedChallenge, reason, feedback); - logoutUser(); + logout(); } catch (e) { log.error("solveChallengeAndDeleteAccount failed", e); somethingWentWrong(); diff --git a/web/apps/photos/src/components/Sidebar/ExitSection.tsx b/web/apps/photos/src/components/Sidebar/ExitSection.tsx index 6f9492b779..272f2c572a 100644 --- a/web/apps/photos/src/components/Sidebar/ExitSection.tsx +++ b/web/apps/photos/src/components/Sidebar/ExitSection.tsx @@ -1,13 +1,11 @@ -import { t } from "i18next"; -import { useContext, useState } from "react"; - -import { logoutUser } from "@ente/accounts/services/user"; import DeleteAccountModal from "components/DeleteAccountModal"; import { EnteMenuItem } from "components/Menu/EnteMenuItem"; +import { t } from "i18next"; import { AppContext } from "pages/_app"; +import { useContext, useState } from "react"; export default function ExitSection() { - const { setDialogMessage } = useContext(AppContext); + const { setDialogMessage, logout } = useContext(AppContext); const [deleteAccountModalView, setDeleteAccountModalView] = useState(false); @@ -19,7 +17,7 @@ export default function ExitSection() { title: t("LOGOUT_MESSAGE"), proceed: { text: t("LOGOUT"), - action: logoutUser, + action: logout, variant: "critical", }, close: { text: t("CANCEL") }, diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index 77e724d292..eacc713eb1 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -53,6 +53,7 @@ import { createContext, useEffect, useRef, useState } from "react"; import LoadingBar from "react-top-loading-bar"; import DownloadManager from "services/download"; import exportService, { resumeExportsIfNeeded } from "services/export"; +import { photosLogout } from "services/logout"; import { getMLSearchConfig, updateMLSearchConfig, @@ -100,6 +101,7 @@ type AppContextType = { setDialogBoxAttributesV2: SetDialogBoxAttributesV2; isCFProxyDisabled: boolean; setIsCFProxyDisabled: (disabled: boolean) => void; + logout: () => Promise; }; export const AppContext = createContext(null); @@ -336,6 +338,11 @@ export default function App({ Component, pageProps }: AppProps) { content: t("UNKNOWN_ERROR"), }); + const logout = async () => { + await photosLogout(); + router.push(PAGES.ROOT); + }; + const title = isI18nReady ? t("TITLE", { context: APPS.PHOTOS }) : APP_TITLES.get(APPS.PHOTOS); @@ -394,6 +401,7 @@ export default function App({ Component, pageProps }: AppProps) { updateMapEnabled, isCFProxyDisabled, setIsCFProxyDisabled, + logout, }} > {(loading || !isI18nReady) && ( diff --git a/web/apps/photos/src/pages/gallery/index.tsx b/web/apps/photos/src/pages/gallery/index.tsx index f870dfb768..1821441bcd 100644 --- a/web/apps/photos/src/pages/gallery/index.tsx +++ b/web/apps/photos/src/pages/gallery/index.tsx @@ -3,6 +3,7 @@ import { APPS } from "@ente/shared/apps/constants"; import { CenteredFlex } from "@ente/shared/components/Container"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; import { PHOTOS_PAGES as PAGES } from "@ente/shared/constants/pages"; +import { getRecoveryKey } from "@ente/shared/crypto/helpers"; import { CustomError } from "@ente/shared/error"; import { useFileInput } from "@ente/shared/hooks/useFileInput"; import useMemoSingleThreaded from "@ente/shared/hooks/useMemoSingleThreaded"; @@ -93,11 +94,7 @@ import { getLocalFiles, syncFiles } from "services/fileService"; import locationSearchService from "services/locationSearchService"; import { getLocalTrashedFiles, syncTrash } from "services/trashService"; import uploadManager from "services/upload/uploadManager"; -import { - isTokenValid, - syncMapEnabled, - validateKey, -} from "services/userService"; +import { isTokenValid, syncMapEnabled } from "services/userService"; import { Collection, CollectionSummaries } from "types/collection"; import { EnteFile } from "types/file"; import { @@ -249,8 +246,13 @@ export default function Gallery() { const [tempHiddenFileIds, setTempHiddenFileIds] = useState>( new Set(), ); - const { startLoading, finishLoading, setDialogMessage, ...appContext } = - useContext(AppContext); + const { + startLoading, + finishLoading, + setDialogMessage, + logout, + ...appContext + } = useContext(AppContext); const [collectionSummaries, setCollectionSummaries] = useState(); const [hiddenCollectionSummaries, setHiddenCollectionSummaries] = @@ -319,6 +321,19 @@ export default function Gallery() { const [isClipSearchResult, setIsClipSearchResult] = useState(false); + // Ensure that the keys in local storage are not malformed by verifying that + // the recoveryKey can be decrypted with the masterKey. + // Note: This is not bullet-proof. + const validateKey = async () => { + try { + await getRecoveryKey(); + return true; + } catch (e) { + await logout(); + return false; + } + }; + useEffect(() => { appContext.showNavBar(true); const key = getKey(SESSION_KEYS.ENCRYPTION_KEY); diff --git a/web/apps/photos/src/pages/shared-albums/index.tsx b/web/apps/photos/src/pages/shared-albums/index.tsx index ab35b23fac..d26e93eade 100644 --- a/web/apps/photos/src/pages/shared-albums/index.tsx +++ b/web/apps/photos/src/pages/shared-albums/index.tsx @@ -1,5 +1,4 @@ import log from "@/next/log"; -import { logoutUser } from "@ente/accounts/services/user"; import { APPS } from "@ente/shared/apps/constants"; import { CenteredFlex, @@ -185,7 +184,7 @@ export default function PublicCollectionGallery() { nonClosable: true, proceed: { text: t("LOGIN"), - action: logoutUser, + action: () => router.push(PAGES.ROOT), variant: "accent", }, }); diff --git a/web/apps/photos/src/services/clip-service.ts b/web/apps/photos/src/services/clip-service.ts index aa724b4d58..010f4eb40d 100644 --- a/web/apps/photos/src/services/clip-service.ts +++ b/web/apps/photos/src/services/clip-service.ts @@ -87,14 +87,15 @@ class CLIPService { return isElectron(); }; - private logoutHandler = async () => { + async logout() { if (this.embeddingExtractionInProgress) { this.embeddingExtractionInProgress.abort(); } if (this.onFileUploadedHandler) { await this.removeOnFileUploadListener(); } - }; + } + setupOnFileUploadListener = async () => { try { diff --git a/web/apps/photos/src/services/logout.ts b/web/apps/photos/src/services/logout.ts new file mode 100644 index 0000000000..298875459a --- /dev/null +++ b/web/apps/photos/src/services/logout.ts @@ -0,0 +1,38 @@ +import log from "@/next/log"; +import { accountLogout } from "@ente/accounts/services/logout"; +import { Events, eventBus } from "@ente/shared/events"; + +/** + * Logout sequence for the photos app. + * + * This function is guaranteed not to throw any errors. + * + * See: [Note: Do not throw during logout]. + */ +export const photosLogout = async () => { + await accountLogout(); + + const electron = globalThis.electron; + if (electron) { + try { + await electron.watch.reset(); + } catch (e) { + log.error("Ignoring error when resetting native folder watches", e); + } + try { + await electron.clearConvertToMP4Results(); + } catch (e) { + log.error("Ignoring error when clearing convert-to-mp4 results", e); + } + try { + await electron.clearStores(); + } catch (e) { + log.error("Ignoring error when clearing native stores", e); + } + } + try { + eventBus.emit(Events.LOGOUT); + } catch (e) { + log.error("Ignoring error in event-bus logout handlers", e); + } +}; diff --git a/web/apps/photos/src/services/userService.ts b/web/apps/photos/src/services/userService.ts index 95b1b95c92..9b831522a6 100644 --- a/web/apps/photos/src/services/userService.ts +++ b/web/apps/photos/src/services/userService.ts @@ -233,19 +233,6 @@ export const deleteAccount = async ( } }; -// Ensure that the keys in local storage are not malformed by verifying that the -// recoveryKey can be decrypted with the masterKey. -// Note: This is not bullet-proof. -export const validateKey = async () => { - try { - await getRecoveryKey(); - return true; - } catch (e) { - await logoutUser(); - return false; - } -}; - export const getFaceSearchEnabledStatus = async () => { try { const token = getToken(); diff --git a/web/packages/accounts/api/user.ts b/web/packages/accounts/api/user.ts index 7a072064e0..7e313b38e5 100644 --- a/web/packages/accounts/api/user.ts +++ b/web/packages/accounts/api/user.ts @@ -43,7 +43,7 @@ export const putAttributes = (token: string, keyAttributes: KeyAttributes) => }, ); -export const _logout = async () => { +export const logout = async () => { try { const token = getToken(); await HTTPService.post(`${ENDPOINT}/users/logout`, null, undefined, { diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index 1e93809c95..cf09c07fe7 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -45,12 +45,12 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { getSRPAttributes } from "../api/srp"; import { PAGES } from "../constants/pages"; +import { logoutUser } from "../services/logout"; import { configureSRP, generateSRPSetupAttributes, loginViaSRP, } from "../services/srp"; -import { logoutUser } from "../services/user"; import { SRPAttributes } from "../types/srp"; export default function Credentials({ appContext, appName }: PageProps) { diff --git a/web/packages/accounts/pages/generate.tsx b/web/packages/accounts/pages/generate.tsx index fb92edb147..0334452346 100644 --- a/web/packages/accounts/pages/generate.tsx +++ b/web/packages/accounts/pages/generate.tsx @@ -1,7 +1,7 @@ import log from "@/next/log"; import { putAttributes } from "@ente/accounts/api/user"; +import { logoutUser } from "@ente/accounts/services/logout"; import { configureSRP } from "@ente/accounts/services/srp"; -import { logoutUser } from "@ente/accounts/services/user"; import { generateKeyAndSRPAttributes } from "@ente/accounts/utils/srp"; import { generateAndSaveIntermediateKeyAttributes, diff --git a/web/packages/accounts/pages/two-factor/recover.tsx b/web/packages/accounts/pages/two-factor/recover.tsx index 150bd47de5..213b1732a3 100644 --- a/web/packages/accounts/pages/two-factor/recover.tsx +++ b/web/packages/accounts/pages/two-factor/recover.tsx @@ -2,7 +2,7 @@ import log from "@/next/log"; import { recoverTwoFactor, removeTwoFactor } from "@ente/accounts/api/user"; import { PAGES } from "@ente/accounts/constants/pages"; import { TwoFactorType } from "@ente/accounts/constants/twofactor"; -import { logoutUser } from "@ente/accounts/services/user"; +import { logoutUser } from "@ente/accounts/services/logout"; import { PageProps } from "@ente/shared/apps/types"; import { VerticallyCentered } from "@ente/shared/components/Container"; import { DialogBoxAttributesV2 } from "@ente/shared/components/DialogBoxV2/types"; diff --git a/web/packages/accounts/pages/two-factor/verify.tsx b/web/packages/accounts/pages/two-factor/verify.tsx index 5498211aef..18f371433a 100644 --- a/web/packages/accounts/pages/two-factor/verify.tsx +++ b/web/packages/accounts/pages/two-factor/verify.tsx @@ -3,7 +3,7 @@ import VerifyTwoFactor, { VerifyTwoFactorCallback, } from "@ente/accounts/components/two-factor/VerifyForm"; import { PAGES } from "@ente/accounts/constants/pages"; -import { logoutUser } from "@ente/accounts/services/user"; +import { logoutUser } from "@ente/accounts/services/logout"; import type { PageProps } from "@ente/shared/apps/types"; import { VerticallyCentered } from "@ente/shared/components/Container"; import FormPaper from "@ente/shared/components/Form/FormPaper"; diff --git a/web/packages/accounts/pages/verify.tsx b/web/packages/accounts/pages/verify.tsx index 6515a96b76..1f2760aa90 100644 --- a/web/packages/accounts/pages/verify.tsx +++ b/web/packages/accounts/pages/verify.tsx @@ -29,8 +29,8 @@ import { HttpStatusCode } from "axios"; import { useRouter } from "next/router"; import { putAttributes, sendOtt, verifyOtt } from "../api/user"; import { PAGES } from "../constants/pages"; +import { logoutUser } from "../services/logout"; import { configureSRP } from "../services/srp"; -import { logoutUser } from "../services/user"; import { SRPSetupAttributes } from "../types/srp"; export default function VerifyPage({ appContext, appName }: PageProps) { diff --git a/web/packages/accounts/services/logout.ts b/web/packages/accounts/services/logout.ts new file mode 100644 index 0000000000..04ada94f29 --- /dev/null +++ b/web/packages/accounts/services/logout.ts @@ -0,0 +1,50 @@ +import { clearCaches } from "@/next/blob-cache"; +import log from "@/next/log"; +import InMemoryStore from "@ente/shared/storage/InMemoryStore"; +import { clearFiles } from "@ente/shared/storage/localForage"; +import { clearData } from "@ente/shared/storage/localStorage"; +import { clearKeys } from "@ente/shared/storage/sessionStorage"; +import { logout as remoteLogout } from "../api/user"; + +/** + * Logout sequence common to all apps that rely on the accounts package. + * + * [Note: Do not throw during logout] + * + * This function is guaranteed to not thrown any errors, and will try to + * independently complete all the steps in the sequence that can be completed. + * This allows the user to logout and start again even if somehow their account + * gets in an unexpected state. + */ +export const accountLogout = async () => { + try { + await remoteLogout(); + } catch (e) { + log.error("Ignoring error during POST /users/logout", e); + } + try { + InMemoryStore.clear(); + } catch (e) { + log.error("Ignoring error when clearing in-memory store", e); + } + try { + clearKeys(); + } catch (e) { + log.error("Ignoring error when clearing keys", e); + } + try { + clearData(); + } catch (e) { + log.error("Ignoring error when clearing data", e); + } + try { + await clearCaches(); + } catch (e) { + log.error("Ignoring error when clearing caches", e); + } + try { + await clearFiles(); + } catch (e) { + log.error("Ignoring error when clearing files", e); + } +}; diff --git a/web/packages/accounts/services/user.ts b/web/packages/accounts/services/user.ts deleted file mode 100644 index fdbfc770b3..0000000000 --- a/web/packages/accounts/services/user.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { clearCaches } from "@/next/blob-cache"; -import log from "@/next/log"; -import { Events, eventBus } from "@ente/shared/events"; -import InMemoryStore from "@ente/shared/storage/InMemoryStore"; -import { clearFiles } from "@ente/shared/storage/localForage/helpers"; -import { clearData } from "@ente/shared/storage/localStorage"; -import { clearKeys } from "@ente/shared/storage/sessionStorage"; -import router from "next/router"; -import { _logout } from "../api/user"; -import { PAGES } from "../constants/pages"; - -export const logoutUser = async () => { - try { - await _logout(); - } catch (e) { - log.error("Ignoring error during POST /users/logout", e); - } - try { - InMemoryStore.clear(); - } catch (e) { - log.error("Ignoring error when clearing in-memory store", e); - } - try { - clearKeys(); - } catch (e) { - log.error("Ignoring error when clearing keys", e); - } - try { - clearData(); - } catch (e) { - log.error("Ignoring error when clearing data", e); - } - try { - await clearCaches(); - } catch (e) { - log.error("Ignoring error when clearing caches", e); - } - try { - await clearFiles(); - } catch (e) { - log.error("Ignoring error when clearing files", e); - } - const electron = globalThis.electron; - if (electron) { - try { - await electron.watch.reset(); - } catch (e) { - log.error("Ignoring error when resetting native folder watches", e); - } - try { - await electron.clearConvertToMP4Results(); - } catch (e) { - log.error("Ignoring error when clearing convert-to-mp4 results", e); - } - try { - await electron.clearStores(); - } catch (e) { - log.error("Ignoring error when clearing native stores", e); - } - } - try { - eventBus.emit(Events.LOGOUT); - } catch (e) { - log.error("Ignoring error in event-bus logout handlers", e); - } - router.push(PAGES.ROOT); -}; diff --git a/web/packages/next/types/ipc.ts b/web/packages/next/types/ipc.ts index d91f7bf3fa..b0eb7fb246 100644 --- a/web/packages/next/types/ipc.ts +++ b/web/packages/next/types/ipc.ts @@ -69,14 +69,14 @@ export interface Electron { * This is a coarse single shot cleanup, meant for use in clearing any * persisted Electron side state during logout. */ - clearStores: () => void; + clearStores: () => Promise; /** * Clear an state corresponding to in-flight convert-to-mp4 requests. * * This is meant for use during logout. */ - clearConvertToMP4Results: () => void; + clearConvertToMP4Results: () => Promise; /** * Return the previously saved encryption key from persistent safe storage. diff --git a/web/packages/shared/storage/localForage/index.ts b/web/packages/shared/storage/localForage.ts similarity index 76% rename from web/packages/shared/storage/localForage/index.ts rename to web/packages/shared/storage/localForage.ts index a3bb4442d0..6c6da257f8 100644 --- a/web/packages/shared/storage/localForage/index.ts +++ b/web/packages/shared/storage/localForage.ts @@ -11,3 +11,7 @@ if (haveWindow()) { } export default localForage; + +export const clearFiles = async () => { + await localForage.clear(); +}; diff --git a/web/packages/shared/storage/localForage/helpers.ts b/web/packages/shared/storage/localForage/helpers.ts deleted file mode 100644 index 913b9f52f9..0000000000 --- a/web/packages/shared/storage/localForage/helpers.ts +++ /dev/null @@ -1,5 +0,0 @@ -import localForage from "."; - -export const clearFiles = async () => { - await localForage.clear(); -}; From 0f452444575693cd44b87c0086992773a0ce6afb Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 12:38:54 +0530 Subject: [PATCH 09/20] Redo logout 2 --- web/apps/photos/src/pages/gallery/index.tsx | 2 +- web/apps/photos/src/services/userService.ts | 6 ------ web/apps/photos/src/utils/ui/index.tsx | 7 ++++--- web/packages/accounts/pages/credentials.tsx | 5 ++--- web/packages/accounts/pages/generate.tsx | 5 ++--- web/packages/accounts/pages/two-factor/recover.tsx | 4 ++-- web/packages/accounts/pages/two-factor/verify.tsx | 8 ++++---- web/packages/accounts/pages/verify.tsx | 9 ++++----- web/packages/accounts/services/logout.ts | 7 ++++--- web/packages/shared/apps/types.ts | 1 + web/packages/shared/storage/localForage.ts | 4 ---- 11 files changed, 24 insertions(+), 34 deletions(-) diff --git a/web/apps/photos/src/pages/gallery/index.tsx b/web/apps/photos/src/pages/gallery/index.tsx index 1821441bcd..d99a1ff839 100644 --- a/web/apps/photos/src/pages/gallery/index.tsx +++ b/web/apps/photos/src/pages/gallery/index.tsx @@ -687,7 +687,7 @@ export default function Gallery() { }, [collections, hiddenCollections]); const showSessionExpiredMessage = () => { - setDialogMessage(getSessionExpiredMessage()); + setDialogMessage(getSessionExpiredMessage(logout)); }; const syncWithRemote = async (force = false, silent = false) => { diff --git a/web/apps/photos/src/services/userService.ts b/web/apps/photos/src/services/userService.ts index 9b831522a6..c8dba0ba0b 100644 --- a/web/apps/photos/src/services/userService.ts +++ b/web/apps/photos/src/services/userService.ts @@ -1,7 +1,5 @@ import log from "@/next/log"; import { putAttributes } from "@ente/accounts/api/user"; -import { logoutUser } from "@ente/accounts/services/user"; -import { getRecoveryKey } from "@ente/shared/crypto/helpers"; import { ApiError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import { getEndpoint, getFamilyPortalURL } from "@ente/shared/network/api"; @@ -104,10 +102,6 @@ export const getRoadmapRedirectURL = async () => { } }; -export const clearFiles = async () => { - await localForage.clear(); -}; - export const isTokenValid = async (token: string) => { try { const resp = await HTTPService.get( diff --git a/web/apps/photos/src/utils/ui/index.tsx b/web/apps/photos/src/utils/ui/index.tsx index 8f4895ead5..562a753fd9 100644 --- a/web/apps/photos/src/utils/ui/index.tsx +++ b/web/apps/photos/src/utils/ui/index.tsx @@ -1,6 +1,5 @@ import { ensureElectron } from "@/next/electron"; import { AppUpdate } from "@/next/types/ipc"; -import { logoutUser } from "@ente/accounts/services/user"; import { DialogBoxAttributes } from "@ente/shared/components/DialogBox/types"; import AutoAwesomeOutlinedIcon from "@mui/icons-material/AutoAwesomeOutlined"; import InfoOutlined from "@mui/icons-material/InfoRounded"; @@ -121,14 +120,16 @@ export const getSubscriptionPurchaseSuccessMessage = ( ), }); -export const getSessionExpiredMessage = (): DialogBoxAttributes => ({ +export const getSessionExpiredMessage = ( + action: () => void, +): DialogBoxAttributes => ({ title: t("SESSION_EXPIRED"), content: t("SESSION_EXPIRED_MESSAGE"), nonClosable: true, proceed: { text: t("LOGIN"), - action: logoutUser, + action, variant: "accent", }, }); diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index cf09c07fe7..82306e992a 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -45,7 +45,6 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { getSRPAttributes } from "../api/srp"; import { PAGES } from "../constants/pages"; -import { logoutUser } from "../services/logout"; import { configureSRP, generateSRPSetupAttributes, @@ -53,7 +52,7 @@ import { } from "../services/srp"; import { SRPAttributes } from "../types/srp"; -export default function Credentials({ appContext, appName }: PageProps) { +export default function Credentials({ appContext, appName, logout }: PageProps) { const [srpAttributes, setSrpAttributes] = useState(); const [keyAttributes, setKeyAttributes] = useState(); const [user, setUser] = useState(); @@ -275,7 +274,7 @@ export default function Credentials({ appContext, appName }: PageProps) { {t("FORGOT_PASSWORD")} - + {t("CHANGE_EMAIL")} diff --git a/web/packages/accounts/pages/generate.tsx b/web/packages/accounts/pages/generate.tsx index 0334452346..20a10f4788 100644 --- a/web/packages/accounts/pages/generate.tsx +++ b/web/packages/accounts/pages/generate.tsx @@ -1,6 +1,5 @@ import log from "@/next/log"; import { putAttributes } from "@ente/accounts/api/user"; -import { logoutUser } from "@ente/accounts/services/logout"; import { configureSRP } from "@ente/accounts/services/srp"; import { generateKeyAndSRPAttributes } from "@ente/accounts/utils/srp"; import { @@ -30,7 +29,7 @@ import { import { KeyAttributes, User } from "@ente/shared/user/types"; import { useRouter } from "next/router"; -export default function Generate({ appContext, appName }: PageProps) { +export default function Generate({ appContext, appName, logout }: PageProps) { const [token, setToken] = useState(); const [user, setUser] = useState(); const [recoverModalView, setRecoveryModalView] = useState(false); @@ -113,7 +112,7 @@ export default function Generate({ appContext, appName }: PageProps) { buttonText={t("SET_PASSPHRASE")} /> - + {t("GO_BACK")} diff --git a/web/packages/accounts/pages/two-factor/recover.tsx b/web/packages/accounts/pages/two-factor/recover.tsx index 213b1732a3..95d3d767cc 100644 --- a/web/packages/accounts/pages/two-factor/recover.tsx +++ b/web/packages/accounts/pages/two-factor/recover.tsx @@ -2,7 +2,6 @@ import log from "@/next/log"; import { recoverTwoFactor, removeTwoFactor } from "@ente/accounts/api/user"; import { PAGES } from "@ente/accounts/constants/pages"; import { TwoFactorType } from "@ente/accounts/constants/twofactor"; -import { logoutUser } from "@ente/accounts/services/logout"; import { PageProps } from "@ente/shared/apps/types"; import { VerticallyCentered } from "@ente/shared/components/Container"; import { DialogBoxAttributesV2 } from "@ente/shared/components/DialogBoxV2/types"; @@ -32,6 +31,7 @@ bip39.setDefaultWordlist("english"); export default function Recover({ appContext, twoFactorType = TwoFactorType.TOTP, + logout, }: PageProps) { const [encryptedTwoFactorSecret, setEncryptedTwoFactorSecret] = useState(null); @@ -77,7 +77,7 @@ export default function Recover({ e instanceof ApiError && e.httpStatusCode === HttpStatusCode.NotFound ) { - logoutUser(); + logout(); } else { log.error("two factor recovery page setup failed", e); setDoesHaveEncryptedRecoveryKey(false); diff --git a/web/packages/accounts/pages/two-factor/verify.tsx b/web/packages/accounts/pages/two-factor/verify.tsx index 18f371433a..e927500629 100644 --- a/web/packages/accounts/pages/two-factor/verify.tsx +++ b/web/packages/accounts/pages/two-factor/verify.tsx @@ -3,7 +3,7 @@ import VerifyTwoFactor, { VerifyTwoFactorCallback, } from "@ente/accounts/components/two-factor/VerifyForm"; import { PAGES } from "@ente/accounts/constants/pages"; -import { logoutUser } from "@ente/accounts/services/logout"; + import type { PageProps } from "@ente/shared/apps/types"; import { VerticallyCentered } from "@ente/shared/components/Container"; import FormPaper from "@ente/shared/components/Form/FormPaper"; @@ -19,7 +19,7 @@ import { t } from "i18next"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; -export const TwoFactorVerify: React.FC = () => { +export const TwoFactorVerify: React.FC = ({ logout }: PageProps) => { const [sessionID, setSessionID] = useState(""); const router = useRouter(); @@ -60,7 +60,7 @@ export const TwoFactorVerify: React.FC = () => { e instanceof ApiError && e.httpStatusCode === HttpStatusCode.NotFound ) { - logoutUser(); + logout(); } else { throw e; } @@ -79,7 +79,7 @@ export const TwoFactorVerify: React.FC = () => { > {t("LOST_DEVICE")} - + {t("CHANGE_EMAIL")} diff --git a/web/packages/accounts/pages/verify.tsx b/web/packages/accounts/pages/verify.tsx index 1f2760aa90..f49fca9f80 100644 --- a/web/packages/accounts/pages/verify.tsx +++ b/web/packages/accounts/pages/verify.tsx @@ -16,7 +16,7 @@ import SingleInputForm, { import { ApiError } from "@ente/shared/error"; import { getAccountsURL } from "@ente/shared/network/api"; import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore"; -import { clearFiles } from "@ente/shared/storage/localForage/helpers"; +import localForage from "@ente/shared/storage/localForage"; import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage"; import { getLocalReferralSource, @@ -29,11 +29,10 @@ import { HttpStatusCode } from "axios"; import { useRouter } from "next/router"; import { putAttributes, sendOtt, verifyOtt } from "../api/user"; import { PAGES } from "../constants/pages"; -import { logoutUser } from "../services/logout"; import { configureSRP } from "../services/srp"; import { SRPSetupAttributes } from "../types/srp"; -export default function VerifyPage({ appContext, appName }: PageProps) { +export default function VerifyPage({ appContext, appName, logout }: PageProps) { const [email, setEmail] = useState(""); const [resend, setResend] = useState(0); @@ -121,7 +120,7 @@ export default function VerifyPage({ appContext, appName }: PageProps) { await configureSRP(srpSetupAttributes); } } - clearFiles(); + localForage.clear(); setIsFirstLogin(true); const redirectURL = InMemoryStore.get(MS_KEYS.REDIRECT_URL); InMemoryStore.delete(MS_KEYS.REDIRECT_URL); @@ -191,7 +190,7 @@ export default function VerifyPage({ appContext, appName }: PageProps) { )} {resend === 1 && {t("SENDING")}} {resend === 2 && {t("SENT")}} - + {t("CHANGE_EMAIL")} diff --git a/web/packages/accounts/services/logout.ts b/web/packages/accounts/services/logout.ts index 04ada94f29..d323e5090d 100644 --- a/web/packages/accounts/services/logout.ts +++ b/web/packages/accounts/services/logout.ts @@ -1,10 +1,11 @@ import { clearCaches } from "@/next/blob-cache"; import log from "@/next/log"; import InMemoryStore from "@ente/shared/storage/InMemoryStore"; -import { clearFiles } from "@ente/shared/storage/localForage"; +import loc } from "@ente/shared/storage/localForage"; import { clearData } from "@ente/shared/storage/localStorage"; import { clearKeys } from "@ente/shared/storage/sessionStorage"; import { logout as remoteLogout } from "../api/user"; +import localForage from "@ente/shared/storage/localForage"; /** * Logout sequence common to all apps that rely on the accounts package. @@ -43,8 +44,8 @@ export const accountLogout = async () => { log.error("Ignoring error when clearing caches", e); } try { - await clearFiles(); + await localForage.clear(); } catch (e) { - log.error("Ignoring error when clearing files", e); + log.error("Ignoring error when clearing local forage", e); } }; diff --git a/web/packages/shared/apps/types.ts b/web/packages/shared/apps/types.ts index 0d5d1aa1a6..37c193a980 100644 --- a/web/packages/shared/apps/types.ts +++ b/web/packages/shared/apps/types.ts @@ -10,4 +10,5 @@ export interface PageProps { }; appName: APPS; twoFactorType?: TwoFactorType; + logout: () => void; } diff --git a/web/packages/shared/storage/localForage.ts b/web/packages/shared/storage/localForage.ts index 6c6da257f8..a3bb4442d0 100644 --- a/web/packages/shared/storage/localForage.ts +++ b/web/packages/shared/storage/localForage.ts @@ -11,7 +11,3 @@ if (haveWindow()) { } export default localForage; - -export const clearFiles = async () => { - await localForage.clear(); -}; From 96cd6b37596acaa721ed81ef42b916aac5c42dd6 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 12:45:39 +0530 Subject: [PATCH 10/20] rl3 --- web/apps/accounts/src/pages/_app.tsx | 8 ++++++++ web/apps/auth/src/pages/_app.tsx | 5 ++--- web/apps/photos/src/pages/_app.tsx | 5 ++--- web/packages/accounts/pages/credentials.tsx | 4 +++- web/packages/accounts/pages/generate.tsx | 4 +++- web/packages/accounts/pages/two-factor/recover.tsx | 3 ++- web/packages/accounts/pages/two-factor/verify.tsx | 6 +++++- web/packages/accounts/pages/verify.tsx | 4 +++- web/packages/shared/apps/types.ts | 2 +- 9 files changed, 29 insertions(+), 12 deletions(-) diff --git a/web/apps/accounts/src/pages/_app.tsx b/web/apps/accounts/src/pages/_app.tsx index 40a4a14588..a1927f52b2 100644 --- a/web/apps/accounts/src/pages/_app.tsx +++ b/web/apps/accounts/src/pages/_app.tsx @@ -1,6 +1,8 @@ import { CustomHead } from "@/next/components/Head"; import { setupI18n } from "@/next/i18n"; import { logUnhandledErrorsAndRejections } from "@/next/log-web"; +import { PAGES } from "@ente/accounts/constants/pages"; +import { accountLogout } from "@ente/accounts/services/logout"; import { APPS, APP_TITLES } from "@ente/shared/apps/constants"; import { Overlay } from "@ente/shared/components/Container"; import DialogBoxV2 from "@ente/shared/components/DialogBoxV2"; @@ -27,6 +29,7 @@ interface AppContextProps { isMobile: boolean; showNavBar: (show: boolean) => void; setDialogBoxAttributesV2: SetDialogBoxAttributesV2; + logout: () => void; } export const AppContext = createContext({} as AppContextProps); @@ -78,6 +81,10 @@ export default function App({ Component, pageProps }: AppProps) { const theme = getTheme(themeColor, APPS.PHOTOS); + const logout = () => { + void accountLogout().then(() => router.push(PAGES.ROOT)); + }; + const title = isI18nReady ? t("TITLE", { context: APPS.ACCOUNTS }) : APP_TITLES.get(APPS.ACCOUNTS); @@ -101,6 +108,7 @@ export default function App({ Component, pageProps }: AppProps) { showNavBar, setDialogBoxAttributesV2: setDialogBoxAttributesV2 as any, + logout, }} > {!isI18nReady && ( diff --git a/web/apps/auth/src/pages/_app.tsx b/web/apps/auth/src/pages/_app.tsx index 1ce3f3a5f2..a0a579a80a 100644 --- a/web/apps/auth/src/pages/_app.tsx +++ b/web/apps/auth/src/pages/_app.tsx @@ -130,9 +130,8 @@ export default function App({ Component, pageProps }: AppProps) { content: t("UNKNOWN_ERROR"), }); - const logout = async () => { - await accountLogout(); - router.push(PAGES.ROOT); + const logout = () => { + void accountLogout().then(() => router.push(PAGES.ROOT)); }; const title = isI18nReady diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index eacc713eb1..a22867173e 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -338,9 +338,8 @@ export default function App({ Component, pageProps }: AppProps) { content: t("UNKNOWN_ERROR"), }); - const logout = async () => { - await photosLogout(); - router.push(PAGES.ROOT); + const logout = () => { + void photosLogout().then(() => router.push(PAGES.ROOT)); }; const title = isI18nReady diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index 82306e992a..777fe97da6 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -52,7 +52,9 @@ import { } from "../services/srp"; import { SRPAttributes } from "../types/srp"; -export default function Credentials({ appContext, appName, logout }: PageProps) { +export default function Credentials({ appContext, appName }: PageProps) { + const { logout } = appContext; + const [srpAttributes, setSrpAttributes] = useState(); const [keyAttributes, setKeyAttributes] = useState(); const [user, setUser] = useState(); diff --git a/web/packages/accounts/pages/generate.tsx b/web/packages/accounts/pages/generate.tsx index 20a10f4788..11c15a4f05 100644 --- a/web/packages/accounts/pages/generate.tsx +++ b/web/packages/accounts/pages/generate.tsx @@ -29,7 +29,9 @@ import { import { KeyAttributes, User } from "@ente/shared/user/types"; import { useRouter } from "next/router"; -export default function Generate({ appContext, appName, logout }: PageProps) { +export default function Generate({ appContext, appName }: PageProps) { + const { logout } = appContext; + const [token, setToken] = useState(); const [user, setUser] = useState(); const [recoverModalView, setRecoveryModalView] = useState(false); diff --git a/web/packages/accounts/pages/two-factor/recover.tsx b/web/packages/accounts/pages/two-factor/recover.tsx index 95d3d767cc..8ed187e0e5 100644 --- a/web/packages/accounts/pages/two-factor/recover.tsx +++ b/web/packages/accounts/pages/two-factor/recover.tsx @@ -31,8 +31,9 @@ bip39.setDefaultWordlist("english"); export default function Recover({ appContext, twoFactorType = TwoFactorType.TOTP, - logout, }: PageProps) { + const { logout } = appContext; + const [encryptedTwoFactorSecret, setEncryptedTwoFactorSecret] = useState(null); const [sessionID, setSessionID] = useState(null); diff --git a/web/packages/accounts/pages/two-factor/verify.tsx b/web/packages/accounts/pages/two-factor/verify.tsx index e927500629..1ec6e437d8 100644 --- a/web/packages/accounts/pages/two-factor/verify.tsx +++ b/web/packages/accounts/pages/two-factor/verify.tsx @@ -19,7 +19,11 @@ import { t } from "i18next"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; -export const TwoFactorVerify: React.FC = ({ logout }: PageProps) => { +export const TwoFactorVerify: React.FC = ({ + appContext, +}: PageProps) => { + const { logout } = appContext; + const [sessionID, setSessionID] = useState(""); const router = useRouter(); diff --git a/web/packages/accounts/pages/verify.tsx b/web/packages/accounts/pages/verify.tsx index f49fca9f80..2a410fd6f2 100644 --- a/web/packages/accounts/pages/verify.tsx +++ b/web/packages/accounts/pages/verify.tsx @@ -32,7 +32,9 @@ import { PAGES } from "../constants/pages"; import { configureSRP } from "../services/srp"; import { SRPSetupAttributes } from "../types/srp"; -export default function VerifyPage({ appContext, appName, logout }: PageProps) { +export default function VerifyPage({ appContext, appName }: PageProps) { + const { logout } = appContext; + const [email, setEmail] = useState(""); const [resend, setResend] = useState(0); diff --git a/web/packages/shared/apps/types.ts b/web/packages/shared/apps/types.ts index 37c193a980..bd3a2d4c5c 100644 --- a/web/packages/shared/apps/types.ts +++ b/web/packages/shared/apps/types.ts @@ -7,8 +7,8 @@ export interface PageProps { showNavBar: (show: boolean) => void; isMobile: boolean; setDialogBoxAttributesV2: SetDialogBoxAttributesV2; + logout: () => void; }; appName: APPS; twoFactorType?: TwoFactorType; - logout: () => void; } From 176431ba1fc7bfd0e7885a689ec5ee4e2cf79a58 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:08:21 +0530 Subject: [PATCH 11/20] Electron side --- desktop/src/main.ts | 12 +++++++++-- desktop/src/main/ipc.ts | 23 +++++++++----------- desktop/src/main/services/logout.ts | 30 ++++++++++++++++++++++++++ desktop/src/main/services/watch.ts | 9 ++++++++ desktop/src/preload.ts | 15 ++++++------- web/apps/photos/src/pages/_app.tsx | 2 +- web/apps/photos/src/services/logout.ts | 14 ++---------- web/packages/next/types/ipc.ts | 25 ++------------------- 8 files changed, 70 insertions(+), 60 deletions(-) create mode 100644 desktop/src/main/services/logout.ts diff --git a/desktop/src/main.ts b/desktop/src/main.ts index 9cba9178df..7ffbdecedd 100644 --- a/desktop/src/main.ts +++ b/desktop/src/main.ts @@ -17,7 +17,11 @@ import { existsSync } from "node:fs"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { attachFSWatchIPCHandlers, attachIPCHandlers } from "./main/ipc"; +import { + attachFSWatchIPCHandlers, + attachIPCHandlers, + attachLogoutIPCHandler, +} from "./main/ipc"; import log, { initLogging } from "./main/log"; import { createApplicationMenu, createTrayContextMenu } from "./main/menu"; import { setupAutoUpdater } from "./main/services/app-update"; @@ -377,8 +381,12 @@ const main = () => { void (async () => { // Create window and prepare for the renderer. mainWindow = createMainWindow(); + + // Setup IPC and streams. + const watcher = createWatcher(mainWindow); attachIPCHandlers(); - attachFSWatchIPCHandlers(createWatcher(mainWindow)); + attachFSWatchIPCHandlers(watcher); + attachLogoutIPCHandler(watcher); registerStreamProtocol(); // Configure the renderer's environment. diff --git a/desktop/src/main/ipc.ts b/desktop/src/main/ipc.ts index 5072db29ea..ab5af51a1c 100644 --- a/desktop/src/main/ipc.ts +++ b/desktop/src/main/ipc.ts @@ -41,16 +41,13 @@ import { fsWriteFile, } from "./services/fs"; import { convertToJPEG, generateImageThumbnail } from "./services/image"; +import { logout } from "./services/logout"; import { clipImageEmbedding, clipTextEmbeddingIfAvailable, } from "./services/ml-clip"; import { detectFaces, faceEmbedding } from "./services/ml-face"; -import { - clearStores, - encryptionKey, - saveEncryptionKey, -} from "./services/store"; +import { encryptionKey, saveEncryptionKey } from "./services/store"; import { clearPendingUploads, listZipItems, @@ -65,11 +62,9 @@ import { watchFindFiles, watchGet, watchRemove, - watchReset, watchUpdateIgnoredFiles, watchUpdateSyncedFiles, } from "./services/watch"; -import { clearConvertToMP4Results } from "./stream"; /** * Listen for IPC events sent/invoked by the renderer process, and route them to @@ -107,10 +102,6 @@ export const attachIPCHandlers = () => { ipcMain.handle("selectDirectory", () => selectDirectory()); - ipcMain.on("clearStores", () => clearStores()); - - ipcMain.on("clearConvertToMP4Results", () => clearConvertToMP4Results()); - ipcMain.handle("saveEncryptionKey", (_, encryptionKey: string) => saveEncryptionKey(encryptionKey), ); @@ -265,6 +256,12 @@ export const attachFSWatchIPCHandlers = (watcher: FSWatcher) => { ipcMain.handle("watchFindFiles", (_, folderPath: string) => watchFindFiles(folderPath), ); - - ipcMain.handle("watchReset", () => watchReset(watcher)); +}; + +/** + * Sibling of {@link attachIPCHandlers} specifically for use with the logout + * event with needs access to the {@link FSWatcher} instance. + */ +export const attachLogoutIPCHandler = (watcher: FSWatcher) => { + ipcMain.handle("logout", () => logout(watcher)); }; diff --git a/desktop/src/main/services/logout.ts b/desktop/src/main/services/logout.ts new file mode 100644 index 0000000000..aaf89833bb --- /dev/null +++ b/desktop/src/main/services/logout.ts @@ -0,0 +1,30 @@ +import type { FSWatcher } from "chokidar"; +import log from "../log"; +import { clearConvertToMP4Results } from "../stream"; +import { clearStores } from "./store"; +import { watchReset } from "./watch"; + +/** + * Perform the native side logout sequence. + * + * This function is guaranteed not to throw any errors. + * + * See: [Note: Do not throw during logout]. + */ +export const logout = (watcher: FSWatcher) => { + try { + watchReset(watcher); + } catch (e) { + log.error("Ignoring error when resetting native folder watches", e); + } + try { + clearConvertToMP4Results(); + } catch (e) { + log.error("Ignoring error when clearing convert-to-mp4 results", e); + } + try { + clearStores(); + } catch (e) { + log.error("Ignoring error when clearing native stores", e); + } +} diff --git a/desktop/src/main/services/watch.ts b/desktop/src/main/services/watch.ts index de66dcca1c..e9629ff703 100644 --- a/desktop/src/main/services/watch.ts +++ b/desktop/src/main/services/watch.ts @@ -151,6 +151,15 @@ export const watchFindFiles = async (dirPath: string) => { return paths; }; +/** + * Stop watching all existing folder watches and remove any callbacks. + * + * This function is meant to be called when the user logs out. It stops + * all existing folder watches and forgets about any "on*" callback + * functions that have been registered. + * + * The persisted state itself gets cleared via {@link clearStores}. + */ export const watchReset = (watcher: FSWatcher) => { watcher.unwatch(folderWatches().map((watch) => watch.folderPath)); }; diff --git a/desktop/src/preload.ts b/desktop/src/preload.ts index d52745184e..764609193c 100644 --- a/desktop/src/preload.ts +++ b/desktop/src/preload.ts @@ -63,10 +63,10 @@ const openLogDirectory = () => ipcRenderer.invoke("openLogDirectory"); const selectDirectory = () => ipcRenderer.invoke("selectDirectory"); -const clearStores = () => ipcRenderer.send("clearStores"); - -const clearConvertToMP4Results = () => - ipcRenderer.send("clearConvertToMP4Results"); +const logout = () => { + watchRemoveListeners(); + ipcRenderer.send("logout"); +}; const encryptionKey = () => ipcRenderer.invoke("encryptionKey"); @@ -212,11 +212,10 @@ const watchOnRemoveDir = (f: (path: string, watch: FolderWatch) => void) => { const watchFindFiles = (folderPath: string) => ipcRenderer.invoke("watchFindFiles", folderPath); -const watchReset = async () => { +const watchRemoveListeners = () => { ipcRenderer.removeAllListeners("watchAddFile"); ipcRenderer.removeAllListeners("watchRemoveFile"); ipcRenderer.removeAllListeners("watchRemoveDir"); - await ipcRenderer.invoke("watchReset"); }; // - Upload @@ -308,8 +307,7 @@ contextBridge.exposeInMainWorld("electron", { openDirectory, openLogDirectory, selectDirectory, - clearStores, - clearConvertToMP4Results, + logout, encryptionKey, saveEncryptionKey, onMainWindowFocus, @@ -360,7 +358,6 @@ contextBridge.exposeInMainWorld("electron", { onRemoveFile: watchOnRemoveFile, onRemoveDir: watchOnRemoveDir, findFiles: watchFindFiles, - reset: watchReset, }, // - Upload diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index a22867173e..4ece036044 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -101,7 +101,7 @@ type AppContextType = { setDialogBoxAttributesV2: SetDialogBoxAttributesV2; isCFProxyDisabled: boolean; setIsCFProxyDisabled: (disabled: boolean) => void; - logout: () => Promise; + logout: () => void; }; export const AppContext = createContext(null); diff --git a/web/apps/photos/src/services/logout.ts b/web/apps/photos/src/services/logout.ts index 298875459a..759a550d13 100644 --- a/web/apps/photos/src/services/logout.ts +++ b/web/apps/photos/src/services/logout.ts @@ -15,19 +15,9 @@ export const photosLogout = async () => { const electron = globalThis.electron; if (electron) { try { - await electron.watch.reset(); + await electron?.logout(); } catch (e) { - log.error("Ignoring error when resetting native folder watches", e); - } - try { - await electron.clearConvertToMP4Results(); - } catch (e) { - log.error("Ignoring error when clearing convert-to-mp4 results", e); - } - try { - await electron.clearStores(); - } catch (e) { - log.error("Ignoring error when clearing native stores", e); + log.error("Ignoring error in native side logout sequence", e); } } try { diff --git a/web/packages/next/types/ipc.ts b/web/packages/next/types/ipc.ts index b0eb7fb246..f468f9ab37 100644 --- a/web/packages/next/types/ipc.ts +++ b/web/packages/next/types/ipc.ts @@ -64,19 +64,9 @@ export interface Electron { selectDirectory: () => Promise; /** - * Clear any stored data. - * - * This is a coarse single shot cleanup, meant for use in clearing any - * persisted Electron side state during logout. + * Perform any logout related cleanup of native side state. */ - clearStores: () => Promise; - - /** - * Clear an state corresponding to in-flight convert-to-mp4 requests. - * - * This is meant for use during logout. - */ - clearConvertToMP4Results: () => Promise; + logout: () => Promise; /** * Return the previously saved encryption key from persistent safe storage. @@ -487,17 +477,6 @@ export interface Electron { * The returned paths are guaranteed to use POSIX separators ('/'). */ findFiles: (folderPath: string) => Promise; - - /** - * Stop watching all existing folder watches and remove any callbacks. - * - * This function is meant to be called when the user logs out. It stops - * all existing folder watches and forgets about any "on*" callback - * functions that have been registered. - * - * The persisted state itself gets cleared via {@link clearStores}. - */ - reset: () => Promise; }; // - Upload From e3b7ffaeb42f9278bfa570bd637bd0fd4ae09f49 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:21:33 +0530 Subject: [PATCH 12/20] clip --- .../photos/src/components/Sidebar/AdvancedSettings.tsx | 9 ++++----- web/apps/photos/src/services/clip-service.ts | 2 +- web/apps/photos/src/services/logout.ts | 8 ++++++++ web/packages/accounts/services/logout.ts | 3 +-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/web/apps/photos/src/components/Sidebar/AdvancedSettings.tsx b/web/apps/photos/src/components/Sidebar/AdvancedSettings.tsx index 6dc9b851e9..ed03bc9175 100644 --- a/web/apps/photos/src/components/Sidebar/AdvancedSettings.tsx +++ b/web/apps/photos/src/components/Sidebar/AdvancedSettings.tsx @@ -1,18 +1,17 @@ +import { VerticallyCenteredFlex } from "@ente/shared/components/Container"; import ChevronRight from "@mui/icons-material/ChevronRight"; import ScienceIcon from "@mui/icons-material/Science"; import { Box, DialogProps, Stack, Typography } from "@mui/material"; import { EnteDrawer } from "components/EnteDrawer"; +import { EnteMenuItem } from "components/Menu/EnteMenuItem"; +import { MenuItemGroup } from "components/Menu/MenuItemGroup"; import MenuSectionTitle from "components/Menu/MenuSectionTitle"; import Titlebar from "components/Titlebar"; import { MLSearchSettings } from "components/ml/MLSearchSettings"; import { t } from "i18next"; -import { useContext, useEffect, useState } from "react"; - -import { VerticallyCenteredFlex } from "@ente/shared/components/Container"; -import { EnteMenuItem } from "components/Menu/EnteMenuItem"; -import { MenuItemGroup } from "components/Menu/MenuItemGroup"; import isElectron from "is-electron"; import { AppContext } from "pages/_app"; +import { useContext, useEffect, useState } from "react"; import { CLIPIndexingStatus, clipService } from "services/clip-service"; import { formatNumber } from "utils/number/format"; diff --git a/web/apps/photos/src/services/clip-service.ts b/web/apps/photos/src/services/clip-service.ts index 010f4eb40d..98b1ea60b7 100644 --- a/web/apps/photos/src/services/clip-service.ts +++ b/web/apps/photos/src/services/clip-service.ts @@ -80,7 +80,7 @@ class CLIPService { this.liveEmbeddingExtractionQueue = new PQueue({ concurrency: 1, }); - eventBus.on(Events.LOGOUT, this.logoutHandler, this); + eventBus.on(Events.LOGOUT, this.logout, this); } isPlatformSupported = () => { diff --git a/web/apps/photos/src/services/logout.ts b/web/apps/photos/src/services/logout.ts index 759a550d13..ae31b92791 100644 --- a/web/apps/photos/src/services/logout.ts +++ b/web/apps/photos/src/services/logout.ts @@ -1,6 +1,7 @@ import log from "@/next/log"; import { accountLogout } from "@ente/accounts/services/logout"; import { Events, eventBus } from "@ente/shared/events"; +import { clipService } from "services/clip-service"; /** * Logout sequence for the photos app. @@ -12,6 +13,12 @@ import { Events, eventBus } from "@ente/shared/events"; export const photosLogout = async () => { await accountLogout(); + try { + await clipService.logout(); + } catch (e) { + log.error("Ignoring error in CLIP logout", e); + } + const electron = globalThis.electron; if (electron) { try { @@ -20,6 +27,7 @@ export const photosLogout = async () => { log.error("Ignoring error in native side logout sequence", e); } } + try { eventBus.emit(Events.LOGOUT); } catch (e) { diff --git a/web/packages/accounts/services/logout.ts b/web/packages/accounts/services/logout.ts index d323e5090d..363c79b763 100644 --- a/web/packages/accounts/services/logout.ts +++ b/web/packages/accounts/services/logout.ts @@ -1,11 +1,10 @@ import { clearCaches } from "@/next/blob-cache"; import log from "@/next/log"; import InMemoryStore from "@ente/shared/storage/InMemoryStore"; -import loc } from "@ente/shared/storage/localForage"; +import localForage from "@ente/shared/storage/localForage"; import { clearData } from "@ente/shared/storage/localStorage"; import { clearKeys } from "@ente/shared/storage/sessionStorage"; import { logout as remoteLogout } from "../api/user"; -import localForage from "@ente/shared/storage/localForage"; /** * Logout sequence common to all apps that rely on the accounts package. From 5dd3315ca28959eba91ab5b1620f4444adbf631c Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:24:05 +0530 Subject: [PATCH 13/20] mlwm --- web/apps/photos/src/pages/_app.tsx | 9 +-------- web/apps/photos/src/services/clip-service.ts | 2 -- web/apps/photos/src/services/logout.ts | 7 +++++++ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index 4ece036044..3dc6503017 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -190,14 +190,6 @@ export default function App({ Component, pageProps }: AppProps) { } }; loadMlSearchState(); - try { - eventBus.on(Events.LOGOUT, () => { - setMlSearchEnabled(false); - mlWorkManager.setMlSearchEnabled(false); - }); - } catch (e) { - log.error("Error while subscribing to logout event", e); - } }, []); useEffect(() => { @@ -339,6 +331,7 @@ export default function App({ Component, pageProps }: AppProps) { }); const logout = () => { + setMlSearchEnabled(false); void photosLogout().then(() => router.push(PAGES.ROOT)); }; diff --git a/web/apps/photos/src/services/clip-service.ts b/web/apps/photos/src/services/clip-service.ts index 98b1ea60b7..eb5d7ada59 100644 --- a/web/apps/photos/src/services/clip-service.ts +++ b/web/apps/photos/src/services/clip-service.ts @@ -80,7 +80,6 @@ class CLIPService { this.liveEmbeddingExtractionQueue = new PQueue({ concurrency: 1, }); - eventBus.on(Events.LOGOUT, this.logout, this); } isPlatformSupported = () => { @@ -96,7 +95,6 @@ class CLIPService { } } - setupOnFileUploadListener = async () => { try { if (this.onFileUploadedHandler) { diff --git a/web/apps/photos/src/services/logout.ts b/web/apps/photos/src/services/logout.ts index ae31b92791..0cd82fdd7e 100644 --- a/web/apps/photos/src/services/logout.ts +++ b/web/apps/photos/src/services/logout.ts @@ -2,6 +2,7 @@ import log from "@/next/log"; import { accountLogout } from "@ente/accounts/services/logout"; import { Events, eventBus } from "@ente/shared/events"; import { clipService } from "services/clip-service"; +import mlWorkManager from "./machineLearning/mlWorkManager"; /** * Logout sequence for the photos app. @@ -19,6 +20,12 @@ export const photosLogout = async () => { log.error("Ignoring error in CLIP logout", e); } + try { + await mlWorkManager.setMlSearchEnabled(false); + } catch (e) { + log.error("Ignoring error in ML logout", e); + } + const electron = globalThis.electron; if (electron) { try { From 585c0a828587ee4873c81506a9be9401069399e2 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:26:45 +0530 Subject: [PATCH 14/20] export --- web/apps/photos/src/pages/_app.tsx | 7 ------- web/apps/photos/src/services/logout.ts | 19 +++++++++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index 3dc6503017..0150bc34cb 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -207,13 +207,6 @@ export default function App({ Component, pageProps }: AppProps) { await resumeExportsIfNeeded(); }; initExport(); - try { - eventBus.on(Events.LOGOUT, () => { - exportService.disableContinuousExport(); - }); - } catch (e) { - log.error("Error while subscribing to logout event", e); - } }, []); const setUserOnline = () => setOffline(false); diff --git a/web/apps/photos/src/services/logout.ts b/web/apps/photos/src/services/logout.ts index 0cd82fdd7e..e9ba495978 100644 --- a/web/apps/photos/src/services/logout.ts +++ b/web/apps/photos/src/services/logout.ts @@ -2,6 +2,7 @@ import log from "@/next/log"; import { accountLogout } from "@ente/accounts/services/logout"; import { Events, eventBus } from "@ente/shared/events"; import { clipService } from "services/clip-service"; +import exportService from "./export"; import mlWorkManager from "./machineLearning/mlWorkManager"; /** @@ -20,14 +21,20 @@ export const photosLogout = async () => { log.error("Ignoring error in CLIP logout", e); } - try { - await mlWorkManager.setMlSearchEnabled(false); - } catch (e) { - log.error("Ignoring error in ML logout", e); - } - const electron = globalThis.electron; if (electron) { + try { + await mlWorkManager.setMlSearchEnabled(false); + } catch (e) { + log.error("Ignoring error in ML logout", e); + } + + try { + exportService.disableContinuousExport(); + } catch (e) { + log.error("Ignoring error when export logout", e); + } + try { await electron?.logout(); } catch (e) { From 1648229f4c22ed5297d050a05ddbbd9a2afaf8e1 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:29:09 +0530 Subject: [PATCH 15/20] Download --- .../photos/src/services/download/index.ts | 26 +++++++------------ web/apps/photos/src/services/logout.ts | 15 ++++++++--- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/web/apps/photos/src/services/download/index.ts b/web/apps/photos/src/services/download/index.ts index a148f2bcfd..0618cd0e60 100644 --- a/web/apps/photos/src/services/download/index.ts +++ b/web/apps/photos/src/services/download/index.ts @@ -6,7 +6,6 @@ import { APPS } from "@ente/shared/apps/constants"; import ComlinkCryptoWorker from "@ente/shared/crypto"; import { DedicatedCryptoWorker } from "@ente/shared/crypto/internal/crypto.worker"; import { CustomError } from "@ente/shared/error"; -import { Events, eventBus } from "@ente/shared/events"; import { isPlaybackPossible } from "@ente/shared/media/video-playback"; import { Remote } from "comlink"; import isElectron from "is-electron"; @@ -107,7 +106,6 @@ class DownloadManagerImpl { // } this.cryptoWorker = await ComlinkCryptoWorker.getInstance(); this.ready = true; - eventBus.on(Events.LOGOUT, this.logoutHandler.bind(this), this); } private ensureInitialized() { @@ -117,21 +115,15 @@ class DownloadManagerImpl { ); } - private async logoutHandler() { - try { - log.info("downloadManger logoutHandler started"); - this.ready = false; - this.cryptoWorker = null; - this.downloadClient = null; - this.fileObjectURLPromises.clear(); - this.fileConversionPromises.clear(); - this.thumbnailObjectURLPromises.clear(); - this.fileDownloadProgress.clear(); - this.progressUpdater = () => {}; - log.info("downloadManager logoutHandler completed"); - } catch (e) { - log.error("downloadManager logoutHandler failed", e); - } + async logout() { + this.ready = false; + this.cryptoWorker = null; + this.downloadClient = null; + this.fileObjectURLPromises.clear(); + this.fileConversionPromises.clear(); + this.thumbnailObjectURLPromises.clear(); + this.fileDownloadProgress.clear(); + this.progressUpdater = () => {}; } updateToken(token: string, passwordToken?: string) { diff --git a/web/apps/photos/src/services/logout.ts b/web/apps/photos/src/services/logout.ts index e9ba495978..73db5cf3fa 100644 --- a/web/apps/photos/src/services/logout.ts +++ b/web/apps/photos/src/services/logout.ts @@ -2,6 +2,7 @@ import log from "@/next/log"; import { accountLogout } from "@ente/accounts/services/logout"; import { Events, eventBus } from "@ente/shared/events"; import { clipService } from "services/clip-service"; +import DownloadManager from "./download"; import exportService from "./export"; import mlWorkManager from "./machineLearning/mlWorkManager"; @@ -15,10 +16,16 @@ import mlWorkManager from "./machineLearning/mlWorkManager"; export const photosLogout = async () => { await accountLogout(); + try { + await DownloadManager.logout(); + } catch (e) { + log.error("Ignoring error during logout (download)", e); + } + try { await clipService.logout(); } catch (e) { - log.error("Ignoring error in CLIP logout", e); + log.error("Ignoring error during logout (CLIP)", e); } const electron = globalThis.electron; @@ -26,19 +33,19 @@ export const photosLogout = async () => { try { await mlWorkManager.setMlSearchEnabled(false); } catch (e) { - log.error("Ignoring error in ML logout", e); + log.error("Ignoring error during logout (ML)", e); } try { exportService.disableContinuousExport(); } catch (e) { - log.error("Ignoring error when export logout", e); + log.error("Ignoring error during logout (export)", e); } try { await electron?.logout(); } catch (e) { - log.error("Ignoring error in native side logout sequence", e); + log.error("Ignoring error during logout (electron)", e); } } From eba910e2023744b8b882245df52f63a782b2d04a Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:30:26 +0530 Subject: [PATCH 16/20] mlwm --- web/apps/photos/src/services/logout.ts | 2 +- .../services/machineLearning/mlWorkManager.ts | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/web/apps/photos/src/services/logout.ts b/web/apps/photos/src/services/logout.ts index 73db5cf3fa..7431923c84 100644 --- a/web/apps/photos/src/services/logout.ts +++ b/web/apps/photos/src/services/logout.ts @@ -31,7 +31,7 @@ export const photosLogout = async () => { const electron = globalThis.electron; if (electron) { try { - await mlWorkManager.setMlSearchEnabled(false); + await mlWorkManager.logout(); } catch (e) { log.error("Ignoring error during logout (ML)", e); } diff --git a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts index cacb6d2376..4b7f8e3f46 100644 --- a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts +++ b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts @@ -175,16 +175,12 @@ class MLWorkManager { } } - private async logoutHandler() { - log.info("logoutHandler"); - try { - this.stopSyncJob(); - this.mlSyncJob = undefined; - await this.terminateLiveSyncWorker(); - await mlIDbStorage.clearMLDB(); - } catch (e) { - log.error("Failed in ML logout Handler", e); - } + async logout() { + this.setMlSearchEnabled(false); + this.stopSyncJob(); + this.mlSyncJob = undefined; + await this.terminateLiveSyncWorker(); + await mlIDbStorage.clearMLDB(); } private async fileUploadedHandler(arg: { From 8726ca8a59de472c41a0d8cd943c06b79aad8cea Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:31:06 +0530 Subject: [PATCH 17/20] Fin --- web/apps/photos/src/services/logout.ts | 7 ------- .../photos/src/services/machineLearning/mlWorkManager.ts | 1 - 2 files changed, 8 deletions(-) diff --git a/web/apps/photos/src/services/logout.ts b/web/apps/photos/src/services/logout.ts index 7431923c84..a6b155c8c2 100644 --- a/web/apps/photos/src/services/logout.ts +++ b/web/apps/photos/src/services/logout.ts @@ -1,6 +1,5 @@ import log from "@/next/log"; import { accountLogout } from "@ente/accounts/services/logout"; -import { Events, eventBus } from "@ente/shared/events"; import { clipService } from "services/clip-service"; import DownloadManager from "./download"; import exportService from "./export"; @@ -48,10 +47,4 @@ export const photosLogout = async () => { log.error("Ignoring error during logout (electron)", e); } } - - try { - eventBus.emit(Events.LOGOUT); - } catch (e) { - log.error("Ignoring error in event-bus logout handlers", e); - } }; diff --git a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts index 4b7f8e3f46..d696e883f7 100644 --- a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts +++ b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts @@ -123,7 +123,6 @@ class MLWorkManager { }); this.mlSearchEnabled = false; - eventBus.on(Events.LOGOUT, this.logoutHandler.bind(this), this); this.debouncedLiveSyncIdle = debounce( () => this.onLiveSyncIdle(), LIVE_SYNC_IDLE_DEBOUNCE_SEC * 1000, From 08c725bede8ec2a3bec51ed46c2bc0ea0e0c541e Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:32:31 +0530 Subject: [PATCH 18/20] Reorder --- web/packages/accounts/services/logout.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/web/packages/accounts/services/logout.ts b/web/packages/accounts/services/logout.ts index 363c79b763..70d67b22ff 100644 --- a/web/packages/accounts/services/logout.ts +++ b/web/packages/accounts/services/logout.ts @@ -20,31 +20,31 @@ export const accountLogout = async () => { try { await remoteLogout(); } catch (e) { - log.error("Ignoring error during POST /users/logout", e); + log.error("Ignoring error during logout (remote)", e); } try { InMemoryStore.clear(); } catch (e) { - log.error("Ignoring error when clearing in-memory store", e); + log.error("Ignoring error during logout (in-memory store)", e); } try { clearKeys(); } catch (e) { - log.error("Ignoring error when clearing keys", e); + log.error("Ignoring error during logout (session store)", e); } try { clearData(); } catch (e) { - log.error("Ignoring error when clearing data", e); - } - try { - await clearCaches(); - } catch (e) { - log.error("Ignoring error when clearing caches", e); + log.error("Ignoring error during logout (local storage)", e); } try { await localForage.clear(); } catch (e) { - log.error("Ignoring error when clearing local forage", e); + log.error("Ignoring error during logout (local forage)", e); + } + try { + await clearCaches(); + } catch (e) { + log.error("Ignoring error during logout (cache)", e); } }; From 901c401d2a596b0c94b33fb6cd0b1d802d4d7ccb Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:43:24 +0530 Subject: [PATCH 19/20] Et --- desktop/src/main/services/logout.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/desktop/src/main/services/logout.ts b/desktop/src/main/services/logout.ts index aaf89833bb..e6cb7666ca 100644 --- a/desktop/src/main/services/logout.ts +++ b/desktop/src/main/services/logout.ts @@ -15,16 +15,16 @@ export const logout = (watcher: FSWatcher) => { try { watchReset(watcher); } catch (e) { - log.error("Ignoring error when resetting native folder watches", e); + log.error("Ignoring error during logout (FS watch)", e); } try { clearConvertToMP4Results(); } catch (e) { - log.error("Ignoring error when clearing convert-to-mp4 results", e); + log.error("Ignoring error during logout (convert-to-mp4)", e); } try { clearStores(); } catch (e) { - log.error("Ignoring error when clearing native stores", e); + log.error("Ignoring error during logout (native stores)", e); } -} +}; From 10b58fc19bb22a92d0b2ef8728c242502f584418 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 15 May 2024 13:48:34 +0530 Subject: [PATCH 20/20] lf --- web/apps/photos/src/pages/_app.tsx | 3 +-- web/apps/photos/src/pages/gallery/index.tsx | 2 +- web/apps/photos/src/services/userService.ts | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index 0150bc34cb..7d82f7cc37 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -26,7 +26,6 @@ import EnteSpinner from "@ente/shared/components/EnteSpinner"; import { MessageContainer } from "@ente/shared/components/MessageContainer"; import AppNavbar from "@ente/shared/components/Navbar/app"; import { PHOTOS_PAGES as PAGES } from "@ente/shared/constants/pages"; -import { Events, eventBus } from "@ente/shared/events"; import { useLocalState } from "@ente/shared/hooks/useLocalState"; import HTTPService from "@ente/shared/network/HTTPService"; import { LS_KEYS, getData } from "@ente/shared/storage/localStorage"; @@ -52,7 +51,7 @@ import "photoswipe/dist/photoswipe.css"; import { createContext, useEffect, useRef, useState } from "react"; import LoadingBar from "react-top-loading-bar"; import DownloadManager from "services/download"; -import exportService, { resumeExportsIfNeeded } from "services/export"; +import { resumeExportsIfNeeded } from "services/export"; import { photosLogout } from "services/logout"; import { getMLSearchConfig, diff --git a/web/apps/photos/src/pages/gallery/index.tsx b/web/apps/photos/src/pages/gallery/index.tsx index d99a1ff839..9ade12fc5e 100644 --- a/web/apps/photos/src/pages/gallery/index.tsx +++ b/web/apps/photos/src/pages/gallery/index.tsx @@ -329,7 +329,7 @@ export default function Gallery() { await getRecoveryKey(); return true; } catch (e) { - await logout(); + logout(); return false; } }; diff --git a/web/apps/photos/src/services/userService.ts b/web/apps/photos/src/services/userService.ts index c8dba0ba0b..47bda4f0a1 100644 --- a/web/apps/photos/src/services/userService.ts +++ b/web/apps/photos/src/services/userService.ts @@ -3,7 +3,6 @@ import { putAttributes } from "@ente/accounts/api/user"; import { ApiError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import { getEndpoint, getFamilyPortalURL } from "@ente/shared/network/api"; -import localForage from "@ente/shared/storage/localForage"; import { LS_KEYS, getData } from "@ente/shared/storage/localStorage"; import { getToken,