mirror of
https://github.com/immich-app/immich.git
synced 2025-12-21 09:15:44 +03:00
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:
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
]);
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -372,7 +372,10 @@
|
||||
if (viewMode === AlbumPageViewMode.VIEW) {
|
||||
void assetStore.updateOptions({ albumId, order: albumOrder });
|
||||
} else if (viewMode === AlbumPageViewMode.SELECT_ASSETS) {
|
||||
void assetStore.updateOptions({ isArchived: false, withPartners: true, timelineAlbumId: albumId });
|
||||
void assetStore.updateOptions({
|
||||
withPartners: true,
|
||||
timelineAlbumId: albumId,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -385,9 +388,6 @@
|
||||
activityManager.reset();
|
||||
assetStore.destroy();
|
||||
});
|
||||
// let timelineStore = new AssetStore();
|
||||
// $effect(() => void timelineStore.updateOptions({ isArchived: false, withPartners: true, timelineAlbumId: albumId }));
|
||||
// onDestroy(() => timelineStore.destroy());
|
||||
|
||||
let isOwned = $derived($user.id == album.ownerId);
|
||||
|
||||
|
||||
@@ -8,17 +8,18 @@
|
||||
import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte';
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import { AssetAction } from '$lib/constants';
|
||||
|
||||
import type { PageData } from './$types';
|
||||
import { mdiPlus, mdiDotsVertical } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
interface Props {
|
||||
data: PageData;
|
||||
@@ -26,7 +27,7 @@
|
||||
|
||||
let { data }: Props = $props();
|
||||
const assetStore = new AssetStore();
|
||||
void assetStore.updateOptions({ isArchived: true });
|
||||
void assetStore.updateOptions({ visibility: AssetVisibility.Archive });
|
||||
onDestroy(() => assetStore.destroy());
|
||||
|
||||
const assetInteraction = new AssetInteraction();
|
||||
|
||||
@@ -9,19 +9,19 @@
|
||||
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
|
||||
import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte';
|
||||
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
|
||||
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import { AssetAction } from '$lib/constants';
|
||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
interface Props {
|
||||
data: PageData;
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
import CreateSharedLink from '$lib/components/photos-page/actions/create-shared-link.svelte';
|
||||
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { mdiPlus, mdiArrowLeft } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
import { mdiArrowLeft, mdiPlus } from '@mdi/js';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
interface Props {
|
||||
data: PageData;
|
||||
@@ -22,7 +23,14 @@
|
||||
let { data }: Props = $props();
|
||||
|
||||
const assetStore = new AssetStore();
|
||||
$effect(() => void assetStore.updateOptions({ userId: data.partner.id, isArchived: false, withStacked: true }));
|
||||
$effect(
|
||||
() =>
|
||||
void assetStore.updateOptions({
|
||||
userId: data.partner.id,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
withStacked: true,
|
||||
}),
|
||||
);
|
||||
onDestroy(() => assetStore.destroy());
|
||||
const assetInteraction = new AssetInteraction();
|
||||
|
||||
|
||||
@@ -34,12 +34,14 @@
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { websocketEvents } from '$lib/stores/websocket';
|
||||
import { getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { isExternalUrl } from '$lib/utils/navigation';
|
||||
import {
|
||||
AssetVisibility,
|
||||
getPersonStatistics,
|
||||
mergePerson,
|
||||
searchPerson,
|
||||
@@ -59,11 +61,10 @@
|
||||
mdiHeartOutline,
|
||||
mdiPlus,
|
||||
} from '@mdi/js';
|
||||
import { DateTime } from 'luxon';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
interface Props {
|
||||
data: PageData;
|
||||
@@ -75,7 +76,7 @@
|
||||
let { isViewing: showAssetViewer } = assetViewingStore;
|
||||
|
||||
const assetStore = new AssetStore();
|
||||
$effect(() => void assetStore.updateOptions({ isArchived: false, personId: data.person.id }));
|
||||
$effect(() => void assetStore.updateOptions({ visibility: AssetVisibility.Timeline, personId: data.person.id }));
|
||||
onDestroy(() => assetStore.destroy());
|
||||
|
||||
const assetInteraction = new AssetInteraction();
|
||||
|
||||
@@ -32,14 +32,14 @@
|
||||
type OnUnlink,
|
||||
} from '$lib/utils/actions';
|
||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||
import { AssetTypeEnum } from '@immich/sdk';
|
||||
import { AssetTypeEnum, AssetVisibility } from '@immich/sdk';
|
||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
let { isViewing: showAssetViewer } = assetViewingStore;
|
||||
const assetStore = new AssetStore();
|
||||
void assetStore.updateOptions({ isArchived: false, withStacked: true, withPartners: true });
|
||||
void assetStore.updateOptions({ visibility: AssetVisibility.Timeline, withStacked: true, withPartners: true });
|
||||
onDestroy(() => assetStore.destroy());
|
||||
|
||||
const assetInteraction = new AssetInteraction();
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate, goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { shortcut } from '$lib/actions/shortcut';
|
||||
import AlbumCardGroup from '$lib/components/album-page/album-card-group.svelte';
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import AddToAlbum from '$lib/components/photos-page/actions/add-to-album.svelte';
|
||||
import ArchiveAction from '$lib/components/photos-page/actions/archive-action.svelte';
|
||||
import AssetJobActions from '$lib/components/photos-page/actions/asset-job-actions.svelte';
|
||||
import ChangeDate from '$lib/components/photos-page/actions/change-date-action.svelte';
|
||||
import ChangeLocation from '$lib/components/photos-page/actions/change-location-action.svelte';
|
||||
import CreateSharedLink from '$lib/components/photos-page/actions/create-shared-link.svelte';
|
||||
import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte';
|
||||
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
|
||||
import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
|
||||
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
|
||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import SearchBar from '$lib/components/shared-components/search-bar/search-bar.svelte';
|
||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { shortcut } from '$lib/actions/shortcut';
|
||||
import type { Viewport } from '$lib/stores/assets-store.svelte';
|
||||
import { lang, locale } from '$lib/stores/preferences.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||
import { parseUtcDate } from '$lib/utils/date-time';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { isAlbumsRoute, isPeopleRoute } from '$lib/utils/navigation';
|
||||
import {
|
||||
type AlbumResponseDto,
|
||||
type AssetResponseDto,
|
||||
@@ -31,21 +44,8 @@
|
||||
type SmartSearchDto,
|
||||
} from '@immich/sdk';
|
||||
import { mdiArrowLeft, mdiDotsVertical, mdiImageOffOutline, mdiPlus, mdiSelectAll } from '@mdi/js';
|
||||
import type { Viewport } from '$lib/stores/assets-store.svelte';
|
||||
import { lang, locale } from '$lib/stores/preferences.store';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
import { parseUtcDate } from '$lib/utils/date-time';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import AlbumCardGroup from '$lib/components/album-page/album-card-group.svelte';
|
||||
import { isAlbumsRoute, isPeopleRoute } from '$lib/utils/navigation';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { tick } from 'svelte';
|
||||
import AssetJobActions from '$lib/components/photos-page/actions/asset-job-actions.svelte';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import TagAction from '$lib/components/photos-page/actions/tag-action.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
const MAX_ASSET_COUNT = 5000;
|
||||
let { isViewing: showAssetViewer } = assetViewingStore;
|
||||
@@ -186,7 +186,7 @@
|
||||
const keyMap: Partial<Record<keyof SearchTerms, string>> = {
|
||||
takenAfter: $t('start_date'),
|
||||
takenBefore: $t('end_date'),
|
||||
isArchived: $t('in_archive'),
|
||||
visibility: $t('in_archive'),
|
||||
isFavorite: $t('favorite'),
|
||||
isNotInAlbum: $t('not_in_any_album'),
|
||||
type: $t('media_type'),
|
||||
@@ -313,7 +313,7 @@
|
||||
<div class="flex place-content-center place-items-center text-xs">
|
||||
<div
|
||||
class="bg-immich-primary py-2 px-4 text-white dark:text-black dark:bg-immich-dark-primary
|
||||
{value === true ? 'rounded-full' : 'roudned-s-full'}"
|
||||
{value === true ? 'rounded-full' : 'rounded-s-full'}"
|
||||
>
|
||||
{getHumanReadableSearchKey(key as keyof SearchTerms)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user