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

@@ -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: '' };

View File

@@ -1,6 +1,6 @@
import type { TitleType } from '$lib/types';
import { writable } from 'svelte/store';
import TitlePageModal from '../TitlePageLayout/TitlePageModal.svelte';
import TitlePageModal from '../components/TitlePageLayout/TitlePageModal.svelte';
type ModalItem = {
id: symbol;

View File

@@ -0,0 +1,59 @@
import { writable } from 'svelte/store';
export type NotificationItem = {
id: symbol;
component: ConstructorOfATypedSvelteComponent;
props: Record<string, any>;
timeout: NodeJS.Timeout | undefined;
duration: number;
height: number;
};
function createNotificationStack() {
const stack = writable<NotificationItem[]>([]);
function create(
component: ConstructorOfATypedSvelteComponent,
props: Record<string, any>,
duration = 5000
) {
const id = Symbol();
const item: NotificationItem = {
id,
component,
props,
timeout: undefined,
duration,
height: 0
};
if (duration > 0) {
item.timeout = setTimeout(() => {
close(id);
}, duration);
}
stack.update((s) => {
s.push(item);
return s;
});
return id;
}
function close(id: symbol) {
stack.update((s) => {
clearTimeout(s.find((i) => i.id === id)?.timeout);
s = s.filter((i) => i.id !== id);
return s;
});
}
return {
...stack,
create,
close
};
}
export const notificationStack = createNotificationStack();

View File

@@ -8,6 +8,7 @@
import { writable } from 'svelte/store';
import '../app.css';
import type { LayoutServerData } from './$types';
import Notifications from '$lib/components/Notification/Notifications.svelte';
export let data: LayoutServerData;
settings.set(data.settings);
@@ -23,6 +24,7 @@
{#key $page.url.pathname}
<DynamicModal />
{/key}
<Notifications />
<UpdateChecker />
</div>
<!-- {:else} -->

View File

@@ -9,7 +9,7 @@
import Card from '$lib/components/Card/Card.svelte';
import { fetchCardTmdbProps } from '$lib/components/Card/card';
import CarouselPlaceholderItems from '$lib/components/Carousel/CarouselPlaceholderItems.svelte';
import { modalStack } from '$lib/components/Modal/Modal';
import { modalStack } from '$lib/stores/modal.store';
import PeopleCard from '$lib/components/PeopleCard/PeopleCard.svelte';
import ProgressBar from '$lib/components/ProgressBar.svelte';
import RequestModal from '$lib/components/RequestModal/RequestModal.svelte';

View File

@@ -14,7 +14,7 @@
import CarouselPlaceholderItems from '$lib/components/Carousel/CarouselPlaceholderItems.svelte';
import UiCarousel from '$lib/components/Carousel/UICarousel.svelte';
import EpisodeCard from '$lib/components/EpisodeCard/EpisodeCard.svelte';
import { modalStack } from '$lib/components/Modal/Modal';
import { modalStack } from '$lib/stores/modal.store';
import PeopleCard from '$lib/components/PeopleCard/PeopleCard.svelte';
import SeriesRequestModal from '$lib/components/RequestModal/SeriesRequestModal.svelte';
import OpenInButton from '$lib/components/TitlePageLayout/OpenInButton.svelte';

View File

@@ -10,6 +10,15 @@ export default {
colors: {
darken: '#07050166',
lighten: '#fde68a20'
},
keyframes: {
timer: {
'0%': { width: '0%' },
'100%': { width: '100%' }
}
},
animation: {
timer: 'timer 1s linear'
}
}
},