e96ffd43e7
* Squash - feature complete * remove need to init assetstore * More optimizations. No need to init. Fix tests * lint * add missing selector for e2e * e2e selectors again * Update: fully reactive store, some transitions, bugfixes * merge fallout * Test fallout * safari quirk * security * lint * lint * Bug fixes * lint/format * accidental commit * lock * null check, more throttle * revert long duration * Fix intersection bounds * Fix bugs in intersection calculation * lint, tweak scrubber ui a tiny bit * bugfix - deselecting asset doesnt work * fix not loading bucket, scroll off-by-1 error, jsdoc, naming
176 lines
5.5 KiB
Svelte
176 lines
5.5 KiB
Svelte
<script lang="ts">
|
|
import { run } from 'svelte/legacy';
|
|
|
|
import { goto } from '$app/navigation';
|
|
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
|
import MapSettingsModal from '$lib/components/map-page/map-settings-modal.svelte';
|
|
import Map from '$lib/components/shared-components/map/map.svelte';
|
|
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
|
import { AppRoute } from '$lib/constants';
|
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
|
import type { MapSettings } from '$lib/stores/preferences.store';
|
|
import { mapSettings } from '$lib/stores/preferences.store';
|
|
import { featureFlags } from '$lib/stores/server-config.store';
|
|
import { getMapMarkers, type MapMarkerResponseDto } from '@immich/sdk';
|
|
import { isEqual } from 'lodash-es';
|
|
import { DateTime, Duration } from 'luxon';
|
|
import { onDestroy, onMount } from 'svelte';
|
|
import type { PageData } from './$types';
|
|
import { handlePromiseError } from '$lib/utils';
|
|
import { navigate } from '$lib/utils/navigation';
|
|
|
|
interface Props {
|
|
data: PageData;
|
|
}
|
|
|
|
let { data }: Props = $props();
|
|
|
|
let { isViewing: showAssetViewer, asset: viewingAsset, setAssetId } = assetViewingStore;
|
|
|
|
let abortController: AbortController;
|
|
let mapMarkers: MapMarkerResponseDto[] = $state([]);
|
|
let viewingAssets: string[] = $state([]);
|
|
let viewingAssetCursor = 0;
|
|
let showSettingsModal = $state(false);
|
|
|
|
onMount(async () => {
|
|
mapMarkers = await loadMapMarkers();
|
|
});
|
|
|
|
onDestroy(() => {
|
|
abortController?.abort();
|
|
assetViewingStore.showAssetViewer(false);
|
|
});
|
|
|
|
run(() => {
|
|
if (!$featureFlags.map) {
|
|
handlePromiseError(goto(AppRoute.PHOTOS));
|
|
}
|
|
});
|
|
const omit = (obj: MapSettings, key: string) => {
|
|
return Object.fromEntries(Object.entries(obj).filter(([k]) => k !== key));
|
|
};
|
|
|
|
async function loadMapMarkers() {
|
|
if (abortController) {
|
|
abortController.abort();
|
|
}
|
|
abortController = new AbortController();
|
|
|
|
const { includeArchived, onlyFavorites, withPartners, withSharedAlbums } = $mapSettings;
|
|
const { fileCreatedAfter, fileCreatedBefore } = getFileCreatedDates();
|
|
|
|
return await getMapMarkers(
|
|
{
|
|
isArchived: includeArchived && undefined,
|
|
isFavorite: onlyFavorites || undefined,
|
|
fileCreatedAfter: fileCreatedAfter || undefined,
|
|
fileCreatedBefore,
|
|
withPartners: withPartners || undefined,
|
|
withSharedAlbums: withSharedAlbums || undefined,
|
|
},
|
|
{
|
|
signal: abortController.signal,
|
|
},
|
|
);
|
|
}
|
|
|
|
function getFileCreatedDates() {
|
|
const { relativeDate, dateAfter, dateBefore } = $mapSettings;
|
|
|
|
if (relativeDate) {
|
|
const duration = Duration.fromISO(relativeDate);
|
|
return {
|
|
fileCreatedAfter: duration.isValid ? DateTime.now().minus(duration).toISO() : undefined,
|
|
};
|
|
}
|
|
|
|
try {
|
|
return {
|
|
fileCreatedAfter: dateAfter ? new Date(dateAfter).toISOString() : undefined,
|
|
fileCreatedBefore: dateBefore ? new Date(dateBefore).toISOString() : undefined,
|
|
};
|
|
} catch {
|
|
$mapSettings.dateAfter = '';
|
|
$mapSettings.dateBefore = '';
|
|
return {};
|
|
}
|
|
}
|
|
|
|
async function onViewAssets(assetIds: string[]) {
|
|
viewingAssets = assetIds;
|
|
viewingAssetCursor = 0;
|
|
await setAssetId(assetIds[0]);
|
|
}
|
|
|
|
async function navigateNext() {
|
|
if (viewingAssetCursor < viewingAssets.length - 1) {
|
|
await setAssetId(viewingAssets[++viewingAssetCursor]);
|
|
await navigate({ targetRoute: 'current', assetId: $viewingAsset.id });
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async function navigatePrevious() {
|
|
if (viewingAssetCursor > 0) {
|
|
await setAssetId(viewingAssets[--viewingAssetCursor]);
|
|
await navigate({ targetRoute: 'current', assetId: $viewingAsset.id });
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async function navigateRandom() {
|
|
if (viewingAssets.length <= 0) {
|
|
return undefined;
|
|
}
|
|
const index = Math.floor(Math.random() * viewingAssets.length);
|
|
const asset = await setAssetId(viewingAssets[index]);
|
|
await navigate({ targetRoute: 'current', assetId: $viewingAsset.id });
|
|
return asset;
|
|
}
|
|
</script>
|
|
|
|
{#if $featureFlags.loaded && $featureFlags.map}
|
|
<UserPageLayout title={data.meta.title}>
|
|
<div class="isolate h-full w-full">
|
|
<Map hash bind:mapMarkers bind:showSettingsModal onSelect={onViewAssets} />
|
|
</div>
|
|
</UserPageLayout>
|
|
<Portal target="body">
|
|
{#if $showAssetViewer}
|
|
{#await import('../../../../../lib/components/asset-viewer/asset-viewer.svelte') then { default: AssetViewer }}
|
|
<AssetViewer
|
|
asset={$viewingAsset}
|
|
showNavigation={viewingAssets.length > 1}
|
|
onNext={navigateNext}
|
|
onPrevious={navigatePrevious}
|
|
onRandom={navigateRandom}
|
|
onClose={() => {
|
|
assetViewingStore.showAssetViewer(false);
|
|
handlePromiseError(navigate({ targetRoute: 'current', assetId: null }));
|
|
}}
|
|
isShared={false}
|
|
/>
|
|
{/await}
|
|
{/if}
|
|
</Portal>
|
|
|
|
{#if showSettingsModal}
|
|
<MapSettingsModal
|
|
settings={{ ...$mapSettings }}
|
|
onClose={() => (showSettingsModal = false)}
|
|
onSave={async (settings) => {
|
|
const shouldUpdate = !isEqual(omit(settings, 'allowDarkMode'), omit($mapSettings, 'allowDarkMode'));
|
|
showSettingsModal = false;
|
|
$mapSettings = settings;
|
|
|
|
if (shouldUpdate) {
|
|
mapMarkers = await loadMapMarkers();
|
|
}
|
|
}}
|
|
/>
|
|
{/if}
|
|
{/if}
|