Merge branch 'main' into feature/web-passkey-recovery

This commit is contained in:
httpjamesm 2024-03-12 13:57:57 -04:00 committed by GitHub
commit d9b1081849
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
164 changed files with 778 additions and 1622 deletions

3
.gitattributes vendored Normal file
View file

@ -0,0 +1,3 @@
# Set line endings of shell scripts to LF, even on Windows, otherwise execution
# within Docker fails.
*.sh text eol=lf

View file

@ -1,11 +1,9 @@
name: "Lint (auth)"
on:
# Run on every push to branches (this also covers pull requests)
# Run on every push to a branch other than main that changes auth/
push:
# See: [Note: Specify branch when specifying a path filter]
branches: ["**"]
# Only run if something changes in these paths
branches-ignore: [main]
paths:
- "auth/**"
- ".github/workflows/auth-lint.yml"

View file

@ -1,11 +1,9 @@
name: "Lint (mobile)"
on:
# Run on every push (this also covers pull requests)
# Run on every push to a branch other than main that changes mobile/
push:
# See: [Note: Specify branch when specifying a path filter]
branches: ["**"]
# Only run if something changes in these paths
branches-ignore: [main]
paths:
- "mobile/**"
- ".github/workflows/mobile-lint.yml"

View file

@ -1,11 +1,9 @@
name: "Lint (server)"
on:
# Run on every push (this also covers pull requests)
# Run on every push to a branch other than main that changes server/
push:
# See: [Note: Specify branch when specifying a path filter]
branches: ["**"]
# Only run if something changes in these paths
branches-ignore: [main]
paths:
- "server/**"
- ".github/workflows/server-lint.yml"

View file

@ -1,20 +1,9 @@
name: "Lint (web)"
on:
# Run on every push (this also covers pull requests)
# Run on every push to a branch other than main that changes web/
push:
# [Note: Specify branch when specifying a path filter]
#
# Path filters are ignored for tag pushes, which causes this workflow to
# always run when we push a tag. Defining an explicit branch solves the
# issue. From GitHub's docs:
#
# > if you define both branches/branches-ignore and paths/paths-ignore,
# > the workflow will only run when both filters are satisfied.
#
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
branches: ["**"]
# Only run if something changes in these paths
branches-ignore: [main]
paths:
- "web/**"
- ".github/workflows/web-lint.yml"

52
.github/workflows/web-preview.yml vendored Normal file
View file

@ -0,0 +1,52 @@
name: "Preview (web)"
on:
workflow_dispatch:
inputs:
app:
description: "App to build and deploy"
type: choice
required: true
default: "photos"
options:
- "accounts"
- "auth"
- "cast"
- "photos"
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build ${{ inputs.app }}
run: yarn build:${{ inputs.app }}
- name: Publish ${{ inputs.app }} to preview
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: preview
directory: web/apps/${{ inputs.app }}/out
wranglerVersion: "3"

4
.gitmodules vendored
View file

@ -9,10 +9,6 @@
[submodule "auth/assets/simple-icons"]
path = auth/assets/simple-icons
url = https://github.com/simple-icons/simple-icons.git
[submodule "desktop/thirdparty/next-electron-server"]
path = desktop/thirdparty/next-electron-server
url = https://github.com/ente-io/next-electron-server.git
branch = desktop
[submodule "mobile/thirdparty/flutter"]
path = mobile/thirdparty/flutter
url = https://github.com/flutter/flutter.git

View file

@ -52,7 +52,4 @@ jobs:
# macOS notarization API key
API_KEY_ID: ${{ secrets.api_key_id }}
API_KEY_ISSUER_ID: ${{ secrets.api_key_issuer_id}}
# setry crash reporting token
SENTRY_AUTH_TOKEN: ${{secrets.sentry_auth_token}}
NEXT_PUBLIC_DISABLE_SENTRY: ${{secrets.next_public_disable_sentry}}
USE_HARD_LINKS: false

9
desktop/.gitignore vendored
View file

@ -11,10 +11,11 @@ node_modules/
.env
.env.*.local
# Generated code during build
# - tsc transpiles src/**/*.ts and emits the generated JS into build/app
# - The out dir from the photos web app is symlinked to build/out
build/
# tsc transpiles src/**/*.ts and emits the generated JS into app
app/
# out is a symlink to the photos web app's dir
out
# electron-builder
dist/

View file

@ -1 +0,0 @@
thirdparty/

View file

