Compare commits

...

5 Commits

Author SHA1 Message Date
github-actions
54bafccbf9 chore: version v1.133.1 2025-05-23 17:37:44 +00:00
Daniel Dietzler
e61c575b01 fix: tailwind issues (#18528)
fix: tailwind issues (maybe)
2025-05-23 12:34:24 -05:00
Alex
e12c67742c fix(mobile): don't show locked asset in local album view (#18536) 2025-05-23 10:20:49 -05:00
Alex
4878c500a5 fix: hard link navigation (#18489) 2025-05-23 08:21:37 -05:00
Alex
2fa7a40996 fix(mobile): chinese translation (#18491)
* fix: Chinese translation

* using Locale.fromsubtags
2025-05-23 08:01:29 -05:00
24 changed files with 141 additions and 125 deletions

6
cli/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@immich/cli",
"version": "2.2.66",
"version": "2.2.67",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/cli",
"version": "2.2.66",
"version": "2.2.67",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"chokidar": "^4.0.3",
@@ -54,7 +54,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.133.0",
"version": "1.133.1",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.2.66",
"version": "2.2.67",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",

View File

@@ -1,4 +1,8 @@
[
{
"label": "v1.133.1",
"url": "https://v1.133.1.archive.immich.app"
},
{
"label": "v1.133.0",
"url": "https://v1.133.0.archive.immich.app"

8
e2e/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "immich-e2e",
"version": "1.133.0",
"version": "1.133.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich-e2e",
"version": "1.133.0",
"version": "1.133.1",
"license": "GNU Affero General Public License version 3",
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
@@ -44,7 +44,7 @@
},
"../cli": {
"name": "@immich/cli",
"version": "2.2.66",
"version": "2.2.67",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
@@ -93,7 +93,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.133.0",
"version": "1.133.1",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "1.133.0",
"version": "1.133.1",
"description": "",
"main": "index.js",
"type": "module",

View File

@@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 198,
"android.injected.version.name" => "1.133.0",
"android.injected.version.code" => 199,
"android.injected.version.name" => "1.133.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')

View File

@@ -22,7 +22,7 @@ platform :ios do
path: "./Runner.xcodeproj",
)
increment_version_number(
version_number: "1.133.0"
version_number: "1.133.1"
)
increment_build_number(
build_number: latest_testflight_build_number + 1,

View File

@@ -6,8 +6,10 @@ const Map<String, Locale> locales = {
// Additional locales
'Arabic (ar)': Locale('ar'),
'Catalan (ca)': Locale('ca'),
'Chinese Simplified (zh_CN)': Locale('zh', 'CN'),
'Chinese Traditional (zh_TW)': Locale('zh', 'TW'),
'Chinese Simplified (zh_CN)':
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'),
'Chinese Traditional (zh_TW)':
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
'Czech (cs)': Locale('cs'),
'Danish (da)': Locale('da'),
'Dutch (nl)': Locale('nl'),

View File

@@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
@@ -229,6 +230,8 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
return db.assets
.where()
.ownerIdEqualToAnyChecksum(fastHash(userId))
.filter()
.visibilityEqualTo(AssetVisibilityEnum.timeline)
.sortByFileCreatedAtDesc()
.findAll();
}
@@ -239,6 +242,7 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
.where()
.ownerIdEqualToAnyChecksum(fastHash(userId))
.filter()
.visibilityEqualTo(AssetVisibilityEnum.timeline)
.livePhotoVideoIdIsNotNull()
.findAll();
}

View File

@@ -72,7 +72,12 @@ class TimelineRepository extends DatabaseRepository
Album album,
GroupAssetsBy groupAssetByOption,
) {
final query = album.assets.filter().isTrashedEqualTo(false);
final query = album.assets
.filter()
.isTrashedEqualTo(false)
.not()
.visibilityEqualTo(AssetVisibilityEnum.locked);
final withSortedOption = switch (album.sortOrder) {
SortOrder.asc => query.sortByFileCreatedAt(),
SortOrder.desc => query.sortByFileCreatedAtDesc(),

View File

@@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.133.0
- API version: 1.133.1
- Generator version: 7.8.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen

View File

@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none'
version: 1.133.0+198
version: 1.133.1+199
environment:
sdk: '>=3.3.0 <4.0.0'

View File

@@ -8132,7 +8132,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "1.133.0",
"version": "1.133.1",
"contact": {}
},
"tags": [],

View File

@@ -1,12 +1,12 @@
{
"name": "@immich/sdk",
"version": "1.133.0",
"version": "1.133.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/sdk",
"version": "1.133.0",
"version": "1.133.1",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@oazapfts/runtime": "^1.0.2"

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/sdk",
"version": "1.133.0",
"version": "1.133.1",
"description": "Auto-generated TypeScript SDK for the Immich API",
"type": "module",
"main": "./build/index.js",

View File

@@ -1,6 +1,6 @@
/**
* Immich
* 1.133.0
* 1.133.1
* DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts
*/

View File

@@ -1,12 +1,12 @@
{
"name": "immich",
"version": "1.133.0",
"version": "1.133.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich",
"version": "1.133.0",
"version": "1.133.1",
"hasInstallScript": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "immich",
"version": "1.133.0",
"version": "1.133.1",
"description": "",
"author": "",
"private": true,

6
web/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "immich-web",
"version": "1.133.0",
"version": "1.133.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich-web",
"version": "1.133.0",
"version": "1.133.1",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@formatjs/icu-messageformat-parser": "^2.9.8",
@@ -86,7 +86,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.133.0",
"version": "1.133.1",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@oazapfts/runtime": "^1.0.2"

View File

@@ -1,6 +1,6 @@
{
"name": "immich-web",
"version": "1.133.0",
"version": "1.133.1",
"license": "GNU Affero General Public License version 3",
"type": "module",
"scripts": {

View File

@@ -1,9 +1,8 @@
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@source "../node_modules/@immich/ui";
/* @import '/usr/ui/dist/theme/default.css'; */
@config '../tailwind.config.js';
@utility immich-form-input {
@apply rounded-xl bg-slate-200 px-3 py-3 text-sm focus:border-immich-primary disabled:cursor-not-allowed disabled:bg-gray-400 disabled:text-gray-100 dark:bg-gray-600 dark:text-immich-dark-fg dark:disabled:bg-gray-800 dark:disabled:text-gray-200;
}
@@ -27,8 +26,48 @@
scrollbar-gutter: stable both-edges;
}
@utility grid-auto-fit-* {
grid-template-columns: repeat(auto-fit, minmax(min(calc(var(--spacing) * --value(number)), 100%), 1fr));
}
@utility grid-auto-fill-* {
grid-template-columns: repeat(auto-fill, minmax(min(calc(var(--spacing) * --value(number)), 100%), 1fr));
}
@custom-variant dark (&:where(.dark, .dark *));
@theme inline {
--color-immich-primary: rgb(var(--immich-primary));
--color-immich-bg: rgb(var(--immich-bg));
--color-immich-fg: rgb(var(--immich-fg));
--color-immich-gray: rgb(var(--immich-gray));
--color-immich-error: rgb(var(--immich-error));
--color-immich-success: rgb(var(--immich-success));
--color-immich-warning: rgb(var(--immich-warning));
--color-immich-dark-primary: rgb(var(--immich-dark-primary));
--color-immich-dark-bg: rgb(var(--immich-dark-bg));
--color-immich-dark-fg: rgb(var(--immich-dark-fg));
--color-immich-dark-gray: rgb(var(--immich-dark-gray));
--color-immich-dark-error: rgb(var(--immich-dark-error));
--color-immich-dark-success: rgb(var(--immich-dark-success));
--color-immich-dark-warning: rgb(var(--immich-dark-warning));
}
@theme {
--font-immich-mono: Overpass Mono, monospace;
--spacing-18: 4.5rem;
--breakpoint-tall: 800px;
--breakpoint-2xl: 1535px;
--breakpoint-xl: 1279px;
--breakpoint-lg: 1023px;
--breakpoint-md: 767px;
--breakpoint-sm: 639px;
--breakpoint-sidebar: 850px;
}
@layer base {
:root {
/* light */

View File

@@ -124,26 +124,38 @@
scrollTo(0);
};
const scrollToAsset = async (assetId: string) => {
try {
const bucket = await assetStore.findBucketForAsset(assetId);
if (bucket) {
const height = bucket.findAssetAbsolutePosition(assetId);
if (height) {
scrollTo(height);
assetStore.updateIntersections();
return true;
}
}
} catch {
// ignore errors - asset may not be in the store
}
return false;
};
const completeNav = async () => {
const scrollTarget = $gridScrollTarget?.at;
let scrolled = false;
if (scrollTarget) {
try {
const bucket = await assetStore.findBucketForAsset(scrollTarget);
if (bucket) {
const height = bucket.findAssetAbsolutePosition(scrollTarget);
if (height) {
scrollTo(height);
assetStore.updateIntersections();
return;
}
}
} catch {
// ignore errors - asset may not be in the store
}
scrolled = await scrollToAsset(scrollTarget);
}
if (!scrolled) {
// if the asset is not found, scroll to the top
scrollToTop();
}
scrollToTop();
};
beforeNavigate(() => (assetStore.suspendTransitions = true));
afterNavigate((nav) => {
const { complete } = nav;
complete.then(completeNav, completeNav);

View File

@@ -217,8 +217,8 @@ export class AssetDateGroup {
return { moveAssets, processedIds, unprocessedIds, changedGeometry };
}
layout(options: CommonLayoutOptions) {
if (!this.bucket.intersecting) {
layout(options: CommonLayoutOptions, noDefer: boolean) {
if (!noDefer && !this.bucket.intersecting) {
this.deferredLayout = true;
return;
}
@@ -602,6 +602,8 @@ export class AssetBucket {
}
findAssetAbsolutePosition(assetId: string) {
this.store.clearDeferredLayout(this);
for (const group of this.dateGroups) {
const intersectingAsset = group.intersetingAssets.find((asset) => asset.id === assetId);
if (intersectingAsset) {
@@ -658,6 +660,12 @@ type AssetStoreLayoutOptions = {
headerHeight?: number;
gap?: number;
};
interface UpdateGeometryOptions {
invalidateHeight: boolean;
noDefer?: boolean;
}
export class AssetStore {
// --- public ----
isInitialized = $state(false);
@@ -932,6 +940,16 @@ export class AssetStore {
);
}
clearDeferredLayout(bucket: AssetBucket) {
const hasDeferred = bucket.dateGroups.some((group) => group.deferredLayout);
if (hasDeferred) {
this.#updateGeometry(bucket, { invalidateHeight: true, noDefer: true });
for (const group of bucket.dateGroups) {
group.deferredLayout = false;
}
}
}
#updateIntersection(bucket: AssetBucket) {
const actuallyIntersecting = this.#calculateIntersecting(bucket, 0, 0);
let preIntersecting = false;
@@ -941,13 +959,7 @@ export class AssetStore {
bucket.intersecting = actuallyIntersecting || preIntersecting;
bucket.actuallyIntersecting = actuallyIntersecting;
if (preIntersecting || actuallyIntersecting) {
const hasDeferred = bucket.dateGroups.some((group) => group.deferredLayout);
if (hasDeferred) {
this.#updateGeometry(bucket, true);
for (const group of bucket.dateGroups) {
group.deferredLayout = false;
}
}
this.clearDeferredLayout(bucket);
}
}
@@ -1052,7 +1064,7 @@ export class AssetStore {
return;
}
for (const bucket of this.buckets) {
this.#updateGeometry(bucket, changedWidth);
this.#updateGeometry(bucket, { invalidateHeight: changedWidth });
}
this.updateIntersections();
this.#createScrubBuckets();
@@ -1079,7 +1091,8 @@ export class AssetStore {
};
}
#updateGeometry(bucket: AssetBucket, invalidateHeight: boolean) {
#updateGeometry(bucket: AssetBucket, options: UpdateGeometryOptions) {
const { invalidateHeight, noDefer = false } = options;
if (invalidateHeight) {
bucket.isBucketHeightActual = false;
}
@@ -1094,10 +1107,10 @@ export class AssetStore {
}
return;
}
this.#layoutBucket(bucket);
this.#layoutBucket(bucket, noDefer);
}
#layoutBucket(bucket: AssetBucket) {
#layoutBucket(bucket: AssetBucket, noDefer: boolean = false) {
// these are top offsets, for each row
let cummulativeHeight = 0;
// these are left offsets of each group, for each row
@@ -1112,7 +1125,7 @@ export class AssetStore {
rowSpaceRemaining.fill(this.viewportWidth, 0, bucket.dateGroups.length);
const options = this.createLayoutOptions();
for (const assetGroup of bucket.dateGroups) {
assetGroup.layout(options);
assetGroup.layout(options, noDefer);
rowSpaceRemaining[dateGroupRow] -= assetGroup.width - 1;
if (dateGroupCol > 0) {
rowSpaceRemaining[dateGroupRow] -= this.gap;
@@ -1266,7 +1279,7 @@ export class AssetStore {
for (const bucket of addContext.updatedBuckets) {
bucket.sortDateGroups();
this.#updateGeometry(bucket, true);
this.#updateGeometry(bucket, { invalidateHeight: true });
}
this.updateIntersections();
}
@@ -1357,7 +1370,7 @@ export class AssetStore {
}
const changedGeometry = changedBuckets.size > 0;
for (const bucket of changedBuckets) {
this.#updateGeometry(bucket, true);
this.#updateGeometry(bucket, { invalidateHeight: true });
}
if (changedGeometry) {
this.updateIntersections();
@@ -1397,7 +1410,7 @@ export class AssetStore {
refreshLayout() {
for (const bucket of this.buckets) {
this.#updateGeometry(bucket, true);
this.#updateGeometry(bucket, { invalidateHeight: true });
}
this.updateIntersections();
}

View File

@@ -1,63 +0,0 @@
import plugin from 'tailwindcss/plugin';
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./src/**/*.{html,js,svelte,ts}',
'./node_modules/@immich/ui/dist/**/*.{svelte,js}',
'../../ui/src/**/*.{html,js,svelte,ts}',
],
theme: {
extend: {
colors: {
// Light Theme
'immich-primary': 'rgb(var(--immich-primary) / <alpha-value>)',
'immich-bg': 'rgb(var(--immich-bg) / <alpha-value>)',
'immich-fg': 'rgb(var(--immich-fg) / <alpha-value>)',
'immich-gray': 'rgb(var(--immich-gray) / <alpha-value>)',
'immich-error': 'rgb(var(--immich-error) / <alpha-value>)',
'immich-success': 'rgb(var(--immich-success) / <alpha-value>)',
'immich-warning': 'rgb(var(--immich-warning) / <alpha-value>)',
// Dark Theme
'immich-dark-primary': 'rgb(var(--immich-dark-primary) / <alpha-value>)',
'immich-dark-bg': 'rgb(var(--immich-dark-bg) / <alpha-value>)',
'immich-dark-fg': 'rgb(var(--immich-dark-fg) / <alpha-value>)',
'immich-dark-gray': 'rgb(var(--immich-dark-gray) / <alpha-value>)',
'immich-dark-error': 'rgb(var(--immich-dark-error) / <alpha-value>)',
'immich-dark-success': 'rgb(var(--immich-dark-success) / <alpha-value>)',
'immich-dark-warning': 'rgb(var(--immich-dark-warning) / <alpha-value>)',
},
fontFamily: {
'immich-mono': ['Overpass Mono', 'monospace'],
},
spacing: {
18: '4.5rem',
},
screens: {
tall: { raw: '(min-height: 800px)' },
'max-2xl': { max: '1535px' },
'max-xl': { max: '1279px' },
'max-lg': { max: '1023px' },
'max-md': { max: '767px' },
'max-sm': { max: '639px' },
sidebar: { min: '850px' },
},
},
},
plugins: [
plugin(({ matchUtilities, theme }) => {
matchUtilities(
{
'grid-auto-fit': (value) => ({
gridTemplateColumns: `repeat(auto-fit, minmax(min(${value}, 100%), 1fr))`,
}),
'grid-auto-fill': (value) => ({
gridTemplateColumns: `repeat(auto-fill, minmax(min(${value}, 100%), 1fr))`,
}),
},
{ values: theme('width') },
);
}),
],
};