mirror of
https://github.com/immich-app/immich.git
synced 2025-12-11 09:13:08 +03:00
Compare commits
1 Commits
release/ne
...
push-vztsm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7818477bd3 |
10
.github/workflows/build-mobile.yml
vendored
10
.github/workflows/build-mobile.yml
vendored
@@ -108,7 +108,7 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
||||||
|
|
||||||
- uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
|
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
@@ -222,7 +222,6 @@ jobs:
|
|||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: '3.3'
|
ruby-version: '3.3'
|
||||||
bundler-cache: true
|
|
||||||
working-directory: ./mobile/ios
|
working-directory: ./mobile/ios
|
||||||
|
|
||||||
- name: Install CocoaPods dependencies
|
- name: Install CocoaPods dependencies
|
||||||
@@ -230,6 +229,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pod install
|
pod install
|
||||||
|
|
||||||
|
- name: Install Fastlane
|
||||||
|
working-directory: ./mobile/ios
|
||||||
|
run: |
|
||||||
|
gem install bundler
|
||||||
|
bundle config set --local path 'vendor/bundle'
|
||||||
|
bundle install
|
||||||
|
|
||||||
- name: Create API Key
|
- name: Create API Key
|
||||||
env:
|
env:
|
||||||
API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
||||||
|
|||||||
2
.github/workflows/cli.yml
vendored
2
.github/workflows/cli.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|||||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
uses: github/codeql-action/autobuild@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -83,6 +83,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
2
.github/workflows/docs-build.yml
vendored
2
.github/workflows/docs-build.yml
vendored
@@ -69,7 +69,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
4
.github/workflows/fix-format.yml
vendored
4
.github/workflows/fix-format.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
2
.github/workflows/merge-translations.yml
vendored
2
.github/workflows/merge-translations.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate_token
|
id: generate_token
|
||||||
if: ${{ inputs.skip != true }}
|
if: ${{ inputs.skip != true }}
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|||||||
8
.github/workflows/prepare-release.yml
vendored
8
.github/workflows/prepare-release.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -126,7 +126,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -144,7 +144,7 @@ jobs:
|
|||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
github-token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag_name: ${{ env.IMMICH_VERSION }}
|
tag_name: ${{ env.IMMICH_VERSION }}
|
||||||
|
|||||||
6
.github/workflows/release-pr.yml
vendored
6
.github/workflows/release-pr.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -159,7 +159,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create PR
|
- name: Create PR
|
||||||
id: create-pr
|
id: create-pr
|
||||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
|
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
commit-message: 'chore: release ${{ steps.bump-type.outputs.next }}'
|
commit-message: 'chore: release ${{ steps.bump-type.outputs.next }}'
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -80,7 +80,7 @@ jobs:
|
|||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
github-token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ steps.version.outputs.result }}
|
tag_name: ${{ steps.version.outputs.result }}
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|||||||
2
.github/workflows/sdk.yml
vendored
2
.github/workflows/sdk.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|||||||
26
.github/workflows/test.yml
vendored
26
.github/workflows/test.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -121,7 +121,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -168,7 +168,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -210,7 +210,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -254,7 +254,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -292,7 +292,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -340,7 +340,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -387,7 +387,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -426,7 +426,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -481,7 +481,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -617,7 +617,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './.github/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -668,7 +668,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -730,7 +730,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
230
CHANGELOG.md
230
CHANGELOG.md
@@ -1,230 +0,0 @@
|
|||||||
|
|
||||||
# v2.4.0
|
|
||||||
|
|
||||||
# v2.4.0
|
|
||||||
|
|
||||||
## Highlights
|
|
||||||
|
|
||||||
Welcome to the release `v2.4.0` of Immich. This release focuses on bug fixes, QoL improvements, and polished UI components across mobile and the web. Let's dive right in.
|
|
||||||
|
|
||||||
* Show the owner's name in the shared album
|
|
||||||
* Command palette
|
|
||||||
* Change search type directly in the search bar
|
|
||||||
* Job details
|
|
||||||
* Simplify the top control bar in the mobile app
|
|
||||||
* Notable fix: fix an issue where metadata extraction could fail on high concurrency
|
|
||||||
|
|
||||||
|
|
||||||
### Show the owner's name in the shared album.
|
|
||||||
|
|
||||||
On the web, in shared albums, you can now toggle an option to display the asset's owner name at the bottom right corner of the thumbnail.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Command palette
|
|
||||||
|
|
||||||
The web app now has an integrated command palette, which can be opened `ctrl + k` on Windows/Linux or `cmd + k` on macOS. This first iteration of command pallets lets you quickly navigate between administration pages by typing the name of the page you want to go to.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Change search type directly in the search bar
|
|
||||||
|
|
||||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
|
||||||
|
|
||||||
## What's Changed
|
|
||||||
|
|
||||||
### 🫥 Deprecated Changes
|
|
||||||
|
|
||||||
* feat: queues by @jrasm91 in <https://github.com/immich-app/immich/pull/24142>
|
|
||||||
|
|
||||||
### 🚀 Features
|
|
||||||
|
|
||||||
* feat: improve performance: don't sort timeline buckets from server by @midzelis in <https://github.com/immich-app/immich/pull/24032>
|
|
||||||
* feat: command palette by @danieldietzler in <https://github.com/immich-app/immich/pull/23693>
|
|
||||||
* feat(web): Shared album owner labels by @xCJPECKOVERx in <https://github.com/immich-app/immich/pull/21171>
|
|
||||||
* feat(mobile): persist album sorting & layout in settings by @YarosMallorca in <https://github.com/immich-app/immich/pull/22133>
|
|
||||||
* feat: queue detail page by @jrasm91 in <https://github.com/immich-app/immich/pull/24352>
|
|
||||||
* chore(mobile): add kebabu menu in asset viewer by @idubnori in <https://github.com/immich-app/immich/pull/24387>
|
|
||||||
|
|
||||||
### 🌟 Enhancements
|
|
||||||
|
|
||||||
* feat(web): allow navigating the map with arrow keys by @lukashass in <https://github.com/immich-app/immich/pull/24080>
|
|
||||||
* feat: separate camera and lens info in detail panel by @fabianbees in <https://github.com/immich-app/immich/pull/23670>
|
|
||||||
* feat(web): shared link card tweaks by @jrasm91 in <https://github.com/immich-app/immich/pull/24192>
|
|
||||||
* feat(server): exclude syncthing folders from external libraries by @SaphuA in <https://github.com/immich-app/immich/pull/24240>
|
|
||||||
* feat(web): search type selection dropdown by @YarosMallorca in <https://github.com/immich-app/immich/pull/24091>
|
|
||||||
* feat: header context menu by @jrasm91 in <https://github.com/immich-app/immich/pull/24374>
|
|
||||||
* feat(mobile): move top bar buttons into kebabu menu in AssetViewer by @idubnori in <https://github.com/immich-app/immich/pull/24461>
|
|
||||||
|
|
||||||
### 🐛 Bug fixes
|
|
||||||
|
|
||||||
* fix: effect loop by @jrasm91 in <https://github.com/immich-app/immich/pull/24014>
|
|
||||||
* fix: do not clear hash on updated_at change by @shenlong-tanwen in <https://github.com/immich-app/immich/pull/24039>
|
|
||||||
* fix: disable animation "add to" action menu by @bwees in <https://github.com/immich-app/immich/pull/24040>
|
|
||||||
* fix: Use correct app store link by @Mraedis in <https://github.com/immich-app/immich/pull/24062>
|
|
||||||
* fix: show archived assets in favorite page by @bwees in <https://github.com/immich-app/immich/pull/24052>
|
|
||||||
* fix(mobile): first video memory on page doesn't play by @YarosMallorca in <https://github.com/immich-app/immich/pull/23906>
|
|
||||||
* feat(web): show detected faces in spherical photos by @meesfrensel in <https://github.com/immich-app/immich/pull/23974>
|
|
||||||
* fix: add users to album by @danieldietzler in <https://github.com/immich-app/immich/pull/24133>
|
|
||||||
* fix(server): sanitize DB_URL for pg_dumpall to remove unknown query params by @lutostag in <https://github.com/immich-app/immich/pull/23333>
|
|
||||||
* fix: use proper updatedAt value in local assets by @shenlong-tanwen in <https://github.com/immich-app/immich/pull/24137>
|
|
||||||
* fix: albums page reactivity loops by @danieldietzler in <https://github.com/immich-app/immich/pull/24046>
|
|
||||||
* fix: getAspectRatio fallback to db width and height by @shenlong-tanwen in <https://github.com/immich-app/immich/pull/24131>
|
|
||||||
* fix(web): fix support & feedback modal wrapping by @Snowknight26 in <https://github.com/immich-app/immich/pull/24018>
|
|
||||||
* fix: don't get OCR data in shared link by @alextran1502 in <https://github.com/immich-app/immich/pull/24152>
|
|
||||||
* fix: duration extraction by @jrasm91 in <https://github.com/immich-app/immich/pull/24178>
|
|
||||||
* fix(ml): Upgrade ONNX Runtime to v1.22.1 to fix ROCm build failures by @LukaPrebil in <https://github.com/immich-app/immich/pull/24045>
|
|
||||||
* fix: update timeline-manager after archive actions by @midzelis in <https://github.com/immich-app/immich/pull/24010>
|
|
||||||
* fix: theme switcher by @jrasm91 in <https://github.com/immich-app/immich/pull/24209>
|
|
||||||
* fix: label 'for' attributes in user-api-key-grid by @kimsey0 in <https://github.com/immich-app/immich/pull/24232>
|
|
||||||
* fix(mobile): enable backup text overflows by @YarosMallorca in <https://github.com/immich-app/immich/pull/24227>
|
|
||||||
* fix(web): integrate zoom toggle button into panorama photo viewer by @meesfrensel in <https://github.com/immich-app/immich/pull/24189>
|
|
||||||
* fix(web): use full tag path when creating nested subtags by @NiklasvonM in <https://github.com/immich-app/immich/pull/24249>
|
|
||||||
* fix: only generate memory based on users assets by @alextran1502 in <https://github.com/immich-app/immich/pull/24151>
|
|
||||||
* fix(mobile): docs link by @mmomjian in <https://github.com/immich-app/immich/pull/24277>
|
|
||||||
* fix(server): use bigrams for cjk by @mertalev in <https://github.com/immich-app/immich/pull/24285>
|
|
||||||
* fix(ml): do not upscale preview by @mertalev in <https://github.com/immich-app/immich/pull/24322>
|
|
||||||
* fix(web): open onboarding documentation link in new tab by @carbonemys in <https://github.com/immich-app/immich/pull/24289>
|
|
||||||
* fix(mobile): use correct timezone displayed in the info sheet by @kao-byte in <https://github.com/immich-app/immich/pull/24310>
|
|
||||||
* fix(web): folder view sort oder by @etnoy in <https://github.com/immich-app/immich/pull/24337>
|
|
||||||
* fix(server): do not delete offline assets by @mertalev in <https://github.com/immich-app/immich/pull/24355>
|
|
||||||
* fix: exposure info and better readability by @alextran1502 in <https://github.com/immich-app/immich/pull/24344>
|
|
||||||
* fix: Adjust the zoom level by @jforseth210 in <https://github.com/immich-app/immich/pull/24353>
|
|
||||||
* fix: local full sync on Android on resume by @alextran1502 in <https://github.com/immich-app/immich/pull/24348>
|
|
||||||
* fix(web): Add minimum content size to logo for consistent visual on small screens by @kiloomar in <https://github.com/immich-app/immich/pull/24372>
|
|
||||||
* fix: use adjustment time in iOS for hash reset by @shenlong-tanwen in <https://github.com/immich-app/immich/pull/24047>
|
|
||||||
* fix(server): update exiftool-vendored to v34 for more robust metadata extraction by @skatsubo in <https://github.com/immich-app/immich/pull/24424>
|
|
||||||
* fix(mobile): cannot create album while name field is focused by @YarosMallorca in <https://github.com/immich-app/immich/pull/24449>
|
|
||||||
* fix(web): \[album table view\] long album title overflows table row by @simonkub in <https://github.com/immich-app/immich/pull/24450>
|
|
||||||
* fix(mobile): fix overflow text in backup card by @YarosMallorca in <https://github.com/immich-app/immich/pull/24448>
|
|
||||||
* fix(mobile): timeline bottom padding on selection by @YarosMallorca in <https://github.com/immich-app/immich/pull/24480>
|
|
||||||
* feat(mobile): Localized backup upload details page by @ArnyminerZ in <https://github.com/immich-app/immich/pull/21136>
|
|
||||||
|
|
||||||
### 📚 Documentation
|
|
||||||
|
|
||||||
* docs: DB_STORAGE_TYPE is only used by the database container by @dionysius in <https://github.com/immich-app/immich/pull/24215>
|
|
||||||
* fix(docs): build `cli` for e2e tests by @roschaefer in <https://github.com/immich-app/immich/pull/24184>
|
|
||||||
* docs(faq): add more info on archiving by @etnoy in <https://github.com/immich-app/immich/pull/24326>
|
|
||||||
* fix(docs): server and machine-learning use IMMICH_HOST and IMMICH_PORT by @dionysius in <https://github.com/immich-app/immich/pull/24335>
|
|
||||||
* fix: prevent OOM on nginx reverse proxy servers by @NicholasFlamy in <https://github.com/immich-app/immich/pull/24351>
|
|
||||||
* fix(docs): obsolete docs about rootless docker by @roschaefer in <https://github.com/immich-app/immich/pull/24376>
|
|
||||||
* fix(docs): websockets in nginx example by @fourthwall in <https://github.com/immich-app/immich/pull/24411>
|
|
||||||
|
|
||||||
### 🌐 Translations
|
|
||||||
|
|
||||||
* chore: add new language requests by @danieldietzler in <https://github.com/immich-app/immich/pull/23991>
|
|
||||||
|
|
||||||
## New Contributors
|
|
||||||
|
|
||||||
* @ujjwal123123 made their first contribution in <https://github.com/immich-app/immich/pull/24101>
|
|
||||||
* @lutostag made their first contribution in <https://github.com/immich-app/immich/pull/23333>
|
|
||||||
* @LukaPrebil made their first contribution in <https://github.com/immich-app/immich/pull/24045>
|
|
||||||
* @kimsey0 made their first contribution in <https://github.com/immich-app/immich/pull/24232>
|
|
||||||
* @SaphuA made their first contribution in <https://github.com/immich-app/immich/pull/24240>
|
|
||||||
* @dionysius made their first contribution in <https://github.com/immich-app/immich/pull/24215>
|
|
||||||
* @NiklasvonM made their first contribution in <https://github.com/immich-app/immich/pull/24249>
|
|
||||||
* @kao-byte made their first contribution in <https://github.com/immich-app/immich/pull/24098>
|
|
||||||
* @carbonemys made their first contribution in <https://github.com/immich-app/immich/pull/24289>
|
|
||||||
* @kiloomar made their first contribution in <https://github.com/immich-app/immich/pull/24372>
|
|
||||||
* @fourthwall made their first contribution in <https://github.com/immich-app/immich/pull/24411>
|
|
||||||
* @simonkub made their first contribution in <https://github.com/immich-app/immich/pull/24450>
|
|
||||||
* @ArnyminerZ made their first contribution in <https://github.com/immich-app/immich/pull/21136>
|
|
||||||
|
|
||||||
**Full Changelog**: <https://github.com/immich-app/immich/compare/v2.3.1...v2.4.0>
|
|
||||||
|
|
||||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
|
||||||
|
|
||||||
## What's Changed
|
|
||||||
### 🫥 Deprecated Changes
|
|
||||||
* feat: queues by @jrasm91 in https://github.com/immich-app/immich/pull/24142
|
|
||||||
### 🚀 Features
|
|
||||||
* feat: improve performance: don't sort timeline buckets from server by @midzelis in https://github.com/immich-app/immich/pull/24032
|
|
||||||
* feat: command palette by @danieldietzler in https://github.com/immich-app/immich/pull/23693
|
|
||||||
* feat(web): Shared album owner labels by @xCJPECKOVERx in https://github.com/immich-app/immich/pull/21171
|
|
||||||
* feat(mobile): persist album sorting & layout in settings by @YarosMallorca in https://github.com/immich-app/immich/pull/22133
|
|
||||||
* feat: queue detail page by @jrasm91 in https://github.com/immich-app/immich/pull/24352
|
|
||||||
* chore(mobile): add kebabu menu in asset viewer by @idubnori in https://github.com/immich-app/immich/pull/24387
|
|
||||||
### 🌟 Enhancements
|
|
||||||
* feat(web): allow navigating the map with arrow keys by @lukashass in https://github.com/immich-app/immich/pull/24080
|
|
||||||
* feat: separate camera and lens info in detail panel by @fabianbees in https://github.com/immich-app/immich/pull/23670
|
|
||||||
* feat(web): shared link card tweaks by @jrasm91 in https://github.com/immich-app/immich/pull/24192
|
|
||||||
* feat(server): exclude syncthing folders from external libraries by @SaphuA in https://github.com/immich-app/immich/pull/24240
|
|
||||||
* feat(web): search type selection dropdown by @YarosMallorca in https://github.com/immich-app/immich/pull/24091
|
|
||||||
* feat: header context menu by @jrasm91 in https://github.com/immich-app/immich/pull/24374
|
|
||||||
* feat(mobile): move top bar buttons into kebabu menu in AssetViewer by @idubnori in https://github.com/immich-app/immich/pull/24461
|
|
||||||
### 🐛 Bug fixes
|
|
||||||
* fix: effect loop by @jrasm91 in https://github.com/immich-app/immich/pull/24014
|
|
||||||
* fix: do not clear hash on updated_at change by @shenlong-tanwen in https://github.com/immich-app/immich/pull/24039
|
|
||||||
* fix: disable animation "add to" action menu by @bwees in https://github.com/immich-app/immich/pull/24040
|
|
||||||
* fix: Use correct app store link by @Mraedis in https://github.com/immich-app/immich/pull/24062
|
|
||||||
* fix: show archived assets in favorite page by @bwees in https://github.com/immich-app/immich/pull/24052
|
|
||||||
* fix(mobile): first video memory on page doesn't play by @YarosMallorca in https://github.com/immich-app/immich/pull/23906
|
|
||||||
* feat(web): show detected faces in spherical photos by @meesfrensel in https://github.com/immich-app/immich/pull/23974
|
|
||||||
* fix: add users to album by @danieldietzler in https://github.com/immich-app/immich/pull/24133
|
|
||||||
* fix(server): sanitize DB_URL for pg_dumpall to remove unknown query params by @lutostag in https://github.com/immich-app/immich/pull/23333
|
|
||||||
* fix: use proper updatedAt value in local assets by @shenlong-tanwen in https://github.com/immich-app/immich/pull/24137
|
|
||||||
* fix: albums page reactivity loops by @danieldietzler in https://github.com/immich-app/immich/pull/24046
|
|
||||||
* fix: getAspectRatio fallback to db width and height by @shenlong-tanwen in https://github.com/immich-app/immich/pull/24131
|
|
||||||
* fix(web): fix support & feedback modal wrapping by @Snowknight26 in https://github.com/immich-app/immich/pull/24018
|
|
||||||
* fix: don't get OCR data in shared link by @alextran1502 in https://github.com/immich-app/immich/pull/24152
|
|
||||||
* fix: duration extraction by @jrasm91 in https://github.com/immich-app/immich/pull/24178
|
|
||||||
* fix(ml): Upgrade ONNX Runtime to v1.22.1 to fix ROCm build failures by @LukaPrebil in https://github.com/immich-app/immich/pull/24045
|
|
||||||
* fix: update timeline-manager after archive actions by @midzelis in https://github.com/immich-app/immich/pull/24010
|
|
||||||
* fix: theme switcher by @jrasm91 in https://github.com/immich-app/immich/pull/24209
|
|
||||||
* fix: label 'for' attributes in user-api-key-grid by @kimsey0 in https://github.com/immich-app/immich/pull/24232
|
|
||||||
* fix(mobile): enable backup text overflows by @YarosMallorca in https://github.com/immich-app/immich/pull/24227
|
|
||||||
* fix(web): integrate zoom toggle button into panorama photo viewer by @meesfrensel in https://github.com/immich-app/immich/pull/24189
|
|
||||||
* fix(web): use full tag path when creating nested subtags by @NiklasvonM in https://github.com/immich-app/immich/pull/24249
|
|
||||||
* fix: only generate memory based on users assets by @alextran1502 in https://github.com/immich-app/immich/pull/24151
|
|
||||||
* fix(mobile): docs link by @mmomjian in https://github.com/immich-app/immich/pull/24277
|
|
||||||
* fix(server): use bigrams for cjk by @mertalev in https://github.com/immich-app/immich/pull/24285
|
|
||||||
* fix(ml): do not upscale preview by @mertalev in https://github.com/immich-app/immich/pull/24322
|
|
||||||
* fix(web): open onboarding documentation link in new tab by @carbonemys in https://github.com/immich-app/immich/pull/24289
|
|
||||||
* fix(mobile): use correct timezone displayed in the info sheet by @kao-byte in https://github.com/immich-app/immich/pull/24310
|
|
||||||
* fix(web): folder view sort oder by @etnoy in https://github.com/immich-app/immich/pull/24337
|
|
||||||
* fix(server): do not delete offline assets by @mertalev in https://github.com/immich-app/immich/pull/24355
|
|
||||||
* fix: exposure info and better readability by @alextran1502 in https://github.com/immich-app/immich/pull/24344
|
|
||||||
* fix: Adjust the zoom level by @jforseth210 in https://github.com/immich-app/immich/pull/24353
|
|
||||||
* fix: local full sync on Android on resume by @alextran1502 in https://github.com/immich-app/immich/pull/24348
|
|
||||||
* fix(web): Add minimum content size to logo for consistent visual on small screens by @kiloomar in https://github.com/immich-app/immich/pull/24372
|
|
||||||
* fix: use adjustment time in iOS for hash reset by @shenlong-tanwen in https://github.com/immich-app/immich/pull/24047
|
|
||||||
* fix(server): update exiftool-vendored to v34 for more robust metadata extraction by @skatsubo in https://github.com/immich-app/immich/pull/24424
|
|
||||||
* fix(mobile): cannot create album while name field is focused by @YarosMallorca in https://github.com/immich-app/immich/pull/24449
|
|
||||||
* fix(web): [album table view] long album title overflows table row by @simonkub in https://github.com/immich-app/immich/pull/24450
|
|
||||||
* fix(mobile): fix overflow text in backup card by @YarosMallorca in https://github.com/immich-app/immich/pull/24448
|
|
||||||
* fix(mobile): timeline bottom padding on selection by @YarosMallorca in https://github.com/immich-app/immich/pull/24480
|
|
||||||
* feat(mobile): Localized backup upload details page by @ArnyminerZ in https://github.com/immich-app/immich/pull/21136
|
|
||||||
* fix(mobile): iOS local permission dialog extra whitespace by @kurtmckee in https://github.com/immich-app/immich/pull/24491
|
|
||||||
* fix(mobile): versionStatus.message text overflow by @idubnori in https://github.com/immich-app/immich/pull/24504
|
|
||||||
### 📚 Documentation
|
|
||||||
* docs: DB_STORAGE_TYPE is only used by the database container by @dionysius in https://github.com/immich-app/immich/pull/24215
|
|
||||||
* fix(docs): build `cli` for e2e tests by @roschaefer in https://github.com/immich-app/immich/pull/24184
|
|
||||||
* docs(faq): add more info on archiving by @etnoy in https://github.com/immich-app/immich/pull/24326
|
|
||||||
* fix(docs): server and machine-learning use IMMICH_HOST and IMMICH_PORT by @dionysius in https://github.com/immich-app/immich/pull/24335
|
|
||||||
* fix: prevent OOM on nginx reverse proxy servers by @NicholasFlamy in https://github.com/immich-app/immich/pull/24351
|
|
||||||
* fix(docs): obsolete docs about rootless docker by @roschaefer in https://github.com/immich-app/immich/pull/24376
|
|
||||||
* fix(docs): websockets in nginx example by @fourthwall in https://github.com/immich-app/immich/pull/24411
|
|
||||||
* fix(docs): slow upload speed with example nginx reverse proxy config by @goalie2002 in https://github.com/immich-app/immich/pull/24490
|
|
||||||
### 🌐 Translations
|
|
||||||
* chore: add new language requests by @danieldietzler in https://github.com/immich-app/immich/pull/23991
|
|
||||||
|
|
||||||
## New Contributors
|
|
||||||
* @ujjwal123123 made their first contribution in https://github.com/immich-app/immich/pull/24101
|
|
||||||
* @lutostag made their first contribution in https://github.com/immich-app/immich/pull/23333
|
|
||||||
* @LukaPrebil made their first contribution in https://github.com/immich-app/immich/pull/24045
|
|
||||||
* @kimsey0 made their first contribution in https://github.com/immich-app/immich/pull/24232
|
|
||||||
* @SaphuA made their first contribution in https://github.com/immich-app/immich/pull/24240
|
|
||||||
* @dionysius made their first contribution in https://github.com/immich-app/immich/pull/24215
|
|
||||||
* @NiklasvonM made their first contribution in https://github.com/immich-app/immich/pull/24249
|
|
||||||
* @kao-byte made their first contribution in https://github.com/immich-app/immich/pull/24098
|
|
||||||
* @carbonemys made their first contribution in https://github.com/immich-app/immich/pull/24289
|
|
||||||
* @kiloomar made their first contribution in https://github.com/immich-app/immich/pull/24372
|
|
||||||
* @fourthwall made their first contribution in https://github.com/immich-app/immich/pull/24411
|
|
||||||
* @simonkub made their first contribution in https://github.com/immich-app/immich/pull/24450
|
|
||||||
* @ArnyminerZ made their first contribution in https://github.com/immich-app/immich/pull/21136
|
|
||||||
* @kurtmckee made their first contribution in https://github.com/immich-app/immich/pull/24491
|
|
||||||
|
|
||||||
**Full Changelog**: https://github.com/immich-app/immich/compare/v2.3.1...v2.4.0
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.104",
|
"version": "2.2.103",
|
||||||
"description": "Command Line Interface (CLI) for Immich",
|
"description": "Command Line Interface (CLI) for Immich",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
|
image: docker.io/valkey/valkey:9@sha256:4503e204c900a00ad393bec83c8c7c4c76b0529cd629e23b34b52011aefd1d27
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
|
image: docker.io/valkey/valkey:9@sha256:4503e204c900a00ad393bec83c8c7c4c76b0529cd629e23b34b52011aefd1d27
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -83,7 +83,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:d936808bdea528155c0154a922cd42fd75716b8bb7ba302641350f9f3eaeba09
|
image: prom/prometheus@sha256:49214755b6153f90a597adcbff0252cc61069f8ab69ce8411285cd4a560e8038
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
|
image: docker.io/valkey/valkey:9@sha256:4503e204c900a00ad393bec83c8c7c4c76b0529cd629e23b34b52011aefd1d27
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ server {
|
|||||||
# disable buffering uploads to prevent OOM on reverse proxy server and make uploads twice as fast (no pause)
|
# disable buffering uploads to prevent OOM on reverse proxy server and make uploads twice as fast (no pause)
|
||||||
proxy_request_buffering off;
|
proxy_request_buffering off;
|
||||||
|
|
||||||
# increase body buffer to avoid limiting upload speed
|
|
||||||
client_body_buffer_size 1024k;
|
|
||||||
|
|
||||||
# Set headers
|
# Set headers
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|||||||
4
docs/static/archived-versions.json
vendored
4
docs/static/archived-versions.json
vendored
@@ -1,8 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"label": "v2.4.0",
|
|
||||||
"url": "https://docs.v2.4.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v2.3.1",
|
"label": "v2.3.1",
|
||||||
"url": "https://docs.v2.3.1.archive.immich.app"
|
"url": "https://docs.v2.3.1.archive.immich.app"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "2.4.0",
|
"version": "2.3.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-unicorn": "^62.0.0",
|
"eslint-plugin-unicorn": "^62.0.0",
|
||||||
"exiftool-vendored": "^34.0.0",
|
"exiftool-vendored": "^33.0.0",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"jose": "^5.6.3",
|
"jose": "^5.6.3",
|
||||||
"luxon": "^3.4.4",
|
"luxon": "^3.4.4",
|
||||||
|
|||||||
@@ -652,7 +652,6 @@
|
|||||||
"backup_options_page_title": "Backup options",
|
"backup_options_page_title": "Backup options",
|
||||||
"backup_setting_subtitle": "Manage background and foreground upload settings",
|
"backup_setting_subtitle": "Manage background and foreground upload settings",
|
||||||
"backup_settings_subtitle": "Manage upload settings",
|
"backup_settings_subtitle": "Manage upload settings",
|
||||||
"backup_upload_details_page_more_details": "Tap for more details",
|
|
||||||
"backward": "Backward",
|
"backward": "Backward",
|
||||||
"biometric_auth_enabled": "Biometric authentication enabled",
|
"biometric_auth_enabled": "Biometric authentication enabled",
|
||||||
"biometric_locked_out": "You are locked out of biometric authentication",
|
"biometric_locked_out": "You are locked out of biometric authentication",
|
||||||
@@ -719,7 +718,6 @@
|
|||||||
"check_corrupt_asset_backup_button": "Perform check",
|
"check_corrupt_asset_backup_button": "Perform check",
|
||||||
"check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.",
|
"check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.",
|
||||||
"check_logs": "Check Logs",
|
"check_logs": "Check Logs",
|
||||||
"checksum": "Checksum",
|
|
||||||
"choose_matching_people_to_merge": "Choose matching people to merge",
|
"choose_matching_people_to_merge": "Choose matching people to merge",
|
||||||
"city": "City",
|
"city": "City",
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
@@ -1168,7 +1166,6 @@
|
|||||||
"header_settings_header_name_input": "Header name",
|
"header_settings_header_name_input": "Header name",
|
||||||
"header_settings_header_value_input": "Header value",
|
"header_settings_header_value_input": "Header value",
|
||||||
"headers_settings_tile_title": "Custom proxy headers",
|
"headers_settings_tile_title": "Custom proxy headers",
|
||||||
"height": "Height",
|
|
||||||
"hi_user": "Hi {name} ({email})",
|
"hi_user": "Hi {name} ({email})",
|
||||||
"hide_all_people": "Hide all people",
|
"hide_all_people": "Hide all people",
|
||||||
"hide_gallery": "Hide gallery",
|
"hide_gallery": "Hide gallery",
|
||||||
@@ -1291,7 +1288,6 @@
|
|||||||
"local": "Local",
|
"local": "Local",
|
||||||
"local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server",
|
"local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server",
|
||||||
"local_assets": "Local Assets",
|
"local_assets": "Local Assets",
|
||||||
"local_id": "Local ID",
|
|
||||||
"local_media_summary": "Local Media Summary",
|
"local_media_summary": "Local Media Summary",
|
||||||
"local_network": "Local network",
|
"local_network": "Local network",
|
||||||
"local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network",
|
"local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network",
|
||||||
@@ -2222,7 +2218,6 @@
|
|||||||
"week": "Week",
|
"week": "Week",
|
||||||
"welcome": "Welcome",
|
"welcome": "Welcome",
|
||||||
"welcome_to_immich": "Welcome to Immich",
|
"welcome_to_immich": "Welcome to Immich",
|
||||||
"width": "Width",
|
|
||||||
"wifi_name": "Wi-Fi Name",
|
"wifi_name": "Wi-Fi Name",
|
||||||
"workflow": "Workflow",
|
"workflow": "Workflow",
|
||||||
"wrong_pin_code": "Wrong PIN code",
|
"wrong_pin_code": "Wrong PIN code",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "immich-ml"
|
name = "immich-ml"
|
||||||
version = "2.4.0"
|
version = "2.3.1"
|
||||||
description = ""
|
description = ""
|
||||||
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
|
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
|
||||||
requires-python = ">=3.10,<4.0"
|
requires-python = ">=3.10,<4.0"
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 3029,
|
"android.injected.version.code" => 3028,
|
||||||
"android.injected.version.name" => "2.4.0",
|
"android.injected.version.name" => "2.3.1",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||||
|
|||||||
@@ -137,7 +137,8 @@
|
|||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
<string>We need to use FaceID to allow access to your locked folder</string>
|
<string>We need to use FaceID to allow access to your locked folder</string>
|
||||||
<key>NSLocalNetworkUsageDescription</key>
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
<string>We need local network permission to connect to the local server using IP address and allow the casting feature to work</string>
|
<string>We need local network permission to connect to the local server using IP address and
|
||||||
|
allow the casting feature to work</string>
|
||||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
<string>We require this permission to access the local WiFi name for background upload mechanism</string>
|
<string>We require this permission to access the local WiFi name for background upload mechanism</string>
|
||||||
<key>NSLocationUsageDescription</key>
|
<key>NSLocationUsageDescription</key>
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class DriftUploadDetailPage extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"backup_upload_details_page_more_details".t(context: context),
|
'Tap for more details',
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: context.colorScheme.onSurface.withValues(alpha: 0.6),
|
color: context.colorScheme.onSurface.withValues(alpha: 0.6),
|
||||||
),
|
),
|
||||||
@@ -239,20 +239,14 @@ class FileDetailDialog extends ConsumerWidget {
|
|||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
if (asset != null) ...[
|
if (asset != null) ...[
|
||||||
_buildInfoSection(context, [
|
_buildInfoSection(context, [
|
||||||
_buildInfoRow(context, "filename".t(context: context), path.basename(uploadStatus.filename)),
|
_buildInfoRow(context, "Filename", path.basename(uploadStatus.filename)),
|
||||||
_buildInfoRow(context, "local_id".t(context: context), asset.id),
|
_buildInfoRow(context, "Local ID", asset.id),
|
||||||
_buildInfoRow(
|
_buildInfoRow(context, "File Size", formatHumanReadableBytes(uploadStatus.fileSize, 2)),
|
||||||
context,
|
if (asset.width != null) _buildInfoRow(context, "Width", "${asset.width}px"),
|
||||||
"file_size".t(context: context),
|
if (asset.height != null) _buildInfoRow(context, "Height", "${asset.height}px"),
|
||||||
formatHumanReadableBytes(uploadStatus.fileSize, 2),
|
_buildInfoRow(context, "Created At", asset.createdAt.toString()),
|
||||||
),
|
_buildInfoRow(context, "Updated At", asset.updatedAt.toString()),
|
||||||
if (asset.width != null) _buildInfoRow(context, "width".t(context: context), "${asset.width}px"),
|
if (asset.checksum != null) _buildInfoRow(context, "Checksum", asset.checksum!),
|
||||||
if (asset.height != null)
|
|
||||||
_buildInfoRow(context, "height".t(context: context), "${asset.height}px"),
|
|
||||||
_buildInfoRow(context, "created_at".t(context: context), asset.createdAt.toString()),
|
|
||||||
_buildInfoRow(context, "updated_at".t(context: context), asset.updatedAt.toString()),
|
|
||||||
if (asset.checksum != null)
|
|
||||||
_buildInfoRow(context, "checksum".t(context: context), asset.checksum!),
|
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
173
mobile/lib/presentation/pages/dev/feat_in_development.page.dart
Normal file
173
mobile/lib/presentation/pages/dev/feat_in_development.page.dart
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:drift/drift.dart' hide Column;
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
final _features = [
|
||||||
|
_Feature(
|
||||||
|
name: 'Main Timeline',
|
||||||
|
icon: Icons.timeline_rounded,
|
||||||
|
onTap: (ctx, _) => ctx.pushRoute(const TabShellRoute()),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Selection Mode Timeline',
|
||||||
|
icon: Icons.developer_mode_rounded,
|
||||||
|
onTap: (ctx, ref) async {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assets = await ref.read(remoteAssetRepositoryProvider).getSome(user.id);
|
||||||
|
|
||||||
|
final selectedAssets = await ctx.pushRoute<Set<BaseAsset>>(
|
||||||
|
DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger("FeaturesInDevelopment").fine("Selected ${selectedAssets?.length ?? 0} assets");
|
||||||
|
|
||||||
|
return Future.value();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()),
|
||||||
|
_Feature(
|
||||||
|
name: 'Sync Local',
|
||||||
|
icon: Icons.photo_album_rounded,
|
||||||
|
onTap: (_, ref) => ref.read(backgroundSyncProvider).syncLocal(),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Sync Local Full (1)',
|
||||||
|
icon: Icons.photo_library_rounded,
|
||||||
|
onTap: (_, ref) => ref.read(backgroundSyncProvider).syncLocal(full: true),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Hash Local Assets (2)',
|
||||||
|
icon: Icons.numbers_outlined,
|
||||||
|
onTap: (_, ref) => ref.read(backgroundSyncProvider).hashAssets(),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Sync Remote (3)',
|
||||||
|
icon: Icons.refresh_rounded,
|
||||||
|
onTap: (_, ref) => ref.read(backgroundSyncProvider).syncRemote(),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'WAL Checkpoint',
|
||||||
|
icon: Icons.save_rounded,
|
||||||
|
onTap: (_, ref) => ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"),
|
||||||
|
),
|
||||||
|
_Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()),
|
||||||
|
_Feature(
|
||||||
|
name: 'Clear Delta Checkpoint',
|
||||||
|
icon: Icons.delete_rounded,
|
||||||
|
onTap: (_, ref) => ref.read(nativeSyncApiProvider).clearSyncCheckpoint(),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Clear Local Data',
|
||||||
|
style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
|
||||||
|
icon: Icons.delete_forever_rounded,
|
||||||
|
onTap: (_, ref) async {
|
||||||
|
final db = ref.read(driftProvider);
|
||||||
|
await db.localAssetEntity.deleteAll();
|
||||||
|
await db.localAlbumEntity.deleteAll();
|
||||||
|
await db.localAlbumAssetEntity.deleteAll();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Clear Remote Data',
|
||||||
|
style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
|
||||||
|
icon: Icons.delete_sweep_rounded,
|
||||||
|
onTap: (_, ref) async {
|
||||||
|
final db = ref.read(driftProvider);
|
||||||
|
await db.remoteAssetEntity.deleteAll();
|
||||||
|
await db.remoteExifEntity.deleteAll();
|
||||||
|
await db.remoteAlbumEntity.deleteAll();
|
||||||
|
await db.remoteAlbumUserEntity.deleteAll();
|
||||||
|
await db.remoteAlbumAssetEntity.deleteAll();
|
||||||
|
await db.memoryEntity.deleteAll();
|
||||||
|
await db.memoryAssetEntity.deleteAll();
|
||||||
|
await db.stackEntity.deleteAll();
|
||||||
|
await db.personEntity.deleteAll();
|
||||||
|
await db.assetFaceEntity.deleteAll();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Local Media Summary',
|
||||||
|
style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold),
|
||||||
|
icon: Icons.table_chart_rounded,
|
||||||
|
onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Remote Media Summary',
|
||||||
|
style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold),
|
||||||
|
icon: Icons.summarize_rounded,
|
||||||
|
onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Reset Sqlite',
|
||||||
|
icon: Icons.table_view_rounded,
|
||||||
|
style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
|
||||||
|
onTap: (_, ref) async {
|
||||||
|
final drift = ref.read(driftProvider);
|
||||||
|
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
|
||||||
|
final migrator = drift.createMigrator();
|
||||||
|
for (final entity in drift.allSchemaEntities) {
|
||||||
|
await migrator.drop(entity);
|
||||||
|
await migrator.create(entity);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class FeatInDevPage extends StatelessWidget {
|
||||||
|
const FeatInDevPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: Text('features_in_development'.tr()), centerTitle: true),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
flex: 1,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
final feat = _features[index];
|
||||||
|
return Consumer(
|
||||||
|
builder: (ctx, ref, _) => ListTile(
|
||||||
|
title: Text(feat.name, style: feat.style),
|
||||||
|
trailing: Icon(feat.icon),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onTap: () => unawaited(feat.onTap(ctx, ref)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: _features.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Feature {
|
||||||
|
const _Feature({required this.name, required this.icon, required this.onTap, this.style});
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
final IconData icon;
|
||||||
|
final TextStyle? style;
|
||||||
|
final Future<void> Function(BuildContext, WidgetRef _) onTap;
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
||||||
import 'package:immich_ui/immich_ui.dart';
|
|
||||||
|
|
||||||
List<Widget> _showcaseBuilder(Function(ImmichVariant variant, ImmichColor color) builder) {
|
|
||||||
final children = <Widget>[];
|
|
||||||
|
|
||||||
final items = [
|
|
||||||
(variant: ImmichVariant.filled, title: "Filled Variant"),
|
|
||||||
(variant: ImmichVariant.ghost, title: "Ghost Variant"),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (final (:variant, :title) in items) {
|
|
||||||
children.add(Text(title));
|
|
||||||
children.add(Row(spacing: 10, children: [for (var color in ImmichColor.values) builder(variant, color)]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RoutePage()
|
|
||||||
class ImmichUIShowcasePage extends StatelessWidget {
|
|
||||||
const ImmichUIShowcasePage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(title: const Text('Immich UI Showcase')),
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
spacing: 10,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text("IconButton", style: context.textTheme.titleLarge),
|
|
||||||
..._showcaseBuilder(
|
|
||||||
(variant, color) =>
|
|
||||||
ImmichIconButton(icon: Icons.favorite, color: color, variant: variant, onTap: () {}),
|
|
||||||
),
|
|
||||||
Text("CloseButton", style: context.textTheme.titleLarge),
|
|
||||||
..._showcaseBuilder((variant, color) => ImmichCloseButton(color: color, variant: variant, onTap: () {})),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,19 +27,8 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
|||||||
bool isAlbumTitleTextFieldFocus = false;
|
bool isAlbumTitleTextFieldFocus = false;
|
||||||
Set<BaseAsset> selectedAssets = {};
|
Set<BaseAsset> selectedAssets = {};
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
albumTitleController.addListener(_onTitleChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onTitleChanged() {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
albumTitleController.removeListener(_onTitleChanged);
|
|
||||||
albumTitleController.dispose();
|
albumTitleController.dispose();
|
||||||
albumDescriptionController.dispose();
|
albumDescriptionController.dispose();
|
||||||
albumTitleTextFieldFocusNode.dispose();
|
albumTitleTextFieldFocusNode.dispose();
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart';
|
import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart';
|
||||||
import 'package:immich_ui/immich_ui.dart';
|
|
||||||
|
|
||||||
/// A widget for cropping an image.
|
/// A widget for cropping an image.
|
||||||
/// This widget uses [HookWidget] to manage its lifecycle and state. It allows
|
/// This widget uses [HookWidget] to manage its lifecycle and state. It allows
|
||||||
@@ -31,13 +30,11 @@ class DriftCropImagePage extends HookWidget {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: context.scaffoldBackgroundColor,
|
backgroundColor: context.scaffoldBackgroundColor,
|
||||||
title: Text("crop".tr()),
|
title: Text("crop".tr()),
|
||||||
leading: const ImmichCloseButton(),
|
leading: CloseButton(color: context.primaryColor),
|
||||||
actions: [
|
actions: [
|
||||||
ImmichIconButton(
|
IconButton(
|
||||||
icon: Icons.done_rounded,
|
icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
|
||||||
color: ImmichColor.primary,
|
onPressed: () async {
|
||||||
variant: ImmichVariant.ghost,
|
|
||||||
onTap: () async {
|
|
||||||
final croppedImage = await cropController.croppedImage();
|
final croppedImage = await cropController.croppedImage();
|
||||||
unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)));
|
unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)));
|
||||||
},
|
},
|
||||||
@@ -75,17 +72,17 @@ class DriftCropImagePage extends HookWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
ImmichIconButton(
|
IconButton(
|
||||||
icon: Icons.rotate_left,
|
icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color),
|
||||||
variant: ImmichVariant.ghost,
|
onPressed: () {
|
||||||
color: ImmichColor.secondary,
|
cropController.rotateLeft();
|
||||||
onTap: () => cropController.rotateLeft(),
|
},
|
||||||
),
|
),
|
||||||
ImmichIconButton(
|
IconButton(
|
||||||
icon: Icons.rotate_right,
|
icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color),
|
||||||
variant: ImmichVariant.ghost,
|
onPressed: () {
|
||||||
color: ImmichColor.secondary,
|
cropController.rotateRight();
|
||||||
onTap: () => cropController.rotateRight(),
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_shee
|
|||||||
enum AddToMenuItem { album, archive, unarchive, lockedFolder }
|
enum AddToMenuItem { album, archive, unarchive, lockedFolder }
|
||||||
|
|
||||||
class AddActionButton extends ConsumerStatefulWidget {
|
class AddActionButton extends ConsumerStatefulWidget {
|
||||||
const AddActionButton({super.key, this.originalTheme});
|
const AddActionButton({super.key});
|
||||||
|
|
||||||
final ThemeData? originalTheme;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<AddActionButton> createState() => _AddActionButtonState();
|
ConsumerState<AddActionButton> createState() => _AddActionButtonState();
|
||||||
@@ -73,7 +71,7 @@ class _AddActionButtonState extends ConsumerState<AddActionButton> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
if (isOwner) ...[
|
if (isOwner) ...[
|
||||||
const Divider(),
|
const PopupMenuDivider(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
child: Text("move_to".tr(), style: context.textTheme.labelMedium),
|
child: Text("move_to".tr(), style: context.textTheme.labelMedium),
|
||||||
@@ -168,27 +166,16 @@ class _AddActionButtonState extends ConsumerState<AddActionButton> {
|
|||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
final themeData = widget.originalTheme ?? context.themeData;
|
|
||||||
|
|
||||||
return MenuAnchor(
|
return MenuAnchor(
|
||||||
consumeOutsideTap: true,
|
consumeOutsideTap: true,
|
||||||
style: MenuStyle(
|
style: MenuStyle(
|
||||||
backgroundColor: WidgetStatePropertyAll(themeData.scaffoldBackgroundColor),
|
backgroundColor: WidgetStatePropertyAll(context.themeData.scaffoldBackgroundColor),
|
||||||
surfaceTintColor: const WidgetStatePropertyAll(Colors.grey),
|
|
||||||
elevation: const WidgetStatePropertyAll(4),
|
elevation: const WidgetStatePropertyAll(4),
|
||||||
shape: const WidgetStatePropertyAll(
|
shape: const WidgetStatePropertyAll(
|
||||||
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
|
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
|
||||||
),
|
),
|
||||||
padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(vertical: 6)),
|
|
||||||
),
|
),
|
||||||
menuChildren: widget.originalTheme != null
|
menuChildren: _buildMenuChildren(),
|
||||||
? [
|
|
||||||
Theme(
|
|
||||||
data: widget.originalTheme!,
|
|
||||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: _buildMenuChildren()),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: _buildMenuChildren(),
|
|
||||||
builder: (context, controller, child) {
|
builder: (context, controller, child) {
|
||||||
return BaseActionButton(
|
return BaseActionButton(
|
||||||
iconData: Icons.add,
|
iconData: Icons.add,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
|
||||||
class BaseActionButton extends ConsumerWidget {
|
class BaseActionButton extends StatelessWidget {
|
||||||
const BaseActionButton({
|
const BaseActionButton({
|
||||||
super.key,
|
super.key,
|
||||||
required this.label,
|
required this.label,
|
||||||
@@ -31,7 +30,7 @@ class BaseActionButton extends ConsumerWidget {
|
|||||||
final void Function()? onLongPressed;
|
final void Function()? onLongPressed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context) {
|
||||||
final miniWidth = minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0);
|
final miniWidth = minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0);
|
||||||
final iconTheme = IconTheme.of(context);
|
final iconTheme = IconTheme.of(context);
|
||||||
final iconSize = iconTheme.size ?? 24.0;
|
final iconSize = iconTheme.size ?? 24.0;
|
||||||
@@ -47,13 +46,14 @@ class BaseActionButton extends ConsumerWidget {
|
|||||||
|
|
||||||
if (menuItem) {
|
if (menuItem) {
|
||||||
final theme = context.themeData;
|
final theme = context.themeData;
|
||||||
|
final effectiveStyle = theme.textTheme.labelLarge;
|
||||||
final effectiveIconColor = iconColor ?? theme.iconTheme.color ?? theme.colorScheme.onSurfaceVariant;
|
final effectiveIconColor = iconColor ?? theme.iconTheme.color ?? theme.colorScheme.onSurfaceVariant;
|
||||||
|
|
||||||
return MenuItemButton(
|
return MenuItemButton(
|
||||||
style: MenuItemButton.styleFrom(alignment: Alignment.centerLeft, padding: const EdgeInsets.all(16)),
|
style: MenuItemButton.styleFrom(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12)),
|
||||||
leadingIcon: Icon(iconData, color: effectiveIconColor),
|
leadingIcon: Icon(iconData, color: effectiveIconColor, size: 20),
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
child: Text(label, style: theme.textTheme.labelLarge?.copyWith(fontSize: 16)),
|
child: Text(label, style: effectiveStyle),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:immich_mobile/providers/cast.provider.dart';
|
|||||||
import 'package:immich_mobile/widgets/asset_viewer/cast_dialog.dart';
|
import 'package:immich_mobile/widgets/asset_viewer/cast_dialog.dart';
|
||||||
|
|
||||||
class CastActionButton extends ConsumerWidget {
|
class CastActionButton extends ConsumerWidget {
|
||||||
const CastActionButton({super.key, this.iconOnly = false, this.menuItem = false});
|
const CastActionButton({super.key, this.iconOnly = true, this.menuItem = false});
|
||||||
|
|
||||||
final bool iconOnly;
|
final bool iconOnly;
|
||||||
final bool menuItem;
|
final bool menuItem;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_bu
|
|||||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||||
|
|
||||||
class MotionPhotoActionButton extends ConsumerWidget {
|
class MotionPhotoActionButton extends ConsumerWidget {
|
||||||
const MotionPhotoActionButton({super.key, this.iconOnly = false, this.menuItem = false});
|
const MotionPhotoActionButton({super.key, this.iconOnly = true, this.menuItem = false});
|
||||||
|
|
||||||
final bool iconOnly;
|
final bool iconOnly;
|
||||||
final bool menuItem;
|
final bool menuItem;
|
||||||
|
|||||||
@@ -38,13 +38,11 @@ class ViewerBottomBar extends ConsumerWidget {
|
|||||||
opacity = 0;
|
opacity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
final originalTheme = context.themeData;
|
|
||||||
|
|
||||||
final actions = <Widget>[
|
final actions = <Widget>[
|
||||||
const ShareActionButton(source: ActionSource.viewer),
|
const ShareActionButton(source: ActionSource.viewer),
|
||||||
if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer),
|
if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer),
|
||||||
if (asset.type == AssetType.image) const EditImageActionButton(),
|
if (asset.type == AssetType.image) const EditImageActionButton(),
|
||||||
if (asset.hasRemote) AddActionButton(originalTheme: originalTheme),
|
if (asset.hasRemote) const AddActionButton(),
|
||||||
|
|
||||||
if (isOwner) ...[
|
if (isOwner) ...[
|
||||||
asset.isLocalOnly
|
asset.isLocalOnly
|
||||||
|
|||||||
@@ -4,19 +4,26 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/constants/enums.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/events.model.dart';
|
import 'package:immich_mobile/domain/models/events.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/action_buttons/cast_action_button.widget.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/favorite_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/favorite_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart';
|
||||||
import 'package:immich_mobile/providers/activity.provider.dart';
|
import 'package:immich_mobile/providers/activity.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/cast.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/providers/routes.provider.dart';
|
import 'package:immich_mobile/providers/routes.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
|
||||||
class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
||||||
const ViewerTopAppBar({super.key});
|
const ViewerTopAppBar({super.key});
|
||||||
@@ -35,6 +42,15 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||||||
final isInLockedView = ref.watch(inLockedViewProvider);
|
final isInLockedView = ref.watch(inLockedViewProvider);
|
||||||
final isReadonlyModeEnabled = ref.watch(readonlyModeProvider);
|
final isReadonlyModeEnabled = ref.watch(readonlyModeProvider);
|
||||||
|
|
||||||
|
final timelineOrigin = ref.read(timelineServiceProvider).origin;
|
||||||
|
final showViewInTimelineButton =
|
||||||
|
timelineOrigin != TimelineOrigin.main &&
|
||||||
|
timelineOrigin != TimelineOrigin.deepLink &&
|
||||||
|
timelineOrigin != TimelineOrigin.trash &&
|
||||||
|
timelineOrigin != TimelineOrigin.archive &&
|
||||||
|
timelineOrigin != TimelineOrigin.localAlbum &&
|
||||||
|
isOwner;
|
||||||
|
|
||||||
final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
|
final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
|
||||||
int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity));
|
int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity));
|
||||||
final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||||
@@ -47,10 +63,11 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||||||
opacity = 0;
|
opacity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
final originalTheme = context.themeData;
|
final isCasting = ref.watch(castProvider.select((c) => c.isCasting));
|
||||||
|
|
||||||
final actions = <Widget>[
|
final actions = <Widget>[
|
||||||
if (asset.isMotionPhoto) const MotionPhotoActionButton(iconOnly: true),
|
if (asset.isRemoteOnly) const DownloadActionButton(source: ActionSource.viewer, iconOnly: true),
|
||||||
|
if (isCasting || (asset.hasRemote)) const CastActionButton(iconOnly: true),
|
||||||
if (album != null && album.isActivityEnabled && album.isShared)
|
if (album != null && album.isActivityEnabled && album.isShared)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.chat_outlined),
|
icon: const Icon(Icons.chat_outlined),
|
||||||
@@ -58,16 +75,28 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||||||
EventStream.shared.emit(const ViewerOpenBottomSheetEvent(activitiesMode: true));
|
EventStream.shared.emit(const ViewerOpenBottomSheetEvent(activitiesMode: true));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (showViewInTimelineButton)
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await context.maybePop();
|
||||||
|
await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()]));
|
||||||
|
EventStream.shared.emit(ScrollToDateEvent(asset.createdAt));
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.image_search),
|
||||||
|
tooltip: 'view_in_timeline'.t(context: context),
|
||||||
|
),
|
||||||
if (asset.hasRemote && isOwner && !asset.isFavorite)
|
if (asset.hasRemote && isOwner && !asset.isFavorite)
|
||||||
const FavoriteActionButton(source: ActionSource.viewer, iconOnly: true),
|
const FavoriteActionButton(source: ActionSource.viewer, iconOnly: true),
|
||||||
if (asset.hasRemote && isOwner && asset.isFavorite)
|
if (asset.hasRemote && isOwner && asset.isFavorite)
|
||||||
const UnFavoriteActionButton(source: ActionSource.viewer, iconOnly: true),
|
const UnFavoriteActionButton(source: ActionSource.viewer, iconOnly: true),
|
||||||
|
if (asset.isMotionPhoto) const MotionPhotoActionButton(iconOnly: true),
|
||||||
ViewerKebabMenu(originalTheme: originalTheme),
|
const ViewerKebabMenu(),
|
||||||
];
|
];
|
||||||
|
|
||||||
final lockedViewActions = <Widget>[ViewerKebabMenu(originalTheme: originalTheme)];
|
final lockedViewActions = <Widget>[
|
||||||
|
if (isCasting || (asset.hasRemote)) const CastActionButton(iconOnly: true),
|
||||||
|
const ViewerKebabMenu(),
|
||||||
|
];
|
||||||
|
|
||||||
return IgnorePointer(
|
return IgnorePointer(
|
||||||
ignoring: opacity < 255,
|
ignoring: opacity < 255,
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/events.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/cast.provider.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
|
||||||
import 'package:immich_mobile/utils/action_button.utils.dart';
|
|
||||||
|
|
||||||
class ViewerKebabMenu extends ConsumerWidget {
|
class ViewerKebabMenu extends ConsumerWidget {
|
||||||
const ViewerKebabMenu({super.key, this.originalTheme});
|
const ViewerKebabMenu({super.key});
|
||||||
|
|
||||||
final ThemeData? originalTheme;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@@ -20,42 +17,25 @@ class ViewerKebabMenu extends ConsumerWidget {
|
|||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
final user = ref.watch(currentUserProvider);
|
final menuChildren = <Widget>[
|
||||||
final isOwner = asset is RemoteAsset && asset.ownerId == user?.id;
|
BaseActionButton(
|
||||||
final isCasting = ref.watch(castProvider.select((c) => c.isCasting));
|
label: 'about'.tr(),
|
||||||
final timelineOrigin = ref.read(timelineServiceProvider).origin;
|
iconData: Icons.info_outline,
|
||||||
|
menuItem: true,
|
||||||
final kebabContext = ViewerKebabMenuButtonContext(
|
onPressed: () => EventStream.shared.emit(const ViewerOpenBottomSheetEvent()),
|
||||||
asset: asset,
|
),
|
||||||
isOwner: isOwner,
|
];
|
||||||
isCasting: isCasting,
|
|
||||||
timelineOrigin: timelineOrigin,
|
|
||||||
originalTheme: originalTheme,
|
|
||||||
);
|
|
||||||
|
|
||||||
final menuChildren = ViewerKebabMenuButtonBuilder.build(kebabContext, context, ref);
|
|
||||||
|
|
||||||
return MenuAnchor(
|
return MenuAnchor(
|
||||||
consumeOutsideTap: true,
|
consumeOutsideTap: true,
|
||||||
style: MenuStyle(
|
style: MenuStyle(
|
||||||
backgroundColor: WidgetStatePropertyAll(context.themeData.scaffoldBackgroundColor),
|
backgroundColor: WidgetStatePropertyAll(context.themeData.scaffoldBackgroundColor),
|
||||||
surfaceTintColor: const WidgetStatePropertyAll(Colors.grey),
|
|
||||||
elevation: const WidgetStatePropertyAll(4),
|
elevation: const WidgetStatePropertyAll(4),
|
||||||
shape: const WidgetStatePropertyAll(
|
shape: const WidgetStatePropertyAll(
|
||||||
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
|
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
|
||||||
),
|
),
|
||||||
padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(vertical: 6)),
|
|
||||||
),
|
),
|
||||||
menuChildren: [
|
menuChildren: menuChildren,
|
||||||
ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(minWidth: 150),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: menuChildren,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
builder: (context, controller, child) {
|
builder: (context, controller, child) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
icon: const Icon(Icons.more_vert_rounded),
|
icon: const Icon(Icons.more_vert_rounded),
|
||||||
|
|||||||
@@ -324,11 +324,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||||||
final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10;
|
final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10;
|
||||||
|
|
||||||
const scrubberBottomPadding = 100.0;
|
const scrubberBottomPadding = 100.0;
|
||||||
const bottomSheetOpenModifier = 120.0;
|
final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding);
|
||||||
final bottomPadding =
|
|
||||||
context.padding.bottom +
|
|
||||||
(widget.appBar == null ? 0 : scrubberBottomPadding) +
|
|
||||||
(isMultiSelectEnabled ? bottomSheetOpenModifier : 0);
|
|
||||||
|
|
||||||
final grid = CustomScrollView(
|
final grid = CustomScrollView(
|
||||||
primary: true,
|
primary: true,
|
||||||
@@ -351,7 +347,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||||||
addRepaintBoundaries: false,
|
addRepaintBoundaries: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverPadding(padding: EdgeInsets.only(bottom: bottomPadding)),
|
const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ import 'package:immich_mobile/pages/search/recently_taken.page.dart';
|
|||||||
import 'package:immich_mobile/pages/search/search.page.dart';
|
import 'package:immich_mobile/pages/search/search.page.dart';
|
||||||
import 'package:immich_mobile/pages/settings/sync_status.page.dart';
|
import 'package:immich_mobile/pages/settings/sync_status.page.dart';
|
||||||
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/ui_showcase.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/download_info.page.dart';
|
import 'package:immich_mobile/presentation/pages/download_info.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_activities.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_activities.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
||||||
@@ -286,6 +286,7 @@ class AppRouter extends RootStackRouter {
|
|||||||
AutoRoute(page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: LockedRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]),
|
AutoRoute(page: LockedRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: PinAuthRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: PinAuthRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
|
AutoRoute(page: FeatInDevRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: DriftBackupRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DriftBackupRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
@@ -337,7 +338,6 @@ class AppRouter extends RootStackRouter {
|
|||||||
AutoRoute(page: DriftBackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DriftBackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: DownloadInfoRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DownloadInfoRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: ImmichUIShowcaseRoute.page, guards: [_authGuard, _duplicateGuard]),
|
|
||||||
// required to handle all deeplinks in deep_link.service.dart
|
// required to handle all deeplinks in deep_link.service.dart
|
||||||
// auto_route_library#1722
|
// auto_route_library#1722
|
||||||
RedirectRoute(path: '*', redirectTo: '/'),
|
RedirectRoute(path: '*', redirectTo: '/'),
|
||||||
|
|||||||
@@ -1648,6 +1648,22 @@ class FavoritesRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [FeatInDevPage]
|
||||||
|
class FeatInDevRoute extends PageRouteInfo<void> {
|
||||||
|
const FeatInDevRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(FeatInDevRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'FeatInDevRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const FeatInDevPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [FilterImagePage]
|
/// [FilterImagePage]
|
||||||
class FilterImageRoute extends PageRouteInfo<FilterImageRouteArgs> {
|
class FilterImageRoute extends PageRouteInfo<FilterImageRouteArgs> {
|
||||||
@@ -1815,22 +1831,6 @@ class HeaderSettingsRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
|
||||||
/// [ImmichUIShowcasePage]
|
|
||||||
class ImmichUIShowcaseRoute extends PageRouteInfo<void> {
|
|
||||||
const ImmichUIShowcaseRoute({List<PageRouteInfo>? children})
|
|
||||||
: super(ImmichUIShowcaseRoute.name, initialChildren: children);
|
|
||||||
|
|
||||||
static const String name = 'ImmichUIShowcaseRoute';
|
|
||||||
|
|
||||||
static PageInfo page = PageInfo(
|
|
||||||
name,
|
|
||||||
builder: (data) {
|
|
||||||
return const ImmichUIShowcasePage();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [LibraryPage]
|
/// [LibraryPage]
|
||||||
class LibraryRoute extends PageRouteInfo<void> {
|
class LibraryRoute extends PageRouteInfo<void> {
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/constants/enums.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/events.model.dart';
|
|
||||||
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
|
||||||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
|
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/cast_action_button.widget.dart';
|
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart';
|
||||||
@@ -28,7 +19,6 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_b
|
|||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
|
||||||
|
|
||||||
class ActionButtonContext {
|
class ActionButtonContext {
|
||||||
final BaseAsset asset;
|
final BaseAsset asset;
|
||||||
@@ -174,98 +164,3 @@ class ActionButtonBuilder {
|
|||||||
return _actionTypes.where((type) => type.shouldShow(context)).map((type) => type.buildButton(context)).toList();
|
return _actionTypes.where((type) => type.shouldShow(context)).map((type) => type.buildButton(context)).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewerKebabMenuButtonContext {
|
|
||||||
final BaseAsset asset;
|
|
||||||
final bool isOwner;
|
|
||||||
final bool isCasting;
|
|
||||||
final TimelineOrigin timelineOrigin;
|
|
||||||
final ThemeData? originalTheme;
|
|
||||||
|
|
||||||
const ViewerKebabMenuButtonContext({
|
|
||||||
required this.asset,
|
|
||||||
required this.isOwner,
|
|
||||||
required this.isCasting,
|
|
||||||
required this.timelineOrigin,
|
|
||||||
this.originalTheme,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ViewerKebabMenuButtonType {
|
|
||||||
openInfo,
|
|
||||||
viewInTimeline,
|
|
||||||
cast,
|
|
||||||
download;
|
|
||||||
|
|
||||||
/// Defines which group each button belongs to.
|
|
||||||
/// Buttons in the same group will be displayed together,
|
|
||||||
/// with dividers separating different groups.
|
|
||||||
int get group => switch (this) {
|
|
||||||
ViewerKebabMenuButtonType.openInfo => 0,
|
|
||||||
ViewerKebabMenuButtonType.viewInTimeline => 1,
|
|
||||||
ViewerKebabMenuButtonType.cast => 1,
|
|
||||||
ViewerKebabMenuButtonType.download => 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool shouldShow(ViewerKebabMenuButtonContext context) {
|
|
||||||
return switch (this) {
|
|
||||||
ViewerKebabMenuButtonType.openInfo => true,
|
|
||||||
ViewerKebabMenuButtonType.viewInTimeline =>
|
|
||||||
context.timelineOrigin != TimelineOrigin.main &&
|
|
||||||
context.timelineOrigin != TimelineOrigin.deepLink &&
|
|
||||||
context.timelineOrigin != TimelineOrigin.trash &&
|
|
||||||
context.timelineOrigin != TimelineOrigin.archive &&
|
|
||||||
context.timelineOrigin != TimelineOrigin.localAlbum &&
|
|
||||||
context.isOwner,
|
|
||||||
ViewerKebabMenuButtonType.cast => context.isCasting || context.asset.hasRemote,
|
|
||||||
ViewerKebabMenuButtonType.download => context.asset.isRemoteOnly,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsumerWidget buildButton(ViewerKebabMenuButtonContext context, BuildContext buildContext) {
|
|
||||||
return switch (this) {
|
|
||||||
ViewerKebabMenuButtonType.openInfo => BaseActionButton(
|
|
||||||
label: 'info'.tr(),
|
|
||||||
iconData: Icons.info_outline,
|
|
||||||
iconColor: context.originalTheme?.iconTheme.color,
|
|
||||||
menuItem: true,
|
|
||||||
onPressed: () => EventStream.shared.emit(const ViewerOpenBottomSheetEvent()),
|
|
||||||
),
|
|
||||||
|
|
||||||
ViewerKebabMenuButtonType.viewInTimeline => BaseActionButton(
|
|
||||||
label: 'view_in_timeline'.t(context: buildContext),
|
|
||||||
iconData: Icons.image_search,
|
|
||||||
iconColor: context.originalTheme?.iconTheme.color,
|
|
||||||
menuItem: true,
|
|
||||||
onPressed: () async {
|
|
||||||
await buildContext.maybePop();
|
|
||||||
await buildContext.navigateTo(const TabShellRoute(children: [MainTimelineRoute()]));
|
|
||||||
EventStream.shared.emit(ScrollToDateEvent(context.asset.createdAt));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ViewerKebabMenuButtonType.cast => const CastActionButton(menuItem: true),
|
|
||||||
ViewerKebabMenuButtonType.download => const DownloadActionButton(source: ActionSource.viewer, menuItem: true),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewerKebabMenuButtonBuilder {
|
|
||||||
static List<Widget> build(ViewerKebabMenuButtonContext context, BuildContext buildContext, WidgetRef ref) {
|
|
||||||
final visibleButtons = ViewerKebabMenuButtonType.values.where((type) => type.shouldShow(context)).toList();
|
|
||||||
|
|
||||||
if (visibleButtons.isEmpty) return [];
|
|
||||||
|
|
||||||
final List<Widget> result = [];
|
|
||||||
int? lastGroup;
|
|
||||||
|
|
||||||
for (final type in visibleButtons) {
|
|
||||||
if (lastGroup != null && type.group != lastGroup) {
|
|
||||||
result.add(const Divider(height: 1));
|
|
||||||
}
|
|
||||||
result.add(type.buildButton(context, buildContext).build(buildContext, ref));
|
|
||||||
lastGroup = type.group;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -53,18 +53,16 @@ class ServerUpdateNotification extends HookConsumerWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Text(
|
||||||
child: Text(
|
serverInfoState.versionStatus.message,
|
||||||
serverInfoState.versionStatus.message,
|
textAlign: TextAlign.start,
|
||||||
textAlign: TextAlign.start,
|
maxLines: 3,
|
||||||
maxLines: 3,
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: context.textTheme.labelLarge,
|
||||||
style: context.textTheme.labelLarge,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (serverInfoState.versionStatus == VersionStatus.serverOutOfDate ||
|
if (serverInfoState.versionStatus == VersionStatus.serverOutOfDate ||
|
||||||
serverInfoState.versionStatus == VersionStatus.clientOutOfDate) ...[
|
serverInfoState.versionStatus == VersionStatus.clientOutOfDate) ...[
|
||||||
const SizedBox(width: 8),
|
const Spacer(),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: openUpdateLink,
|
onPressed: openUpdateLink,
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
|
|||||||
@@ -155,8 +155,8 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||||||
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
|
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
|
||||||
if (kDebugMode || kProfileMode)
|
if (kDebugMode || kProfileMode)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.palette_rounded),
|
icon: const Icon(Icons.science_rounded),
|
||||||
onPressed: () => context.pushRoute(const ImmichUIShowcaseRoute()),
|
onPressed: () => context.pushRoute(const FeatInDevRoute()),
|
||||||
),
|
),
|
||||||
if (isCasting)
|
if (isCasting)
|
||||||
Padding(
|
Padding(
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ class ImmichSliverAppBar extends ConsumerWidget {
|
|||||||
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
|
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
|
||||||
if ((kDebugMode || kProfileMode) && !isReadonlyModeEnabled)
|
if ((kDebugMode || kProfileMode) && !isReadonlyModeEnabled)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.palette_rounded),
|
icon: const Icon(Icons.science_rounded),
|
||||||
onPressed: () => context.pushRoute(const ImmichUIShowcaseRoute()),
|
onPressed: () => context.pushRoute(const FeatInDevRoute()),
|
||||||
),
|
),
|
||||||
if (showUploadButton && !isReadonlyModeEnabled)
|
if (showUploadButton && !isReadonlyModeEnabled)
|
||||||
const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()),
|
const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()),
|
||||||
|
|||||||
@@ -2,27 +2,26 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
|
|
||||||
class EntityCountTile extends StatelessWidget {
|
class EntitiyCountTile extends StatelessWidget {
|
||||||
final int count;
|
final int count;
|
||||||
final String label;
|
final String label;
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
|
|
||||||
const EntityCountTile({super.key, required this.count, required this.label, required this.icon});
|
const EntitiyCountTile({super.key, required this.count, required this.label, required this.icon});
|
||||||
|
|
||||||
String zeroPadding(int number, int targetWidth) {
|
String zeroPadding(int number, int targetWidth) {
|
||||||
final numStr = number.toString();
|
final numStr = number.toString();
|
||||||
return numStr.length < targetWidth ? "0" * (targetWidth - numStr.length) : "";
|
return numStr.length < targetWidth ? "0" * (targetWidth - numStr.length) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int calculateMaxDigits(double availableWidth) {
|
||||||
|
const double charWidth = 11.0;
|
||||||
|
return (availableWidth / charWidth).floor().clamp(1, 8);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
final availableWidth = (screenWidth - 32 - 8) / 2;
|
|
||||||
const double charWidth = 11.0;
|
|
||||||
final maxDigits = ((availableWidth - 32) / charWidth).floor().clamp(1, 8);
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
height: double.infinity,
|
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.colorScheme.surfaceContainerLow,
|
color: context.colorScheme.surfaceContainerLow,
|
||||||
@@ -30,6 +29,7 @@ class EntityCountTile extends StatelessWidget {
|
|||||||
border: Border.all(width: 0.5, color: context.colorScheme.outline.withAlpha(25)),
|
border: Border.all(width: 0.5, color: context.colorScheme.outline.withAlpha(25)),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// Icon and Label
|
// Icon and Label
|
||||||
@@ -38,30 +38,33 @@ class EntityCountTile extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Icon(icon, color: context.primaryColor),
|
Icon(icon, color: context.primaryColor),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Flexible(
|
Text(
|
||||||
child: Text(
|
label,
|
||||||
label,
|
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16),
|
||||||
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
// Number
|
// Number
|
||||||
const Spacer(),
|
LayoutBuilder(
|
||||||
RichText(
|
builder: (context, constraints) {
|
||||||
text: TextSpan(
|
final maxDigits = calculateMaxDigits(constraints.maxWidth);
|
||||||
style: const TextStyle(fontSize: 18, fontFamily: 'OverpassMono', fontWeight: FontWeight.w600),
|
return RichText(
|
||||||
children: [
|
text: TextSpan(
|
||||||
TextSpan(
|
style: const TextStyle(fontSize: 18, fontFamily: 'OverpassMono', fontWeight: FontWeight.w600),
|
||||||
text: zeroPadding(count, maxDigits),
|
children: [
|
||||||
style: TextStyle(color: context.colorScheme.onSurfaceSecondary.withAlpha(75)),
|
TextSpan(
|
||||||
|
text: zeroPadding(count, maxDigits),
|
||||||
|
style: TextStyle(color: context.colorScheme.onSurfaceSecondary.withAlpha(75)),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: count.toString(),
|
||||||
|
style: TextStyle(color: context.primaryColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
TextSpan(
|
);
|
||||||
text: count.toString(),
|
},
|
||||||
style: TextStyle(color: context.primaryColor),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -282,87 +282,76 @@ class _SyncStatsCounts extends ConsumerWidget {
|
|||||||
_SectionHeaderText(text: "assets".t(context: context)),
|
_SectionHeaderText(text: "assets".t(context: context)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
||||||
// 1. Wrap in IntrinsicHeight
|
child: Flex(
|
||||||
child: IntrinsicHeight(
|
direction: Axis.horizontal,
|
||||||
child: Flex(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
direction: Axis.horizontal,
|
spacing: 8.0,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
children: [
|
||||||
// 2. Stretch children vertically to fill the IntrinsicHeight
|
Expanded(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
child: EntitiyCountTile(
|
||||||
spacing: 8.0,
|
label: "local".t(context: context),
|
||||||
children: [
|
count: localAssetCount,
|
||||||
Expanded(
|
icon: Icons.smartphone,
|
||||||
child: EntityCountTile(
|
|
||||||
label: "local".t(context: context),
|
|
||||||
count: localAssetCount,
|
|
||||||
icon: Icons.smartphone,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: EntityCountTile(
|
Expanded(
|
||||||
label: "remote".t(context: context),
|
child: EntitiyCountTile(
|
||||||
count: remoteAssetCount,
|
label: "remote".t(context: context),
|
||||||
icon: Icons.cloud,
|
count: remoteAssetCount,
|
||||||
),
|
icon: Icons.cloud,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_SectionHeaderText(text: "albums".t(context: context)),
|
_SectionHeaderText(text: "albums".t(context: context)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
||||||
child: IntrinsicHeight(
|
child: Flex(
|
||||||
child: Flex(
|
direction: Axis.horizontal,
|
||||||
direction: Axis.horizontal,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
spacing: 8.0,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch, // Added
|
children: [
|
||||||
spacing: 8.0,
|
Expanded(
|
||||||
children: [
|
child: EntitiyCountTile(
|
||||||
Expanded(
|
label: "local".t(context: context),
|
||||||
child: EntityCountTile(
|
count: localAlbumCount,
|
||||||
label: "local".t(context: context),
|
icon: Icons.smartphone,
|
||||||
count: localAlbumCount,
|
|
||||||
icon: Icons.smartphone,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: EntityCountTile(
|
Expanded(
|
||||||
label: "remote".t(context: context),
|
child: EntitiyCountTile(
|
||||||
count: remoteAlbumCount,
|
label: "remote".t(context: context),
|
||||||
icon: Icons.cloud,
|
count: remoteAlbumCount,
|
||||||
),
|
icon: Icons.cloud,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_SectionHeaderText(text: "other".t(context: context)),
|
_SectionHeaderText(text: "other".t(context: context)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
||||||
child: IntrinsicHeight(
|
child: Flex(
|
||||||
child: Flex(
|
direction: Axis.horizontal,
|
||||||
direction: Axis.horizontal,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
spacing: 8.0,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch, // Added
|
children: [
|
||||||
spacing: 8.0,
|
Expanded(
|
||||||
children: [
|
child: EntitiyCountTile(
|
||||||
Expanded(
|
label: "memories".t(context: context),
|
||||||
child: EntityCountTile(
|
count: memoryCount,
|
||||||
label: "memories".t(context: context),
|
icon: Icons.calendar_today,
|
||||||
count: memoryCount,
|
|
||||||
icon: Icons.calendar_today,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: EntityCountTile(
|
Expanded(
|
||||||
label: "hashed_assets".t(context: context),
|
child: EntitiyCountTile(
|
||||||
count: localHashedCount,
|
label: "hashed_assets".t(context: context),
|
||||||
icon: Icons.tag,
|
count: localHashedCount,
|
||||||
),
|
icon: Icons.tag,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// To be removed once the experimental feature is stable
|
// To be removed once the experimental feature is stable
|
||||||
@@ -375,29 +364,26 @@ class _SyncStatsCounts extends ConsumerWidget {
|
|||||||
return counts.when(
|
return counts.when(
|
||||||
data: (c) => Padding(
|
data: (c) => Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
||||||
child: IntrinsicHeight(
|
child: Flex(
|
||||||
child: Flex(
|
direction: Axis.horizontal,
|
||||||
direction: Axis.horizontal,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
spacing: 8.0,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch, // Added
|
children: [
|
||||||
spacing: 8.0,
|
Expanded(
|
||||||
children: [
|
child: EntitiyCountTile(
|
||||||
Expanded(
|
label: "local".t(context: context),
|
||||||
child: EntityCountTile(
|
count: c.total,
|
||||||
label: "local".t(context: context),
|
icon: Icons.delete_outline,
|
||||||
count: c.total,
|
|
||||||
icon: Icons.delete_outline,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: EntityCountTile(
|
Expanded(
|
||||||
label: "hashed_assets".t(context: context),
|
child: EntitiyCountTile(
|
||||||
count: c.hashed,
|
label: "hashed_assets".t(context: context),
|
||||||
icon: Icons.tag,
|
count: c.hashed,
|
||||||
),
|
icon: Icons.tag,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
loading: () => const CircularProgressIndicator(),
|
loading: () => const CircularProgressIndicator(),
|
||||||
|
|||||||
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@@ -3,7 +3,7 @@ Immich API
|
|||||||
|
|
||||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||||
|
|
||||||
- API version: 2.4.0
|
- API version: 2.3.1
|
||||||
- Generator version: 7.8.0
|
- Generator version: 7.8.0
|
||||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export 'src/buttons/close_button.dart';
|
|
||||||
export 'src/buttons/icon_button.dart';
|
|
||||||
export 'src/types.dart';
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:immich_ui/src/buttons/icon_button.dart';
|
|
||||||
import 'package:immich_ui/src/types.dart';
|
|
||||||
|
|
||||||
class ImmichCloseButton extends StatelessWidget {
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
final ImmichVariant variant;
|
|
||||||
final ImmichColor color;
|
|
||||||
|
|
||||||
const ImmichCloseButton({
|
|
||||||
super.key,
|
|
||||||
this.onTap,
|
|
||||||
this.color = ImmichColor.primary,
|
|
||||||
this.variant = ImmichVariant.ghost,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => ImmichIconButton(
|
|
||||||
key: key,
|
|
||||||
icon: Icons.close,
|
|
||||||
color: color,
|
|
||||||
variant: variant,
|
|
||||||
onTap: onTap ?? () => Navigator.of(context).pop(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:immich_ui/src/types.dart';
|
|
||||||
|
|
||||||
class ImmichIconButton extends StatelessWidget {
|
|
||||||
final IconData icon;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final ImmichVariant variant;
|
|
||||||
final ImmichColor color;
|
|
||||||
|
|
||||||
const ImmichIconButton({
|
|
||||||
super.key,
|
|
||||||
required this.icon,
|
|
||||||
required this.onTap,
|
|
||||||
this.color = ImmichColor.primary,
|
|
||||||
this.variant = ImmichVariant.filled,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final background = switch (variant) {
|
|
||||||
ImmichVariant.filled => switch (color) {
|
|
||||||
ImmichColor.primary => Theme.of(context).colorScheme.primary,
|
|
||||||
ImmichColor.secondary => Theme.of(context).colorScheme.secondary,
|
|
||||||
},
|
|
||||||
ImmichVariant.ghost => Colors.transparent,
|
|
||||||
};
|
|
||||||
|
|
||||||
final foreground = switch (variant) {
|
|
||||||
ImmichVariant.filled => switch (color) {
|
|
||||||
ImmichColor.primary => Theme.of(context).colorScheme.onPrimary,
|
|
||||||
ImmichColor.secondary => Theme.of(context).colorScheme.onSecondary,
|
|
||||||
},
|
|
||||||
ImmichVariant.ghost => switch (color) {
|
|
||||||
ImmichColor.primary => Theme.of(context).colorScheme.primary,
|
|
||||||
ImmichColor.secondary => Theme.of(context).colorScheme.secondary,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return IconButton(
|
|
||||||
icon: Icon(icon),
|
|
||||||
onPressed: onTap,
|
|
||||||
style: IconButton.styleFrom(
|
|
||||||
backgroundColor: background,
|
|
||||||
foregroundColor: foreground,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
enum ImmichVariant {
|
|
||||||
filled,
|
|
||||||
ghost,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ImmichColor {
|
|
||||||
primary,
|
|
||||||
secondary,
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# Generated by pub
|
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
|
||||||
packages:
|
|
||||||
characters:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: characters
|
|
||||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.4.0"
|
|
||||||
collection:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: collection
|
|
||||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.19.1"
|
|
||||||
flutter:
|
|
||||||
dependency: "direct main"
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
material_color_utilities:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: material_color_utilities
|
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.11.1"
|
|
||||||
meta:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: meta
|
|
||||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.16.0"
|
|
||||||
sky_engine:
|
|
||||||
dependency: transitive
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
vector_math:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: vector_math
|
|
||||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
sdks:
|
|
||||||
dart: ">=3.8.0-0 <4.0.0"
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
name: immich_ui
|
|
||||||
publish_to: none
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
||||||
@@ -1015,13 +1015,6 @@ packages:
|
|||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
immich_ui:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
path: "packages/ui"
|
|
||||||
relative: true
|
|
||||||
source: path
|
|
||||||
version: "0.0.0"
|
|
||||||
integration_test:
|
integration_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: immich_mobile
|
|||||||
description: Immich - selfhosted backup media file on mobile phone
|
description: Immich - selfhosted backup media file on mobile phone
|
||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 2.4.0+3029
|
version: 2.3.1+3028
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.8.0 <4.0.0'
|
sdk: '>=3.8.0 <4.0.0'
|
||||||
@@ -43,8 +43,6 @@ dependencies:
|
|||||||
hooks_riverpod: ^2.6.1
|
hooks_riverpod: ^2.6.1
|
||||||
http: ^1.5.0
|
http: ^1.5.0
|
||||||
image_picker: ^1.2.0
|
image_picker: ^1.2.0
|
||||||
immich_ui:
|
|
||||||
path: './packages/ui'
|
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
isar:
|
isar:
|
||||||
git:
|
git:
|
||||||
|
|||||||
@@ -14268,7 +14268,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Immich",
|
"title": "Immich",
|
||||||
"description": "Immich API",
|
"description": "Immich API",
|
||||||
"version": "2.4.0",
|
"version": "2.3.1",
|
||||||
"contact": {}
|
"contact": {}
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "2.4.0",
|
"version": "2.3.1",
|
||||||
"description": "Auto-generated TypeScript SDK for the Immich API",
|
"description": "Auto-generated TypeScript SDK for the Immich API",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Immich
|
* Immich
|
||||||
* 2.4.0
|
* 2.3.1
|
||||||
* DO NOT MODIFY - This file has been generated using oazapfts.
|
* DO NOT MODIFY - This file has been generated using oazapfts.
|
||||||
* See https://www.npmjs.com/package/oazapfts
|
* See https://www.npmjs.com/package/oazapfts
|
||||||
*/
|
*/
|
||||||
|
|||||||
214
plugins/package-lock.json
generated
214
plugins/package-lock.json
generated
@@ -15,9 +15,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz",
|
||||||
"integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==",
|
"integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz",
|
||||||
"integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==",
|
"integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz",
|
||||||
"integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==",
|
"integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -66,9 +66,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz",
|
||||||
"integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==",
|
"integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -83,9 +83,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz",
|
||||||
"integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==",
|
"integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -100,9 +100,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz",
|
||||||
"integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==",
|
"integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -117,9 +117,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz",
|
||||||
"integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==",
|
"integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -134,9 +134,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz",
|
||||||
"integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==",
|
"integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -151,9 +151,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz",
|
||||||
"integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==",
|
"integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -168,9 +168,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz",
|
||||||
"integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==",
|
"integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -185,9 +185,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz",
|
||||||
"integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==",
|
"integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -202,9 +202,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz",
|
||||||
"integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==",
|
"integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -219,9 +219,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz",
|
||||||
"integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==",
|
"integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@@ -236,9 +236,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz",
|
||||||
"integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==",
|
"integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -253,9 +253,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz",
|
||||||
"integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==",
|
"integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -270,9 +270,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz",
|
||||||
"integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==",
|
"integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -287,9 +287,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz",
|
||||||
"integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==",
|
"integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -304,9 +304,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz",
|
||||||
"integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==",
|
"integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -321,9 +321,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz",
|
||||||
"integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==",
|
"integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -338,9 +338,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz",
|
||||||
"integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==",
|
"integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -355,9 +355,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz",
|
||||||
"integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==",
|
"integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -372,9 +372,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openharmony-arm64": {
|
"node_modules/@esbuild/openharmony-arm64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz",
|
||||||
"integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==",
|
"integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -389,9 +389,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz",
|
||||||
"integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==",
|
"integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -406,9 +406,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz",
|
||||||
"integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==",
|
"integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -423,9 +423,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz",
|
||||||
"integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==",
|
"integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -440,9 +440,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz",
|
||||||
"integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==",
|
"integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -467,9 +467,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.27.1",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz",
|
||||||
"integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==",
|
"integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -480,32 +480,32 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/aix-ppc64": "0.27.1",
|
"@esbuild/aix-ppc64": "0.27.0",
|
||||||
"@esbuild/android-arm": "0.27.1",
|
"@esbuild/android-arm": "0.27.0",
|
||||||
"@esbuild/android-arm64": "0.27.1",
|
"@esbuild/android-arm64": "0.27.0",
|
||||||
"@esbuild/android-x64": "0.27.1",
|
"@esbuild/android-x64": "0.27.0",
|
||||||
"@esbuild/darwin-arm64": "0.27.1",
|
"@esbuild/darwin-arm64": "0.27.0",
|
||||||
"@esbuild/darwin-x64": "0.27.1",
|
"@esbuild/darwin-x64": "0.27.0",
|
||||||
"@esbuild/freebsd-arm64": "0.27.1",
|
"@esbuild/freebsd-arm64": "0.27.0",
|
||||||
"@esbuild/freebsd-x64": "0.27.1",
|
"@esbuild/freebsd-x64": "0.27.0",
|
||||||
"@esbuild/linux-arm": "0.27.1",
|
"@esbuild/linux-arm": "0.27.0",
|
||||||
"@esbuild/linux-arm64": "0.27.1",
|
"@esbuild/linux-arm64": "0.27.0",
|
||||||
"@esbuild/linux-ia32": "0.27.1",
|
"@esbuild/linux-ia32": "0.27.0",
|
||||||
"@esbuild/linux-loong64": "0.27.1",
|
"@esbuild/linux-loong64": "0.27.0",
|
||||||
"@esbuild/linux-mips64el": "0.27.1",
|
"@esbuild/linux-mips64el": "0.27.0",
|
||||||
"@esbuild/linux-ppc64": "0.27.1",
|
"@esbuild/linux-ppc64": "0.27.0",
|
||||||
"@esbuild/linux-riscv64": "0.27.1",
|
"@esbuild/linux-riscv64": "0.27.0",
|
||||||
"@esbuild/linux-s390x": "0.27.1",
|
"@esbuild/linux-s390x": "0.27.0",
|
||||||
"@esbuild/linux-x64": "0.27.1",
|
"@esbuild/linux-x64": "0.27.0",
|
||||||
"@esbuild/netbsd-arm64": "0.27.1",
|
"@esbuild/netbsd-arm64": "0.27.0",
|
||||||
"@esbuild/netbsd-x64": "0.27.1",
|
"@esbuild/netbsd-x64": "0.27.0",
|
||||||
"@esbuild/openbsd-arm64": "0.27.1",
|
"@esbuild/openbsd-arm64": "0.27.0",
|
||||||
"@esbuild/openbsd-x64": "0.27.1",
|
"@esbuild/openbsd-x64": "0.27.0",
|
||||||
"@esbuild/openharmony-arm64": "0.27.1",
|
"@esbuild/openharmony-arm64": "0.27.0",
|
||||||
"@esbuild/sunos-x64": "0.27.1",
|
"@esbuild/sunos-x64": "0.27.0",
|
||||||
"@esbuild/win32-arm64": "0.27.1",
|
"@esbuild/win32-arm64": "0.27.0",
|
||||||
"@esbuild/win32-ia32": "0.27.1",
|
"@esbuild/win32-ia32": "0.27.0",
|
||||||
"@esbuild/win32-x64": "0.27.1"
|
"@esbuild/win32-x64": "0.27.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
|
|||||||
1279
pnpm-lock.yaml
generated
1279
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -50,15 +50,13 @@ RUN --mount=type=cache,id=pnpm-cli,target=/buildcache/pnpm-store \
|
|||||||
|
|
||||||
FROM builder AS plugins
|
FROM builder AS plugins
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
|
|
||||||
COPY --from=ghcr.io/jdx/mise:2025.11.3@sha256:ac26f5978c0e2783f3e68e58ce75eddb83e41b89bf8747c503bac2aa9baf22c5 /usr/local/bin/mise /usr/local/bin/mise
|
COPY --from=ghcr.io/jdx/mise:2025.11.3@sha256:ac26f5978c0e2783f3e68e58ce75eddb83e41b89bf8747c503bac2aa9baf22c5 /usr/local/bin/mise /usr/local/bin/mise
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
COPY ./plugins/mise.toml ./plugins/
|
COPY ./plugins/mise.toml ./plugins/
|
||||||
ENV MISE_TRUSTED_CONFIG_PATHS=/usr/src/app/plugins/mise.toml
|
ENV MISE_TRUSTED_CONFIG_PATHS=/usr/src/app/plugins/mise.toml
|
||||||
ENV MISE_DATA_DIR=/buildcache/mise
|
ENV MISE_DATA_DIR=/buildcache/mise
|
||||||
RUN --mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \
|
RUN --mount=type=cache,id=mise-tools,target=/buildcache/mise \
|
||||||
mise install --cd plugins
|
mise install --cd plugins
|
||||||
|
|
||||||
COPY ./plugins ./plugins/
|
COPY ./plugins ./plugins/
|
||||||
@@ -68,7 +66,7 @@ RUN --mount=type=cache,id=pnpm-plugins,target=/buildcache/pnpm-store \
|
|||||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
||||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
||||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
||||||
--mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \
|
--mount=type=cache,id=mise-tools,target=/buildcache/mise \
|
||||||
cd plugins && mise run build
|
cd plugins && mise run build
|
||||||
|
|
||||||
FROM ghcr.io/immich-app/base-server-prod:202511261514@sha256:c04c1c38dd90e53455b180aedf93c3c63474c8d20ffe2c6d7a3a61a2181e6d29
|
FROM ghcr.io/immich-app/base-server-prod:202511261514@sha256:c04c1c38dd90e53455b180aedf93c3c63474c8d20ffe2c6d7a3a61a2181e6d29
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "2.4.0",
|
"version": "2.3.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
"cookie": "^1.0.2",
|
"cookie": "^1.0.2",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cron": "4.3.3",
|
"cron": "4.3.3",
|
||||||
"exiftool-vendored": "^34.0.0",
|
"exiftool-vendored": "^33.0.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-web",
|
"name": "immich-web",
|
||||||
"version": "2.4.0",
|
"version": "2.3.1",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"build:stats": "BUILD_STATS=true vite build",
|
"build:stats": "BUILD_STATS=true vite build",
|
||||||
"package": "svelte-kit package",
|
"package": "svelte-kit package",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'state_referenced_locally:ignore'",
|
"check:svelte": "svelte-check --no-tsconfig --fail-on-warnings",
|
||||||
"check:typescript": "tsc --noEmit",
|
"check:typescript": "tsc --noEmit",
|
||||||
"check:watch": "pnpm run check:svelte --watch",
|
"check:watch": "pnpm run check:svelte --watch",
|
||||||
"check:code": "pnpm run format && pnpm run lint && pnpm run check:svelte && pnpm run check:typescript",
|
"check:code": "pnpm run format && pnpm run lint && pnpm run check:svelte && pnpm run check:typescript",
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||||
"@immich/justified-layout-wasm": "^0.4.3",
|
"@immich/justified-layout-wasm": "^0.4.3",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@immich/ui": "^0.50.1",
|
"@immich/ui": "^0.50.0",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@photo-sphere-viewer/core": "^5.14.0",
|
"@photo-sphere-viewer/core": "^5.14.0",
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
"prettier-plugin-sort-json": "^4.1.1",
|
"prettier-plugin-sort-json": "^4.1.1",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
"rollup-plugin-visualizer": "^6.0.0",
|
"rollup-plugin-visualizer": "^6.0.0",
|
||||||
"svelte": "5.45.5",
|
"svelte": "5.45.2",
|
||||||
"svelte-check": "^4.1.5",
|
"svelte-check": "^4.1.5",
|
||||||
"svelte-eslint-parser": "^1.3.3",
|
"svelte-eslint-parser": "^1.3.3",
|
||||||
"tailwindcss": "^4.1.7",
|
"tailwindcss": "^4.1.7",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<tr
|
<tr
|
||||||
class="flex w-full place-items-center border-3 border-transparent p-2 text-center even:bg-subtle/20 odd:bg-subtle/80 hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:px-5 md:py-2"
|
class="flex h-12 w-full place-items-center border-3 border-transparent p-2 text-center even:bg-subtle/20 odd:bg-subtle/80 hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5"
|
||||||
onclick={() => goto(resolve(`${AppRoute.ALBUMS}/${album.id}`))}
|
onclick={() => goto(resolve(`${AppRoute.ALBUMS}/${album.id}`))}
|
||||||
{oncontextmenu}
|
{oncontextmenu}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -126,7 +126,6 @@
|
|||||||
|
|
||||||
const onMouseLeave = () => {
|
const onMouseLeave = () => {
|
||||||
mouseOver = false;
|
mouseOver = false;
|
||||||
onMouseEvent?.({ isMouseOver: false, selectedGroupIndex: groupIndex });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|||||||
@@ -188,7 +188,7 @@
|
|||||||
// the performance benefits of deferred layouts while still supporting deep linking
|
// the performance benefits of deferred layouts while still supporting deep linking
|
||||||
// to assets at the end of the timeline.
|
// to assets at the end of the timeline.
|
||||||
timelineManager.isScrollingOnLoad = true;
|
timelineManager.isScrollingOnLoad = true;
|
||||||
const monthGroup = await timelineManager.findMonthGroupForAsset({ id: assetId });
|
const monthGroup = await timelineManager.findMonthGroupForAsset(assetId);
|
||||||
if (!monthGroup) {
|
if (!monthGroup) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timeline-util';
|
import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timeline-util';
|
||||||
import { AssetOrder, type AssetResponseDto } from '@immich/sdk';
|
import { AssetOrder } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import type { MonthGroup } from '../month-group.svelte';
|
import type { MonthGroup } from '../month-group.svelte';
|
||||||
import { TimelineManager } from '../timeline-manager.svelte';
|
import { TimelineManager } from '../timeline-manager.svelte';
|
||||||
@@ -7,16 +7,12 @@ import type { AssetDescriptor, Direction, TimelineAsset } from '../types';
|
|||||||
|
|
||||||
export async function getAssetWithOffset(
|
export async function getAssetWithOffset(
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
assetDescriptor: AssetDescriptor | AssetResponseDto,
|
assetDescriptor: AssetDescriptor,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<TimelineAsset | undefined> {
|
||||||
const monthGroup = await timelineManager.findMonthGroupForAsset(assetDescriptor);
|
const { asset, monthGroup } = findMonthGroupForAsset(timelineManager, assetDescriptor.id) ?? {};
|
||||||
if (!monthGroup) {
|
if (!monthGroup || !asset) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
const asset = monthGroup.findAssetById(assetDescriptor);
|
|
||||||
if (!asset) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -524,7 +524,6 @@ describe('TimelineManager', () => {
|
|||||||
{ count: 3, timeBucket: '2024-01-01T00:00:00.000Z' },
|
{ count: 3, timeBucket: '2024-01-01T00:00:00.000Z' },
|
||||||
]);
|
]);
|
||||||
sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket]));
|
sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket]));
|
||||||
sdkMock.getAssetInfo.mockRejectedValue(new Error('Asset not found'));
|
|
||||||
await timelineManager.updateViewport({ width: 1588, height: 1000 });
|
await timelineManager.updateViewport({ width: 1588, height: 1000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,12 @@ import { WebsocketSupport } from '$lib/managers/timeline-manager/internal/websoc
|
|||||||
import { CancellableTask } from '$lib/utils/cancellable-task';
|
import { CancellableTask } from '$lib/utils/cancellable-task';
|
||||||
import { PersistedLocalStorage } from '$lib/utils/persisted';
|
import { PersistedLocalStorage } from '$lib/utils/persisted';
|
||||||
import {
|
import {
|
||||||
isAssetResponseDto,
|
|
||||||
setDifference,
|
setDifference,
|
||||||
toTimelineAsset,
|
toTimelineAsset,
|
||||||
type TimelineDateTime,
|
type TimelineDateTime,
|
||||||
type TimelineYearMonth,
|
type TimelineYearMonth,
|
||||||
} from '$lib/utils/timeline-util';
|
} from '$lib/utils/timeline-util';
|
||||||
import { AssetOrder, getAssetInfo, getTimeBuckets, type AssetResponseDto } from '@immich/sdk';
|
import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk';
|
||||||
import { clamp, isEqual } from 'lodash-es';
|
import { clamp, isEqual } from 'lodash-es';
|
||||||
import { SvelteDate, SvelteSet } from 'svelte/reactivity';
|
import { SvelteDate, SvelteSet } from 'svelte/reactivity';
|
||||||
import { DayGroup } from './day-group.svelte';
|
import { DayGroup } from './day-group.svelte';
|
||||||
@@ -344,30 +343,27 @@ export class TimelineManager extends VirtualScrollManager {
|
|||||||
this.addAssetsUpsertSegments([...notExcluded]);
|
this.addAssetsUpsertSegments([...notExcluded]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findMonthGroupForAsset(asset: AssetDescriptor | AssetResponseDto) {
|
async findMonthGroupForAsset(id: string) {
|
||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
await this.initTask.waitUntilCompletion();
|
await this.initTask.waitUntilCompletion();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = asset;
|
|
||||||
let { monthGroup } = findMonthGroupForAssetUtil(this, id) ?? {};
|
let { monthGroup } = findMonthGroupForAssetUtil(this, id) ?? {};
|
||||||
if (monthGroup) {
|
if (monthGroup) {
|
||||||
return monthGroup;
|
return monthGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = isAssetResponseDto(asset)
|
const response = await getAssetInfo({ ...authManager.params, id }).catch(() => null);
|
||||||
? asset
|
|
||||||
: await getAssetInfo({ ...authManager.params, id }).catch(() => null);
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timelineAsset = toTimelineAsset(response);
|
const asset = toTimelineAsset(response);
|
||||||
if (this.isExcluded(timelineAsset)) {
|
if (!asset || this.isExcluded(asset)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
monthGroup = await this.#loadMonthGroupAtTime(timelineAsset.localDateTime, { cancelable: false });
|
monthGroup = await this.#loadMonthGroupAtTime(asset.localDateTime, { cancelable: false });
|
||||||
if (monthGroup?.findAssetById({ id })) {
|
if (monthGroup?.findAssetById({ id })) {
|
||||||
return monthGroup;
|
return monthGroup;
|
||||||
}
|
}
|
||||||
@@ -536,14 +532,14 @@ export class TimelineManager extends VirtualScrollManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getLaterAsset(
|
async getLaterAsset(
|
||||||
assetDescriptor: AssetDescriptor | AssetResponseDto,
|
assetDescriptor: AssetDescriptor,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<TimelineAsset | undefined> {
|
||||||
return await getAssetWithOffset(this, assetDescriptor, interval, 'later');
|
return await getAssetWithOffset(this, assetDescriptor, interval, 'later');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEarlierAsset(
|
async getEarlierAsset(
|
||||||
assetDescriptor: AssetDescriptor | AssetResponseDto,
|
assetDescriptor: AssetDescriptor,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<TimelineAsset | undefined> {
|
||||||
return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier');
|
return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier');
|
||||||
|
|||||||
@@ -1,8 +1,39 @@
|
|||||||
const broadcast = new BroadcastChannel('immich');
|
const broadcast = new BroadcastChannel('immich');
|
||||||
|
|
||||||
|
let isLoadedReplyListeners: ((url: string, isUrlCached: boolean) => void)[] = [];
|
||||||
|
broadcast.addEventListener('message', (event) => {
|
||||||
|
if (event.data.type == 'isImageUrlCachedReply') {
|
||||||
|
for (const listener of isLoadedReplyListeners) {
|
||||||
|
listener(event.data.url, event.data.isImageUrlCached);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export function cancelImageUrl(url: string) {
|
export function cancelImageUrl(url: string) {
|
||||||
broadcast.postMessage({ type: 'cancel', url });
|
broadcast.postMessage({ type: 'cancel', url });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function preloadImageUrl(url: string) {
|
export function preloadImageUrl(url: string) {
|
||||||
broadcast.postMessage({ type: 'preload', url });
|
broadcast.postMessage({ type: 'preload', url });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isImageUrlCached(url: string) {
|
||||||
|
if (!globalThis.isSecureContext) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const listener = (urlReply: string, isUrlCached: boolean) => {
|
||||||
|
if (urlReply === url) {
|
||||||
|
cleanup(isUrlCached);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const cleanup = (isUrlCached: boolean) => {
|
||||||
|
isLoadedReplyListeners = isLoadedReplyListeners.filter((element) => element !== listener);
|
||||||
|
resolve(isUrlCached);
|
||||||
|
};
|
||||||
|
isLoadedReplyListeners.push(listener);
|
||||||
|
broadcast.postMessage({ type: 'isImageUrlCached', url });
|
||||||
|
|
||||||
|
setTimeout(() => cleanup(false), 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { AssetDescriptor, TimelineAsset, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { getAssetRatio } from '$lib/utils/asset-utils';
|
import { getAssetRatio } from '$lib/utils/asset-utils';
|
||||||
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
||||||
@@ -192,13 +192,8 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset):
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isTimelineAsset = (
|
export const isTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): unknownAsset is TimelineAsset =>
|
||||||
unknownAsset: AssetDescriptor | AssetResponseDto | TimelineAsset,
|
(unknownAsset as TimelineAsset).ratio !== undefined;
|
||||||
): unknownAsset is TimelineAsset => (unknownAsset as TimelineAsset).ratio !== undefined;
|
|
||||||
|
|
||||||
export const isAssetResponseDto = (
|
|
||||||
unknownAsset: AssetDescriptor | AssetResponseDto | TimelineAsset,
|
|
||||||
): unknownAsset is AssetResponseDto => (unknownAsset as AssetResponseDto).type !== undefined;
|
|
||||||
|
|
||||||
export const isTimelineAssets = (assets: AssetResponseDto[] | TimelineAsset[]): assets is TimelineAsset[] =>
|
export const isTimelineAssets = (assets: AssetResponseDto[] | TimelineAsset[]): assets is TimelineAsset[] =>
|
||||||
assets.length === 0 || 'ratio' in assets[0];
|
assets.length === 0 || 'ratio' in assets[0];
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { handleCancel, handlePreload } from './request';
|
import { handleCancel, handleIsUrlCached, handlePreload } from './request';
|
||||||
|
|
||||||
|
export const broadcast = new BroadcastChannel('immich');
|
||||||
|
|
||||||
export const installBroadcastChannelListener = () => {
|
export const installBroadcastChannelListener = () => {
|
||||||
const broadcast = new BroadcastChannel('immich');
|
|
||||||
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
||||||
broadcast.onmessage = (event) => {
|
broadcast.onmessage = (event) => {
|
||||||
if (!event.data) {
|
if (!event.data) {
|
||||||
@@ -20,6 +21,15 @@ export const installBroadcastChannelListener = () => {
|
|||||||
handleCancel(url);
|
handleCancel(url);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'isImageUrlCached': {
|
||||||
|
void handleIsUrlCached(url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const replyIsImageUrlCached = (url: string, isImageUrlCached: boolean) => {
|
||||||
|
broadcast.postMessage({ type: 'isImageUrlCachedReply', url, isImageUrlCached });
|
||||||
|
};
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ export const put = async (key: string, response: Response) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.put(key, response.clone());
|
try {
|
||||||
|
await cache.put(key, response.clone());
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ignoring error during cache put', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prune = async () => {
|
export const prune = async () => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { replyIsImageUrlCached } from './broadcast-channel';
|
||||||
import { get, put } from './cache';
|
import { get, put } from './cache';
|
||||||
|
|
||||||
const pendingRequests = new Map<string, AbortController>();
|
const pendingRequests = new Map<string, AbortController>();
|
||||||
@@ -44,7 +45,7 @@ export const handleRequest = async (request: URL | Request) => {
|
|||||||
const response = await fetch(request, { signal: cancelToken.signal });
|
const response = await fetch(request, { signal: cancelToken.signal });
|
||||||
|
|
||||||
assertResponse(response);
|
assertResponse(response);
|
||||||
put(cacheKey, response);
|
await put(cacheKey, response);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -71,3 +72,9 @@ export const handleCancel = (url: URL) => {
|
|||||||
pendingRequest.abort();
|
pendingRequest.abort();
|
||||||
pendingRequests.delete(cacheKey);
|
pendingRequests.delete(cacheKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handleIsUrlCached = async (url: URL) => {
|
||||||
|
const cacheKey = getCacheKey(url);
|
||||||
|
const isImageUrlCached = !!(await get(cacheKey));
|
||||||
|
replyIsImageUrlCached(url.pathname + url.search + url.hash, isImageUrlCached);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user