Merge branch 'feat/event-notifications'

This commit is contained in:
Aleksi Lassila
2023-08-21 15:10:55 +03:00
19 changed files with 150 additions and 14 deletions

View File

@@ -7,7 +7,7 @@
import { Clock, Star } from 'radix-icons-svelte';
import ContextMenu from '../ContextMenu/ContextMenu.svelte';
import LibraryItemContextItems from '../ContextMenu/LibraryItemContextItems.svelte';
import { openTitleModal } from '../Modal/Modal';
import { openTitleModal } from '../../stores/modal.store';
import ProgressBar from '../ProgressBar.svelte';
export let tmdbId: number;

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import classNames from 'classnames';
import { modalStack } from './Modal';
import { modalStack } from '../../stores/modal.store';
import { fade } from 'svelte/transition';
import { onDestroy } from 'svelte';

View File

@@ -1,69 +0,0 @@
import type { TitleType } from '$lib/types';
import { writable } from 'svelte/store';
import TitlePageModal from '../TitlePageLayout/TitlePageModal.svelte';
type ModalItem = {
id: symbol;
group: symbol;
component: ConstructorOfATypedSvelteComponent;
props: Record<string, any>;
};
function createDynamicModalStack() {
const store = writable<{ stack: ModalItem[]; top: ModalItem | undefined }>({
stack: [],
top: undefined
});
function close(symbol: symbol) {
store.update((s) => {
s.stack = s.stack.filter((i) => i.id !== symbol);
s.top = s.stack[s.stack.length - 1];
return s;
});
}
function closeGroup(group: symbol) {
store.update((s) => {
s.stack = s.stack.filter((i) => i.group !== group);
s.top = s.stack[s.stack.length - 1];
return s;
});
}
function create(
component: ConstructorOfATypedSvelteComponent,
props: Record<string, any>,
group: symbol | undefined = undefined
) {
const id = Symbol();
const item = { id, component, props, group: group || id };
store.update((s) => {
s.stack.push(item);
s.top = item;
return s;
});
return id;
}
function reset() {
store.set({ stack: [], top: undefined });
}
return {
...store,
create,
close,
closeGroup,
reset
};
}
export const modalStack = createDynamicModalStack();
let lastTitleModal: symbol | undefined = undefined;
export function openTitleModal(tmdbId: number, type: TitleType) {
if (lastTitleModal) {
modalStack.close(lastTitleModal);
}
lastTitleModal = modalStack.create(TitlePageModal, { tmdbId, type });
}

View File

@@ -5,7 +5,7 @@
import TitleSearchModal from './TitleSearchModal.svelte';
import IconButton from '../IconButton.svelte';
import { fade } from 'svelte/transition';
import { modalStack } from '../Modal/Modal';
import { modalStack } from '../../stores/modal.store';
import { _ } from 'svelte-i18n';
let y = 0;

View File

@@ -3,7 +3,7 @@
import { searchTmdbTitles } from '$lib/apis/tmdb/tmdbApi';
import { TMDB_POSTER_SMALL } from '$lib/constants';
import { MagnifyingGlass } from 'radix-icons-svelte';
import { modalStack } from '../Modal/Modal';
import { modalStack } from '../../stores/modal.store';
import ModalContent from '../Modal/ModalContainer.svelte';
import ModalHeader from '../Modal/ModalHeader.svelte';
import { onMount } from 'svelte';

View File

