Browse Source

[web] Miscellaneous tweaks (#1363)

- Clean up environment detection code
- Remove l11n unsafe direct string manipulation
- Inline
- Remove isCanvasBlocked checker
- Remove unused stuff
Manav Rathi 1 year ago
parent
commit
873b158718
28 changed files with 103 additions and 252 deletions
  1. 2 3
      web/apps/photos/src/components/MachineLearning/MLSearchSettings/enableFaceSearch.tsx
  2. 5 3
      web/apps/photos/src/components/MachineLearning/MLSearchSettings/enableMLSearch.tsx
  3. 3 3
      web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx
  4. 7 16
      web/apps/photos/src/components/Sidebar/HelpSection.tsx
  5. 1 2
      web/apps/photos/src/components/Upload/UploadProgress/dialog.tsx
  6. 15 25
      web/apps/photos/src/components/Upload/Uploader.tsx
  7. 1 4
      web/apps/photos/src/components/pages/gallery/PlanSelector/card/index.tsx
  8. 10 5
      web/apps/photos/src/pages/gallery/index.tsx
  9. 3 2
      web/apps/photos/src/services/locationSearchService.ts
  10. 4 1
      web/apps/photos/src/services/machineLearning/machineLearningFactory.ts
  11. 2 2
      web/apps/photos/src/utils/comlink/ComlinkConvertWorker.ts
  12. 2 2
      web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts
  13. 2 2
      web/apps/photos/src/utils/comlink/ComlinkSearchWorker.ts
  14. 0 5
      web/apps/photos/src/utils/common/concurrency.ts
  15. 1 80
      web/apps/photos/src/utils/common/index.ts
  16. 3 2
      web/apps/photos/src/utils/storage/mlIDbStorage.ts
  17. 7 5
      web/apps/photos/src/utils/ui/index.tsx
  18. 0 53
      web/apps/photos/src/utils/upload/isCanvasBlocked.ts
  19. 25 0
      web/packages/next/env.ts
  20. 1 2
      web/packages/next/locales/en-US/translation.json
  21. 1 0
      web/packages/next/package.json
  22. 4 0
      web/packages/next/tsconfig.json
  23. 0 16
      web/packages/shared/constants/urls.ts
  24. 2 2
      web/packages/shared/electron/service.ts
  25. 0 1
      web/packages/shared/error/index.ts
  26. 0 13
      web/packages/shared/platform/index.ts
  27. 2 2
      web/packages/shared/storage/localForage/index.ts
  28. 0 1
      web/packages/utils/package.json

+ 2 - 3
web/apps/photos/src/components/MachineLearning/MLSearchSettings/enableFaceSearch.tsx

@@ -1,4 +1,3 @@
-import { FACE_SEARCH_PRIVACY_POLICY_LINK } from "@ente/shared/constants/urls";
 import {
     Button,
     Checkbox,
@@ -60,8 +59,8 @@ export default function EnableFaceSearch({
                             components={{
                                 a: (
                                     <Link
-                                        target={"_blank"}
-                                        href={FACE_SEARCH_PRIVACY_POLICY_LINK}
+                                        target="_blank"
+                                        href="https://ente.io/privacy#8-biometric-information-privacy-policy"
                                         underline="always"
                                         sx={{
                                             color: "inherit",

+ 5 - 3
web/apps/photos/src/components/MachineLearning/MLSearchSettings/enableMLSearch.tsx

@@ -1,4 +1,3 @@
-import { ML_BLOG_LINK } from "@ente/shared/constants/urls";
 import { Box, Button, Stack, Typography } from "@mui/material";
 import Titlebar from "components/Titlebar";
 import { t } from "i18next";
@@ -10,6 +9,9 @@ export default function EnableMLSearch({
     enableMlSearch,
     onRootClose,
 }) {
+    const showDetails = () =>
+        openLink("https://ente.io/blog/desktop-ml-beta", true);
+
     return (
         <Stack spacing={"4px"} py={"12px"}>
             <Titlebar
@@ -33,9 +35,9 @@ export default function EnableMLSearch({
                         {t("ENABLE")}
                     </Button>
                     <Button
-                        color={"secondary"}
+                        color="secondary"
                         size="large"
-                        onClick={() => openLink(ML_BLOG_LINK, true)}
+                        onClick={showDetails}
                     >
                         {t("ML_MORE_DETAILS")}
                     </Button>

+ 3 - 3
web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx

@@ -1,13 +1,13 @@
+import { haveWindow } from "@/next/env";
 import { styled } from "@mui/material";
 import { useEffect, useRef } from "react";
-import { runningInBrowser } from "utils/common";
 import { MapButton } from "./MapButton";
 
 import { t } from "i18next";
 import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.webpack.css"; // Re-uses images from ~leaflet package
 import "leaflet/dist/leaflet.css";
-runningInBrowser() && require("leaflet-defaulticon-compatibility");
-const L = runningInBrowser()
+haveWindow() && require("leaflet-defaulticon-compatibility");
+const L = haveWindow()
     ? (require("leaflet") as typeof import("leaflet"))
     : null;
 

+ 7 - 16
web/apps/photos/src/components/Sidebar/HelpSection.tsx

@@ -2,10 +2,6 @@ import { t } from "i18next";
 import { useContext } from "react";
 
 import EnteSpinner from "@ente/shared/components/EnteSpinner";
-import {
-    DESKTOP_ROADMAP_URL,
-    WEB_ROADMAP_URL,
-} from "@ente/shared/constants/urls";
 import { Typography } from "@mui/material";
 import { EnteMenuItem } from "components/Menu/EnteMenuItem";
 import { NoStyleAnchor } from "components/pages/sharedAlbum/GoToEnte";
@@ -20,17 +16,12 @@ export default function HelpSection() {
     const { setDialogMessage } = useContext(AppContext);
     const { openExportModal } = useContext(GalleryContext);
 
-    async function openRoadmap() {
-        let roadmapURL: string;
-        if (isElectron()) {
-            roadmapURL = DESKTOP_ROADMAP_URL;
-        } else {
-            roadmapURL = WEB_ROADMAP_URL;
-        }
-        openLink(roadmapURL, true);
-    }
+    const openRoadmap = () =>
+        openLink("https://github.com/ente-io/ente/discussions", true);
+
+    const contactSupport = () => openLink("mailto:support@ente.io", true);
 
-    function handleExportOpen() {
+    function openExport() {
         if (isElectron()) {
             openExportModal();
         } else {
@@ -46,7 +37,7 @@ export default function HelpSection() {
                 variant="secondary"
             />
             <EnteMenuItem
-                onClick={() => openLink("mailto:support@ente.io", true)}
+                onClick={contactSupport}
                 labelComponent={
                     <NoStyleAnchor href="mailto:support@ente.io">
                         <Typography fontWeight={"bold"}>
@@ -57,7 +48,7 @@ export default function HelpSection() {
                 variant="secondary"
             />
             <EnteMenuItem
-                onClick={handleExportOpen}
+                onClick={openExport}
                 label={t("EXPORT")}
                 endIcon={
                     exportService.isExportInProgress() && (

+ 1 - 2
web/apps/photos/src/components/Upload/UploadProgress/dialog.tsx

@@ -2,7 +2,6 @@ import { Dialog, DialogContent, Link } from "@mui/material";
 import { t } from "i18next";
 
 import { dialogCloseHandler } from "@ente/shared/components/DialogBox/TitleWithCloseButton";
-import { APP_DOWNLOAD_URL } from "@ente/shared/constants/urls";
 import { UPLOAD_RESULT, UPLOAD_STAGES } from "constants/upload";
 import UploadProgressContext from "contexts/uploadProgress";
 import { useContext, useEffect, useState } from "react";
@@ -84,7 +83,7 @@ export function UploadProgressDialog() {
                                         components={{
                                             a: (
                                                 <Link
-                                                    href={APP_DOWNLOAD_URL}
+                                                    href="https://ente.io/download/desktop"
                                                     target="_blank"
                                                 />
                                             ),

+ 15 - 25
web/apps/photos/src/components/Upload/Uploader.tsx

@@ -1,15 +1,8 @@
-import { useContext, useEffect, useRef, useState } from "react";
-
-import { t } from "i18next";
-import { Trans } from "react-i18next";
-import { getLatestCollections } from "services/collectionService";
-
-import UploadProgress from "./UploadProgress";
-
 import ElectronAPIs from "@ente/shared/electron";
 import { CustomError } from "@ente/shared/error";
 import { addLogLine } from "@ente/shared/logging";
 import { logError } from "@ente/shared/sentry";
+import { isPromise } from "@ente/shared/utils";
 import DiscFullIcon from "@mui/icons-material/DiscFull";
 import UserNameInputDialog from "components/UserNameInputDialog";
 import {
@@ -18,10 +11,13 @@ import {
     UPLOAD_STAGES,
     UPLOAD_STRATEGY,
 } from "constants/upload";
+import { t } from "i18next";
 import isElectron from "is-electron";
 import { AppContext } from "pages/_app";
 import { GalleryContext } from "pages/gallery";
+import { useContext, useEffect, useRef, useState } from "react";
 import billingService from "services/billingService";
+import { getLatestCollections } from "services/collectionService";
 import ImportService from "services/importService";
 import {
     getPublicCollectionUID,
@@ -52,7 +48,6 @@ import {
     UploadFileNames,
 } from "types/upload/ui";
 import { getOrCreateAlbum } from "utils/collection";
-import { downloadApp, waitAndRun } from "utils/common";
 import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery";
 import {
     getDownloadAppMessage,
@@ -63,8 +58,8 @@ import {
     getImportSuggestion,
     groupFilesBasedOnParentFolder,
 } from "utils/upload";
-import { isCanvasBlocked } from "utils/upload/isCanvasBlocked";
 import { SetCollectionNamerAttributes } from "../Collections/CollectionNamer";
+import UploadProgress from "./UploadProgress";
 import UploadStrategyChoiceModal from "./UploadStrategyChoiceModal";
 import UploadTypeSelector from "./UploadTypeSelector";
 
@@ -312,21 +307,6 @@ export default function Uploader(props: Props) {
                     return;
                 }
             }
-            if (isCanvasBlocked()) {
-                addLogLine("canvas blocked, blocking upload");
-                appContext.setDialogMessage({
-                    title: t("CANVAS_BLOCKED_TITLE"),
-
-                    content: <Trans i18nKey="CANVAS_BLOCKED_MESSAGE" />,
-                    close: { text: t("CLOSE") },
-                    proceed: {
-                        text: t("DOWNLOAD"),
-                        action: downloadApp,
-                        variant: "accent",
-                    },
-                });
-                return;
-            }
             uploadRunning.current = true;
             props.closeUploadTypeSelector();
             props.setLoading(true);
@@ -844,3 +824,13 @@ export default function Uploader(props: Props) {
         </>
     );
 }
+
+async function waitAndRun(
+    waitPromise: Promise<void>,
+    task: () => Promise<void>,
+) {
+    if (waitPromise && isPromise(waitPromise)) {
+        await waitPromise;
+    }
+    await task();
+}

+ 1 - 4
web/apps/photos/src/components/pages/gallery/PlanSelector/card/index.tsx

@@ -24,7 +24,6 @@ import {
     planForSubscription,
     updateSubscription,
 } from "utils/billing";
-import { reverseString } from "utils/common";
 import { getLocalUserDetails } from "utils/user";
 import { getTotalFamilyUsage, isPartOfFamily } from "utils/user/family";
 import FreeSubscriptionPlanSelectorCard from "./free";
@@ -130,9 +129,7 @@ function PlanSelectorCard(props: Props) {
             }
         } else if (hasStripeSubscription(subscription)) {
             appContext.setDialogMessage({
-                title: `${t("CONFIRM")} ${reverseString(
-                    t("UPDATE_SUBSCRIPTION"),
-                )}`,
+                title: t("update_subscription_title"),
                 content: t("UPDATE_SUBSCRIPTION_MESSAGE"),
                 proceed: {
                     text: t("UPDATE_SUBSCRIPTION"),

+ 10 - 5
web/apps/photos/src/pages/gallery/index.tsx

@@ -49,7 +49,7 @@ import {
     syncMapEnabled,
     validateKey,
 } from "services/userService";
-import { mergeMaps, preloadImage } from "utils/common";
+import { preloadImage } from "utils/common";
 import {
     FILE_OPS_TYPE,
     constructFileToCollectionMap,
@@ -135,7 +135,6 @@ import {
 import { Search, SearchResultSummary, UpdateSearch } from "types/search";
 import { FamilyData } from "types/user";
 import ComlinkSearchWorker from "utils/comlink/ComlinkSearchWorker";
-import { checkConnectivity } from "utils/common";
 import { isArchivedFile } from "utils/magicMetadata";
 import { getSessionExpiredMessage } from "utils/ui";
 import { getLocalFamilyData } from "utils/user/family";
@@ -680,13 +679,13 @@ export default function Gallery() {
     };
 
     const syncWithRemote = async (force = false, silent = false) => {
+        if (!navigator.onLine) return;
         if (syncInProgress.current && !force) {
             resync.current = { force, silent };
             return;
         }
         syncInProgress.current = true;
         try {
-            checkConnectivity();
             const token = getToken();
             if (!token) {
                 return;
@@ -719,8 +718,6 @@ export default function Gallery() {
                     clearKeys();
                     router.push(PAGES.CREDENTIALS);
                     break;
-                case CustomError.NO_INTERNET_CONNECTION:
-                    break;
                 default:
                     logError(e, "syncWithRemote failed");
             }
@@ -1252,3 +1249,11 @@ function useEffectSingleThreaded(
         main(deps);
     }, deps);
 }
+
+const mergeMaps = <K, V>(map1: Map<K, V>, map2: Map<K, V>) => {
+    const mergedMap = new Map<K, V>(map1);
+    map2.forEach((value, key) => {
+        mergedMap.set(key, value);
+    });
+    return mergedMap;
+};

+ 3 - 2
web/apps/photos/src/services/locationSearchService.ts

@@ -1,4 +1,3 @@
-import { CITIES_URL } from "@ente/shared/constants/urls";
 import { logError } from "@ente/shared/sentry";
 import { LocationTagData } from "types/entity";
 import { Location } from "types/upload";
@@ -22,7 +21,9 @@ class LocationSearchService {
             if (this.citiesPromise) {
                 return;
             }
-            this.citiesPromise = fetch(CITIES_URL).then((response) => {
+            this.citiesPromise = fetch(
+                "https://static.ente.io/world_cities.json",
+            ).then((response) => {
                 return response.json().then((data) => {
                     this.cities = data["data"];
                 });

+ 4 - 1
web/apps/photos/src/services/machineLearning/machineLearningFactory.ts

@@ -1,3 +1,4 @@
+import { haveWindow } from "@/next/env";
 import { getDedicatedCryptoWorker } from "@ente/shared/crypto";
 import { DedicatedCryptoWorker } from "@ente/shared/crypto/internal/crypto.worker";
 import { addLogLine } from "@ente/shared/logging";
@@ -24,7 +25,6 @@ import {
     SceneDetectionMethod,
     SceneDetectionService,
 } from "types/machineLearning";
-import { getConcurrency } from "utils/common/concurrency";
 import { logQueueStats } from "utils/machineLearning";
 import arcfaceAlignmentService from "./arcfaceAlignmentService";
 import arcfaceCropService from "./arcfaceCropService";
@@ -232,3 +232,6 @@ export class LocalMLSyncContext implements MLSyncContext {
         }
     }
 }
+
+export const getConcurrency = () =>
+    haveWindow() && Math.max(2, Math.ceil(navigator.hardwareConcurrency / 2));

+ 2 - 2
web/apps/photos/src/utils/comlink/ComlinkConvertWorker.ts

@@ -1,6 +1,6 @@
+import { haveWindow } from "@/next/env";
 import { ComlinkWorker } from "@ente/shared/worker/comlinkWorker";
 import { Remote } from "comlink";
-import { runningInBrowser } from "utils/common";
 import { DedicatedConvertWorker } from "worker/convert.worker";
 
 class ComlinkConvertWorker {
@@ -16,7 +16,7 @@ class ComlinkConvertWorker {
 }
 
 export const getDedicatedConvertWorker = () => {
-    if (runningInBrowser()) {
+    if (haveWindow()) {
         const cryptoComlinkWorker = new ComlinkWorker<
             typeof DedicatedConvertWorker
         >(

+ 2 - 2
web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts

@@ -1,9 +1,9 @@
+import { haveWindow } from "@/next/env";
 import { ComlinkWorker } from "@ente/shared/worker/comlinkWorker";
-import { runningInBrowser } from "utils/common";
 import { DedicatedMLWorker } from "worker/ml.worker";
 
 export const getDedicatedMLWorker = (name: string) => {
-    if (runningInBrowser()) {
+    if (haveWindow()) {
         const cryptoComlinkWorker = new ComlinkWorker<typeof DedicatedMLWorker>(
             name ?? "ente-ml-worker",
             new Worker(new URL("worker/ml.worker.ts", import.meta.url)),

+ 2 - 2
web/apps/photos/src/utils/comlink/ComlinkSearchWorker.ts

@@ -1,6 +1,6 @@
+import { haveWindow } from "@/next/env";
 import { ComlinkWorker } from "@ente/shared/worker/comlinkWorker";
 import { Remote } from "comlink";
-import { runningInBrowser } from "utils/common";
 import { DedicatedSearchWorker } from "worker/search.worker";
 
 class ComlinkSearchWorker {
@@ -16,7 +16,7 @@ class ComlinkSearchWorker {
 }
 
 export const getDedicatedSearchWorker = () => {
-    if (runningInBrowser()) {
+    if (haveWindow()) {
         const cryptoComlinkWorker = new ComlinkWorker<
             typeof DedicatedSearchWorker
         >(

+ 0 - 5
web/apps/photos/src/utils/common/concurrency.ts

@@ -1,5 +0,0 @@
-import { runningInBrowser } from ".";
-
-export const getConcurrency = () =>
-    runningInBrowser() &&
-    Math.max(2, Math.ceil(navigator.hardwareConcurrency / 2));

+ 1 - 80
web/apps/photos/src/utils/common/index.ts

@@ -1,65 +1,3 @@
-import { APP_DOWNLOAD_URL } from "@ente/shared/constants/urls";
-import { CustomError } from "@ente/shared/error";
-import { isPromise } from "@ente/shared/utils";
-import isElectron from "is-electron";
-
-export function checkConnectivity() {
-    if (navigator.onLine) {
-        return true;
-    }
-    throw new Error(CustomError.NO_INTERNET_CONNECTION);
-}
-
-export function runningInBrowser() {
-    return typeof window !== "undefined";
-}
-
-export function runningInWorker() {
-    return typeof importScripts === "function";
-}
-
-export function runningInElectron() {
-    return isElectron();
-}
-
-export function runningInChrome(includeMobile: boolean) {
-    try {
-        const userAgentData = navigator["userAgentData"];
-        const chromeBrand = userAgentData?.brands?.filter(
-            (b) => b.brand === "Google Chrome" || b.brand === "Chromium",
-        )?.[0];
-        return chromeBrand && (includeMobile || userAgentData.mobile === false);
-    } catch (error) {
-        console.error("Error in runningInChrome: ", error);
-        return false;
-    }
-}
-
-export function offscreenCanvasSupported() {
-    return !(typeof OffscreenCanvas === "undefined");
-}
-
-export function webglSupported() {
-    try {
-        const canvas = document.createElement("canvas");
-        const gl = canvas.getContext("webgl");
-        return gl && gl instanceof WebGLRenderingContext;
-    } catch (error) {
-        console.error("Error in webglSupported: ", error);
-        return false;
-    }
-}
-
-export function downloadApp() {
-    openLink(APP_DOWNLOAD_URL, true);
-}
-
-export function reverseString(title: string) {
-    return title
-        ?.split(" ")
-        .reduce((reversedString, currWord) => `${currWord} ${reversedString}`);
-}
-
 export function initiateEmail(email: string) {
     const a = document.createElement("a");
     a.href = "mailto:" + email;
@@ -74,6 +12,7 @@ export const preloadImage = (imgBasePath: string) => {
     }
     new Image().srcset = srcSet.join(",");
 };
+
 export function openLink(href: string, newTab?: boolean) {
     const a = document.createElement("a");
     a.href = href;
@@ -84,16 +23,6 @@ export function openLink(href: string, newTab?: boolean) {
     a.click();
 }
 
-export async function waitAndRun(
-    waitPromise: Promise<void>,
-    task: () => Promise<void>,
-) {
-    if (waitPromise && isPromise(waitPromise)) {
-        await waitPromise;
-    }
-    await task();
-}
-
 export function isClipboardItemPresent() {
     return typeof ClipboardItem !== "undefined";
 }
@@ -105,11 +34,3 @@ export function batch<T>(arr: T[], batchSize: number): T[][] {
     }
     return batches;
 }
-
-export const mergeMaps = <K, V>(map1: Map<K, V>, map2: Map<K, V>) => {
-    const mergedMap = new Map<K, V>(map1);
-    map2.forEach((value, key) => {
-        mergedMap.set(key, value);
-    });
-    return mergedMap;
-};

+ 3 - 2
web/apps/photos/src/utils/storage/mlIDbStorage.ts

@@ -1,3 +1,4 @@
+import { haveWindow } from "@/next/env";
 import { addLogLine } from "@ente/shared/logging";
 import { logError } from "@ente/shared/sentry";
 import {
@@ -14,6 +15,7 @@ import {
     deleteDB,
     openDB,
 } from "idb";
+import isElectron from "is-electron";
 import {
     Face,
     MLLibraryData,
@@ -23,7 +25,6 @@ import {
     Thing,
 } from "types/machineLearning";
 import { IndexStatus } from "types/machineLearning/ui";
-import { runningInBrowser, runningInElectron } from "utils/common";
 
 interface Config {}
 
@@ -64,7 +65,7 @@ class MLIDbStorage {
     public _db: Promise<IDBPDatabase<MLDb>>;
 
     constructor() {
-        if (!runningInBrowser() || !runningInElectron()) {
+        if (!haveWindow() || !isElectron()) {
             return;
         }
 

+ 7 - 5
web/apps/photos/src/utils/ui/index.tsx

@@ -1,16 +1,16 @@
-import { DialogBoxAttributes } from "@ente/shared/components/DialogBox/types";
-import AutoAwesomeOutlinedIcon from "@mui/icons-material/AutoAwesomeOutlined";
-import { t } from "i18next";
-import { downloadApp } from "utils/common";
-
 import { logoutUser } from "@ente/accounts/services/user";
+import { DialogBoxAttributes } from "@ente/shared/components/DialogBox/types";
 import ElectronAPIs from "@ente/shared/electron";
 import { AppUpdateInfo } from "@ente/shared/electron/types";
+import AutoAwesomeOutlinedIcon from "@mui/icons-material/AutoAwesomeOutlined";
 import InfoOutlined from "@mui/icons-material/InfoRounded";
 import { Link } from "@mui/material";
 import { OPEN_STREET_MAP_LINK } from "components/Sidebar/EnableMap";
+import { t } from "i18next";
 import { Trans } from "react-i18next";
 import { Subscription } from "types/billing";
+import { openLink } from "utils/common";
+
 export const getDownloadAppMessage = (): DialogBoxAttributes => {
     return {
         title: t("DOWNLOAD_APP"),
@@ -27,6 +27,8 @@ export const getDownloadAppMessage = (): DialogBoxAttributes => {
     };
 };
 
+const downloadApp = () => openLink("https://ente.io/download/desktop", true);
+
 export const getTrashFilesMessage = (
     deleteFileHelper,
 ): DialogBoxAttributes => ({

+ 0 - 53
web/apps/photos/src/utils/upload/isCanvasBlocked.ts

@@ -1,53 +0,0 @@
-//
-// Canvas Blocker &
-// Firefox privacy.resistFingerprinting Detector.
-// (c) 2018 // JOHN OZBAY // CRYPT.EE
-// MIT License
-
-// Credits: https://github.com/johnozbay/canvas-block-detector/blob/master/isCanvasBlocked.js
-
-//
-export function isCanvasBlocked() {
-    // create a 1px image data
-    let blocked = false;
-    const canvas = document.createElement("canvas");
-    const ctx = canvas.getContext("2d");
-
-    // some blockers just return an undefined ctx. So let's check that first.
-    if (ctx) {
-        const imageData = ctx.createImageData(1, 1);
-        const originalImageData = imageData.data;
-
-        // set pixels to RGB 128
-        originalImageData[0] = 128;
-        originalImageData[1] = 128;
-        originalImageData[2] = 128;
-        originalImageData[3] = 255;
-
-        // set this to canvas
-        ctx.putImageData(imageData, 1, 1);
-
-        try {
-            // now get the data back from canvas.
-            const checkData = ctx.getImageData(1, 1, 1, 1).data;
-
-            // If this is firefox, and privacy.resistFingerprinting is enabled,
-            // OR a browser extension blocking the canvas,
-            // This will return RGB all white (255,255,255) instead of the (128,128,128) we put.
-
-            // so let's check the R and G to see if they're 255 or 128 (matching what we've initially set)
-            if (
-                originalImageData[0] !== checkData[0] &&
-                originalImageData[1] !== checkData[1]
-            ) {
-                blocked = true;
-            }
-        } catch (error) {
-            // some extensions will return getImageData null. this is to account for that.
-            blocked = true;
-        }
-    } else {
-        blocked = true;
-    }
-    return blocked;
-}

+ 25 - 0
web/packages/next/env.ts

@@ -10,3 +10,28 @@
  *   all other commands.
  */
 export const isDevBuild = process.env.NODE_ENV === "development";
+
+/**
+ * `true` if we're running in the default global context (aka the main thread)
+ * of a web browser.
+ *
+ * In particular, this is `false` when we're running in a Web Worker,
+ * irrespecitve of whether the worker is running in a Node.js context or a web
+ * browser context.
+ *
+ * > We can be running in a browser context either if the user has the page open
+ *   in a web browser, or if we're the renderer process of an Electron app.
+ *
+ * Note that this cannot be a constant, otherwise it'll get inlined during SSR
+ * with the wrong value.
+ */
+export const haveWindow = () => typeof window !== "undefined";
+
+/**
+ * Return true if we are running in a [Web
+ * Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
+ *
+ * Note that this cannot be a constant, otherwise it'll get inlined during SSR
+ * with the wrong value.
+ */
+export const inWorker = () => typeof importScripts === "function";

+ 1 - 2
web/packages/next/locales/en-US/translation.json

@@ -168,6 +168,7 @@
     "UPDATE_PAYMENT_METHOD": "Update payment method",
     "MONTHLY": "Monthly",
     "YEARLY": "Yearly",
+    "update_subscription_title": "Confirm plan change",
     "UPDATE_SUBSCRIPTION_MESSAGE": "Are you sure you want to change your plan?",
     "UPDATE_SUBSCRIPTION": "Change plan",
     "CANCEL_SUBSCRIPTION": "Cancel subscription",
@@ -421,8 +422,6 @@
     "ENTER_TWO_FACTOR_OTP": "Enter the 6-digit code from your authenticator app.",
     "CREATE_ACCOUNT": "Create account",
     "COPIED": "Copied",
-    "CANVAS_BLOCKED_TITLE": "Unable to generate thumbnail",
-    "CANVAS_BLOCKED_MESSAGE": "<p>It looks like your browser has disabled access to canvas, which is necessary to generate thumbnails for your photos </p> <p> Please enable access to your browser's canvas, or check out our desktop app</p>",
     "WATCH_FOLDERS": "Watch folders",
     "UPGRADE_NOW": "Upgrade now",
     "RENEW_NOW": "Renew now",

+ 1 - 0
web/packages/next/package.json

@@ -10,6 +10,7 @@
         "get-user-locale": "^2.3",
         "i18next": "^23.10",
         "i18next-resources-to-backend": "^1.2.0",
+        "is-electron": "^2.2",
         "next": "^14.1",
         "react": "^18",
         "react-dom": "^18",

+ 4 - 0
web/packages/next/tsconfig.json

@@ -1,5 +1,9 @@
 {
     "extends": "@/build-config/tsconfig-typecheck.json",
+    "compilerOptions": {
+        /* Also indicate expectation of a WebWorker runtime */
+        "lib": ["ESnext", "DOM", "DOM.Iterable", "WebWorker"]
+    },
     /* Typecheck all files with the given extensions (here or in subfolders) */
     "include": ["**/*.ts", "**/*.tsx"]
 }

+ 0 - 16
web/packages/shared/constants/urls.ts

@@ -1,21 +1,5 @@
 export const ENTE_WEBSITE_LINK = "https://ente.io";
 
-export const ML_BLOG_LINK = "https://ente.io/blog/desktop-ml-beta";
-
-export const FACE_SEARCH_PRIVACY_POLICY_LINK =
-    "https://ente.io/privacy#8-biometric-information-privacy-policy";
-
 export const SUPPORT_EMAIL = "support@ente.io";
 
-export const APP_DOWNLOAD_URL = "https://ente.io/download/desktop";
-
-export const FEEDBACK_EMAIL = "feedback@ente.io";
-
 export const DELETE_ACCOUNT_EMAIL = "account-deletion@ente.io";
-
-export const WEB_ROADMAP_URL = "https://github.com/ente-io/ente/discussions";
-
-export const DESKTOP_ROADMAP_URL =
-    "https://github.com/ente-io/ente/discussions";
-
-export const CITIES_URL = "https://static.ente.io/world_cities.json";

+ 2 - 2
web/packages/shared/electron/service.ts

@@ -1,4 +1,4 @@
-import { runningInWorker } from "@ente/shared/platform";
+import { inWorker } from "@/next/env";
 import * as Comlink from "comlink";
 import { wrap } from "comlink";
 import { ElectronAPIsType } from "./types";
@@ -17,7 +17,7 @@ class WorkerSafeElectronServiceImpl implements LimitedElectronAPIs {
         this.ready = this.init();
     }
     private async init() {
-        if (runningInWorker()) {
+        if (inWorker()) {
             const workerSafeElectronClient =
                 wrap<typeof WorkerSafeElectronClient>(self);
 

+ 0 - 1
web/packages/shared/error/index.ts

@@ -70,7 +70,6 @@ export const CustomError = {
     EXPORT_STOPPED: "export stopped",
     NO_EXPORT_FOLDER_SELECTED: "no export folder selected",
     EXPORT_FOLDER_DOES_NOT_EXIST: "export folder does not exist",
-    NO_INTERNET_CONNECTION: "no internet connection",
     AUTH_KEY_NOT_FOUND: "auth key not found",
     EXIF_DATA_NOT_FOUND: "exif data not found",
     SELECT_FOLDER_ABORTED: "select folder aborted",

+ 0 - 13
web/packages/shared/platform/index.ts

@@ -1,13 +0,0 @@
-import isElectron from "is-electron";
-
-export function runningInBrowser() {
-    return typeof window !== "undefined";
-}
-
-export function runningInWorker() {
-    return typeof importScripts === "function";
-}
-
-export function runningInElectron() {
-    return isElectron();
-}

+ 2 - 2
web/packages/shared/storage/localForage/index.ts

@@ -1,8 +1,8 @@
-import { runningInBrowser } from "../../platform";
+import { haveWindow } from "@/next/env";
 
 import localForage from "localforage";
 
-if (runningInBrowser()) {
+if (haveWindow()) {
     localForage.config({
         name: "ente-files",
         version: 1.0,

+ 0 - 1
web/packages/utils/package.json

@@ -3,7 +3,6 @@
     "version": "0.0.0",
     "private": true,
     "dependencies": {
-        "is-electron": "^2.2",
         "libsodium-wrappers": "0.7.9",
         "yup": "^1.4"
     },