This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
identity/identity-web/src/routes/dashboard/Entries.svelte
2024-06-30 19:23:23 +02:00

193 lines
5.4 KiB
Svelte

<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/. -->
<script lang="ts">
import type { Entry as EntryType, EntryKind, KnownFeeling } from '$lib/entry';
import ExternalLink from './utils/ExternalLink.svelte';
import FeelingPill from './utils/FeelingPill.svelte';
import Entry from './utils/Entry.svelte';
import EntryDescription from './utils/EntryDescription.svelte';
import AssetPreview from './utils/AssetPreview.svelte';
import { createEventDispatcher } from 'svelte';
import Fuse from 'fuse.js';
import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome';
import { faLocationDot } from '@fortawesome/free-solid-svg-icons';
let dispatch = createEventDispatcher();
export let entries: EntryType[];
let filteredEntries = entries;
export let filters: {
fromDate: null | Date;
toDate: null | Date;
kind: null | EntryKind[];
feelings: null | {
exclusive: boolean;
feelings: KnownFeeling[];
};
searchQuery: null | string;
};
let extended: string[] = [];
function applyFilters(filters: {
fromDate: null | Date;
toDate: null | Date;
kind: null | EntryKind[];
feelings: null | {
exclusive: boolean;
feelings: KnownFeeling[];
};
searchQuery: null | string;
}) {
filteredEntries = entries;
if (filters.fromDate != null) {
filteredEntries = entries.filter((v) => new Date(v.creationDate) >= filters.fromDate!);
}
if (filters.toDate != null) {
filteredEntries = entries.filter((v) => new Date(v.creationDate) <= filters.toDate!);
}
if (filters.kind != null) {
filteredEntries = entries.filter((v) => filters.kind!.includes(v.base.kind));
}
if (filters.feelings != null) {
let feelings = filters.feelings!.feelings;
if (filters.feelings.exclusive) {
filteredEntries = entries.filter((v) => {
let v1 = v.feelings.filter((f) => typeof f === 'string' && feelings.includes(f));
return v.feelings.length === v1.length;
});
} else {
filteredEntries = entries.filter((v) => {
let includes = false;
feelings.forEach((f) => {
if (v.feelings.includes(f)) {
includes = true;
}
});
return includes;
});
}
}
if (filters.searchQuery != null) {
let fuse = new Fuse(entries, {
keys: [
{
name: 'title',
weight: 2
},
'description'
]
});
let results = fuse.search(filters.searchQuery!);
filteredEntries = results.map((v) => v.item);
}
if (filteredEntries.length !== entries.length) {
dispatch('updatedFilterStatus', true);
} else {
dispatch('updatedFilterStatus', false);
}
}
$: applyFilters(filters);
</script>
{#if entries.length != filteredEntries.length && filteredEntries.length === 0}
<div class="flex justify-center py-6">
<div role="status" class="flex items-center justify-center gap-5">
<span class="text-xl">No results found</span>
</div>
</div>
{/if}
{#each filteredEntries as entry (entry.id)}
<Entry
on:extended={(e) => (extended = [e.detail.id, ...extended])}
on:contracted={(e) => (extended = extended.filter((v) => v !== e.detail.id))}
on:deleted={(e) => {
dispatch('deleted', e.detail);
}}
id={entry.id}
kind={entry.base.kind}
creationDate={new Date(entry.creationDate)}
title={entry.base.kind === 'date'
? new Date(entry.base.referencedDate).toLocaleDateString()
: entry.title}
isExtended={extended.includes(entry.id)}
>
<div slot="contracted">
{#if entry.base.kind === 'song' || entry.base.kind === 'album'}
{#if entry.base.link[0] != null}
<ExternalLink href={entry.base.link[0]}>
{entry.base.artist} &dash; {entry.base.title}
</ExternalLink>
{:else}
<span class="font-bold">{entry.base.artist} &dash; {entry.base.title}</span>
{/if}
{/if}
{#if entry.base.kind === 'feeling'}
<div class="flex gap-1">
{#each entry.feelings as feeling}
{#if typeof feeling === 'string'}
<FeelingPill {feeling} />
{:else}
<FeelingPill
feeling={feeling.identifier}
bgColor={feeling.backgroundColor}
textColor={feeling.textColor}
/>
{/if}
{/each}
</div>
{/if}
</div>
<div slot="extended">
<div class="mb-2 flex gap-1">
{#each entry.feelings as feeling}
{#if typeof feeling === 'string'}
<FeelingPill {feeling} />
{:else}
<FeelingPill
feeling={feeling.identifier}
bgColor={feeling.backgroundColor}
textColor={feeling.textColor}
/>
{/if}
{/each}
</div>
{#if entry.base.kind === 'song' || entry.base.kind === 'album'}
{#if entry.base.link[0] != null}
<ExternalLink href={entry.base.link[0]}>
{entry.base.artist} &dash; {entry.base.title}
</ExternalLink>
{:else}
<span class="font-bold">{entry.base.artist} &dash; {entry.base.title}</span>
{/if}
{/if}
{#if entry.base.kind === "environment" && typeof entry.base.location === "string"}
<div class="mb-1">
<FontAwesomeIcon class="pr-2" icon={faLocationDot}/>
<span class="text-xl">{entry.base.location}</span>
</div>
{/if}
{#if entry.description != null}
<EntryDescription>{entry.description}</EntryDescription>
{/if}
<div class="mt-2 flex gap-1">
{#each entry.assets as asset}
<AssetPreview asset_id={asset} />
{/each}
</div>
</div>
</Entry>
{/each}