feat(web): timeline bucket for albums (4) (#3604)

* feat: server changes for album timeline

* feat(web): album timeline view

* chore: open api

* chore: remove archive action

* fix: favorite for non-owners
This commit is contained in:
Jason Rasmussen 2023-08-11 12:00:51 -04:00 committed by GitHub
parent 36dc7bd924
commit 5cd13227ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 1014 additions and 757 deletions

View file

@ -25,10 +25,14 @@
export let assetStore: AssetStore;
export let assetInteractionStore: AssetInteractionStore;
const { selectedGroup, selectedAssets, assetsInAlbumState, assetSelectionCandidates, isMultiSelectState } =
assetInteractionStore;
const { selectedGroup, selectedAssets, assetSelectionCandidates, isMultiSelectState } = assetInteractionStore;
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{
select: { title: string; assets: AssetResponseDto[] };
selectAssets: AssetResponseDto;
selectAssetCandidates: AssetResponseDto | null;
shift: { heightDelta: number };
}>();
let isMouseOverGroup = false;
let actualBucketHeight: number;
@ -86,64 +90,44 @@
return width;
};
const assetClickHandler = (
asset: AssetResponseDto,
assetsInDateGroup: AssetResponseDto[],
dateGroupTitle: string,
) => {
const assetClickHandler = (asset: AssetResponseDto, assetsInDateGroup: AssetResponseDto[], groupTitle: string) => {
if (isSelectionMode || $isMultiSelectState) {
assetSelectHandler(asset, assetsInDateGroup, dateGroupTitle);
assetSelectHandler(asset, assetsInDateGroup, groupTitle);
return;
}
assetViewingStore.setAssetId(asset.id);
};
const selectAssetGroupHandler = (selectAssetGroupHandler: AssetResponseDto[], dateGroupTitle: string) => {
if ($selectedGroup.has(dateGroupTitle)) {
assetInteractionStore.removeGroupFromMultiselectGroup(dateGroupTitle);
selectAssetGroupHandler.forEach((asset) => {
assetInteractionStore.removeAssetFromMultiselectGroup(asset);
});
} else {
assetInteractionStore.addGroupToMultiselectGroup(dateGroupTitle);
selectAssetGroupHandler.forEach((asset) => {
assetInteractionStore.addAssetToMultiselectGroup(asset);
});
}
};
const handleSelectGroup = (title: string, assets: AssetResponseDto[]) => dispatch('select', { title, assets });
const assetSelectHandler = (
asset: AssetResponseDto,
assetsInDateGroup: AssetResponseDto[],
dateGroupTitle: string,
) => {
dispatch('selectAssets', { asset });
const assetSelectHandler = (asset: AssetResponseDto, assetsInDateGroup: AssetResponseDto[], groupTitle: string) => {
dispatch('selectAssets', asset);
// Check if all assets are selected in a group to toggle the group selection's icon
let selectedAssetsInGroupCount = assetsInDateGroup.filter((asset) => $selectedAssets.has(asset)).length;
// if all assets are selected in a group, add the group to selected group
if (selectedAssetsInGroupCount == assetsInDateGroup.length) {
assetInteractionStore.addGroupToMultiselectGroup(dateGroupTitle);
assetInteractionStore.addGroupToMultiselectGroup(groupTitle);
} else {
assetInteractionStore.removeGroupFromMultiselectGroup(dateGroupTitle);
assetInteractionStore.removeGroupFromMultiselectGroup(groupTitle);
}
};
const assetMouseEventHandler = (dateGroupTitle: string, asset: AssetResponseDto | null) => {
const assetMouseEventHandler = (groupTitle: string, asset: AssetResponseDto | null) => {
// Show multi select icon on hover on date group
hoveredDateGroup = dateGroupTitle;
hoveredDateGroup = groupTitle;
if ($isMultiSelectState) {
dispatch('selectAssetCandidates', { asset });
dispatch('selectAssetCandidates', asset);
}
};
</script>
<section id="asset-group-by-date" class="flex flex-wrap gap-x-12" bind:clientHeight={actualBucketHeight}>
{#each assetsGroupByDate as assetsInDateGroup, groupIndex (assetsInDateGroup[0].id)}
{@const dateGroupTitle = formatGroupTitle(DateTime.fromISO(assetsInDateGroup[0].fileCreatedAt).startOf('day'))}
{#each assetsGroupByDate as groupAssets, groupIndex (groupAssets[0].id)}
{@const groupTitle = formatGroupTitle(DateTime.fromISO(groupAssets[0].fileCreatedAt).startOf('day'))}
<!-- Asset Group By Date -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
@ -151,11 +135,11 @@
class="mt-5 flex flex-col"
on:mouseenter={() => {
isMouseOverGroup = true;
assetMouseEventHandler(dateGroupTitle, null);
assetMouseEventHandler(groupTitle, null);
}}
on:mouseleave={() => {
isMouseOverGroup = false;
assetMouseEventHandler(dateGroupTitle, null);
assetMouseEventHandler(groupTitle, null);
}}
>
<!-- Date group title -->
@ -163,14 +147,14 @@
class="mb-2 flex h-6 place-items-center text-xs font-medium text-immich-fg dark:text-immich-dark-fg md:text-sm"
style="width: {geometry[groupIndex].containerWidth}px"
>
{#if !singleSelect && ((hoveredDateGroup == dateGroupTitle && isMouseOverGroup) || $selectedGroup.has(dateGroupTitle))}
{#if !singleSelect && ((hoveredDateGroup == groupTitle && isMouseOverGroup) || $selectedGroup.has(groupTitle))}
<div
transition:fly={{ x: -24, duration: 200, opacity: 0.5 }}
class="inline-block px-2 hover:cursor-pointer"
on:click={() => selectAssetGroupHandler(assetsInDateGroup, dateGroupTitle)}
on:keydown={() => selectAssetGroupHandler(assetsInDateGroup, dateGroupTitle)}
on:click={() => handleSelectGroup(groupTitle, groupAssets)}
on:keydown={() => handleSelectGroup(groupTitle, groupAssets)}
>
{#if $selectedGroup.has(dateGroupTitle)}
{#if $selectedGroup.has(groupTitle)}
<CheckCircle size="24" color="#4250af" />
{:else}
<CircleOutline size="24" color="#757575" />
@ -178,8 +162,8 @@
</div>
{/if}
<span class="truncate first-letter:capitalize" title={dateGroupTitle}>
{dateGroupTitle}
<span class="truncate first-letter:capitalize" title={groupTitle}>
{groupTitle}
</span>
</p>
@ -188,7 +172,7 @@
class="relative"
style="height: {geometry[groupIndex].containerHeight}px;width: {geometry[groupIndex].containerWidth}px"
>
{#each assetsInDateGroup as asset, index (asset.id)}
{#each groupAssets as asset, index (asset.id)}
{@const box = geometry[groupIndex].boxes[index]}
<div
class="absolute"
@ -197,12 +181,12 @@
<Thumbnail
{asset}
{groupIndex}
on:click={() => assetClickHandler(asset, assetsInDateGroup, dateGroupTitle)}
on:select={() => assetSelectHandler(asset, assetsInDateGroup, dateGroupTitle)}
on:mouse-event={() => assetMouseEventHandler(dateGroupTitle, asset)}
selected={$selectedAssets.has(asset) || $assetsInAlbumState.some(({ id }) => id === asset.id)}
on:click={() => assetClickHandler(asset, groupAssets, groupTitle)}
on:select={() => assetSelectHandler(asset, groupAssets, groupTitle)}
on:mouse-event={() => assetMouseEventHandler(groupTitle, asset)}
selected={$selectedAssets.has(asset) || $assetStore.albumAssets.has(asset.id)}
selectionCandidate={$assetSelectionCandidates.has(asset)}
disabled={$assetsInAlbumState.some(({ id }) => id === asset.id)}
disabled={$assetStore.albumAssets.has(asset.id)}
thumbnailWidth={box.width}
thumbnailHeight={box.height}
/>