diff --git a/desktop/README.md b/desktop/README.md index 496b8dbed..05149f5d0 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -28,7 +28,7 @@ Install dependencies yarn install ``` -Run in development mode (with hot reload) +Run in development mode (supports hot reload for the renderer process) ```sh yarn dev diff --git a/desktop/docs/dev.md b/desktop/docs/dev.md index a175f9b81..f4fa69b55 100644 --- a/desktop/docs/dev.md +++ b/desktop/docs/dev.md @@ -4,14 +4,16 @@ ### yarn dev -Launch the app in development mode +Launch the app in development mode: -- Runs a development server for the renderer, with HMR. +- Transpiles the files in `src/` and starts the main process. + +- Runs a development server for the renderer (with hot module reload). - Starts tsc in watch mode to recompile the JS files used by the main process. - -- Starts the main process, reloading it on changes to the the TS files in - `src/`. + Note that the main process is not restarted on changes automatically, you'll + still need to restart the app manually – running tsc in watch mode is still + useful to notice any errors. ### yarn build diff --git a/desktop/src/services/clipService.ts b/desktop/src/services/clipService.ts index 268694a7f..e2aaa056e 100644 --- a/desktop/src/services/clipService.ts +++ b/desktop/src/services/clipService.ts @@ -357,7 +357,7 @@ export async function computeONNXTextEmbedding( } async function getRGBData(inputFilePath: string) { - const jpegData = await readFile(inputFilePath); + const jpegData = await fs.readFile(inputFilePath); let rawImageData; try { rawImageData = jpeg.decode(jpegData, { diff --git a/desktop/src/services/fs.ts b/desktop/src/services/fs.ts index c74790d01..356b49c18 100644 --- a/desktop/src/services/fs.ts +++ b/desktop/src/services/fs.ts @@ -25,16 +25,14 @@ export const getDirFilePaths = async (dirPath: string) => { return files; }; -export const getFileStream = async (filePath: string) => { +const getFileStream = async (filePath: string) => { const file = await fs.open(filePath, "r"); let offset = 0; const readableStream = new ReadableStream({ async pull(controller) { try { const buff = new Uint8Array(FILE_STREAM_CHUNK_SIZE); - // original types were not working correctly - const bytesRead = (await fs.read( - file, + const bytesRead = (await file.read( buff, 0, FILE_STREAM_CHUNK_SIZE, @@ -43,16 +41,16 @@ export const getFileStream = async (filePath: string) => { offset += bytesRead; if (bytesRead === 0) { controller.close(); - await fs.close(file); + await file.close(); } else { controller.enqueue(buff.slice(0, bytesRead)); } } catch (e) { - await fs.close(file); + await file.close(); } }, async cancel() { - await fs.close(file); + await file.close(); }, }); return readableStream; diff --git a/web/apps/cast/src/services/readerService.ts b/web/apps/cast/src/services/readerService.ts index 344fd9f20..5aa42b6d2 100644 --- a/web/apps/cast/src/services/readerService.ts +++ b/web/apps/cast/src/services/readerService.ts @@ -56,38 +56,3 @@ async function* fileChunkReaderMaker(file: File, chunkSize: number) { } return null; } - -// depreciated -// eslint-disable-next-line @typescript-eslint/no-unused-vars -async function getUint8ArrayViewOld( - reader: FileReader, - file: Blob, -): Promise { - return await new Promise((resolve, reject) => { - reader.onabort = () => - reject( - Error( - `file reading was aborted, file size= ${convertBytesToHumanReadable( - file.size, - )}`, - ), - ); - reader.onerror = () => - reject( - Error( - `file reading has failed, file size= ${convertBytesToHumanReadable( - file.size, - )} , reason= ${reader.error}`, - ), - ); - reader.onload = () => { - // Do whatever you want with the file contents - const result = - typeof reader.result === "string" - ? new TextEncoder().encode(reader.result) - : new Uint8Array(reader.result); - resolve(result); - }; - reader.readAsArrayBuffer(file); - }); -} diff --git a/web/apps/cast/src/services/wasmHeicConverter/wasmHEICConverterService.ts b/web/apps/cast/src/services/wasmHeicConverter/wasmHEICConverterService.ts index a49d8e4f8..c6d33b772 100644 --- a/web/apps/cast/src/services/wasmHeicConverter/wasmHEICConverterService.ts +++ b/web/apps/cast/src/services/wasmHeicConverter/wasmHEICConverterService.ts @@ -1,11 +1,11 @@ import { CustomError } from "@ente/shared/error"; import { addLogLine } from "@ente/shared/logging"; +import { retryAsyncFunction } from "@ente/shared/promise"; import { logError } from "@ente/shared/sentry"; import { convertBytesToHumanReadable } from "@ente/shared/utils/size"; import QueueProcessor from "services/queueProcessor"; import { getDedicatedConvertWorker } from "utils/comlink/ComlinkConvertWorker"; import { ComlinkWorker } from "utils/comlink/comlinkWorker"; -import { retryAsyncFunction } from "utils/network"; import { DedicatedConvertWorker } from "worker/convert.worker"; const WORKER_POOL_SIZE = 2; diff --git a/web/apps/cast/src/utils/network/index.ts b/web/apps/cast/src/utils/network/index.ts deleted file mode 100644 index f7bac98ec..000000000 --- a/web/apps/cast/src/utils/network/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { sleep } from "@ente/shared/sleep"; - -const waitTimeBeforeNextAttemptInMilliSeconds = [2000, 5000, 10000]; - -export async function retryAsyncFunction( - request: (abort?: () => void) => Promise, - waitTimeBeforeNextTry?: number[], -): Promise { - if (!waitTimeBeforeNextTry) { - waitTimeBeforeNextTry = waitTimeBeforeNextAttemptInMilliSeconds; - } - - for ( - let attemptNumber = 0; - attemptNumber <= waitTimeBeforeNextTry.length; - attemptNumber++ - ) { - try { - const resp = await request(); - return resp; - } catch (e) { - if (attemptNumber === waitTimeBeforeNextTry.length) { - throw e; - } - await sleep(waitTimeBeforeNextTry[attemptNumber]); - } - } -} diff --git a/web/apps/cast/src/utils/time/format.ts b/web/apps/cast/src/utils/time/format.ts deleted file mode 100644 index 0e2dc68b5..000000000 --- a/web/apps/cast/src/utils/time/format.ts +++ /dev/null @@ -1,78 +0,0 @@ -import i18n, { t } from "i18next"; - -const dateTimeFullFormatter1 = new Intl.DateTimeFormat(i18n.language, { - weekday: "short", - month: "short", - day: "numeric", -}); - -const dateTimeFullFormatter2 = new Intl.DateTimeFormat(i18n.language, { - year: "numeric", -}); -const dateTimeShortFormatter = new Intl.DateTimeFormat(i18n.language, { - month: "short", - day: "numeric", - year: "numeric", - hour: "2-digit", - minute: "2-digit", -}); - -const timeFormatter = new Intl.DateTimeFormat(i18n.language, { - timeStyle: "short", -}); - -export function formatDateFull(date: number | Date) { - return [dateTimeFullFormatter1, dateTimeFullFormatter2] - .map((f) => f.format(date)) - .join(" "); -} - -export function formatDate(date: number | Date) { - const withinYear = - new Date().getFullYear() === new Date(date).getFullYear(); - const dateTimeFormat2 = !withinYear ? dateTimeFullFormatter2 : null; - return [dateTimeFullFormatter1, dateTimeFormat2] - .filter((f) => !!f) - .map((f) => f.format(date)) - .join(" "); -} - -export function formatDateTimeShort(date: number | Date) { - return dateTimeShortFormatter.format(date); -} - -export function formatTime(date: number | Date) { - return timeFormatter.format(date).toUpperCase(); -} - -export function formatDateTimeFull(dateTime: number | Date): string { - return [formatDateFull(dateTime), t("at"), formatTime(dateTime)].join(" "); -} - -export function formatDateTime(dateTime: number | Date): string { - return [formatDate(dateTime), t("at"), formatTime(dateTime)].join(" "); -} - -export function formatDateRelative(date: number) { - const units = { - year: 24 * 60 * 60 * 1000 * 365, - month: (24 * 60 * 60 * 1000 * 365) / 12, - day: 24 * 60 * 60 * 1000, - hour: 60 * 60 * 1000, - minute: 60 * 1000, - second: 1000, - }; - const relativeDateFormat = new Intl.RelativeTimeFormat(i18n.language, { - localeMatcher: "best fit", - numeric: "always", - style: "long", - }); - const elapsed = date - Date.now(); // "Math.abs" accounts for both "past" & "future" scenarios - - for (const u in units) - if (Math.abs(elapsed) > units[u] || u === "second") - return relativeDateFormat.format( - Math.round(elapsed / units[u]), - u as Intl.RelativeTimeFormatUnit, - ); -} diff --git a/web/apps/cast/src/utils/time/index.ts b/web/apps/cast/src/utils/time/index.ts deleted file mode 100644 index 592d2c7af..000000000 --- a/web/apps/cast/src/utils/time/index.ts +++ /dev/null @@ -1,136 +0,0 @@ -export interface TimeDelta { - hours?: number; - days?: number; - months?: number; - years?: number; -} - -interface DateComponent { - year: T; - month: T; - day: T; - hour: T; - minute: T; - second: T; -} - -export function validateAndGetCreationUnixTimeInMicroSeconds(dateTime: Date) { - if (!dateTime || isNaN(dateTime.getTime())) { - return null; - } - const unixTime = dateTime.getTime() * 1000; - //ignoring dateTimeString = "0000:00:00 00:00:00" - if (unixTime === Date.UTC(0, 0, 0, 0, 0, 0, 0) || unixTime === 0) { - return null; - } else if (unixTime > Date.now() * 1000) { - return null; - } else { - return unixTime; - } -} - -/* -generates data component for date in format YYYYMMDD-HHMMSS - */ -export function parseDateFromFusedDateString(dateTime: string) { - const dateComponent: DateComponent = convertDateComponentToNumber({ - year: dateTime.slice(0, 4), - month: dateTime.slice(4, 6), - day: dateTime.slice(6, 8), - hour: dateTime.slice(9, 11), - minute: dateTime.slice(11, 13), - second: dateTime.slice(13, 15), - }); - return validateAndGetDateFromComponents(dateComponent); -} - -/* sample date format = 2018-08-19 12:34:45 - the date has six symbol separated number values - which we would extract and use to form the date - */ -export function tryToParseDateTime(dateTime: string): Date { - const dateComponent = getDateComponentsFromSymbolJoinedString(dateTime); - if (dateComponent.year?.length === 8 && dateComponent.month?.length === 6) { - // the filename has size 8 consecutive and then 6 consecutive digits - // high possibility that the it is a date in format YYYYMMDD-HHMMSS - const possibleDateTime = dateComponent.year + "-" + dateComponent.month; - return parseDateFromFusedDateString(possibleDateTime); - } - return validateAndGetDateFromComponents( - convertDateComponentToNumber(dateComponent), - ); -} - -function getDateComponentsFromSymbolJoinedString( - dateTime: string, -): DateComponent { - const [year, month, day, hour, minute, second] = - dateTime.match(/\d+/g) ?? []; - - return { year, month, day, hour, minute, second }; -} - -function validateAndGetDateFromComponents( - dateComponent: DateComponent, -) { - let date = getDateFromComponents(dateComponent); - if (hasTimeValues(dateComponent) && !isTimePartValid(date, dateComponent)) { - // if the date has time values but they are not valid - // then we remove the time values and try to validate the date - date = getDateFromComponents(removeTimeValues(dateComponent)); - } - if (!isDatePartValid(date, dateComponent)) { - return null; - } - return date; -} - -function isTimePartValid(date: Date, dateComponent: DateComponent) { - return ( - date.getHours() === dateComponent.hour && - date.getMinutes() === dateComponent.minute && - date.getSeconds() === dateComponent.second - ); -} - -function isDatePartValid(date: Date, dateComponent: DateComponent) { - return ( - date.getFullYear() === dateComponent.year && - date.getMonth() === dateComponent.month && - date.getDate() === dateComponent.day - ); -} - -function convertDateComponentToNumber( - dateComponent: DateComponent, -): DateComponent { - return { - year: Number(dateComponent.year), - // https://stackoverflow.com/questions/2552483/why-does-the-month-argument-range-from-0-to-11-in-javascripts-date-constructor - month: Number(dateComponent.month) - 1, - day: Number(dateComponent.day), - hour: Number(dateComponent.hour), - minute: Number(dateComponent.minute), - second: Number(dateComponent.second), - }; -} - -function getDateFromComponents(dateComponent: DateComponent) { - const { year, month, day, hour, minute, second } = dateComponent; - if (hasTimeValues(dateComponent)) { - return new Date(year, month, day, hour, minute, second); - } else { - return new Date(year, month, day); - } -} - -function hasTimeValues(dateComponent: DateComponent) { - const { hour, minute, second } = dateComponent; - return !isNaN(hour) && !isNaN(minute) && !isNaN(second); -} - -function removeTimeValues( - dateComponent: DateComponent, -): DateComponent { - return { ...dateComponent, hour: 0, minute: 0, second: 0 }; -} diff --git a/web/apps/photos/src/services/download/clients/photos.ts b/web/apps/photos/src/services/download/clients/photos.ts index 11666041d..7796903fd 100644 --- a/web/apps/photos/src/services/download/clients/photos.ts +++ b/web/apps/photos/src/services/download/clients/photos.ts @@ -1,9 +1,9 @@ import { CustomError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import { getFileURL, getThumbnailURL } from "@ente/shared/network/api"; +import { retryAsyncFunction } from "@ente/shared/promise"; import { DownloadClient } from "services/download"; import { EnteFile } from "types/file"; -import { retryAsyncFunction } from "utils/network"; export class PhotosDownloadClient implements DownloadClient { constructor( diff --git a/web/apps/photos/src/services/download/clients/publicAlbums.ts b/web/apps/photos/src/services/download/clients/publicAlbums.ts index f3412595c..7245fd68d 100644 --- a/web/apps/photos/src/services/download/clients/publicAlbums.ts +++ b/web/apps/photos/src/services/download/clients/publicAlbums.ts @@ -4,9 +4,9 @@ import { getPublicCollectionFileURL, getPublicCollectionThumbnailURL, } from "@ente/shared/network/api"; +import { retryAsyncFunction } from "@ente/shared/promise"; import { DownloadClient } from "services/download"; import { EnteFile } from "types/file"; -import { retryAsyncFunction } from "utils/network"; export class PublicAlbumsDownloadClient implements DownloadClient { constructor( diff --git a/web/apps/photos/src/services/readerService.ts b/web/apps/photos/src/services/readerService.ts index 344fd9f20..5aa42b6d2 100644 --- a/web/apps/photos/src/services/readerService.ts +++ b/web/apps/photos/src/services/readerService.ts @@ -56,38 +56,3 @@ async function* fileChunkReaderMaker(file: File, chunkSize: number) { } return null; } - -// depreciated -// eslint-disable-next-line @typescript-eslint/no-unused-vars -async function getUint8ArrayViewOld( - reader: FileReader, - file: Blob, -): Promise { - return await new Promise((resolve, reject) => { - reader.onabort = () => - reject( - Error( - `file reading was aborted, file size= ${convertBytesToHumanReadable( - file.size, - )}`, - ), - ); - reader.onerror = () => - reject( - Error( - `file reading has failed, file size= ${convertBytesToHumanReadable( - file.size, - )} , reason= ${reader.error}`, - ), - ); - reader.onload = () => { - // Do whatever you want with the file contents - const result = - typeof reader.result === "string" - ? new TextEncoder().encode(reader.result) - : new Uint8Array(reader.result); - resolve(result); - }; - reader.readAsArrayBuffer(file); - }); -} diff --git a/web/apps/photos/src/services/wasmHeicConverter/wasmHEICConverterService.ts b/web/apps/photos/src/services/wasmHeicConverter/wasmHEICConverterService.ts index 4ace3064a..c782d4069 100644 --- a/web/apps/photos/src/services/wasmHeicConverter/wasmHEICConverterService.ts +++ b/web/apps/photos/src/services/wasmHeicConverter/wasmHEICConverterService.ts @@ -1,11 +1,11 @@ import { CustomError } from "@ente/shared/error"; import { addLogLine } from "@ente/shared/logging"; +import { retryAsyncFunction } from "@ente/shared/promise"; import { logError } from "@ente/shared/sentry"; import { convertBytesToHumanReadable } from "@ente/shared/utils/size"; import { ComlinkWorker } from "@ente/shared/worker/comlinkWorker"; import QueueProcessor from "services/queueProcessor"; import { getDedicatedConvertWorker } from "utils/comlink/ComlinkConvertWorker"; -import { retryAsyncFunction } from "utils/network"; import { DedicatedConvertWorker } from "worker/convert.worker"; const WORKER_POOL_SIZE = 2; diff --git a/web/apps/photos/src/utils/network/index.ts b/web/apps/photos/src/utils/network/index.ts deleted file mode 100644 index 244924c49..000000000 --- a/web/apps/photos/src/utils/network/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { sleep } from "utils/common"; - -const waitTimeBeforeNextAttemptInMilliSeconds = [2000, 5000, 10000]; - -export async function retryAsyncFunction( - request: (abort?: () => void) => Promise, - waitTimeBeforeNextTry?: number[], -): Promise { - if (!waitTimeBeforeNextTry) { - waitTimeBeforeNextTry = waitTimeBeforeNextAttemptInMilliSeconds; - } - - for ( - let attemptNumber = 0; - attemptNumber <= waitTimeBeforeNextTry.length; - attemptNumber++ - ) { - try { - const resp = await request(); - return resp; - } catch (e) { - if (attemptNumber === waitTimeBeforeNextTry.length) { - throw e; - } - await sleep(waitTimeBeforeNextTry[attemptNumber]); - } - } -} diff --git a/web/packages/shared/promise/index.ts b/web/packages/shared/promise/index.ts index c2434ea59..39ce09362 100644 --- a/web/packages/shared/promise/index.ts +++ b/web/packages/shared/promise/index.ts @@ -1,5 +1,33 @@ +import { sleep } from "@ente/shared/sleep"; import { CustomError } from "../error"; +const waitTimeBeforeNextAttemptInMilliSeconds = [2000, 5000, 10000]; + +export async function retryAsyncFunction( + request: (abort?: () => void) => Promise, + waitTimeBeforeNextTry?: number[], +): Promise { + if (!waitTimeBeforeNextTry) { + waitTimeBeforeNextTry = waitTimeBeforeNextAttemptInMilliSeconds; + } + + for ( + let attemptNumber = 0; + attemptNumber <= waitTimeBeforeNextTry.length; + attemptNumber++ + ) { + try { + const resp = await request(); + return resp; + } catch (e) { + if (attemptNumber === waitTimeBeforeNextTry.length) { + throw e; + } + await sleep(waitTimeBeforeNextTry[attemptNumber]); + } + } +} + export const promiseWithTimeout = async ( request: Promise, timeout: number,