diff --git a/web/apps/photos/src/components/MachineLearning/MLSearchSettings/enableFaceSearch.tsx b/web/apps/photos/src/components/MachineLearning/MLSearchSettings/enableFaceSearch.tsx index 4691446396d1ae7ea64b79aaf540f7c7c1b9d14f..a007cb398838845a1ffdd7e8f7c80907ecc98357 100644 --- a/web/apps/photos/src/components/MachineLearning/MLSearchSettings/enableFaceSearch.tsx +++ b/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: ( + openLink("https://ente.io/blog/desktop-ml-beta", true); + return ( diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx index d7e3b9e9123bb57a61c3c0d0185986814527295c..c4d552fbc927250e7ac7ec75624d59a7efe3f8b1 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx +++ b/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; diff --git a/web/apps/photos/src/components/Sidebar/HelpSection.tsx b/web/apps/photos/src/components/Sidebar/HelpSection.tsx index 1f7ce95cc88c5024b62522c1c9169d2086810d91..4cc97c414a1bfd2596f38146cd89095fdd3e8e37 100644 --- a/web/apps/photos/src/components/Sidebar/HelpSection.tsx +++ b/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" /> openLink("mailto:support@ente.io", true)} + onClick={contactSupport} labelComponent={ @@ -57,7 +48,7 @@ export default function HelpSection() { variant="secondary" /> ), diff --git a/web/apps/photos/src/components/Upload/Uploader.tsx b/web/apps/photos/src/components/Upload/Uploader.tsx index ef91f0670c54ca434007cdeca9fac0539e7705d5..f1d6d03ff38ca17907c0b194161d0b67f982de33 100644 --- a/web/apps/photos/src/components/Upload/Uploader.tsx +++ b/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: , - 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, + task: () => Promise, +) { + if (waitPromise && isPromise(waitPromise)) { + await waitPromise; + } + await task(); +} diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/card/index.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/card/index.tsx index 7bafa04037fe6af5ae74dcd56024d4afc0b8e820..41e577675d36e1503d953c9b2f2544397c6d2225 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/card/index.tsx +++ b/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"), diff --git a/web/apps/photos/src/pages/gallery/index.tsx b/web/apps/photos/src/pages/gallery/index.tsx index f62376ea662f01cc6f13fb4516f0736a05c32f8a..5930dd30bedd4b17f9415704328148d856504456 100644 --- a/web/apps/photos/src/pages/gallery/index.tsx +++ b/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 = (map1: Map, map2: Map) => { + const mergedMap = new Map(map1); + map2.forEach((value, key) => { + mergedMap.set(key, value); + }); + return mergedMap; +}; diff --git a/web/apps/photos/src/services/locationSearchService.ts b/web/apps/photos/src/services/locationSearchService.ts index a77557dee2f028ae94b7056ddfb8d2f205e54393..bfc6990da925e26b26119db992fda39771b0bf10 100644 --- a/web/apps/photos/src/services/locationSearchService.ts +++ b/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"]; }); diff --git a/web/apps/photos/src/services/machineLearning/machineLearningFactory.ts b/web/apps/photos/src/services/machineLearning/machineLearningFactory.ts index d397a60ad05d6c303347a1b17216d409c7c22bf1..b93474cb3bd963e585fc428a487f34fc719b19d4 100644 --- a/web/apps/photos/src/services/machineLearning/machineLearningFactory.ts +++ b/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)); diff --git a/web/apps/photos/src/utils/comlink/ComlinkConvertWorker.ts b/web/apps/photos/src/utils/comlink/ComlinkConvertWorker.ts index 3dc0341607b599f2f35786edf851e35106c2675f..7136fbd62e3e63f7e4dfd5a8c50993909559a70b 100644 --- a/web/apps/photos/src/utils/comlink/ComlinkConvertWorker.ts +++ b/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 >( diff --git a/web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts b/web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts index f61754c6bbb0c27cbe8c09143c53053345cf165a..f75c0f866a4b8d77cf916c8708def7b419864ced 100644 --- a/web/apps/photos/src/utils/comlink/ComlinkMLWorker.ts +++ b/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( name ?? "ente-ml-worker", new Worker(new URL("worker/ml.worker.ts", import.meta.url)), diff --git a/web/apps/photos/src/utils/comlink/ComlinkSearchWorker.ts b/web/apps/photos/src/utils/comlink/ComlinkSearchWorker.ts index 971f12338dcd02c7e132eb8dfcb340375c7fdbeb..b4ec05490f33f4b065802cc9a8c0fce4852c71a4 100644 --- a/web/apps/photos/src/utils/comlink/ComlinkSearchWorker.ts +++ b/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 >( diff --git a/web/apps/photos/src/utils/common/concurrency.ts b/web/apps/photos/src/utils/common/concurrency.ts deleted file mode 100644 index bba75f0097fd3465ad0a454f9f7888eab6ee575c..0000000000000000000000000000000000000000 --- a/web/apps/photos/src/utils/common/concurrency.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { runningInBrowser } from "."; - -export const getConcurrency = () => - runningInBrowser() && - Math.max(2, Math.ceil(navigator.hardwareConcurrency / 2)); diff --git a/web/apps/photos/src/utils/common/index.ts b/web/apps/photos/src/utils/common/index.ts index 4fcc07717f57fb973a0607703017d2a0f5660082..059dfed2be1ad6898e780757c5c0051ecd8d7362 100644 --- a/web/apps/photos/src/utils/common/index.ts +++ b/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, - task: () => Promise, -) { - if (waitPromise && isPromise(waitPromise)) { - await waitPromise; - } - await task(); -} - export function isClipboardItemPresent() { return typeof ClipboardItem !== "undefined"; } @@ -105,11 +34,3 @@ export function batch(arr: T[], batchSize: number): T[][] { } return batches; } - -export const mergeMaps = (map1: Map, map2: Map) => { - const mergedMap = new Map(map1); - map2.forEach((value, key) => { - mergedMap.set(key, value); - }); - return mergedMap; -}; diff --git a/web/apps/photos/src/utils/storage/mlIDbStorage.ts b/web/apps/photos/src/utils/storage/mlIDbStorage.ts index 7ee4a5fcf44af61e59c89ae42063c457b36b49e8..865b6cb8cf13bddb5b0cea95685e2e573eff75d0 100644 --- a/web/apps/photos/src/utils/storage/mlIDbStorage.ts +++ b/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>; constructor() { - if (!runningInBrowser() || !runningInElectron()) { + if (!haveWindow() || !isElectron()) { return; } diff --git a/web/apps/photos/src/utils/ui/index.tsx b/web/apps/photos/src/utils/ui/index.tsx index 1b938fb0330d45afd53506a187435293bcb43e35..75b0cd23dc6fbe7e0bc5ab1d78bab8dc1c0f177c 100644 --- a/web/apps/photos/src/utils/ui/index.tsx +++ b/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 => ({ diff --git a/web/apps/photos/src/utils/upload/isCanvasBlocked.ts b/web/apps/photos/src/utils/upload/isCanvasBlocked.ts deleted file mode 100644 index e9f9b5abe47ed5aa5dcdaf1f345ebf7d216f99d5..0000000000000000000000000000000000000000 --- a/web/apps/photos/src/utils/upload/isCanvasBlocked.ts +++ /dev/null @@ -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; -} diff --git a/web/packages/next/env.ts b/web/packages/next/env.ts index 8e66e7fd9c97ca51ba21b6fbccfac3a47753758e..ea145d892d78bf8dd1f7d3b55b4aa45e22ad3cb3 100644 --- a/web/packages/next/env.ts +++ b/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"; diff --git a/web/packages/next/locales/en-US/translation.json b/web/packages/next/locales/en-US/translation.json index 29469cd0f9fd5c3d674f81116be21e2189abbe98..5fdb380d5bd6f51ac5e62f8143683a38a5e06819 100644 --- a/web/packages/next/locales/en-US/translation.json +++ b/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": "

It looks like your browser has disabled access to canvas, which is necessary to generate thumbnails for your photos

Please enable access to your browser's canvas, or check out our desktop app

", "WATCH_FOLDERS": "Watch folders", "UPGRADE_NOW": "Upgrade now", "RENEW_NOW": "Renew now", diff --git a/web/packages/next/package.json b/web/packages/next/package.json index f0bbe854480a62ba790b8fcde7a4d99b76c15734..f5a325f14d56a2b12a1dcbc820feb53e0da99d11 100644 --- a/web/packages/next/package.json +++ b/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", diff --git a/web/packages/next/tsconfig.json b/web/packages/next/tsconfig.json index f29c34811385ff06a448608c23badaf01a5ea8ae..10fe5229cf62cf4d7973ef4c6251d9db75dd9837 100644 --- a/web/packages/next/tsconfig.json +++ b/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"] } diff --git a/web/packages/shared/constants/urls.ts b/web/packages/shared/constants/urls.ts index 747e1fa22198f91420833bcb57a03d52bf0d439d..89d5faa16976140d936d2fe874367dd085d34381 100644 --- a/web/packages/shared/constants/urls.ts +++ b/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"; diff --git a/web/packages/shared/electron/service.ts b/web/packages/shared/electron/service.ts index 0484b0746575f6ea763ee1680e1561c5c016bc8a..f926d20b2d348cba4a49fadc1ffa6ea899c35c0b 100644 --- a/web/packages/shared/electron/service.ts +++ b/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(self); diff --git a/web/packages/shared/error/index.ts b/web/packages/shared/error/index.ts index 3d3309379a154d2b022960a0aca568758b196e88..6ed4c748645a598ae59a0c6b5b63bbd56d9ad032 100644 --- a/web/packages/shared/error/index.ts +++ b/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", diff --git a/web/packages/shared/platform/index.ts b/web/packages/shared/platform/index.ts deleted file mode 100644 index a7a63dd6f494aaf0aa46264cddc512a9aeba87cd..0000000000000000000000000000000000000000 --- a/web/packages/shared/platform/index.ts +++ /dev/null @@ -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(); -} diff --git a/web/packages/shared/storage/localForage/index.ts b/web/packages/shared/storage/localForage/index.ts index 9ddf80d99eb343fed2146333d0e0562012802637..a3bb4442d092aaa0325d5462504964a71686d7ea 100644 --- a/web/packages/shared/storage/localForage/index.ts +++ b/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, diff --git a/web/packages/utils/package.json b/web/packages/utils/package.json index bfe78b5f7f2216727a7cff541911c4713b03b0fb..930e3aa44f36679d6eae189c88d37ce3df930343 100644 --- a/web/packages/utils/package.json +++ b/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" },