feat(server): visibility column (#17939)

* feat: private view

* pr feedback

* sql generation

* feat: visibility column

* fix: set visibility value as the same as the still part after unlinked live photos

* fix: test

* pr feedback
This commit is contained in:
Alex
2025-05-06 12:12:48 -05:00
committed by GitHub
parent 016d7a6ceb
commit d33ce13561
90 changed files with 1137 additions and 867 deletions

View File

@@ -1,8 +1,8 @@
<script lang="ts" module>
import type { SearchLocationFilter } from './search-location-section.svelte';
import type { SearchDisplayFilters } from './search-display-section.svelte';
import type { SearchDateFilter } from './search-date-section.svelte';
import { MediaType, QueryType, validQueryTypes } from '$lib/constants';
import type { SearchDateFilter } from './search-date-section.svelte';
import type { SearchDisplayFilters } from './search-display-section.svelte';
import type { SearchLocationFilter } from './search-location-section.svelte';
export type SearchFilter = {
query: string;
@@ -19,24 +19,24 @@
</script>
<script lang="ts">
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
import { preferences } from '$lib/stores/user.store';
import { parseUtcDate } from '$lib/utils/date-time';
import { generateId } from '$lib/utils/generate-id';
import { AssetTypeEnum, AssetVisibility, type MetadataSearchDto, type SmartSearchDto } from '@immich/sdk';
import { Button } from '@immich/ui';
import { AssetTypeEnum, type SmartSearchDto, type MetadataSearchDto } from '@immich/sdk';
import SearchPeopleSection from './search-people-section.svelte';
import SearchTagsSection from './search-tags-section.svelte';
import SearchLocationSection from './search-location-section.svelte';
import { mdiTune } from '@mdi/js';
import { t } from 'svelte-i18n';
import { SvelteSet } from 'svelte/reactivity';
import SearchCameraSection, { type SearchCameraFilter } from './search-camera-section.svelte';
import SearchDateSection from './search-date-section.svelte';
import SearchMediaSection from './search-media-section.svelte';
import SearchRatingsSection from './search-ratings-section.svelte';
import { parseUtcDate } from '$lib/utils/date-time';
import SearchDisplaySection from './search-display-section.svelte';
import SearchLocationSection from './search-location-section.svelte';
import SearchMediaSection from './search-media-section.svelte';
import SearchPeopleSection from './search-people-section.svelte';
import SearchRatingsSection from './search-ratings-section.svelte';
import SearchTagsSection from './search-tags-section.svelte';
import SearchTextSection from './search-text-section.svelte';
import { t } from 'svelte-i18n';
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
import { mdiTune } from '@mdi/js';
import { generateId } from '$lib/utils/generate-id';
import { SvelteSet } from 'svelte/reactivity';
import { preferences } from '$lib/stores/user.store';
interface Props {
searchQuery: MetadataSearchDto | SmartSearchDto;
@@ -83,7 +83,7 @@
takenBefore: searchQuery.takenBefore ? toStartOfDayDate(searchQuery.takenBefore) : undefined,
},
display: {
isArchive: searchQuery.isArchived,
isArchive: searchQuery.visibility === AssetVisibility.Archive,
isFavorite: searchQuery.isFavorite,
isNotInAlbum: 'isNotInAlbum' in searchQuery ? searchQuery.isNotInAlbum : undefined,
},
@@ -132,7 +132,7 @@
model: filter.camera.model,
takenAfter: parseOptionalDate(filter.date.takenAfter)?.startOf('day').toISO() || undefined,
takenBefore: parseOptionalDate(filter.date.takenBefore)?.endOf('day').toISO() || undefined,
isArchived: filter.display.isArchive || undefined,
visibility: filter.display.isArchive ? AssetVisibility.Archive : undefined,
isFavorite: filter.display.isFavorite || undefined,
isNotInAlbum: filter.display.isNotInAlbum || undefined,
personIds: filter.personIds.size > 0 ? [...filter.personIds] : undefined,

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import { locale } from '$lib/stores/preferences.store';
import {
AssetVisibility,
getAlbumStatistics,
getAssetStatistics,
type AlbumStatisticsResponseDto,
@@ -41,9 +42,9 @@
const getUsage = async () => {
[timelineStats, favoriteStats, archiveStats, trashStats, albumStats] = await Promise.all([
getAssetStatistics({ isArchived: false }),
getAssetStatistics({ visibility: AssetVisibility.Timeline }),
getAssetStatistics({ isFavorite: true }),
getAssetStatistics({ isArchived: true }),
getAssetStatistics({ visibility: AssetVisibility.Archive }),
getAssetStatistics({ isTrashed: true }),
getAlbumStatistics(),
]);

View File

@@ -10,6 +10,7 @@ import { formatDateGroupTitle, fromLocalDateTime } from '$lib/utils/timeline-uti
import { TUNABLES } from '$lib/utils/tunables';
import {
AssetOrder,
AssetVisibility,
getAssetInfo,
getTimeBucket,
getTimeBuckets,
@@ -1375,7 +1376,7 @@ export class AssetStore {
isExcluded(asset: AssetResponseDto) {
return (
isMismatched(this.#options.isArchived, asset.isArchived) ||
isMismatched(this.#options.visibility === AssetVisibility.Archive, asset.isArchived) ||
isMismatched(this.#options.isFavorite, asset.isFavorite) ||
isMismatched(this.#options.isTrashed, asset.isTrashed)
);

View File

@@ -1,7 +1,7 @@
import { goto } from '$app/navigation';
import FormatBoldMessage from '$lib/components/i18n/format-bold-message.svelte';
import type { InterpolationValues } from '$lib/components/i18n/format-message';
import { NotificationType, notificationController } from '$lib/components/shared-components/notification/notification';
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
import { AppRoute } from '$lib/constants';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { downloadManager } from '$lib/managers/download-manager.svelte';
@@ -15,6 +15,7 @@ import { getFormatter } from '$lib/utils/i18n';
import { navigate } from '$lib/utils/navigation';
import {
addAssetsToAlbum as addAssets,
AssetVisibility,
createStack,
deleteAssets,
deleteStacks,
@@ -507,7 +508,7 @@ export const toggleArchive = async (asset: AssetResponseDto) => {
const data = await updateAsset({
id: asset.id,
updateAssetDto: {
isArchived: !asset.isArchived,
visibility: asset.isArchived ? AssetVisibility.Timeline : AssetVisibility.Archive,
},
});
@@ -531,7 +532,9 @@ export const archiveAssets = async (assets: AssetResponseDto[], archive: boolean
try {
if (ids.length > 0) {
await updateAssets({ assetBulkUpdateDto: { ids, isArchived } });
await updateAssets({
assetBulkUpdateDto: { ids, visibility: isArchived ? AssetVisibility.Archive : AssetVisibility.Timeline },
});
}
for (const asset of assets) {