@ -4,128 +4,128 @@
### New
- Option to select file download location.
- Add support for searching popular cities
- Sorted duplicates in desecending order of size
- Add Counter to upload section
- Display full name and collection name on hover on dedupe screen photos
- Option to select file download location.
- Add support for searching popular cities
- Sorted duplicates in desecending order of size
- Add Counter to upload section
- Display full name and collection name on hover on dedupe screen photos
### Bug Fixes
- Fix add to album padding issue
- Fix double uncategorized album issue
- Hide Hidden collection files from all section
- Fix add to album padding issue
- Fix double uncategorized album issue
- Hide Hidden collection files from all section
## v1.6.62
### New
- Integrated onnx clip runner
- Integrated onnx clip runner
### Bug Fixes
- Fixes login button requiring double click issue
- Fixes Collection sort state not preserved issue
- Fixes continuous export causing app crash
- Improves ML related copies for better distinction from clip
- Added Better favicon for light mode
- Fixed face indexing issues
- Fixed thumbnail load issue
- Fixes login button requiring double click issue
- Fixes Collection sort state not preserved issue
- Fixes continuous export causing app crash
- Improves ML related copies for better distinction from clip
- Added Better favicon for light mode
- Fixed face indexing issues
- Fixed thumbnail load issue
## v1.6.60
### Bug Fixes
- Fix Thumbnail Orientation issue
- Fix ML logging issue
- Fix Thumbnail Orientation issue
- Fix ML logging issue
## v1.6.59
### New
- Added arm64 builds for linux
- Added arm64 builds for linux
### Bug Fixes
- Fix Editor file not loading issue
- Fix ML results missing thumbnail issue
- Fix Editor file not loading issue
- Fix ML results missing thumbnail issue
## v1.6.58
### Bug Fixes
- Fix File load issue
- Fix File load issue
## v1.6.57
### New Features
- Added encrypted Disk caching for files
- Added option to customize cache folder location
- Added encrypted Disk caching for files
- Added option to customize cache folder location
### Bug Fixes
- Fixed caching issue,causing multiple download of file during ml sync
- Fixed caching issue,causing multiple download of file during ml sync
## v1.6.55
### Bug Fixes
- Added manage family portal option if add-on is active
- Fixed filename date parsing issue
- Fixed storage limit ui glitch
- Fixed dedupe page layout issue
- Fixed ElectronAPI refactoring issue
- Fixed Search related issues
- Added manage family portal option if add-on is active
- Fixed filename date parsing issue
- Fixed storage limit ui glitch
- Fixed dedupe page layout issue
- Fixed ElectronAPI refactoring issue
- Fixed Search related issues
## v1.6.54
### New Features
- Added support for HEIC and raw image in photo editor
- Added support for HEIC and raw image in photo editor
### Bug Fixes
- Fixed 16bit HDR HEIC images support
- Fixed blocked login due safe storage issue
- Fixed Search related issues
- Fixed issue of watch folder not cleared on logout
- other under the hood ui/ux improvements
- Fixed 16bit HDR HEIC images support
- Fixed blocked login due safe storage issue
- Fixed Search related issues
- Fixed issue of watch folder not cleared on logout
- other under the hood ui/ux improvements
## v1.6.53
### Bug Fixes
- Fixed watch folder disabled issue
- Fixed BF Add on related issues
- Fixed clip sync issue and added better logging
- Fixed mov file upload
- Fixed clip extraction related issue
- Fixed watch folder disabled issue
- Fixed BF Add on related issues
- Fixed clip sync issue and added better logging
- Fixed mov file upload
- Fixed clip extraction related issue
## v1.6.52
### New Features
- Added Clip Desktop on windows
- Added Clip Desktop on windows
### Bug Fixes
- fixed google json matching issue
- other under-the-hood changes to improve performance and bug fixes
- fixed google json matching issue
- other under-the-hood changes to improve performance and bug fixes
## v1.6.50
### New Features
- Added Clip desktop
- Added Clip desktop
### Bug Fixes
- Fixed desktop downloaded file had extra dot in the name
- Cleanup error messages
- fix the motion photo clustering issue
- Add option to disable cf proxy locally
- other under-the-hood changes to improve UX
- Fixed desktop downloaded file had extra dot in the name
- Cleanup error messages
- fix the motion photo clustering issue
- Add option to disable cf proxy locally
- other under-the-hood changes to improve UX
## v1.6.49
@ -137,54 +137,54 @@ Check out our [blog](https://ente.io/blog/introducing-web-desktop-photo-editor/)
### Bug Fixes
- Fixed misaligned icons in photo-viewer
- Fixed issue with Motion photo upload
- Fixed issue with Live-photo upload
- other minor ux improvement
- Fixed misaligned icons in photo-viewer
- Fixed issue with Motion photo upload
- Fixed issue with Live-photo upload
- other minor ux improvement
## v1.6.46
### Bug Fixes
- Fixes OOM crashes during file upload [#1379](https://github.com/ente-io/photos-web/pull/1379)
- Fixes OOM crashes during file upload [#1379](https://github.com/ente-io/photos-web/pull/1379)
## v1.6.45
### Bug Fixes
- Fixed app keeps reloading issue [#235](https://github.com/ente-io/photos-desktop/pull/235)
- Fixed dng and arw preview issue [#1378](https://github.com/ente-io/photos-web/pull/1378)
- Added view crash report option (help menu) for user to share electron crash report locally
- Fixed app keeps reloading issue [#235](https://github.com/ente-io/photos-desktop/pull/235)
- Fixed dng and arw preview issue [#1378](https://github.com/ente-io/photos-web/pull/1378)
- Added view crash report option (help menu) for user to share electron crash report locally
## v1.6.44
- Upgraded electron to get latest security patches and other improvements.
- Upgraded electron to get latest security patches and other improvements.
## v1.6.43
### Added
- #### Check for update and changelog option
- #### Check for update and changelog option
Added options to check for update manually and a view changelog via the app menubar
- #### Opt out of crash reporting
- #### Opt out of crash reporting
Added option to out of a crash reporting, it can accessed from the settings -> preferences -> disable crash reporting
- #### Type search
- #### Type search
Added new search option to search files based on file type i.e, image, video, live-photo.
- #### Manual Convert Button
- #### Manual Convert Button
In case the video is not playable, Now there is a convert button which can be used to trigger conversion of the video to supported format.
- #### File Download Progress
- #### File Download Progress
The file loader now also shows the exact percentage download progress, instead of just a simple loader.
- #### Bug fixes & other enhancements
- #### Bug fixes & other enhancements
We have squashed a few pesky bugs that were reported by our community
@ -192,21 +192,21 @@ Check out our [blog](https://ente.io/blog/introducing-web-desktop-photo-editor/)
### Added
- #### Hidden albums
- #### Hidden albums
You can now hide albums, just like individual memories.
- #### Email verification
- #### Email verification
We have now made email verification optional, so you can sign in with just your email address and password, without waiting for a verification code.
You can opt in / out of email verification from Settings > Security.
- #### Download Album
- #### Download Album
You can now chose the download location for downloading albums. Along with that we have also added progress bar for album download.
- #### Bug fixes & other enhancements
- #### Bug fixes & other enhancements
We have squashed a few pesky bugs that were reported by our community

View file

@ -10,40 +10,28 @@ To know more about Ente, see [our main README](../README.md) or visit
## Building from source
> [!CAUTION]
>
> We moved a few things around when switching to a monorepo recently, and the
> desktop app is not currently building with these instructions below. Hang
> tight, we're on it, and will fix soon.
Fetch submodules
```sh
git submodule update --init --recursive
```
Install dependencies
```sh
yarn install
```
Run the app
Run in development mode (with hot reload)
```sh
yarn dev
```
To recompile automatically using electron-reload, run this in a separate
terminal:
> [!CAUTION]
>
> `yarn dev` is currently not working (we'll fix soon). If you just want to
> build from source and use the generated binary, use `yarn build`.
```bash
yarn watch
```
`yarn dev` is handy during development, but if you wish, you can also create a
binary for your platform by using
Or create a binary for your platform
```sh
yarn build
```
That's the gist of it. For more development related documentation, see
[docs](docs/README.md).

View file

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />

View file

Before

Width:  |  Height:  |  Size: 989 B

After

Width:  |  Height:  |  Size: 989 B

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 607 B

After

Width:  |  Height:  |  Size: 607 B

View file

Before

Width:  |  Height:  |  Size: 259 B

After

Width:  |  Height:  |  Size: 259 B

View file

Before

Width:  |  Height:  |  Size: 458 B

After

Width:  |  Height:  |  Size: 458 B

View file

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 655 B

View file

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<title>Electron Updater Example</title>

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -4,3 +4,11 @@ See [web/docs/dependencies.md](../../web/docs/dependencies.md) for general web
specific dependencies. See [electron.md](electron.md) for our main dependency,
Electron. The rest of this document describes the remaining, desktop specific
dependencies that are used by the Photos desktop app.
## Electron related
### next-electron-server
This spins up a server for serving files using a protocol handler inside our
Electron process. This allows us to directly use the output produced by `next
build` for loading into our renderer process.

4
desktop/docs/dev.md Normal file
View file

@ -0,0 +1,4 @@
# Development tips
- `yarn build:quick` is a variant of `yarn build` that uses the
`--config.compression=store` flag to (slightly) speed up electron-builder.

View file

@ -7,15 +7,15 @@ Electron embeds Chromium and Node.js in the generated app's binary. The
generated app thus consists of two separate processes - the _main_ process, and
a _renderer_ process.
* The _main_ process is runs the embedded node. This process can deal with the
host OS - it is conceptually like a `node` repl running on your machine. In our
case, the TypeScript code (in the `src/` directory) gets transpiled by `tsc`
into JavaScript in the `build/app/` directory, which gets bundled in the
generated app's binary and is loaded by the node (main) process when the app
starts.
- The _main_ process is runs the embedded node. This process can deal with the
host OS - it is conceptually like a `node` repl running on your machine. In our
case, the TypeScript code (in the `src/` directory) gets transpiled by `tsc`
into JavaScript in the `build/app/` directory, which gets bundled in the
generated app's binary and is loaded by the node (main) process when the app
starts.
* The _renderer_ process is a regular web app that gets loaded into the embedded
Chromium. When the main process starts, it creates a new "window" that shows
this embedded Chromium. In our case, we build and bundle a static export of
the [Photos web app](../web/README.md) in the generated app. This gets loaded
by the embedded Chromium at runtime, acting as the app's UI.
- The _renderer_ process is a regular web app that gets loaded into the embedded
Chromium. When the main process starts, it creates a new "window" that shows
this embedded Chromium. In our case, we build and bundle a static export of
the [Photos web app](../web/README.md) in the generated app. This gets loaded
by the embedded Chromium at runtime, acting as the app's UI.

View file

@ -4,21 +4,22 @@
"private": true,
"description": "Desktop client for Ente Photos",
"author": "Ente <code@ente.io>",
"main": "build/app/main.js",
"main": "app/main.js",
"scripts": {
"build": "mkdir -p build && yarn build-renderer && yarn build-main",
"build-main": "tsc && electron-builder --config.compression=store",
"build-renderer": "cd ../web && yarn install && yarn build:photos && cd ../desktop/build && rm -f out && ln -sf ../../web/apps/photos/out",
"dev": "concurrently \"yarn dev-main\" \"yarn dev-renderer\"",
"dev-main": "tsc && electron build/app/main.js",
"dev-renderer": "cd ../web && yarn install && yarn dev:photos && cd ../desktop/build && rm -f out && ln -sf ../../web/apps/photos/out",
"build": "yarn build-renderer && yarn build-main",
"build-main": "tsc && electron-builder",
"build-main:quick": "tsc && electron-builder --config.compression=store",
"build-renderer": "cd ../web && yarn install && yarn build:photos && cd ../desktop && rm -f out && ln -sf ../web/apps/photos/out",
"build:quick": "yarn build-renderer && yarn build-main:quick",
"dev": "concurrently --names 'main,rndr,tscw' \"yarn dev-main\" \"yarn dev-renderer\" \"yarn dev-main-watch\"",
"dev-main": "tsc && electron app/main.js",
"dev-main-watch": "tsc --watch --preserveWatchOutput",
"dev-renderer": "cd ../web && yarn install && yarn dev:photos",
"postinstall": "electron-builder install-app-deps",
"lint": "yarn prettier --check . && eslint \"src/**/*.ts\"",
"lint-fix": "yarn prettier --write . && eslint --fix .",
"watch": "tsc -w"
"lint-fix": "yarn prettier --write . && eslint --fix src"
},
"dependencies": {
"@sentry/electron": "^2.5.1",
"any-shell-escape": "^0.1.1",
"auto-launch": "^5.0.5",
"chokidar": "^3.5.3",
@ -31,14 +32,13 @@
"get-folder-size": "^2.0.1",
"html-entities": "^2.4.0",
"jpeg-js": "^0.4.4",
"next-electron-server": "file:./thirdparty/next-electron-server",
"next-electron-server": "^1",
"node-fetch": "^2.6.7",
"node-stream-zip": "^1.15.0",
"onnxruntime-node": "^1.16.3",
"promise-fs": "^2.1.1"
},
"devDependencies": {
"@sentry/cli": "^1.68.0",
"@types/auto-launch": "^5.0.2",
"@types/ffmpeg-static": "^3.0.1",
"@types/get-folder-size": "^2.0.0",
@ -55,7 +55,7 @@
"eslint": "^7.23.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^8.5.0",
"prettier": "2.5.1",
"prettier": "^3",
"prettier-plugin-organize-imports": "^3.2",
"prettier-plugin-packagejson": "^2.4",
"typescript": "^4.2.3"
@ -112,26 +112,20 @@
"x64ArchFiles": "Contents/Resources/ggmlclip-mac"
},
"afterSign": "electron-builder-notarize",
"extraFiles": [
{
"from": "resources",
"to": "resources",
"filter": [
"**/*"
]
}
],
"asarUnpack": [
"node_modules/ffmpeg-static/bin/${os}/${arch}/ffmpeg",
"node_modules/ffmpeg-static/index.js",
"node_modules/ffmpeg-static/package.json"
],
"files": [
"build/app/**/*",
"extraFiles": [
{
"from": "build/out",
"to": "out"
"from": "build",
"to": "resources"
}
],
"files": [
"app/**/*",
"out"
]
},
"productName": "ente",

View file

@ -1,3 +0,0 @@
defaults.url=https://sentry.ente.io/
defaults.org=ente
defaults.project=desktop-photos

View file

@ -22,7 +22,7 @@ const getCacheBucketDir = async (cacheName: string) => {
export async function openDiskCache(
cacheName: string,
cacheLimitInBytes?: number
cacheLimitInBytes?: number,
) {
const cacheBucketDir = await getCacheBucketDir(cacheName);
if (!existsSync(cacheBucketDir)) {
@ -42,7 +42,7 @@ export async function deleteDiskCache(cacheName: string) {
}
export async function setCustomCacheDirectory(
directory: string
directory: string,
): Promise<void> {
await ipcRenderer.invoke("set-custom-cache-directory", directory);
}

View file

@ -5,7 +5,7 @@ import { isExecError, parseExecError } from "../utils/error";
export async function computeImageEmbedding(
model: Model,
imageData: Uint8Array
imageData: Uint8Array,
): Promise<Float32Array> {
let tempInputFilePath = null;
try {
@ -15,7 +15,7 @@ export async function computeImageEmbedding(
const embedding = await ipcRenderer.invoke(
"compute-image-embedding",
model,
tempInputFilePath
tempInputFilePath,
);
return embedding;
} catch (err) {
@ -34,13 +34,13 @@ export async function computeImageEmbedding(
export async function computeTextEmbedding(
model: Model,
text: string
text: string,
): Promise<Float32Array> {
try {
const embedding = await ipcRenderer.invoke(
"compute-text-embedding",
model,
text
text,
);
return embedding;
} catch (err) {

View file

@ -36,9 +36,4 @@ export const getPlatform = async (): Promise<"mac" | "windows" | "linux"> => {
}
};
export {
getSentryUserID,
logToDisk,
openLogDirectory,
updateOptOutOfCrashReports,
} from "../services/logging";
export { logToDisk, openLogDirectory } from "../services/logging";

View file

@ -2,7 +2,6 @@ import { logError } from "../services/logging";
import { keysStore } from "../stores/keys.store";
import { safeStorageStore } from "../stores/safeStorage.store";
import { uploadStatusStore } from "../stores/upload.store";
import { userPreferencesStore } from "../stores/userPreferences.store";
import { watchStore } from "../stores/watch.store";
export const clearElectronStore = () => {
@ -11,7 +10,6 @@ export const clearElectronStore = () => {
keysStore.clear();
safeStorageStore.clear();
watchStore.clear();
userPreferencesStore.delete("optOutOfCrashReports");
} catch (e) {
logError(e, "error while clearing electron store");
throw e;

View file

@ -13,7 +13,7 @@ export const checkExistsAndCreateDir = async (dirPath: string) => {
export const saveStreamToDisk = async (
filePath: string,
fileStream: ReadableStream<Uint8Array>
fileStream: ReadableStream<Uint8Array>,
) => {
await writeStream(filePath, fileStream);
};

View file

@ -8,7 +8,7 @@ export async function runFFmpegCmd(
cmd: string[],
inputFile: File | ElectronFile,
outputFileName: string,
dontTimeout?: boolean
dontTimeout?: boolean,
) {
let inputFilePath = null;
let createdTempInputFile = null;
@ -16,7 +16,7 @@ export async function runFFmpegCmd(
if (!existsSync(inputFile.path)) {
const tempFilePath = await ipcRenderer.invoke(
"get-temp-file-path",
inputFile.name
inputFile.name,
);
await writeStream(tempFilePath, await inputFile.stream());
inputFilePath = tempFilePath;
@ -29,7 +29,7 @@ export async function runFFmpegCmd(
cmd,
inputFilePath,
outputFileName,
dontTimeout
dontTimeout,
);
return new File([outputFileData], outputFileName);
} finally {

View file

@ -8,7 +8,7 @@ import { isPlatform } from "../utils/common/platform";
export async function convertToJPEG(
fileData: Uint8Array,
filename: string
filename: string,
): Promise<Uint8Array> {
if (isPlatform("windows")) {
throw Error(CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED);
@ -16,7 +16,7 @@ export async function convertToJPEG(
const convertedFileData = await ipcRenderer.invoke(
"convert-to-jpeg",
fileData,
filename
filename,
);
return convertedFileData;
}
@ -24,20 +24,20 @@ export async function convertToJPEG(
export async function generateImageThumbnail(
inputFile: File | ElectronFile,
maxDimension: number,
maxSize: number
maxSize: number,
): Promise<Uint8Array> {
let inputFilePath = null;
let createdTempInputFile = null;
try {
if (isPlatform("windows")) {
throw Error(
CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED
CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED,
);
}
if (!existsSync(inputFile.path)) {
const tempFilePath = await ipcRenderer.invoke(
"get-temp-file-path",
inputFile.name
inputFile.name,
);
await writeStream(tempFilePath, await inputFile.stream());
inputFilePath = tempFilePath;
@ -49,7 +49,7 @@ export async function generateImageThumbnail(
"generate-image-thumbnail",
inputFilePath,
maxDimension,
maxSize
maxSize,
);
return thumbnail;
} finally {

View file

@ -6,7 +6,7 @@ export async function setEncryptionKey(encryptionKey: string) {
try {
const encryptedKey: Buffer = await ipcRenderer.invoke(
"safeStorage-encrypt",
encryptionKey
encryptionKey,
);
const b64EncryptedKey = Buffer.from(encryptedKey).toString("base64");
safeStorageStore.set("encryptionKey", b64EncryptedKey);
@ -21,7 +21,7 @@ export async function getEncryptionKey(): Promise<string> {
const b64EncryptedKey = safeStorageStore.get("encryptionKey");
if (b64EncryptedKey) {
const keyBuffer = new Uint8Array(
Buffer.from(b64EncryptedKey, "base64")
Buffer.from(b64EncryptedKey, "base64"),
);
return await ipcRenderer.invoke("safeStorage-decrypt", keyBuffer);
}

View file

@ -9,7 +9,7 @@ export const reloadWindow = () => {
};
export const registerUpdateEventListener = (
showUpdateDialog: (updateInfo: AppUpdateInfo) => void
showUpdateDialog: (updateInfo: AppUpdateInfo) => void,
) => {
ipcRenderer.removeAllListeners("show-update-dialog");
ipcRenderer.on("show-update-dialog", (_, updateInfo: AppUpdateInfo) => {

View file

@ -39,7 +39,7 @@ export const getPendingUploads = async () => {
export const showUploadDirsDialog = async () => {
try {
const filePaths: string[] = await ipcRenderer.invoke(
"show-upload-dirs-dialog"
"show-upload-dirs-dialog",
);
const files = await Promise.all(filePaths.map(getElectronFile));
return files;
@ -51,7 +51,7 @@ export const showUploadDirsDialog = async () => {
export const showUploadFilesDialog = async () => {
try {
const filePaths: string[] = await ipcRenderer.invoke(
"show-upload-files-dialog"
"show-upload-files-dialog",
);
const files = await Promise.all(filePaths.map(getElectronFile));
return files;
@ -63,7 +63,7 @@ export const showUploadFilesDialog = async () => {
export const showUploadZipDialog = async () => {
try {
const filePaths: string[] = await ipcRenderer.invoke(
"show-upload-zip-dialog"
"show-upload-zip-dialog",
);
let files: ElectronFile[] = [];

View file

@ -9,7 +9,7 @@ import { isMappingPresent } from "../utils/watch";
export async function addWatchMapping(
rootFolderName: string,
folderPath: string,
uploadStrategy: number
uploadStrategy: number,
) {
ElectronLog.log(`Adding watch mapping: ${folderPath}`);
const watchMappings = getWatchMappings();
@ -35,7 +35,7 @@ export async function addWatchMapping(
export async function removeWatchMapping(folderPath: string) {
let watchMappings = getWatchMappings();
const watchMapping = watchMappings.find(
(mapping) => mapping.folderPath === folderPath
(mapping) => mapping.folderPath === folderPath,
);
if (!watchMapping) {
@ -47,7 +47,7 @@ export async function removeWatchMapping(folderPath: string) {
});
watchMappings = watchMappings.filter(
(mapping) => mapping.folderPath !== watchMapping.folderPath
(mapping) => mapping.folderPath !== watchMapping.folderPath,
);
setWatchMappings(watchMappings);
@ -55,11 +55,11 @@ export async function removeWatchMapping(folderPath: string) {
export function updateWatchMappingSyncedFiles(
folderPath: string,
files: WatchMapping["syncedFiles"]
files: WatchMapping["syncedFiles"],
): void {
const watchMappings = getWatchMappings();
const watchMapping = watchMappings.find(
(mapping) => mapping.folderPath === folderPath
(mapping) => mapping.folderPath === folderPath,
);
if (!watchMapping) {
@ -72,11 +72,11 @@ export function updateWatchMappingSyncedFiles(
export function updateWatchMappingIgnoredFiles(
folderPath: string,
files: WatchMapping["ignoredFiles"]
files: WatchMapping["ignoredFiles"],
): void {
const watchMappings = getWatchMappings();
const watchMapping = watchMappings.find(
(mapping) => mapping.folderPath === folderPath
(mapping) => mapping.folderPath === folderPath,
);
if (!watchMapping) {
@ -90,7 +90,7 @@ export function updateWatchMappingIgnoredFiles(
export function registerWatcherFunctions(
addFile: (file: ElectronFile) => Promise<void>,
removeFile: (path: string) => Promise<void>,
removeFolder: (folderPath: string) => Promise<void>
removeFolder: (folderPath: string) => Promise<void>,
) {
ipcRenderer.removeAllListeners("watch-add");
ipcRenderer.removeAllListeners("watch-change");

View file

@ -1,20 +0,0 @@
const PROD_HOST_URL: string = "ente://app";
const RENDERER_OUTPUT_DIR: string = "./out";
const LOG_FILENAME = "ente.log";
const MAX_LOG_SIZE = 50 * 1024 * 1024; // 50MB
const FILE_STREAM_CHUNK_SIZE: number = 4 * 1024 * 1024;
const SENTRY_DSN = "https://759d8498487a81ac33a0c2efa2a42c4f@sentry.ente.io/9";
const RELEASE_VERSION = require("../../package.json").version;
export {
PROD_HOST_URL,
RENDERER_OUTPUT_DIR,
FILE_STREAM_CHUNK_SIZE,
LOG_FILENAME,
MAX_LOG_SIZE,
SENTRY_DSN,
RELEASE_VERSION,
};

View file

@ -1,7 +1,7 @@
import { app, BrowserWindow } from "electron";
import electronReload from "electron-reload";
import serveNextAt from "next-electron-server";
import { initWatcher } from "./services/chokidar";
import { initSentry } from "./services/sentry";
import { getOptOutOfCrashReports } from "./services/userPreference";
import { isDev } from "./utils/common";
import { addAllowOriginHeader } from "./utils/cors";
import { createWindow } from "./utils/createWindow";
@ -16,9 +16,7 @@ import {
handleUpdates,
logSystemInfo,
setupMacWindowOnDockIconClick,
setupMainHotReload,
setupMainMenu,
setupNextElectronServe,
setupTrayItem,
} from "./utils/main";
import { setupMainProcessStatsLogger } from "./utils/processStats";
@ -29,8 +27,6 @@ let appIsQuitting = false;
let updateIsAvailable = false;
let optedOutOfCrashReports = false;
export const isAppQuitting = (): boolean => {
return appIsQuitting;
};
@ -42,22 +38,48 @@ export const setIsAppQuitting = (value: boolean): void => {
export const isUpdateAvailable = (): boolean => {
return updateIsAvailable;
};
export const setIsUpdateAvailable = (value: boolean): void => {
updateIsAvailable = value;
};
export const hasOptedOutOfCrashReports = (): boolean => {
return optedOutOfCrashReports;
/**
* Hot reload the main process if anything changes in the source directory that
* we're running from.
*
* In particular, this gets triggered when the `tsc -w` rebuilds JS files in the
* `app/` directory when we change the TS files in the `src/` directory.
*/
const setupMainHotReload = () => {
if (isDev) {
electronReload(__dirname, {});
}
};
export const updateOptOutOfCrashReports = (value: boolean): void => {
optedOutOfCrashReports = value;
/**
* The URL where the renderer HTML is being served from.
*/
export const rendererURL = "next://app";
/**
* next-electron-server allows up to directly use the output of `next build` in
* production mode and `next dev` in development mode, whilst keeping the rest
* of our code the same.
*
* It uses protocol handlers to serve files from the "next://app" protocol
*
* - In development this is proxied to http://localhost:3000
* - In production it serves files from the `/out` directory
*
* For more details, see this comparison:
* https://github.com/HaNdTriX/next-electron-server/issues/5
*/
const setupRendererServer = () => {
serveNextAt(rendererURL);
};
setupMainHotReload();
setupNextElectronServe();
setupRendererServer();
setupLogging(isDev);
const gotTheLock = app.requestSingleInstanceLock();
@ -83,11 +105,6 @@ if (!gotTheLock) {
app.on("ready", async () => {
logSystemInfo();
setupMainProcessStatsLogger();
const hasOptedOutOfCrashReports = getOptOutOfCrashReports();
updateOptOutOfCrashReports(hasOptedOutOfCrashReports);
if (!hasOptedOutOfCrashReports) {
initSentry();
}
mainWindow = await createWindow();
const tray = setupTrayItem(mainWindow);
const watcher = initWatcher(mainWindow);

View file

@ -8,12 +8,10 @@ import { computeImageEmbedding, computeTextEmbedding } from "./api/clip";
import {
getAppVersion,
getPlatform,
getSentryUserID,
logToDisk,
openDirectory,
openLogDirectory,
selectDirectory,
updateOptOutOfCrashReports,
} from "./api/common";
import { clearElectronStore } from "./api/electronStore";
import {
@ -61,13 +59,11 @@ import {
updateWatchMappingSyncedFiles,
} from "./api/watch";
import { setupLogging } from "./utils/logging";
import { fixHotReloadNext12 } from "./utils/preload";
import {
logRendererProcessMemoryUsage,
setupRendererProcessStatsLogger,
} from "./utils/processStats";
fixHotReloadNext12();
setupLogging();
setupRendererProcessStatsLogger();
@ -108,7 +104,6 @@ windowObject["ElectronAPIs"] = {
registerUpdateEventListener,
updateAndRestart,
skipAppUpdate,
getSentryUserID,
getAppVersion,
runFFmpegCmd,
muteUpdateNotification,
@ -120,7 +115,6 @@ windowObject["ElectronAPIs"] = {
deleteFolder,
rename,
deleteFile,
updateOptOutOfCrashReports,
computeImageEmbedding,
computeTextEmbedding,
getPlatform,

View file

@ -25,7 +25,7 @@ export function setupAutoUpdater(mainWindow: BrowserWindow) {
checkForUpdateAndNotify(mainWindow);
setInterval(
() => checkForUpdateAndNotify(mainWindow),
ONE_DAY_IN_MICROSECOND
ONE_DAY_IN_MICROSECOND,
);
}
@ -47,7 +47,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
if (
compareVersions(
updateCheckResult.updateInfo.version,
app.getVersion()
app.getVersion(),
) <= 0
) {
log.debug("already at latest version");
@ -60,7 +60,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
) {
log.info(
"user chose to skip version ",
updateCheckResult.updateInfo.version
updateCheckResult.updateInfo.version,
);
return;
}
@ -70,7 +70,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
isPlatform("mac") &&
compareVersions(
updateCheckResult.updateInfo.version,
desktopCutoffVersion
desktopCutoffVersion,
) > 0
) {
log.debug("auto update not possible due to key change");
@ -91,7 +91,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
) {
log.info(
"user chose to mute update notification for version ",
updateCheckResult.updateInfo.version
updateCheckResult.updateInfo.version,
);
return;
}
@ -102,7 +102,7 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
autoUpdatable: true,
version: updateCheckResult.updateInfo.version,
}),
FIVE_MIN_IN_MICROSECOND
FIVE_MIN_IN_MICROSECOND,
);
});
autoUpdater.on("error", (error) => {
@ -152,7 +152,7 @@ async function getDesktopCutoffVersion() {
function showUpdateDialog(
mainWindow: BrowserWindow,
updateInfo: AppUpdateInfo
updateInfo: AppUpdateInfo,
) {
mainWindow.webContents.send("show-update-dialog", updateInfo);
}

View file

@ -103,7 +103,7 @@ export async function getClipImageModelPath(type: "ggml" | "onnx") {
log.info("clip image model not found, downloading");
imageModelDownloadInProgress = downloadModel(
modelSavePath,
IMAGE_MODEL_DOWNLOAD_URL[type]
IMAGE_MODEL_DOWNLOAD_URL[type],
);
await imageModelDownloadInProgress;
} else {
@ -111,11 +111,11 @@ export async function getClipImageModelPath(type: "ggml" | "onnx") {
if (localFileSize !== IMAGE_MODEL_SIZE_IN_BYTES[type]) {
log.info(
"clip image model size mismatch, downloading again got:",
localFileSize
localFileSize,
);
imageModelDownloadInProgress = downloadModel(
modelSavePath,
IMAGE_MODEL_DOWNLOAD_URL[type]
IMAGE_MODEL_DOWNLOAD_URL[type],
);
await imageModelDownloadInProgress;
}
@ -150,7 +150,7 @@ export async function getClipTextModelPath(type: "ggml" | "onnx") {
if (localFileSize !== TEXT_MODEL_SIZE_IN_BYTES[type]) {
log.info(
"clip text model size mismatch, downloading again got:",
localFileSize
localFileSize,
);
textModelDownloadInProgress = true;
downloadModel(modelSavePath, TEXT_MODEL_DOWNLOAD_URL[type])
@ -212,7 +212,7 @@ function getTokenizer() {
export async function computeImageEmbedding(
model: Model,
inputFilePath: string
inputFilePath: string,
): Promise<Float32Array> {
if (!existsSync(inputFilePath)) {
throw Error(CustomErrors.INVALID_FILE_PATH);
@ -227,7 +227,7 @@ export async function computeImageEmbedding(
}
export async function computeGGMLImageEmbedding(
inputFilePath: string
inputFilePath: string,
): Promise<Float32Array> {
try {
const clipModelPath = await getClipImageModelPath("ggml");
@ -263,7 +263,7 @@ export async function computeGGMLImageEmbedding(
}
export async function computeONNXImageEmbedding(
inputFilePath: string
inputFilePath: string,
): Promise<Float32Array> {
try {
const imageSession = await getOnnxImageSession();
@ -277,7 +277,7 @@ export async function computeONNXImageEmbedding(
log.info(
`onnx image embedding time: ${Date.now() - t1} ms (prep:${
t2 - t1
} ms, extraction: ${Date.now() - t2} ms)`
} ms, extraction: ${Date.now() - t2} ms)`,
);
const imageEmbedding = results["output"].data; // Float32Array
return normalizeEmbedding(imageEmbedding);
@ -289,7 +289,7 @@ export async function computeONNXImageEmbedding(
export async function computeTextEmbedding(
model: Model,
text: string
text: string,
): Promise<Float32Array> {
if (model === Model.GGML_CLIP) {
return await computeGGMLTextEmbedding(text);
@ -299,7 +299,7 @@ export async function computeTextEmbedding(
}
export async function computeGGMLTextEmbedding(
text: string
text: string,
): Promise<Float32Array> {
try {
const clipModelPath = await getClipTextModelPath("ggml");
@ -339,7 +339,7 @@ export async function computeGGMLTextEmbedding(
}
export async function computeONNXTextEmbedding(
text: string
text: string,
): Promise<Float32Array> {
try {
const imageSession = await getOnnxTextSession();
@ -354,7 +354,7 @@ export async function computeONNXTextEmbedding(
log.info(
`onnx text embedding time: ${Date.now() - t1} ms (prep:${
t2 - t1
} ms, extraction: ${Date.now() - t2} ms)`
} ms, extraction: ${Date.now() - t2} ms)`,
);
const textEmbedding = results["output"].data; // Float32Array
return normalizeEmbedding(textEmbedding);
@ -444,7 +444,7 @@ async function getRGBData(inputFilePath: string) {
export const computeClipMatchScore = async (
imageEmbedding: Float32Array,
textEmbedding: Float32Array
textEmbedding: Float32Array,
) => {
if (imageEmbedding.length !== textEmbedding.length) {
throw Error("imageEmbedding and textEmbedding length mismatch");

View file

@ -11,7 +11,7 @@ const DEFAULT_CACHE_LIMIT = 1000 * 1000 * 1000; // 1GB
export class DiskCache implements LimitedCache {
constructor(
private cacheBucketDir: string,
private cacheLimit = DEFAULT_CACHE_LIMIT
private cacheLimit = DEFAULT_CACHE_LIMIT,
) {}
async put(cacheKey: string, response: Response): Promise<void> {
@ -19,13 +19,13 @@ export class DiskCache implements LimitedCache {
await writeStream(cachePath, response.body);
DiskLRUService.enforceCacheSizeLimit(
this.cacheBucketDir,
this.cacheLimit
this.cacheLimit,
);
}
async match(
cacheKey: string,
{ sizeInBytes }: { sizeInBytes?: number } = {}
{ sizeInBytes }: { sizeInBytes?: number } = {},
): Promise<Response> {
const cachePath = path.join(this.cacheBucketDir, cacheKey);
if (existsSync(cachePath)) {
@ -33,7 +33,7 @@ export class DiskCache implements LimitedCache {
if (sizeInBytes && fileStats.size !== sizeInBytes) {
logError(
Error(),
"Cache key exists but size does not match. Deleting cache key."
"Cache key exists but size does not match. Deleting cache key.",
);
unlink(cachePath).catch((e) => {
if (e.code === "ENOENT") return;
@ -47,14 +47,14 @@ export class DiskCache implements LimitedCache {
// add fallback for old cache keys
const oldCachePath = getOldAssetCachePath(
this.cacheBucketDir,
cacheKey
cacheKey,
);
if (existsSync(oldCachePath)) {
const fileStats = await stat(oldCachePath);
if (sizeInBytes && fileStats.size !== sizeInBytes) {
logError(
Error(),
"Old cache key exists but size does not match. Deleting cache key."
"Old cache key exists but size does not match. Deleting cache key.",
);
unlink(oldCachePath).catch((e) => {
if (e.code === "ENOENT") return;

View file

@ -60,7 +60,7 @@ class DiskLRUService {
if (e.code !== "ENOENT") {
logError(
e,
"Failed to evict least recently used"
"Failed to evict least recently used",
);
}
// ignoring the error, as it would get retried on the next run
@ -77,7 +77,7 @@ class DiskLRUService {
private async findLeastRecentlyUsed(
dir: string,
result?: LeastRecentlyUsedResult
result?: LeastRecentlyUsedResult,
): Promise<LeastRecentlyUsedResult> {
result = result || { atime: new Date(), path: "" };

View file

@ -24,7 +24,7 @@ export async function runFFmpegCmd(
cmd: string[],
inputFilePath: string,
outputFileName: string,
dontTimeout = false
dontTimeout = false,
) {
let tempOutputFilePath: string;
try {
@ -49,7 +49,7 @@ export async function runFFmpegCmd(
} else {
await promiseWithTimeout(
execAsync(escapedCmd),
FFMPEG_EXECUTION_WAIT_TIME
FFMPEG_EXECUTION_WAIT_TIME,
);
}
if (!existsSync(tempOutputFilePath)) {
@ -59,7 +59,7 @@ export async function runFFmpegCmd(
"ffmpeg command execution time ",
escapedCmd,
Date.now() - startTime,
"ms"
"ms",
);
const outputFile = await readFile(tempOutputFilePath);
@ -87,7 +87,7 @@ export async function deleteTempFile(tempFilePath: string) {
if (!tempFilePath.startsWith(tempDirPath)) {
logErrorSentry(
Error("not a temp file"),
"tried to delete a non temp file"
"tried to delete a non temp file",
);
}
rmSync(tempFilePath, { force: true });

View file

@ -3,10 +3,11 @@ import StreamZip from "node-stream-zip";
import path from "path";
import * as fs from "promise-fs";
import { Readable } from "stream";
import { FILE_STREAM_CHUNK_SIZE } from "../config";
import { ElectronFile } from "../types";
import { logError } from "./logging";
const FILE_STREAM_CHUNK_SIZE: number = 4 * 1024 * 1024;
// https://stackoverflow.com/a/63111390
export const getDirFilePaths = async (dirPath: string) => {
if (!(await fs.stat(dirPath)).isDirectory()) {
@ -37,7 +38,7 @@ export const getFileStream = async (filePath: string) => {
buff,
0,
FILE_STREAM_CHUNK_SIZE,
offset
offset,
)) as unknown as number;
offset += bytesRead;
if (bytesRead === 0) {
@ -102,7 +103,7 @@ export const getValidPaths = (paths: string[]) => {
export const getZipFileStream = async (
zip: StreamZip.StreamZipAsync,
filePath: string
filePath: string,
) => {
const stream = await zip.stream(filePath);
const done = {
@ -203,7 +204,7 @@ export async function isFolder(dirPath: string) {
}
export const convertBrowserStreamToNode = (
fileStream: ReadableStream<Uint8Array>
fileStream: ReadableStream<Uint8Array>,
) => {
const reader = fileStream.getReader();
const rs = new Readable();
@ -228,7 +229,7 @@ export const convertBrowserStreamToNode = (
export async function writeNodeStream(
filePath: string,
fileStream: NodeJS.ReadableStream
fileStream: NodeJS.ReadableStream,
) {
const writeable = fs.createWriteStream(filePath);
@ -251,7 +252,7 @@ export async function writeNodeStream(
export async function writeStream(
filePath: string,
fileStream: ReadableStream<Uint8Array>
fileStream: ReadableStream<Uint8Array>,
) {
const readable = convertBrowserStreamToNode(fileStream);
await writeNodeStream(filePath, readable);
@ -266,7 +267,7 @@ export async function readTextFile(filePath: string) {
export async function moveFile(
sourcePath: string,
destinationPath: string
destinationPath: string,
): Promise<void> {
if (!existsSync(sourcePath)) {
throw new Error("File does not exist");

View file

@ -80,7 +80,7 @@ function getImageMagickStaticPath() {
export async function convertToJPEG(
fileData: Uint8Array,
filename: string
filename: string,
): Promise<Uint8Array> {
let tempInputFilePath: string;
let tempOutputFilePath: string;
@ -96,7 +96,7 @@ export async function convertToJPEG(
throw new Error("heic convert output file not found");
}
const convertedFileData = new Uint8Array(
await readFile(tempOutputFilePath)
await readFile(tempOutputFilePath),
);
return convertedFileData;
} catch (e) {
@ -118,11 +118,11 @@ export async function convertToJPEG(
async function runConvertCommand(
tempInputFilePath: string,
tempOutputFilePath: string
tempOutputFilePath: string,
) {
const convertCmd = constructConvertCommand(
tempInputFilePath,
tempOutputFilePath
tempOutputFilePath,
);
const escapedCmd = shellescape(convertCmd);
log.info("running convert command: " + escapedCmd);
@ -131,7 +131,7 @@ async function runConvertCommand(
function constructConvertCommand(
tempInputFilePath: string,
tempOutputFilePath: string
tempOutputFilePath: string,
) {
let convertCmd: string[];
if (isPlatform("mac")) {
@ -157,7 +157,7 @@ function constructConvertCommand(
return tempOutputFilePath;
}
return cmdPart;
}
},
);
} else {
throw Error(CustomErrors.INVALID_OS(process.platform));
@ -168,7 +168,7 @@ function constructConvertCommand(
export async function generateImageThumbnail(
inputFilePath: string,
width: number,
maxSize: number
maxSize: number,
): Promise<Uint8Array> {
let tempOutputFilePath: string;
let quality = MAX_QUALITY;
@ -180,7 +180,7 @@ export async function generateImageThumbnail(
inputFilePath,
tempOutputFilePath,
width,
quality
quality,
);
if (!existsSync(tempOutputFilePath)) {
@ -206,14 +206,14 @@ async function runThumbnailGenerationCommand(
inputFilePath: string,
tempOutputFilePath: string,
maxDimension: number,
quality: number
quality: number,
) {
const thumbnailGenerationCmd: string[] =
constructThumbnailGenerationCommand(
inputFilePath,
tempOutputFilePath,
maxDimension,
quality
quality,
);
const escapedCmd = shellescape(thumbnailGenerationCmd);
log.info("running thumbnail generation command: " + escapedCmd);
@ -223,7 +223,7 @@ function constructThumbnailGenerationCommand(
inputFilePath: string,
tempOutputFilePath: string,
maxDimension: number,
quality: number
quality: number,
) {
let thumbnailGenerationCmd: string[];
if (isPlatform("mac")) {
@ -242,7 +242,7 @@ function constructThumbnailGenerationCommand(
return quality.toString();
}
return cmdPart;
}
},
);
} else if (isPlatform("linux")) {
thumbnailGenerationCmd =
@ -259,13 +259,13 @@ function constructThumbnailGenerationCommand(
if (cmdPart.includes(SAMPLE_SIZE_PLACEHOLDER)) {
return cmdPart.replaceAll(
SAMPLE_SIZE_PLACEHOLDER,
(2 * maxDimension).toString()
(2 * maxDimension).toString(),
);
}
if (cmdPart.includes(MAX_DIMENSION_PLACEHOLDER)) {
return cmdPart.replaceAll(
MAX_DIMENSION_PLACEHOLDER,
maxDimension.toString()
maxDimension.toString(),
);
}
if (cmdPart === QUALITY_PLACEHOLDER) {

View file

@ -12,11 +12,3 @@ export function openLogDirectory() {
export function logError(error: Error, message: string, info?: string): void {
ipcRenderer.invoke("log-error", error, message, info);
}
export function getSentryUserID(): Promise<string> {
return ipcRenderer.invoke("get-sentry-id");
}
export function updateOptOutOfCrashReports(optOut: boolean) {
return ipcRenderer.invoke("update-opt-out-crash-reports", optOut);
}

View file

@ -1,68 +1,18 @@
import * as Sentry from "@sentry/electron/dist/main";
import { RELEASE_VERSION, SENTRY_DSN } from "../config";
import { hasOptedOutOfCrashReports } from "../main";
import { keysStore } from "../stores/keys.store";
import { isDev } from "../utils/common";
import { makeID } from "../utils/logging";
import { logToDisk } from "./logging";
const ENV_DEVELOPMENT = "development";
const isDEVSentryENV = () =>
process.env.NEXT_PUBLIC_SENTRY_ENV === ENV_DEVELOPMENT;
export function initSentry(): void {
Sentry.init({
dsn: SENTRY_DSN,
release: RELEASE_VERSION,
environment: isDev ? "development" : "production",
});
Sentry.setUser({ id: getSentryUserID() });
}
/** Deprecated, but no alternative yet */
export function logErrorSentry(
error: any,
msg: string,
info?: Record<string, unknown>
info?: Record<string, unknown>,
) {
const err = errorWithContext(error, msg);
logToDisk(
`error: ${error?.name} ${error?.message} ${
error?.stack
} msg: ${msg} info: ${JSON.stringify(info)}`
} msg: ${msg} info: ${JSON.stringify(info)}`,
);
if (isDEVSentryENV()) {
if (isDev) {
console.log(error, { msg, info });
}
if (hasOptedOutOfCrashReports()) {
return;
}
Sentry.captureException(err, {
level: Sentry.Severity.Info,
user: { id: getSentryUserID() },
contexts: {
...(info && {
info: info,
}),
rootCause: { message: error?.message },
},
});
}
function errorWithContext(originalError: Error, context: string) {
const errorWithContext = new Error(context);
errorWithContext.stack =
errorWithContext.stack.split("\n").slice(2, 4).join("\n") +
"\n" +
originalError.stack;
return errorWithContext;
}
export function getSentryUserID() {
let anonymizeUserID = keysStore.get("AnonymizeUserID")?.id;
if (!anonymizeUserID) {
anonymizeUserID = makeID(6);
keysStore.set("AnonymizeUserID", { id: anonymizeUserID });
}
return anonymizeUserID;
}

View file

@ -7,7 +7,7 @@ import { getValidPaths, getZipFileStream } from "./fs";
export const getSavedFilePaths = (type: FILE_PATH_TYPE) => {
const paths =
getValidPaths(
uploadStatusStore.get(FILE_PATH_KEYS[type]) as string[]
uploadStatusStore.get(FILE_PATH_KEYS[type]) as string[],
) ?? [];
setToUploadFiles(type, paths);
@ -17,7 +17,7 @@ export const getSavedFilePaths = (type: FILE_PATH_TYPE) => {
export async function getZipEntryAsElectronFile(
zipName: string,
zip: StreamZip.StreamZipAsync,
entry: StreamZip.ZipEntry
entry: StreamZip.ZipEntry,
): Promise<ElectronFile> {
return {
path: path

View file

@ -24,14 +24,6 @@ export function setMuteUpdateNotificationVersion(version: string) {
userPreferencesStore.set("muteUpdateNotificationVersion", version);
}
export function getOptOutOfCrashReports() {
return userPreferencesStore.get("optOutOfCrashReports") ?? false;
}
export function setOptOutOfCrashReports(optOut: boolean) {
userPreferencesStore.set("optOutOfCrashReports", optOut);
}
export function clearSkipAppVersion() {
userPreferencesStore.delete("skipAppVersion");
}

View file

@ -11,9 +11,6 @@ const userPreferencesSchema: Schema<UserPreferencesType> = {
muteUpdateNotificationVersion: {
type: "string",
},
optOutOfCrashReports: {
type: "boolean",
},
customCacheDirectory: {
type: "string",
},

View file

@ -1,7 +1,7 @@
export interface LimitedCache {
match: (
key: string,
options?: { sizeInBytes?: number }
options?: { sizeInBytes?: number },
) => Promise<Response>;
put: (key: string, data: Response) => Promise<void>;
delete: (key: string) => Promise<boolean>;

View file

@ -58,7 +58,6 @@ export interface UserPreferencesType {
hideDockIcon: boolean;
skipAppVersion: string;
muteUpdateNotificationVersion: string;
optOutOfCrashReports: boolean;
customCacheDirectory: string;
}

View file

@ -1,20 +1,21 @@
# CLIP Byte Pair Encoding JavaScript Port
A JavaScript port of [OpenAI's CLIP byte-pair-encoding tokenizer](https://github.com/openai/CLIP/blob/3bee28119e6b28e75b82b811b87b56935314e6a5/clip/simple_tokenizer.py).
```js
import Tokenizer from "https://deno.land/x/clip_bpe@v0.0.6/mod.js";
let t = new Tokenizer();
t.encode("hello") // [3306]
t.encode("magnificent") // [10724]
t.encode("magnificently") // [9725, 2922]
t.decode(t.encode("HELLO")) // "hello "
t.decode(t.encode("abc123")) // "abc 1 2 3 "
t.decode(st.encode("let's see here")) // "let 's see here "
t.encode("hello world!") // [3306, 1002, 256]
t.encode("hello"); // [3306]
t.encode("magnificent"); // [10724]
t.encode("magnificently"); // [9725, 2922]
t.decode(t.encode("HELLO")); // "hello "
t.decode(t.encode("abc123")); // "abc 1 2 3 "
t.decode(st.encode("let's see here")); // "let 's see here "
t.encode("hello world!"); // [3306, 1002, 256]
// to encode for CLIP (trims to maximum of 77 tokens and adds start and end token, and pads with zeros if less than 77 tokens):
t.encodeForCLIP("hello world!") // [49406,3306,1002,256,49407,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
t.encodeForCLIP("hello world!"); // [49406,3306,1002,256,49407,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
```
This encoder/decoder behaves differently to the the GPT-2/3 tokenizer (JavaScript version of that [here](https://github.com/latitudegames/GPT-3-Encoder)). For example, it doesn't preserve capital letters, as shown above.

View file

@ -75,7 +75,7 @@ export default class {
constructor() {
this.byteEncoder = bytesToUnicode();
this.byteDecoder = Object.fromEntries(
Object.entries(this.byteEncoder).map(([k, v]) => [v, Number(k)])
Object.entries(this.byteEncoder).map(([k, v]) => [v, Number(k)]),
);
let merges = bpeVocabData.text.split("\n");
merges = merges.slice(1, 49152 - 256 - 2 + 1);
@ -346,10 +346,10 @@ export default class {
vocab.push("<|startoftext|>", "<|endoftext|>");
this.encoder = Object.fromEntries(vocab.map((v, i) => [v, i]));
this.decoder = Object.fromEntries(
Object.entries(this.encoder).map(([k, v]) => [v, k])
Object.entries(this.encoder).map(([k, v]) => [v, k]),
);
this.bpeRanks = Object.fromEntries(
mergedMerges.map((v, i) => [v.join("·😎·"), i])
mergedMerges.map((v, i) => [v.join("·😎·"), i]),
); // ·😎· because js doesn't yet have tuples
this.cache = {
"<|startoftext|>": "<|startoftext|>",
@ -436,7 +436,7 @@ export default class {
bpeTokens.push(
...this.bpe(token)
.split(" ")
.map((bpeToken: string) => this.encoder[bpeToken])
.map((bpeToken: string) => this.encoder[bpeToken]),
);
}
return bpeTokens;

View file

@ -4,7 +4,7 @@ export const isDev = !app.isPackaged;
export const promiseWithTimeout = async <T>(
request: Promise<T>,
timeout: number
timeout: number,
): Promise<T> => {
const timeoutRef: {
current: NodeJS.Timeout;
@ -12,7 +12,7 @@ export const promiseWithTimeout = async <T>(
const rejectOnTimeout = new Promise<null>((_, reject) => {
timeoutRef.current = setTimeout(
() => reject(Error(CustomErrors.WAIT_TIME_EXCEEDED)),
timeout
timeout,
);
});
const requestWithTimeOutCancellation = async () => {

View file

@ -16,6 +16,6 @@ export function addAllowOriginHeader(mainWindow: BrowserWindow) {
callback({
responseHeaders: details.responseHeaders,
});
}
},
);
}

View file

@ -1,8 +1,7 @@
import { app, BrowserWindow, nativeImage } from "electron";
import ElectronLog from "electron-log";
import * as path from "path";
import { PROD_HOST_URL } from "../config";
import { isAppQuitting } from "../main";
import { isAppQuitting, rendererURL } from "../main";
import autoLauncher from "../services/autoLauncher";
import { logErrorSentry } from "../services/sentry";
import { getHideDockIconPreference } from "../services/userPreference";
@ -41,21 +40,21 @@ export async function createWindow(): Promise<BrowserWindow> {
if (isDev) {
splash.loadFile(`../resources/splash.html`);
mainWindow.loadURL(PROD_HOST_URL);
mainWindow.loadURL(rendererURL);
// Open the DevTools.
mainWindow.webContents.openDevTools();
} else {
splash.loadURL(
`file://${path.join(process.resourcesPath, "splash.html")}`
`file://${path.join(process.resourcesPath, "splash.html")}`,
);
mainWindow.loadURL(PROD_HOST_URL);
mainWindow.loadURL(rendererURL);
}
mainWindow.webContents.on("did-fail-load", () => {
splash.close();
isDev
? mainWindow.loadFile(`../resources/error.html`)
: splash.loadURL(
`file://${path.join(process.resourcesPath, "error.html")}`
`file://${path.join(process.resourcesPath, "error.html")}`,
);
mainWindow.maximize();
mainWindow.show();
@ -76,7 +75,7 @@ export async function createWindow(): Promise<BrowserWindow> {
logErrorSentry(
Error("render-process-gone"),
"webContents event render-process-gone",
{ details }
{ details },
);
ElectronLog.log("webContents event render-process-gone", details);
});

View file

@ -9,7 +9,7 @@ export const parseExecError = (err: any) => {
if (errMessage.includes("Bad CPU type in executable")) {
return CustomErrors.UNSUPPORTED_PLATFORM(
process.platform,
process.arch
process.arch,
);
} else {
return errMessage;

View file

@ -10,7 +10,6 @@ import {
Tray,
} from "electron";
import path from "path";
import { updateOptOutOfCrashReports } from "../main";
import {
getAppVersion,
muteUpdateNotification,
@ -27,11 +26,10 @@ import {
convertToJPEG,
generateImageThumbnail,
} from "../services/imageProcessor";
import { getSentryUserID, logErrorSentry } from "../services/sentry";
import { logErrorSentry } from "../services/sentry";
import {
getCustomCacheDirectory,
setCustomCacheDirectory,
setOptOutOfCrashReports,
} from "../services/userPreference";
import { getPlatform } from "./common/platform";
import { createWindow } from "./createWindow";
@ -40,7 +38,7 @@ import { generateTempFilePath } from "./temp";
export default function setupIpcComs(
tray: Tray,
mainWindow: BrowserWindow,
watcher: chokidar.FSWatcher
watcher: chokidar.FSWatcher,
): void {
ipcMain.handle("select-dir", async () => {
const result = await dialog.showOpenDialog({
@ -146,9 +144,6 @@ export default function setupIpcComs(
ipcMain.on("mute-update-notification", (_, version) => {
muteUpdateNotification(version);
});
ipcMain.handle("get-sentry-id", () => {
return getSentryUserID();
});
ipcMain.handle("get-app-version", () => {
return getAppVersion();
@ -161,9 +156,9 @@ export default function setupIpcComs(
cmd,
inputFilePath,
outputFileName,
dontTimeout
dontTimeout,
);
}
},
);
ipcMain.handle("get-temp-file-path", (_, formatSuffix) => {
return generateTempFilePath(formatSuffix);
@ -176,13 +171,9 @@ export default function setupIpcComs(
"generate-image-thumbnail",
(_, fileData, maxDimension, maxSize) => {
return generateImageThumbnail(fileData, maxDimension, maxSize);
}
},
);
ipcMain.handle("update-opt-out-crash-reports", (_, optOut) => {
setOptOutOfCrashReports(optOut);
updateOptOutOfCrashReports(optOut);
});
ipcMain.handle("compute-image-embedding", (_, model, inputFilePath) => {
return computeImageEmbedding(model, inputFilePath);
});

View file

@ -1,9 +1,8 @@
import log from "electron-log";
import { LOG_FILENAME, MAX_LOG_SIZE } from "../config";
export function setupLogging(isDev?: boolean) {
log.transports.file.fileName = LOG_FILENAME;
log.transports.file.maxSize = MAX_LOG_SIZE;
log.transports.file.fileName = "ente.log";
log.transports.file.maxSize = 50 * 1024 * 1024; // 50MB;
if (!isDev) {
log.transports.console.level = false;
}
@ -11,22 +10,9 @@ export function setupLogging(isDev?: boolean) {
"[{y}-{m}-{d}T{h}:{i}:{s}{z}] [{level}]{scope} {text}";
}
export function makeID(length: number) {
let result = "";
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(
Math.floor(Math.random() * charactersLength)
);
}
return result;
}
export function convertBytesToHumanReadable(
bytes: number,
precision = 2
precision = 2,
): string {
if (bytes === 0 || isNaN(bytes)) {
return "0 MB";

View file

@ -1,12 +1,10 @@
import { app, BrowserWindow, Menu, nativeImage, Tray } from "electron";
import ElectronLog from "electron-log";
import electronReload from "electron-reload";
import serveNextAt from "next-electron-server";
import os from "os";
import path from "path";
import { existsSync } from "promise-fs";
import util from "util";
import { PROD_HOST_URL, RENDERER_OUTPUT_DIR } from "../config";
import { rendererURL } from "../main";
import { setupAutoUpdater } from "../services/appUpdater";
import autoLauncher from "../services/autoLauncher";
import { getHideDockIconPreference } from "../services/userPreference";
@ -27,7 +25,7 @@ export function setupTrayItem(mainWindow: BrowserWindow) {
: "taskbar-icon.png";
const trayImgPath = path.join(
isDev ? "build" : process.resourcesPath,
iconName
iconName,
);
const trayIcon = nativeImage.createFromPath(trayImgPath);
const tray = new Tray(trayIcon);
@ -39,11 +37,22 @@ export function setupTrayItem(mainWindow: BrowserWindow) {
export function handleDownloads(mainWindow: BrowserWindow) {
mainWindow.webContents.session.on("will-download", (_, item) => {
item.setSavePath(
getUniqueSavePath(item.getFilename(), app.getPath("downloads"))
getUniqueSavePath(item.getFilename(), app.getPath("downloads")),
);
});
}
export function handleExternalLinks(mainWindow: BrowserWindow) {
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (!url.startsWith(rendererURL)) {
require("electron").shell.openExternal(url);
return { action: "deny" };
} else {
return { action: "allow" };
}
});
}
export function getUniqueSavePath(filename: string, directory: string): string {
let uniqueFileSavePath = path.join(directory, filename);
const { name: filenameWithoutExtension, ext: extension } =
@ -76,18 +85,6 @@ export async function setupMainMenu(mainWindow: BrowserWindow) {
Menu.setApplicationMenu(await buildMenuBar(mainWindow));
}
export function setupMainHotReload() {
if (isDev) {
electronReload(__dirname, {});
}
}
export function setupNextElectronServe() {
serveNextAt(PROD_HOST_URL, {
outputDir: RENDERER_OUTPUT_DIR,
});
}
export async function handleDockIconHideOnAutoLaunch() {
const shouldHideDockIcon = getHideDockIconPreference();
const wasAutoLaunched = await autoLauncher.wasAutoLaunched();
@ -110,17 +107,6 @@ export function logSystemInfo() {
ElectronLog.info({ appVersion });
}
export function handleExternalLinks(mainWindow: BrowserWindow) {
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (!url.startsWith(PROD_HOST_URL)) {
require("electron").shell.openExternal(url);
return { action: "deny" };
} else {
return { action: "allow" };
}
});
}
export async function checkIfInstalledViaBrew() {
if (!isPlatform("mac")) {
return false;

View file

@ -64,7 +64,7 @@ export async function buildMenuBar(mainWindow: BrowserWindow): Promise<Menu> {
label: "View Changelog",
click: () => {
shell.openExternal(
"https://github.com/ente-io/ente/blob/main/desktop/CHANGELOG.md"
"https://github.com/ente-io/ente/blob/main/desktop/CHANGELOG.md",
);
},
},

View file

@ -1,16 +0,0 @@
import { webFrame } from "electron";
export const fixHotReloadNext12 = () => {
webFrame.executeJavaScript(`Object.defineProperty(globalThis, 'WebSocket', {
value: new Proxy(WebSocket, {
construct: (Target, [url, protocols]) => {
if (url.endsWith('/_next/webpack-hmr')) {
// Fix the Next.js hmr client url
return new Target("ws://localhost:3000/_next/webpack-hmr", protocols)
} else {
return new Target(url, protocols)
}
}
})
});`);
};

View file

@ -16,11 +16,11 @@ const HIGH_RENDERER_MEMORY_USAGE_THRESHOLD_IN_KILOBYTES = 1024 * 1024; // 1 GB
async function logMainProcessStats() {
const processMemoryInfo = await getNormalizedProcessMemoryInfo(
await process.getProcessMemoryInfo()
await process.getProcessMemoryInfo(),
);
const cpuUsage = process.getCPUUsage();
const heapStatistics = getNormalizedHeapStatistics(
process.getHeapStatistics()
process.getHeapStatistics(),
);
ElectronLog.log("main process stats", {
@ -42,11 +42,11 @@ async function logSpikeMainMemoryUsage() {
const processMemoryInfo = await process.getProcessMemoryInfo();
const currentMemoryUsage = Math.max(
processMemoryInfo.residentSet ?? 0,
processMemoryInfo.private
processMemoryInfo.private,
);
const previousMemoryUsage = Math.max(
previousMainProcessMemoryInfo.residentSet ?? 0,
previousMainProcessMemoryInfo.private
previousMainProcessMemoryInfo.private,
);
const isSpiking =
currentMemoryUsage - previousMemoryUsage >=
@ -66,7 +66,7 @@ async function logSpikeMainMemoryUsage() {
await getNormalizedProcessMemoryInfo(previousMainProcessMemoryInfo);
const cpuUsage = process.getCPUUsage();
const heapStatistics = getNormalizedHeapStatistics(
process.getHeapStatistics()
process.getHeapStatistics(),
);
ElectronLog.log("reporting main memory usage spike", {
@ -94,12 +94,12 @@ async function logSpikeRendererMemoryUsage() {
const processMemoryInfo = await process.getProcessMemoryInfo();
const currentMemoryUsage = Math.max(
processMemoryInfo.residentSet ?? 0,
processMemoryInfo.private
processMemoryInfo.private,
);
const previousMemoryUsage = Math.max(
previousRendererProcessMemoryInfo.private,
previousRendererProcessMemoryInfo.residentSet ?? 0
previousRendererProcessMemoryInfo.residentSet ?? 0,
);
const isSpiking =
currentMemoryUsage - previousMemoryUsage >=
@ -117,11 +117,11 @@ async function logSpikeRendererMemoryUsage() {
await getNormalizedProcessMemoryInfo(processMemoryInfo);
const normalizedPreviousProcessMemoryInfo =
await getNormalizedProcessMemoryInfo(
previousRendererProcessMemoryInfo
previousRendererProcessMemoryInfo,
);
const cpuUsage = process.getCPUUsage();
const heapStatistics = getNormalizedHeapStatistics(
process.getHeapStatistics()
process.getHeapStatistics(),
);
ElectronLog.log("reporting renderer memory usage spike", {
@ -140,11 +140,11 @@ async function logSpikeRendererMemoryUsage() {
async function logRendererProcessStats() {
const blinkMemoryInfo = getNormalizedBlinkMemoryInfo();
const heapStatistics = getNormalizedHeapStatistics(
process.getHeapStatistics()
process.getHeapStatistics(),
);
const webFrameResourceUsage = getNormalizedWebFrameResourceUsage();
const processMemoryInfo = await getNormalizedProcessMemoryInfo(
await process.getProcessMemoryInfo()
await process.getProcessMemoryInfo(),
);
ElectronLog.log("renderer process stats", {
blinkMemoryInfo,
@ -157,7 +157,7 @@ async function logRendererProcessStats() {
export function setupMainProcessStatsLogger() {
setInterval(
logSpikeMainMemoryUsage,
SPIKE_DETECTION_INTERVAL_IN_MICROSECONDS
SPIKE_DETECTION_INTERVAL_IN_MICROSECONDS,
);
setInterval(logMainProcessStats, LOGGING_INTERVAL_IN_MICROSECONDS);
}
@ -165,7 +165,7 @@ export function setupMainProcessStatsLogger() {
export function setupRendererProcessStatsLogger() {
setInterval(
logSpikeRendererMemoryUsage,
SPIKE_DETECTION_INTERVAL_IN_MICROSECONDS
SPIKE_DETECTION_INTERVAL_IN_MICROSECONDS,
);
setInterval(logRendererProcessStats, LOGGING_INTERVAL_IN_MICROSECONDS);
}
@ -174,21 +174,21 @@ export async function logRendererProcessMemoryUsage(message: string) {
const processMemoryInfo = await process.getProcessMemoryInfo();
const processMemory = Math.max(
processMemoryInfo.private,
processMemoryInfo.residentSet ?? 0
processMemoryInfo.residentSet ?? 0,
);
ElectronLog.log(
"renderer ProcessMemory",
message,
convertBytesToHumanReadable(processMemory * 1024)
convertBytesToHumanReadable(processMemory * 1024),
);
}
const getNormalizedProcessMemoryInfo = async (
processMemoryInfo: Electron.ProcessMemoryInfo
processMemoryInfo: Electron.ProcessMemoryInfo,
) => {
return {
residentSet: convertBytesToHumanReadable(
processMemoryInfo.residentSet * 1024
processMemoryInfo.residentSet * 1024,
),
private: convertBytesToHumanReadable(processMemoryInfo.private * 1024),
shared: convertBytesToHumanReadable(processMemoryInfo.shared * 1024),
@ -199,40 +199,40 @@ const getNormalizedBlinkMemoryInfo = () => {
const blinkMemoryInfo = process.getBlinkMemoryInfo();
return {
allocated: convertBytesToHumanReadable(
blinkMemoryInfo.allocated * 1024
blinkMemoryInfo.allocated * 1024,
),
total: convertBytesToHumanReadable(blinkMemoryInfo.total * 1024),
};
};
const getNormalizedHeapStatistics = (
heapStatistics: Electron.HeapStatistics
heapStatistics: Electron.HeapStatistics,
) => {
return {
totalHeapSize: convertBytesToHumanReadable(
heapStatistics.totalHeapSize * 1024
heapStatistics.totalHeapSize * 1024,
),
totalHeapSizeExecutable: convertBytesToHumanReadable(
heapStatistics.totalHeapSizeExecutable * 1024
heapStatistics.totalHeapSizeExecutable * 1024,
),
totalPhysicalSize: convertBytesToHumanReadable(
heapStatistics.totalPhysicalSize * 1024
heapStatistics.totalPhysicalSize * 1024,
),
totalAvailableSize: convertBytesToHumanReadable(
heapStatistics.totalAvailableSize * 1024
heapStatistics.totalAvailableSize * 1024,
),
usedHeapSize: convertBytesToHumanReadable(
heapStatistics.usedHeapSize * 1024
heapStatistics.usedHeapSize * 1024,
),
heapSizeLimit: convertBytesToHumanReadable(
heapStatistics.heapSizeLimit * 1024
heapStatistics.heapSizeLimit * 1024,
),
mallocedMemory: convertBytesToHumanReadable(
heapStatistics.mallocedMemory * 1024
heapStatistics.mallocedMemory * 1024,
),
peakMallocedMemory: convertBytesToHumanReadable(
heapStatistics.peakMallocedMemory * 1024
heapStatistics.peakMallocedMemory * 1024,
),
doesZapGarbage: heapStatistics.doesZapGarbage,
};
@ -244,51 +244,51 @@ const getNormalizedWebFrameResourceUsage = () => {
images: {
count: webFrameResourceUsage.images.count,
size: convertBytesToHumanReadable(
webFrameResourceUsage.images.size
webFrameResourceUsage.images.size,
),
liveSize: convertBytesToHumanReadable(
webFrameResourceUsage.images.liveSize
webFrameResourceUsage.images.liveSize,
),
},
scripts: {
count: webFrameResourceUsage.scripts.count,
size: convertBytesToHumanReadable(
webFrameResourceUsage.scripts.size
webFrameResourceUsage.scripts.size,
),
liveSize: convertBytesToHumanReadable(
webFrameResourceUsage.scripts.liveSize
webFrameResourceUsage.scripts.liveSize,
),
},
cssStyleSheets: {
count: webFrameResourceUsage.cssStyleSheets.count,
size: convertBytesToHumanReadable(
webFrameResourceUsage.cssStyleSheets.size
webFrameResourceUsage.cssStyleSheets.size,
),
liveSize: convertBytesToHumanReadable(
webFrameResourceUsage.cssStyleSheets.liveSize
webFrameResourceUsage.cssStyleSheets.liveSize,
),
},
xslStyleSheets: {
count: webFrameResourceUsage.xslStyleSheets.count,
size: convertBytesToHumanReadable(
webFrameResourceUsage.xslStyleSheets.size
webFrameResourceUsage.xslStyleSheets.size,
),
liveSize: convertBytesToHumanReadable(
webFrameResourceUsage.xslStyleSheets.liveSize
webFrameResourceUsage.xslStyleSheets.liveSize,
),
},
fonts: {
count: webFrameResourceUsage.fonts.count,
size: convertBytesToHumanReadable(webFrameResourceUsage.fonts.size),
liveSize: convertBytesToHumanReadable(
webFrameResourceUsage.fonts.liveSize
webFrameResourceUsage.fonts.liveSize,
),
},
other: {
count: webFrameResourceUsage.other.count,
size: convertBytesToHumanReadable(webFrameResourceUsage.other.size),
liveSize: convertBytesToHumanReadable(
webFrameResourceUsage.other.liveSize
webFrameResourceUsage.other.liveSize,
),
},
};

View file

@ -21,7 +21,7 @@ function generateTempName(length: number) {
const charactersLength = CHARACTERS.length;
for (let i = 0; i < length; i++) {
result += CHARACTERS.charAt(
Math.floor(Math.random() * charactersLength)
Math.floor(Math.random() * charactersLength),
);
}
return result;

View file

@ -2,10 +2,10 @@ import { WatchMapping } from "../types";
export function isMappingPresent(
watchMappings: WatchMapping[],
folderPath: string
folderPath: string,
) {
const watchMapping = watchMappings?.find(
(mapping) => mapping.folderPath === folderPath
(mapping) => mapping.folderPath === folderPath,
);
return !!watchMapping;
}

@ -1 +0,0 @@
Subproject commit a88030295c89dd8f43d9e3a45025678d95c78a45

View file

@ -3,8 +3,8 @@
"target": "es2021",
"module": "commonjs",
"esModuleInterop": true,
/* Emit the generated JS into build/app */
"outDir": "build/app",
/* Emit the generated JS into app */
"outDir": "app",
"noImplicitAny": true,
"sourceMap": true,
"baseUrl": "src",

View file

@ -188,110 +188,6 @@
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
"@sentry/browser@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.7.1.tgz#e01144a08984a486ecc91d7922cc457e9c9bd6b7"
integrity sha512-R5PYx4TTvifcU790XkK6JVGwavKaXwycDU0MaAwfc4Vf7BLm5KCNJCsDySu1RPAap/017MVYf54p6dWvKiRviA==
dependencies:
"@sentry/core" "6.7.1"
"@sentry/types" "6.7.1"
"@sentry/utils" "6.7.1"
tslib "^1.9.3"
"@sentry/cli@^1.68.0":
version "1.74.4"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.4.tgz#7df82f68045a155e1885bfcbb5d303e5259eb18e"
integrity sha512-BMfzYiedbModsNBJlKeBOLVYUtwSi99LJ8gxxE4Bp5N8hyjNIN0WVrozAVZ27mqzAuy6151Za3dpmOLO86YlGw==
dependencies:
https-proxy-agent "^5.0.0"
mkdirp "^0.5.5"
node-fetch "^2.6.7"
npmlog "^4.1.2"
progress "^2.0.3"
proxy-from-env "^1.1.0"
which "^2.0.2"
"@sentry/core@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.7.1.tgz#c3aaa6415d06bec65ac446b13b84f073805633e3"
integrity sha512-VAv8OR/7INn2JfiLcuop4hfDcyC7mfL9fdPndQEhlacjmw8gRrgXjR7qyhnCTgzFLkHI7V5bcdIzA83TRPYQpA==
dependencies:
"@sentry/hub" "6.7.1"
"@sentry/minimal" "6.7.1"
"@sentry/types" "6.7.1"
"@sentry/utils" "6.7.1"
tslib "^1.9.3"
"@sentry/electron@^2.5.1":
version "2.5.4"
resolved "https://registry.yarnpkg.com/@sentry/electron/-/electron-2.5.4.tgz#337b2f7e228e805a3e4eb3611c7b12c6cf87c618"
integrity sha512-tCCK+P581TmdjsDpHBQz7qYcldzGdUk1Fd6FPxPy1JKGzeY4uf/uSLKzR80Lzs5kTpEZFOwiMHSA8yjwFp5qoA==
dependencies:
"@sentry/browser" "6.7.1"
"@sentry/core" "6.7.1"
"@sentry/minimal" "6.7.1"
"@sentry/node" "6.7.1"
"@sentry/types" "6.7.1"
"@sentry/utils" "6.7.1"
tslib "^2.2.0"
"@sentry/hub@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.7.1.tgz#d46d24deec67f0731a808ca16796e6765b371bc1"
integrity sha512-eVCTWvvcp6xa0A5GGNHMQEWslmKPlisE5rGmsV/kjvSUv3zSrI0eIDfb51ikdnCiBjHpK2NBWP8Vy8cZOEJegg==
dependencies:
"@sentry/types" "6.7.1"
"@sentry/utils" "6.7.1"
tslib "^1.9.3"
"@sentry/minimal@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.7.1.tgz#babf85ee2f167e9dcf150d750d7a0b250c98449c"
integrity sha512-HDDPEnQRD6hC0qaHdqqKDStcdE1KhkFh0RCtJNMCDn0zpav8Dj9AteF70x6kLSlliAJ/JFwi6AmQrLz+FxPexw==
dependencies:
"@sentry/hub" "6.7.1"
"@sentry/types" "6.7.1"
tslib "^1.9.3"
"@sentry/node@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.7.1.tgz#b09e2eca8e187168feba7bd865a23935bf0f5cc0"
integrity sha512-rtZo1O8ROv4lZwuljQz3iFZW89oXSlgXCG2VqkxQyRspPWu89abROpxLjYzsWwQ8djnur1XjFv51kOLDUTS6Qw==
dependencies:
"@sentry/core" "6.7.1"
"@sentry/hub" "6.7.1"
"@sentry/tracing" "6.7.1"
"@sentry/types" "6.7.1"
"@sentry/utils" "6.7.1"
cookie "^0.4.1"
https-proxy-agent "^5.0.0"
lru_map "^0.3.3"
tslib "^1.9.3"
"@sentry/tracing@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.7.1.tgz#b11f0c17a6c5dc14ef44733e5436afb686683268"
integrity sha512-wyS3nWNl5mzaC1qZ2AIp1hjXnfO9EERjMIJjCihs2LWBz1r3efxrHxJHs8wXlNWvrT3KLhq/7vvF5CdU82uPeQ==
dependencies:
"@sentry/hub" "6.7.1"
"@sentry/minimal" "6.7.1"
"@sentry/types" "6.7.1"
"@sentry/utils" "6.7.1"
tslib "^1.9.3"
"@sentry/types@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.7.1.tgz#c8263e1886df5e815570c4668eb40a1cfaa1c88b"
integrity sha512-9AO7HKoip2MBMNQJEd6+AKtjj2+q9Ze4ooWUdEvdOVSt5drg7BGpK221/p9JEOyJAZwEPEXdcMd3VAIMiOb4MA==
"@sentry/utils@6.7.1":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.7.1.tgz#909184ad580f0f6375e1e4d4a6ffd33dfe64a4d1"
integrity sha512-Tq2otdbWlHAkctD+EWTYKkEx6BL1Qn3Z/imkO06/PvzpWvVhJWQ5qHAzz5XnwwqNHyV03KVzYB6znq1Bea9HuA==
dependencies:
"@sentry/types" "6.7.1"
tslib "^1.9.3"
"@sindresorhus/is@^4.0.0":
version "4.6.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
@ -662,19 +558,6 @@ applescript@^1.0.0:
resolved "https://registry.yarnpkg.com/applescript/-/applescript-1.0.0.tgz#bb87af568cad034a4e48c4bdaf6067a3a2701317"
integrity sha512-yvtNHdWvtbYEiIazXAdp/NY+BBb65/DAseqlNiJQjOx9DynuzOYDbVLBJvuc0ve0VL9x6B3OHF6eH52y9hCBtQ==
aproba@^1.0.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
are-we-there-yet@~1.1.2:
version "1.1.7"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146"
integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==
dependencies:
delegates "^1.0.0"
readable-stream "^2.0.6"
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@ -1090,16 +973,6 @@ config-file-ts@^0.2.4:
glob "^7.1.6"
typescript "^4.0.2"
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
cookie@^0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
core-util-is@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -1201,11 +1074,6 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
detect-indent@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.1.tgz#cbb060a12842b9c4d333f1cac4aa4da1bb66bc25"
@ -1848,20 +1716,6 @@ gar@^1.0.4:
resolved "https://registry.yarnpkg.com/gar/-/gar-1.0.4.tgz#f777bc7db425c0572fdeb52676172ca1ae9888b8"
integrity sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
integrity sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==
dependencies:
aproba "^1.0.3"
console-control-strings "^1.0.0"
has-unicode "^2.0.0"
object-assign "^4.1.0"
signal-exit "^3.0.0"
string-width "^1.0.1"
strip-ansi "^3.0.1"
wide-align "^1.1.0"
get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@ -2033,11 +1887,6 @@ has-symbols@^1.0.3:
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
@ -2164,7 +2013,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
inherits@2, inherits@^2.0.3, inherits@~2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -2249,11 +2098,6 @@ isarray@0.0.1:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
isbinaryfile@^4.0.8:
version "4.0.10"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3"
@ -2462,11 +2306,6 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
lru_map@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==
matcher@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
@ -2570,7 +2409,7 @@ minizlib@^2.1.1:
minipass "^3.0.0"
yallist "^4.0.0"
mkdirp@^0.5.1, mkdirp@^0.5.5:
mkdirp@^0.5.1:
version "0.5.6"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
@ -2602,8 +2441,10 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
"next-electron-server@file:./thirdparty/next-electron-server":
version "0.0.8"
next-electron-server@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-electron-server/-/next-electron-server-1.0.0.tgz#03e133ed64a5ef671b6c6409f908c4901b1828cb"
integrity sha512-fTUaHwT0Jry2fbdUSIkAiIqgDAInI5BJFF4/j90/okvZCYlyx6yxpXB30KpzmOG6TN/ESwyvsFJVvS2WHT8PAA==
node-addon-api@^1.6.3:
version "1.7.2"
@ -2642,16 +2483,6 @@ normalize-url@^6.0.1:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
npmlog@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
dependencies:
are-we-there-yet "~1.1.2"
console-control-strings "~1.1.0"
gauge "~2.7.3"
set-blocking "~2.0.0"
nugget@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/nugget/-/nugget-2.0.2.tgz#398b591377b740b3dd308fabecd5ea09cf3443da"
@ -2675,11 +2506,6 @@ oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
@ -2873,21 +2699,16 @@ prettier-plugin-packagejson@^2.4:
sort-package-json "2.8.0"
synckit "0.9.0"
prettier@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
prettier@^3:
version "3.2.5"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368"
integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==
pretty-bytes@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9"
integrity sha512-yJAF+AjbHKlxQ8eezMd/34Mnj/YTQ3i6kLzvVsH4l/BfIFtp444n0wVbnsn66JimZ9uBofv815aRp1zCppxlWw==
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
progress-stream@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-1.2.0.tgz#2cd3cfea33ba3a89c9c121ec3347abe9ab125f77"
@ -2916,11 +2737,6 @@ promise-retry@^2.0.1:
err-code "^2.0.2"
retry "^0.12.0"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
psl@^1.1.28:
version "1.9.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
@ -2995,19 +2811,6 @@ read-pkg@^5.2.0:
parse-json "^5.0.0"
type-fest "^0.6.0"
readable-stream@^2.0.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.0.2:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
@ -3149,11 +2952,6 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
@ -3207,11 +3005,6 @@ serialize-error@^7.0.1:
dependencies:
type-fest "^0.13.1"
set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@ -3229,11 +3022,6 @@ shell-quote@^1.7.3:
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123"
integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==
signal-exit@^3.0.0:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
simple-update-notifier@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb"
@ -3387,7 +3175,7 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -3408,14 +3196,7 @@ string_decoder@~0.10.x:
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
strip-ansi@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==
@ -3587,12 +3368,12 @@ truncate-utf8-bytes@^1.0.0:
dependencies:
utf8-byte-length "^1.0.1"
tslib@^1.8.1, tslib@^1.9.3:
tslib@^1.8.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.1.0, tslib@^2.2.0:
tslib@^2.1.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
@ -3695,7 +3476,7 @@ utf8-byte-length@^1.0.1:
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
@ -3749,20 +3530,13 @@ whatwg-url@^5.0.0:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
which@^2.0.1, which@^2.0.2:
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
wide-align@^1.1.0:
version "1.1.5"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
dependencies:
string-width "^1.0.2 || 2 || 3 || 4"
winreg@1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b"

1
mobile/.gitignore vendored
View file

@ -34,6 +34,7 @@ lib/generated_plugin_registrant.dart
android/key.properties
android/app/.settings/*
android/.settings/
.env

View file

@ -1,7 +1,7 @@
# CHANGELOG
## v0.8.66
## v0.8.67
### Added
* #### Home Widget ✨

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 343 KiB

View file

@ -7,17 +7,24 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:home_widget/home_widget.dart' as hw;
import 'package:logging/logging.dart';
import 'package:media_extension/media_extension_action_types.dart';
import 'package:photos/ente_theme_data.dart';
import "package:photos/generated/l10n.dart";
import "package:photos/l10n/l10n.dart";
import "package:photos/models/collection/collection_items.dart";
import 'package:photos/services/app_lifecycle_service.dart';
import "package:photos/services/collections_service.dart";
import "package:photos/services/favorites_service.dart";
import "package:photos/services/home_widget_service.dart";
import "package:photos/services/machine_learning/machine_learning_controller.dart";
import 'package:photos/services/sync_service.dart';
import 'package:photos/ui/tabs/home_widget.dart';
import "package:photos/ui/viewer/actions/file_viewer.dart";
import "package:photos/ui/viewer/gallery/collection_page.dart";
import "package:photos/utils/intent_util.dart";
import "package:photos/utils/navigation_util.dart";
class EnteApp extends StatefulWidget {
final Future<void> Function(String) runBackgroundTask;
@ -55,6 +62,46 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_checkForWidgetLaunch();
hw.HomeWidget.widgetClicked.listen(_launchedFromWidget);
}
void _checkForWidgetLaunch() {
hw.HomeWidget.initiallyLaunchedFromHomeWidget().then(_launchedFromWidget);
}
Future<void> _launchedFromWidget(Uri? uri) async {
if (uri == null) return;
final collectionID =
await FavoritesService.instance.getFavoriteCollectionID();
if (collectionID == null) {
return;
}
final collection = CollectionsService.instance.getCollectionByID(
collectionID,
);
if (collection == null) {
return;
}
unawaited(HomeWidgetService.instance.initHomeWidget());
final thumbnail = await CollectionsService.instance.getCover(collection);
unawaited(
routeToPage(
context,
CollectionPage(
CollectionWithThumbnail(
collection,
thumbnail,
),
),
),
);
}
setLocale(Locale newLocale) {
setState(() {
locale = newLocale;

View file

@ -24,6 +24,7 @@ import 'package:photos/models/private_key_attributes.dart';
import 'package:photos/services/billing_service.dart';
import 'package:photos/services/collections_service.dart';
import 'package:photos/services/favorites_service.dart';
import "package:photos/services/home_widget_service.dart";
import 'package:photos/services/ignored_files_service.dart';
import 'package:photos/services/machine_learning/semantic_search/semantic_search_service.dart';
import 'package:photos/services/memories_service.dart';
@ -31,7 +32,6 @@ import 'package:photos/services/search_service.dart';
import 'package:photos/services/sync_service.dart';
import 'package:photos/utils/crypto_util.dart';
import 'package:photos/utils/file_uploader.dart';
import "package:photos/utils/home_widget_util.dart";
import 'package:photos/utils/validator_util.dart';
import 'package:shared_preferences/shared_preferences.dart';
import "package:tuple/tuple.dart";
@ -175,7 +175,7 @@ class Configuration {
MemoriesService.instance.clearCache();
BillingService.instance.clearCache();
SearchService.instance.clearCache();
unawaited(clearHomeWidget());
unawaited(HomeWidgetService.instance.clearHomeWidget());
Bus.instance.fire(UserLoggedOutEvent());
} else {
await _preferences.setBool("auto_logout", true);

View file

@ -27,6 +27,7 @@ import 'package:photos/services/collections_service.dart';
import "package:photos/services/entity_service.dart";
import 'package:photos/services/favorites_service.dart';
import 'package:photos/services/feature_flag_service.dart';
import 'package:photos/services/home_widget_service.dart';
import 'package:photos/services/local_file_update_service.dart';
import 'package:photos/services/local_sync_service.dart';
import "package:photos/services/location_service.dart";
@ -46,7 +47,6 @@ import 'package:photos/ui/tools/app_lock.dart';
import 'package:photos/ui/tools/lock_screen.dart';
import 'package:photos/utils/crypto_util.dart';
import 'package:photos/utils/file_uploader.dart';
import "package:photos/utils/home_widget_util.dart";
import 'package:photos/utils/local_settings.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -110,8 +110,8 @@ ThemeMode _themeMode(AdaptiveThemeMode? savedThemeMode) {
Future<void> _homeWidgetSync() async {
if (!Platform.isAndroid) return;
try {
if (await countHomeWidgets() != 0) {
await initHomeWidget();
if (await HomeWidgetService.instance.countHomeWidgets() != 0) {
await HomeWidgetService.instance.initHomeWidget();
}
} catch (e, s) {
_logger.severe("Error in initSlideshowWidget", e, s);
@ -210,6 +210,12 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
LocalFileUpdateService.instance.init(preferences);
SearchService.instance.init();
StorageBonusService.instance.init(preferences);
if (!isBackground &&
Platform.isAndroid &&
await HomeWidgetService.instance.countHomeWidgets() == 0) {
unawaited(HomeWidgetService.instance.initHomeWidget());
}
if (Platform.isIOS) {
// ignore: unawaited_futures
PushService.instance.init().then((_) {
@ -275,9 +281,15 @@ Future<void> _scheduleHeartBeat(
}
Future<void> _scheduleFGHomeWidgetSync() async {
Future.delayed(kFGHomeWidgetSyncFrequency, () async {
unawaited(_homeWidgetSyncPeriodic());
});
}
Future<void> _homeWidgetSyncPeriodic() async {
await _homeWidgetSync();
Future.delayed(kFGHomeWidgetSyncFrequency, () async {
unawaited(_scheduleFGHomeWidgetSync());
unawaited(_homeWidgetSyncPeriodic());
});
}

View file

@ -0,0 +1,173 @@
import "dart:math";
import "package:flutter/material.dart";
import 'package:home_widget/home_widget.dart' as hw;
import "package:logging/logging.dart";
import "package:photos/core/configuration.dart";
import "package:photos/core/constants.dart";
import "package:photos/db/files_db.dart";
import "package:photos/models/file/file_type.dart";
import "package:photos/services/favorites_service.dart";
import "package:photos/utils/file_util.dart";
import "package:photos/utils/preload_util.dart";
class HomeWidgetService {
final Logger _logger = Logger((HomeWidgetService).toString());
HomeWidgetService._privateConstructor();
static final HomeWidgetService instance =
HomeWidgetService._privateConstructor();
Future<void> initHomeWidget() async {
final isLoggedIn = Configuration.instance.isLoggedIn();
if (!isLoggedIn) {
await clearHomeWidget();
_logger.info("user not logged in");
return;
}
final collectionID =
await FavoritesService.instance.getFavoriteCollectionID();
if (collectionID == null) {
await clearHomeWidget();
_logger.info("Favorite collection not found");
return;
}
try {
await hw.HomeWidget.setAppGroupId(iOSGroupID);
final res = await FilesDB.instance.getFilesInCollection(
collectionID,
galleryLoadStartTime,
galleryLoadEndTime,
);
final previousGeneratedId =
await hw.HomeWidget.getWidgetData<int>("home_widget_last_img");
if (res.files.length == 1 &&
res.files[0].generatedID == previousGeneratedId) {
_logger
.info("Only one image found and it's the same as the previous one");
return;
}
if (res.files.isEmpty) {
await clearHomeWidget();
_logger.info("No images found");
return;
}
final files = res.files.where(
(element) =>
element.generatedID != previousGeneratedId &&
element.fileType == FileType.image,
);
final randomNumber = Random().nextInt(files.length);
final randomFile = files.elementAt(randomNumber);
final fullImage = await getFileFromServer(randomFile);
if (fullImage == null) throw Exception("File not found");
final image = await decodeImageFromList(await fullImage.readAsBytes());
final width = image.width.toDouble();
final height = image.height.toDouble();
final size = min(min(width, height), 1024.0);
final aspectRatio = width / height;
late final int cacheWidth;
late final int cacheHeight;
if (aspectRatio > 1) {
cacheWidth = 1024;
cacheHeight = (1024 / aspectRatio).round();
} else if (aspectRatio < 1) {
cacheHeight = 1024;
cacheWidth = (1024 * aspectRatio).round();
} else {
cacheWidth = 1024;
cacheHeight = 1024;
}
final Image img = Image.file(
fullImage,
fit: BoxFit.cover,
cacheWidth: cacheWidth,
cacheHeight: cacheHeight,
);
await PreloadImage.loadImage(img.image);
final widget = ClipRRect(
borderRadius: BorderRadius.circular(32),
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(image: img.image, fit: BoxFit.cover),
),
),
);
await hw.HomeWidget.renderFlutterWidget(
widget,
logicalSize: Size(size, size),
key: "slideshow",
);
if (randomFile.generatedID != null) {
await hw.HomeWidget.saveWidgetData<int>(
"home_widget_last_img",
randomFile.generatedID!,
);
}
await hw.HomeWidget.updateWidget(
name: 'SlideshowWidgetProvider',
androidName: 'SlideshowWidgetProvider',
qualifiedAndroidName: 'io.ente.photos.SlideshowWidgetProvider',
iOSName: 'SlideshowWidget',
);
_logger.info(
">>> OG size of SlideshowWidget image: ${width} x $height",
);
_logger.info(
">>> SlideshowWidget image rendered with size ${cacheWidth} x $cacheHeight",
);
} catch (e) {
_logger.severe("Error rendering widget", e);
}
}
Future<int> countHomeWidgets() async {
return await hw.HomeWidget.getWidgetCount(
name: 'SlideshowWidgetProvider',
androidName: 'SlideshowWidgetProvider',
qualifiedAndroidName: 'io.ente.photos.SlideshowWidgetProvider',
iOSName: 'SlideshowWidget',
) ??
0;
}
Future<void> clearHomeWidget() async {
final previousGeneratedId =
await hw.HomeWidget.getWidgetData<int>("home_widget_last_img");
if (previousGeneratedId == null) return;
_logger.info("Clearing SlideshowWidget");
await hw.HomeWidget.saveWidgetData(
"slideshow",
null,
);
await hw.HomeWidget.updateWidget(
name: 'SlideshowWidgetProvider',
androidName: 'SlideshowWidgetProvider',
qualifiedAndroidName: 'io.ente.photos.SlideshowWidgetProvider',
iOSName: 'SlideshowWidget',
);
await hw.HomeWidget.saveWidgetData<int>(
"home_widget_last_img",
null,
);
_logger.info(">>> SlideshowWidget cleared");
}
}

View file

@ -1,147 +0,0 @@
import "dart:math";
import "package:flutter/material.dart";
import 'package:home_widget/home_widget.dart' as hw;
import "package:logging/logging.dart";
import "package:photos/core/configuration.dart";
import "package:photos/core/constants.dart";
import "package:photos/db/files_db.dart";
import "package:photos/models/file/file_type.dart";
import "package:photos/services/favorites_service.dart";
import "package:photos/utils/file_util.dart";
import "package:photos/utils/preload_util.dart";
Future<int> countHomeWidgets() async {
return await hw.HomeWidget.getWidgetCount(
name: 'SlideshowWidgetProvider',
androidName: 'SlideshowWidgetProvider',
qualifiedAndroidName: 'io.ente.photos.SlideshowWidgetProvider',
iOSName: 'SlideshowWidget',
) ??
0;
}
Future<void> initHomeWidget() async {
final Logger logger = Logger("initHomeWidget");
final user = Configuration.instance.getUserID();
if (user == null) {
await clearHomeWidget();
throw Exception("User not found");
}
final collectionID =
await FavoritesService.instance.getFavoriteCollectionID();
if (collectionID == null) {
await clearHomeWidget();
throw Exception("Collection not found");
}
try {
await hw.HomeWidget.setAppGroupId(iOSGroupID);
final res = await FilesDB.instance.getFilesInCollection(
collectionID,
galleryLoadStartTime,
galleryLoadEndTime,
);
final previousGeneratedId =
await hw.HomeWidget.getWidgetData<int>("home_widget_last_img");
if (res.files.length == 1 &&
res.files[0].generatedID == previousGeneratedId) {
logger.info("Only one image found and it's the same as the previous one");
return;
}
if (res.files.isEmpty) {
await clearHomeWidget();
return;
}
final files = res.files.where(
(element) =>
element.generatedID != previousGeneratedId &&
element.fileType == FileType.image,
);
final randomNumber = Random().nextInt(files.length);
final randomFile = files.elementAt(randomNumber);
final fullImage = await getFileFromServer(randomFile);
if (fullImage == null) throw Exception("File not found");
Image img = Image.file(fullImage);
var imgProvider = img.image;
await PreloadImage.loadImage(imgProvider);
img = Image.file(fullImage);
imgProvider = img.image;
final image = await decodeImageFromList(await fullImage.readAsBytes());
final width = image.width.toDouble();
final height = image.height.toDouble();
final size = min(min(width, height), 1024.0);
final widget = ClipRRect(
borderRadius: BorderRadius.circular(32),
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(image: imgProvider, fit: BoxFit.cover),
),
),
);
await hw.HomeWidget.renderFlutterWidget(
widget,
logicalSize: Size(size, size),
key: "slideshow",
);
await hw.HomeWidget.updateWidget(
name: 'SlideshowWidgetProvider',
androidName: 'SlideshowWidgetProvider',
qualifiedAndroidName: 'io.ente.photos.SlideshowWidgetProvider',
iOSName: 'SlideshowWidget',
);
if (randomFile.generatedID != null) {
await hw.HomeWidget.saveWidgetData<int>(
"home_widget_last_img",
randomFile.generatedID!,
);
}
logger.info(
">>> SlideshowWidget rendered with size ${width}x$height",
);
} catch (_) {
throw Exception("Error rendering widget");
}
}
Future<void> clearHomeWidget() async {
final previousGeneratedId =
await hw.HomeWidget.getWidgetData<int>("home_widget_last_img");
if (previousGeneratedId == null) return;
final Logger logger = Logger("clearHomeWidget");
logger.info("Clearing SlideshowWidget");
await hw.HomeWidget.saveWidgetData(
"slideshow",
null,
);
await hw.HomeWidget.updateWidget(
name: 'SlideshowWidgetProvider',
androidName: 'SlideshowWidgetProvider',
qualifiedAndroidName: 'io.ente.photos.SlideshowWidgetProvider',
iOSName: 'SlideshowWidget',
);
await hw.HomeWidget.saveWidgetData<int>(
"home_widget_last_img",
null,
);
logger.info(">>> SlideshowWidget cleared");
}

View file

@ -12,7 +12,7 @@ description: ente photos application
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.8.66+586
version: 0.8.67+587
publish_to: none
environment:

View file

@ -176,11 +176,6 @@ type SubscriptionUpdateResponse struct {
ClientSecret string `json:"clientSecret"`
}
type StripeSubscriptionInfo struct {
PlanCountry string
AccountCountry StripeAccountCountry
}
type StripeEventLog struct {
UserID int64
StripeSubscription stripe.Subscription

View file

@ -14,7 +14,7 @@ type Passkey struct {
var MaxPasskeys = 10
type SetPasskeyRecoveryRequest struct {
Secret uuid.UUID `json:"secret" binding:"required"`
Secret string `json:"secret" binding:"required"`
// The UserSecretCipher has SkipSecret encrypted with the user's recoveryKey
// If the user sends the correct UserSecretCipher, we can be sure that the user has the recoveryKey,
// and we can allow the user to recover their MFA.

View file

@ -85,30 +85,27 @@ func (c *BillingController) GetPlansV2(countryCode string, stripeAccountCountry
// GetStripeAccountCountry returns the stripe account country the user's existing plan is from
// if he doesn't have a stripe subscription then ente.DefaultStripeAccountCountry is returned
func (c *BillingController) GetStripeAccountCountry(userID int64) (ente.StripeAccountCountry, error) {
stipeSubInfo, hasStripeSub, err := c.GetUserStripeSubscriptionInfo(userID)
subscription, err := c.BillingRepo.GetUserSubscription(userID)
if err != nil {
return "", stacktrace.Propagate(err, "")
}
if hasStripeSub {
return stipeSubInfo.AccountCountry, nil
} else {
if subscription.PaymentProvider != ente.Stripe {
//if user doesn't have a stripe subscription, return the default stripe account country
return ente.DefaultStripeAccountCountry, nil
} else {
return subscription.Attributes.StripeAccountCountry, nil
}
}
// GetUserPlans returns the active plans for a user
func (c *BillingController) GetUserPlans(ctx *gin.Context, userID int64) ([]ente.BillingPlan, error) {
stripeSubInfo, hasStripeSub, err := c.GetUserStripeSubscriptionInfo(userID)
stripeAccountCountry, err := c.GetStripeAccountCountry(userID)
if err != nil {
return []ente.BillingPlan{}, stacktrace.Propagate(err, "Failed to get user's subscription country and stripe account")
}
if hasStripeSub {
return c.GetPlansV2(stripeSubInfo.PlanCountry, stripeSubInfo.AccountCountry), nil
} else {
// user doesn't have a stipe subscription, so return the default account plans for the country the user is from
return c.GetPlansV2(network.GetClientCountry(ctx), ente.DefaultStripeAccountCountry), nil
return []ente.BillingPlan{}, stacktrace.Propagate(err, "Failed to get user's country stripe account")
}
// always return the plans based on the user's country determined by the IP
return c.GetPlansV2(network.GetClientCountry(ctx), stripeAccountCountry), nil
}
// GetSubscription returns the current subscription for a user if any
@ -208,23 +205,6 @@ func (c *BillingController) HasActiveSelfOrFamilySubscription(userID int64) erro
return nil
}
func (c *BillingController) GetUserStripeSubscriptionInfo(userID int64) (ente.StripeSubscriptionInfo, bool, error) {
s, err := c.BillingRepo.GetUserSubscription(userID)
if err != nil {
return ente.StripeSubscriptionInfo{}, false, stacktrace.Propagate(err, "")
}
// skipping country code extraction for non-stripe subscriptions
// as they have same product id across countries and hence can't be distinquished
if s.PaymentProvider != ente.Stripe {
return ente.StripeSubscriptionInfo{}, false, nil
}
_, countryCode, err := c.getPlanWithCountry(s)
if err != nil {
return ente.StripeSubscriptionInfo{}, false, stacktrace.Propagate(err, "")
}
return ente.StripeSubscriptionInfo{PlanCountry: countryCode, AccountCountry: s.Attributes.StripeAccountCountry}, true, nil
}
// VerifySubscription verifies and returns the verified subscription
func (c *BillingController) VerifySubscription(
userID int64,

Some files were not shown because too many files have changed in this diff Show more