@@ -0,0 +1,49 @@
<script lang="ts">
import { notificationStack } from '$lib/stores/notification.store';
import classNames from 'classnames';
import { Cross2 } from 'radix-icons-svelte';
import { fade, fly } from 'svelte/transition';
import IconButton from '../IconButton.svelte';
export let id: symbol;
export let title: string;
export let description: string;
export let duration = 0;
function handleClose() {
console.log('close');
notificationStack.close(id);
}
</script>
<div
class="bg-zinc-900 bg-opacity-60 rounded-lg backdrop-blur-xl overflow-hidden
flex flex-col w-72"
in:fly|global={{ duration: 150, x: 50 }}
out:fade|global={{ duration: 150 }}
>
<div class="relative">
<div
class={classNames('left-0 absolute bg-zinc-200 bg-opacity-10 h-full w-full', {
'animate-timer': duration
})}
style="animation-duration: {duration - 1000}ms;"
hidden={!duration}
/>
<div
class="relative z-[1] flex items-center justify-between bg-zinc-200 bg-opacity-10 p-1 px-3"
>
<div>
<h1 class="text-zinc-200 font-medium text-sm">{title}</h1>
</div>
<IconButton on:click={handleClose}>
<Cross2 size={15} />
</IconButton>
</div>
</div>
<div class="flex-1 flex flex-col p-2 px-3">
<p class="text-sm text-zinc-400">{description}</p>
</div>
</div>

View File

@@ -0,0 +1,17 @@
<script lang="ts">
import { notificationStack } from '$lib/stores/notification.store';
import { flip } from 'svelte/animate';
</script>
<div class="fixed bottom-8 right-8 z-50 flex flex-col gap-4">
{#each $notificationStack as notification (notification.id)}
<div animate:flip={{ duration: 500 }}>
<svelte:component
this={notification.component}
id={notification.id}
duration={notification.duration}
{...notification.props}
/>
</div>
{/each}
</div>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { fetchSonarrEpisodes, type SonarrEpisode } from '$lib/apis/sonarr/sonarrApi';
import { modalStack } from '../Modal/Modal';
import { modalStack } from '../../stores/modal.store';
import ModalContainer from '../Modal/ModalContainer.svelte';
import ModalContent from '../Modal/ModalContent.svelte';
import ModalHeader from '../Modal/ModalHeader.svelte';

View File

@@ -10,7 +10,7 @@
import { createEventDispatcher } from 'svelte';
import HeightHider from '../HeightHider.svelte';
import IconButton from '../IconButton.svelte';
import { modalStack } from '../Modal/Modal';
import { modalStack } from '../../stores/modal.store';
import ModalContent from '../Modal/ModalContainer.svelte';
import ModalHeader from '../Modal/ModalHeader.svelte';

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { ChevronRight } from 'radix-icons-svelte';
import Button from '../Button.svelte';
import { modalStack } from '../Modal/Modal';
import { modalStack } from '../../stores/modal.store';
import ModalContainer from '../Modal/ModalContainer.svelte';
import ModalContent from '../Modal/ModalContent.svelte';
import ModalHeader from '../Modal/ModalHeader.svelte';

View File

@@ -3,7 +3,7 @@
import { fly } from 'svelte/transition';
import MoviePage from '../../../routes/movie/[id]/MoviePage.svelte';
import SeriesPage from '../../../routes/series/[id]/SeriesPage.svelte';
import { modalStack } from '../Modal/Modal';
import { modalStack } from '../../stores/modal.store';
export let tmdbId: number;
export let type: TitleType;

View File

@@ -10,7 +10,7 @@
import { onMount } from 'svelte';
import { fade, fly } from 'svelte/transition';
import type { TitleType } from '$lib/types';
import { openTitleModal } from '../Modal/Modal';
import { openTitleModal } from '../../stores/modal.store';
import { _ } from 'svelte-i18n';
const TRAILER_TIMEOUT = 3000;

View File

@@ -30,7 +30,7 @@
import ContextMenu from '../ContextMenu/ContextMenu.svelte';
import SelectableContextMenuItem from '../ContextMenu/SelectableContextMenuItem.svelte';
import IconButton from '../IconButton.svelte';
import { modalStack } from '../Modal/Modal';
import { modalStack } from '../../stores/modal.store';
import Slider from './Slider.svelte';
import { playerState } from './VideoPlayer';

View File

@@ -1,6 +1,6 @@
import { library } from '$lib/stores/library.store';
import { writable } from 'svelte/store';
import { modalStack } from '../Modal/Modal';
import { modalStack } from '../../stores/modal.store';
import VideoPlayer from './VideoPlayer.svelte';
const initialValue = { visible: false, jellyfinId: '' };