Browse Source

Implement notification box for web (#533)

* Added test button

* styling notification box

* Added auto dismission and animation to each notificaiont list

* Remove test button
Alex 2 years ago
parent
commit
f9b1b12b10

+ 70 - 0
web/src/lib/components/shared-components/notification/notification-card.svelte

@@ -0,0 +1,70 @@
+<script lang="ts">
+	import { fade } from 'svelte/transition';
+	import CloseCircleOutline from 'svelte-material-icons/CloseCircleOutline.svelte';
+	import InformationOutline from 'svelte-material-icons/InformationOutline.svelte';
+
+	import {
+		ImmichNotification,
+		notificationList,
+		NotificationType
+	} from '$lib/components/shared-components/notification/notification';
+	import { onMount } from 'svelte';
+
+	export let notificationInfo: ImmichNotification;
+
+	let infoPrimaryColor = '#4250AF';
+	let errorPrimaryColor = '#E64132';
+
+	$: icon =
+		notificationInfo.type === NotificationType.Error ? CloseCircleOutline : InformationOutline;
+
+	$: backgroundColor = () => {
+		if (notificationInfo.type === NotificationType.Info) {
+			return '#E0E2F0';
+		}
+
+		if (notificationInfo.type === NotificationType.Error) {
+			return '#FBE8E6';
+		}
+	};
+
+	$: borderStyle = () => {
+		if (notificationInfo.type === NotificationType.Info) {
+			return '1px solid #D8DDFF';
+		}
+
+		if (notificationInfo.type === NotificationType.Error) {
+			return '1px solid #F0E8E7';
+		}
+	};
+
+	$: primaryColor = () => {
+		if (notificationInfo.type === NotificationType.Info) {
+			return infoPrimaryColor;
+		}
+
+		if (notificationInfo.type === NotificationType.Error) {
+			return errorPrimaryColor;
+		}
+	};
+
+	onMount(() => {
+		setTimeout(() => {
+			notificationList.removeNotificationById(notificationInfo.id);
+		}, 2500);
+	});
+</script>
+
+<div
+	transition:fade={{ duration: 250 }}
+	style:background-color={backgroundColor()}
+	style:border={borderStyle()}
+	class="min-h-[80px] w-[300px] rounded-2xl z-[999999] shadow-md p-4 mb-4"
+>
+	<div class="flex gap-2 place-items-center">
+		<svelte:component this={icon} color={primaryColor()} size="20" />
+		<h2 style:color={primaryColor()} class="font-medium">{notificationInfo.type.toString()}</h2>
+	</div>
+
+	<p class="text-sm pl-[28px] pr-[16px]">{notificationInfo.message} {notificationInfo.id}</p>
+</div>

+ 22 - 0
web/src/lib/components/shared-components/notification/notification-list.svelte

@@ -0,0 +1,22 @@
+<script lang="ts">
+	import { notificationList } from './notification';
+	import { fade } from 'svelte/transition';
+
+	import NotificationCard from './notification-card.svelte';
+	import { flip } from 'svelte/animate';
+	import { quintOut } from 'svelte/easing';
+</script>
+
+{#if $notificationList?.length > 0}
+	<section
+		transition:fade={{ duration: 250 }}
+		id="notification-list"
+		class="absolute right-5 top-[80px] z-[99999999]"
+	>
+		{#each $notificationList as notificationInfo (notificationInfo.id)}
+			<div animate:flip={{ duration: 250, easing: quintOut }}>
+				<NotificationCard {notificationInfo} />
+			</div>
+		{/each}
+	</section>
+{/if}

+ 36 - 0
web/src/lib/components/shared-components/notification/notification.ts

@@ -0,0 +1,36 @@
+import { writable } from 'svelte/store';
+
+export enum NotificationType {
+	Info = 'Info',
+	Error = 'Error'
+}
+
+export class ImmichNotification {
+	id = new Date().getTime();
+	type!: NotificationType;
+	message!: string;
+}
+
+function createNotificationList() {
+	const { set, update, subscribe } = writable<ImmichNotification[]>([]);
+
+	const show = ({ message = '', type = NotificationType.Info }) => {
+		const notification = new ImmichNotification();
+		notification.message = message;
+		notification.type = type;
+
+		update((currentList) => [...currentList, notification]);
+	};
+
+	const removeNotificationById = (id: number) => {
+		update((currentList) => currentList.filter((n) => n.id != id));
+	};
+
+	return {
+		show,
+		removeNotificationById,
+		subscribe
+	};
+}
+
+export const notificationList = createNotificationList();

+ 1 - 2
web/src/lib/components/shared-components/side-bar/side-bar.svelte

@@ -1,5 +1,4 @@
 <script lang="ts">
-
 	import { AppSideBarSelection } from '$lib/models/admin-sidebar-selection';
 	import { onMount } from 'svelte';
 	import { page } from '$app/stores';
@@ -50,8 +49,8 @@
 			isSelected={selectedAction === AppSideBarSelection.ALBUMS}
 		/>
 	</a>
-	<!-- Status Box -->
 
+	<!-- Status Box -->
 	<div class="mb-6 mt-auto">
 		<StatusBox />
 	</div>

+ 1 - 1
web/src/lib/components/shared-components/status-box.svelte

@@ -34,7 +34,7 @@
 			const { data: serverInfoRes } = await api.serverInfoApi.getServerInfo();
 			serverInfo = serverInfoRes;
 		} catch (e) {
-			console.log('Error [StatusBox] [pingServerInterval]');
+			console.log('Error [StatusBox] [pingServerInterval]', e);
 			isServerOk = false;
 		}
 	}, 10000);

+ 2 - 1
web/src/routes/+layout.svelte

@@ -10,6 +10,7 @@
 	import { page } from '$app/stores';
 	import { afterNavigate, beforeNavigate } from '$app/navigation';
 	import NavigationLoadingBar from '$lib/components/shared-components/navigation-loading-bar.svelte';
+	import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte';
 
 	let shouldShowAnnouncement: boolean;
 	let localVersion: string;
@@ -44,7 +45,7 @@
 
 			<DownloadPanel />
 			<UploadPanel />
-
+			<NotificationList />
 			{#if shouldShowAnnouncement}
 				<AnnouncementBox
 					{localVersion}