Merge branch 'feat/event-notifications'
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
49
src/lib/components/Notification/Notification.svelte
Normal file
49
src/lib/components/Notification/Notification.svelte
Normal 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>
|
||||
17
src/lib/components/Notification/Notifications.svelte
Normal file
17
src/lib/components/Notification/Notifications.svelte
Normal 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>
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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: '' };
|
||||
|
||||
@@ -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;
|
||||
59
src/lib/stores/notification.store.ts
Normal file
59
src/lib/stores/notification.store.ts
Normal 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();
|
||||
@@ -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} -->
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -10,6 +10,15 @@ export default {
|
||||
colors: {
|
||||
darken: '#07050166',
|
||||
lighten: '#fde68a20'
|
||||
},
|
||||
keyframes: {
|
||||
timer: {
|
||||
'0%': { width: '0%' },
|
||||
'100%': { width: '100%' }
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
timer: 'timer 1s linear'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user