Compare commits

..

1 Commits

Author SHA1 Message Date
Alex Tran 34aa6c1329 Revert "feat(web): make asset grid row height responsive (#16970)"
This reverts commit 7075c5b393.
2025-03-26 09:08:23 -05:00
22 changed files with 34 additions and 181 deletions
+3 -3
View File
@@ -1,12 +1,12 @@
{ {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.56", "version": "2.2.55",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.56", "version": "2.2.55",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"chokidar": "^4.0.3", "chokidar": "^4.0.3",
@@ -55,7 +55,7 @@
}, },
"../open-api/typescript-sdk": { "../open-api/typescript-sdk": {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.130.2", "version": "1.130.1",
"dev": true, "dev": true,
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.56", "version": "2.2.55",
"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",
-4
View File
@@ -1,8 +1,4 @@
[ [
{
"label": "v1.130.2",
"url": "https://v1.130.2.archive.immich.app"
},
{ {
"label": "v1.130.1", "label": "v1.130.1",
"url": "https://v1.130.1.archive.immich.app" "url": "https://v1.130.1.archive.immich.app"
+4 -4
View File
@@ -1,12 +1,12 @@
{ {
"name": "immich-e2e", "name": "immich-e2e",
"version": "1.130.2", "version": "1.130.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "immich-e2e", "name": "immich-e2e",
"version": "1.130.2", "version": "1.130.1",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.1.0", "@eslint/eslintrc": "^3.1.0",
@@ -45,7 +45,7 @@
}, },
"../cli": { "../cli": {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.2.56", "version": "2.2.55",
"dev": true, "dev": true,
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
@@ -95,7 +95,7 @@
}, },
"../open-api/typescript-sdk": { "../open-api/typescript-sdk": {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.130.2", "version": "1.130.1",
"dev": true, "dev": true,
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "immich-e2e", "name": "immich-e2e",
"version": "1.130.2", "version": "1.130.1",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
-127
View File
@@ -454,133 +454,6 @@ describe('/libraries', () => {
utils.removeImageFile(`${testAssetDir}/temp/folder${char}2/asset2.png`); utils.removeImageFile(`${testAssetDir}/temp/folder${char}2/asset2.png`);
}); });
it('should respect exclusion patterns when using multiple import paths', async () => {
// https://github.com/immich-app/immich/issues/17121
const library = await utils.createLibrary(admin.accessToken, {
ownerId: admin.userId,
importPaths: [`${testAssetDirInternal}/temp/exclusion/`, `${testAssetDirInternal}/temp/exclusion2/`],
});
const excludedFolder = `Raw`;
utils.createImageFile(`${testAssetDir}/temp/exclusion/asset1.png`);
utils.createImageFile(`${testAssetDir}/temp/exclusion/${excludedFolder}/asset2.png`);
await utils.scan(admin.accessToken, library.id);
{
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
expect(assets.items).toEqual(
expect.arrayContaining([
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
expect.objectContaining({ originalPath: expect.stringContaining(`${excludedFolder}/asset2.png`) }),
]),
);
}
await utils.scan(admin.accessToken, library.id);
{
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
expect(assets.items).toEqual(
expect.arrayContaining([
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
expect.objectContaining({ originalPath: expect.stringContaining(`${excludedFolder}/asset2.png`) }),
]),
);
}
await utils.updateLibrary(admin.accessToken, library.id, { exclusionPatterns: [`**/${excludedFolder}/**`] });
await utils.scan(admin.accessToken, library.id);
{
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
expect(assets.items).toEqual([
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
]);
}
await utils.scan(admin.accessToken, library.id);
{
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
expect(assets.items).toEqual([
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
]);
}
utils.removeImageFile(`${testAssetDir}/temp/exclusion/asset1.png`);
utils.removeImageFile(`${testAssetDir}/temp/exclusion/${excludedFolder}/asset2.png`);
});
const annoyingExclusionPatterns = ['@', '#', '$', '%', '^', '&', '='];
it.each(annoyingExclusionPatterns)('should support exclusion patterns with %s', async (char) => {
const library = await utils.createLibrary(admin.accessToken, {
ownerId: admin.userId,
importPaths: [`${testAssetDirInternal}/temp/exclusion/`],
});
const excludedFolder = `${char}folder`;
utils.createImageFile(`${testAssetDir}/temp/exclusion/asset1.png`);
utils.createImageFile(`${testAssetDir}/temp/exclusion/${excludedFolder}/asset2.png`);
await utils.scan(admin.accessToken, library.id);
{
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
expect(assets.items).toEqual(
expect.arrayContaining([
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
expect.objectContaining({ originalPath: expect.stringContaining(`${excludedFolder}/asset2.png`) }),
]),
);
}
await utils.scan(admin.accessToken, library.id);
{
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
expect(assets.items).toEqual(
expect.arrayContaining([
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
expect.objectContaining({ originalPath: expect.stringContaining(`${excludedFolder}/asset2.png`) }),
]),
);
}
await utils.updateLibrary(admin.accessToken, library.id, { exclusionPatterns: [`**/${excludedFolder}/**`] });
await utils.scan(admin.accessToken, library.id);
{
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
expect(assets.items).toEqual([
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
]);
}
await utils.scan(admin.accessToken, library.id);
{
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
expect(assets.items).toEqual([
expect.objectContaining({ originalPath: expect.stringContaining(`/asset1.png`) }),
]);
}
utils.removeImageFile(`${testAssetDir}/temp/exclusion/asset1.png`);
utils.removeImageFile(`${testAssetDir}/temp/exclusion/${excludedFolder}/asset2.png`);
});
it('should reimport a modified file', async () => { it('should reimport a modified file', async () => {
const library = await utils.createLibrary(admin.accessToken, { const library = await utils.createLibrary(admin.accessToken, {
ownerId: admin.userId, ownerId: admin.userId,
+2 -2
View File
@@ -35,8 +35,8 @@ platform :android do
task: 'bundle', task: 'bundle',
build_type: 'Release', build_type: 'Release',
properties: { properties: {
"android.injected.version.code" => 190, "android.injected.version.code" => 189,
"android.injected.version.name" => "1.130.2", "android.injected.version.name" => "1.130.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')
+1 -1
View File
@@ -19,7 +19,7 @@ platform :ios do
desc "iOS Release" desc "iOS Release"
lane :release do lane :release do
increment_version_number( increment_version_number(
version_number: "1.130.2" version_number: "1.130.1"
) )
increment_build_number( increment_build_number(
build_number: latest_testflight_build_number + 1, build_number: latest_testflight_build_number + 1,
+1 -1
View File
@@ -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: 1.130.2 - API version: 1.130.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 -1
View File
@@ -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: 1.130.2+190 version: 1.130.1+189
environment: environment:
sdk: '>=3.3.0 <4.0.0' sdk: '>=3.3.0 <4.0.0'
+1 -1
View File
@@ -7656,7 +7656,7 @@
"info": { "info": {
"title": "Immich", "title": "Immich",
"description": "Immich API", "description": "Immich API",
"version": "1.130.2", "version": "1.130.1",
"contact": {} "contact": {}
}, },
"tags": [], "tags": [],
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.130.2", "version": "1.130.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.130.2", "version": "1.130.1",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.130.2", "version": "1.130.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 -1
View File
@@ -1,6 +1,6 @@
/** /**
* Immich * Immich
* 1.130.2 * 1.130.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
*/ */
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "immich", "name": "immich",
"version": "1.130.2", "version": "1.130.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "immich", "name": "immich",
"version": "1.130.2", "version": "1.130.1",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@nestjs/bullmq": "^11.0.1", "@nestjs/bullmq": "^11.0.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "immich", "name": "immich",
"version": "1.130.2", "version": "1.130.1",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,
+1 -4
View File
@@ -1062,10 +1062,7 @@ export class AssetRepository {
.where('isExternal', '=', true) .where('isExternal', '=', true)
.where('libraryId', '=', asUuid(libraryId)) .where('libraryId', '=', asUuid(libraryId))
.where((eb) => .where((eb) =>
eb.or([ eb.or([eb('originalPath', 'not like', paths.join('|')), eb('originalPath', 'like', exclusions.join('|'))]),
eb.not(eb.or(paths.map((path) => eb('originalPath', 'like', path)))),
eb('originalPath', 'like', exclusions.join('|')),
]),
) )
.executeTakeFirstOrThrow(); .executeTakeFirstOrThrow();
} }
+3 -3
View File
@@ -1,12 +1,12 @@
{ {
"name": "immich-web", "name": "immich-web",
"version": "1.130.2", "version": "1.130.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "immich-web", "name": "immich-web",
"version": "1.130.2", "version": "1.130.1",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@formatjs/icu-messageformat-parser": "^2.9.8", "@formatjs/icu-messageformat-parser": "^2.9.8",
@@ -81,7 +81,7 @@
}, },
"../open-api/typescript-sdk": { "../open-api/typescript-sdk": {
"name": "@immich/sdk", "name": "@immich/sdk",
"version": "1.130.2", "version": "1.130.1",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"dependencies": { "dependencies": {
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "immich-web", "name": "immich-web",
"version": "1.130.2", "version": "1.130.1",
"license": "GNU Affero General Public License version 3", "license": "GNU Affero General Public License version 3",
"scripts": { "scripts": {
"dev": "vite dev --host 0.0.0.0 --port 3000", "dev": "vite dev --host 0.0.0.0 --port 3000",
@@ -1,6 +1,4 @@
<script lang="ts" module> <script lang="ts" module>
import { get } from 'svelte/store';
export type ComboBoxOption = { export type ComboBoxOption = {
id?: string; id?: string;
label: string; label: string;
@@ -30,6 +28,7 @@
import { generateId } from '$lib/utils/generate-id'; import { generateId } from '$lib/utils/generate-id';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import { get } from 'svelte/store';
interface Props { interface Props {
label: string; label: string;
@@ -283,6 +282,7 @@
class:cursor-pointer={!isActive} class:cursor-pointer={!isActive}
class="immich-form-input text-sm text-left w-full !pr-12 transition-all" class="immich-form-input text-sm text-left w-full !pr-12 transition-all"
id={inputId} id={inputId}
onclick={activate}
onfocus={activate} onfocus={activate}
oninput={onInput} oninput={onInput}
role="combobox" role="combobox"
@@ -366,7 +366,7 @@
aria-disabled={true} aria-disabled={true}
class="text-left w-full px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 cursor-default aria-selected:bg-gray-200 aria-selected:dark:bg-gray-700" class="text-left w-full px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 cursor-default aria-selected:bg-gray-200 aria-selected:dark:bg-gray-700"
id={`${listboxId}-${0}`} id={`${listboxId}-${0}`}
onclick={closeDropdown} onclick={() => closeDropdown()}
> >
{allowCreate ? searchQuery : $t('no_results')} {allowCreate ? searchQuery : $t('no_results')}
</li> </li>
@@ -63,14 +63,11 @@
const _assets = assets; const _assets = assets;
updateSlidingWindow(); updateSlidingWindow();
const rowWidth = Math.floor(viewport.width);
const rowHeight = rowWidth < 850 ? 100 : 235;
geometry = getJustifiedLayoutFromAssets(_assets, { geometry = getJustifiedLayoutFromAssets(_assets, {
spacing: 2, spacing: 2,
heightTolerance: 0.15, heightTolerance: 0.15,
rowHeight, rowHeight: 235,
rowWidth, rowWidth: Math.floor(viewport.width),
}); });
}); });
+2 -12
View File
@@ -39,9 +39,7 @@ export type AssetStoreOptions = Omit<AssetApiGetTimeBucketsRequest, 'size'> & {
timelineAlbumId?: string; timelineAlbumId?: string;
deferInit?: boolean; deferInit?: boolean;
}; };
export type AssetStoreLayoutOptions = {
rowHeight: number;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
function updateObject(target: any, source: any): boolean { function updateObject(target: any, source: any): boolean {
if (!target) { if (!target) {
@@ -578,7 +576,6 @@ export class AssetStore {
// --- private // --- private
static #INIT_OPTIONS = {}; static #INIT_OPTIONS = {};
#rowHeight = 235;
#viewportHeight = $state(0); #viewportHeight = $state(0);
#viewportWidth = $state(0); #viewportWidth = $state(0);
#scrollTop = $state(0); #scrollTop = $state(0);
@@ -621,7 +618,6 @@ export class AssetStore {
const changed = value !== this.#viewportWidth; const changed = value !== this.#viewportWidth;
this.#viewportWidth = value; this.#viewportWidth = value;
this.suspendTransitions = true; this.suspendTransitions = true;
this.#rowHeight = value < 850 ? 100 : 235;
// side-effect - its ok! // side-effect - its ok!
void this.#updateViewportGeometry(changed); void this.#updateViewportGeometry(changed);
} }
@@ -797,11 +793,6 @@ export class AssetStore {
this.#updateViewportGeometry(false); this.#updateViewportGeometry(false);
} }
updateLayoutOptions(options: AssetStoreLayoutOptions) {
this.#rowHeight = options.rowHeight;
this.refreshLayout();
}
async #init(options: AssetStoreOptions) { async #init(options: AssetStoreOptions) {
// doing the following outside of the task reduces flickr // doing the following outside of the task reduces flickr
this.isInitialized = false; this.isInitialized = false;
@@ -871,11 +862,10 @@ export class AssetStore {
createLayoutOptions() { createLayoutOptions() {
const viewportWidth = this.viewportWidth; const viewportWidth = this.viewportWidth;
return { return {
spacing: 2, spacing: 2,
heightTolerance: 0.15, heightTolerance: 0.15,
rowHeight: this.#rowHeight, rowHeight: 235,
rowWidth: Math.floor(viewportWidth), rowWidth: Math.floor(viewportWidth),
}; };
} }