Skip to content

Commit

Permalink
Adding the toaster notification when something may go wrong
Browse files Browse the repository at this point in the history
  • Loading branch information
dejanvasic85 committed Jan 5, 2025
1 parent 5873ea0 commit 819fe70
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 2 deletions.
51 changes: 51 additions & 0 deletions src/components/Toaster.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script lang="ts">
import { slide } from 'svelte/transition';
import { createToaster, melt } from '@melt-ui/svelte';
import { getToastMessages } from '$lib/state/toastMessages.svelte';
import Icon from '$components/Icon.svelte';
import type { ToastMessage } from '$lib/types';
const toastMessages = getToastMessages();
const {
elements: { content, description, close },
helpers,
states: { toasts },
actions: { portal }
} = createToaster<ToastMessage>({});
$effect(() => {
toastMessages.messages
.filter((m) => m.isShown === false)
.forEach((message) => {
helpers.addToast({ type: 'background', data: message });
message.isShown = true;
});
});
</script>

<div use:portal class="fixed left-1/2 top-2 z-toaster w-80 -translate-x-1/2 transform">
{#each $toasts as { id, data } (id)}
<div use:melt={$content(id)}>
<div
in:slide={{ duration: 200 }}
out:slide={{ duration: 200 }}
class="relative mb-2 flex flex-col items-start justify-between rounded-lg border border-gray-200 bg-white px-8 py-4 text-sm shadow-lg"
class:bg-red-300={data.type === 'error'}
class:bg-green-300={data.type === 'success'}
>
<div use:melt={$description(id)}>
{data.message}
</div>
<button
use:melt={$close(id)}
aria-label="close notification"
class="absolute right-0 top-0 p-2"
>
<Icon icon="x-mark" />
</button>
</div>
</div>
{/each}
</div>
6 changes: 6 additions & 0 deletions src/lib/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,11 @@ export const icons = {
},
users: {
path: 'M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z'
},
'x-circle': {
path: 'm9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z'
},
'x-mark': {
path: 'M6 18 18 6M6 6l12 12'
}
};
32 changes: 32 additions & 0 deletions src/lib/state/toastMessages.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { getContext, setContext } from 'svelte';
import { nanoid } from 'nanoid';

import { type ToastMessage } from '$lib/types';

type ToastMessageWithId = ToastMessage & { id: string; isShown: boolean };

export class ToastMessages {
messages: ToastMessageWithId[] = $state([]);

constructor() {}

addMessage(message: ToastMessage) {
const id = nanoid();

this.messages.push({
...message,
id,
isShown: false
});
}
}

const ToastMessagesKey = Symbol('ToastMessages');

export function setToastMessages() {
return setContext(ToastMessagesKey, new ToastMessages());
}

export function getToastMessages() {
return getContext<ReturnType<typeof setToastMessages>>(ToastMessagesKey);
}
5 changes: 5 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,8 @@ export type ToggleFriendShare = {
noteId: string;
selected: boolean;
};

export type ToastMessage = {
message: string;
type: 'success' | 'error';
};
4 changes: 4 additions & 0 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<script lang="ts">
import './app.css';
import Toaster from '$components/Toaster.svelte';
import { setBoardState } from '$lib/state/boardState.svelte';
import { setToastMessages } from '$lib/state/toastMessages.svelte';
let { children } = $props();
setBoardState();
setToastMessages();
</script>

<Toaster />
{@render children()}
6 changes: 6 additions & 0 deletions src/routes/my/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import logo from '$lib/images/notes-main.png';
import { tryFetch } from '$lib/browserFetch';
import { getBoardState } from '$lib/state/boardState.svelte';
import { getToastMessages } from '$lib/state/toastMessages.svelte';
let { children, data } = $props();
const boardState = getBoardState();
const toastMessages = getToastMessages();
async function handleCreateNote() {
const newNote = boardState.createNewNote();
Expand All @@ -20,6 +22,10 @@
if (resp.type === 'error') {
boardState.deleteNoteById(newNote.id);
goto('/my/board');
toastMessages.addMessage({
type: 'error',
message: 'There was a problem creating a note. Try again.'
});
} else {
goto(`/my/board?id=${newNote.id}`);
}
Expand Down
7 changes: 6 additions & 1 deletion src/routes/my/board/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import type { Note, NoteOrdered, SharedNote, ToggleFriendShare } from '$lib/types';
import { tryFetch } from '$lib/browserFetch';
import { getBoardState } from '$lib/state/boardState.svelte';
import { getToastMessages } from '$lib/state/toastMessages.svelte';
import Board from '$components/Board.svelte';
import Button from '$components/Button.svelte';
Expand All @@ -15,6 +16,8 @@
const numberOfSkeletons = 4;
const boardState = getBoardState();
const toastMessages = getToastMessages();
let { data } = $props();
let selectedNote: NoteOrdered | null = $state(null);
let selectedSharedNote: SharedNote | null = $state(null);
Expand Down Expand Up @@ -68,6 +71,7 @@
});
if (type === 'error') {
boardState.updateNote(original);
toastMessages.addMessage({ message: 'Failed to update note. Try again.', type: 'error' });
}
}
Expand All @@ -80,6 +84,7 @@
);
if (resp.type === 'error') {
boardState.createNoteAtIndex(index, deletedNote);
toastMessages.addMessage({ message: 'Failed to delete note. Try again.', type: 'error' });
} else {
goto('/my/board');
}
Expand All @@ -93,8 +98,8 @@
body: JSON.stringify(boardPatch)
});
if (result.type === 'error') {
console.log('Error reordering notes');
boardState.reorderNotes(toIndex, fromIndex);
toastMessages.addMessage({ message: 'Failed to reorder notes. Try again.', type: 'error' });
}
}
</script>
Expand Down
3 changes: 2 additions & 1 deletion tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export default {
menu: 10,
overlay: 20,
dialog: 25,
dropdown: 30
dropdown: 30,
toaster: 40
}
}
},
Expand Down

0 comments on commit 819fe70

Please sign in to comment.