Skip to content

Commit

Permalink
feat: Message box for user notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
lasuillard committed Jul 19, 2024
1 parent 94277ba commit 74cfcae
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 27 deletions.
10 changes: 4 additions & 6 deletions src/components/Config.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import P from 'flowbite-svelte/P.svelte';
import { get } from 'svelte/store';
import Eye from '~/components/Eye.svelte';
import { putMessage } from '~/lib/messages';
import { launchWebAuthFlow as _launchWebAuthFlow } from '~/lib/raindrop/auth';
import { accessToken, clientID, clientSecret, notifications, refreshToken } from '~/lib/settings';
import { accessToken, clientID, clientSecret, refreshToken } from '~/lib/settings';
let showClientID = false;
let showClientSecret = false;
Expand All @@ -20,13 +21,10 @@
});
accessToken.set(result.accessToken);
refreshToken.set(result.refreshToken);
putMessage({ type: 'success', message: 'Successfully authorized app.' });
} catch (err) {
// TODO: Show error message as modal or toast
console.error('Failed to authorize app:', err);
$notifications[$notifications.length] = {
type: 'error',
message: String(err)
};
putMessage({ type: 'error', message: String(err) });
}
};
</script>
Expand Down
42 changes: 42 additions & 0 deletions src/components/Message.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script lang="ts">
import { Toast } from 'flowbite-svelte';
import {
CheckCircleSolid,
CloseCircleSolid,
ExclamationCircleSolid
} from 'flowbite-svelte-icons';
import type { ComponentType } from 'svelte';
import { dismissMessage, type Message } from '~/lib/messages';
const messageMapping: {
[type: string]: { icon: ComponentType; color: 'blue' | 'green' | 'red' };
} = {
success: {
icon: CheckCircleSolid,
color: 'green'
},
info: {
icon: ExclamationCircleSolid,
color: 'blue'
},
error: {
icon: CloseCircleSolid,
color: 'red'
}
};
export let message: Message;
</script>

{#if message}
{@const color = messageMapping[message.type].color}
{@const icon = messageMapping[message.type].icon}
<div {...$$restProps}>
<Toast {color} on:close={() => dismissMessage(message.id)}>
<svelte:fragment slot="icon">
<svelte:component this={icon} class="h-5 w-5" />
</svelte:fragment>
{message.message}
</Toast>
</div>
{/if}
9 changes: 9 additions & 0 deletions src/components/Message.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @vitest-environment jsdom
import { render } from '@testing-library/svelte';
import { expect, it } from 'vitest';
import Message from './Message.svelte';

it('renders OK', () => {
const { container } = render(Message);
expect(container).toBeTruthy();
});
48 changes: 48 additions & 0 deletions src/lib/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { writable } from 'svelte/store';

export type Message = {
id?: string;
type: 'success' | 'info' | 'error';
message: string;
};

export type MessageBox = {
[id: string]: Message;
};

export const messageBox = writable<MessageBox>({});

/**
* Put message to message box.
* @param message Message to put. If `.id` is not set, random generated ID will be used.
* @returns ID of message.
*/
export function putMessage(message: Message): string {
const id = message.id ?? Math.random().toString(36).substring(2);
messageBox.update((box) => {
box[id] = { id, ...message };
return box;
});
return id;
}

Check warning on line 27 in src/lib/messages.ts

View check run for this annotation

Codecov / codecov/patch

src/lib/messages.ts#L21-L27

Added lines #L21 - L27 were not covered by tests

/**
* Dismiss message with ID.
* @param id ID of message.
*/
export function dismissMessage(id?: string) {
if (!id) return;

messageBox.update((box) => {
delete box[id];
return box;
});
}

Check warning on line 40 in src/lib/messages.ts

View check run for this annotation

Codecov / codecov/patch

src/lib/messages.ts#L34-L40

Added lines #L34 - L40 were not covered by tests

/* c8 ignore start */
if (import.meta.vitest) {
const { describe } = import.meta.vitest;

describe.todo('To Do');
}
/* c8 ignore stop */
7 changes: 0 additions & 7 deletions src/lib/settings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { writable } from 'svelte/store';
import { persisted } from './stores';

const options = {
Expand All @@ -19,12 +18,6 @@ export const refreshToken = await persisted('refreshToken', '', options);
*/
export const lastTouch = await persisted('lastTouch', null, options);

type Noti = {
type: 'info' | 'success' | 'warning' | 'error';
message: string;
};
export const notifications = writable<Noti[]>([]);

/* c8 ignore start */
if (import.meta.vitest) {
const { get } = await import('svelte/store');
Expand Down
20 changes: 6 additions & 14 deletions src/pages/options/Page.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<script lang="ts">
import { Toast } from 'flowbite-svelte';
import { ExclamationCircleSolid } from 'flowbite-svelte-icons';
import BookmarkOutline from 'flowbite-svelte-icons/BookmarkOutline.svelte';
import QuestionCircleOutline from 'flowbite-svelte-icons/QuestionCircleOutline.svelte';
import SearchOutline from 'flowbite-svelte-icons/SearchOutline.svelte';
Expand All @@ -11,8 +9,9 @@
import About from '~/components/About.svelte';
import Bookmarks from '~/components/Bookmarks.svelte';
import Config from '~/components/Config.svelte';
import Message from '~/components/Message.svelte';

Check warning on line 12 in src/pages/options/Page.svelte

View check run for this annotation

Codecov / codecov/patch

src/pages/options/Page.svelte#L12

Added line #L12 was not covered by tests
import TryIt from '~/components/TryIt.svelte';
import { notifications } from '~/lib/settings';
import { messageBox } from '~/lib/messages';

Check warning on line 14 in src/pages/options/Page.svelte

View check run for this annotation

Codecov / codecov/patch

src/pages/options/Page.svelte#L14

Added line #L14 was not covered by tests
</script>

<main class="mx-1 mt-4 self-center">
Expand Down Expand Up @@ -47,17 +46,10 @@
</TabItem>
</Tabs>

<!-- TODO: Currently implemented for error event only, component-ize for more types -->
<!-- TODO: Notification should be removed from array when dismissed -->
<div class="absolute right-8 top-8 space-y-4">
{#each $notifications as noti}
<Toast color="red">
<svelte:fragment slot="icon">
<ExclamationCircleSolid class="h-5 w-5" />
<span class="sr-only">Warning icon</span>
</svelte:fragment>
{noti.message}
</Toast>
<!-- Global message box -->
<div class="absolute right-6 top-6 space-y-2">
{#each Object.entries($messageBox) as [id, message] (id)}
<Message {message} />
{/each}
</div>

Check warning on line 54 in src/pages/options/Page.svelte

View check run for this annotation

Codecov / codecov/patch

src/pages/options/Page.svelte#L48-L54

Added lines #L48 - L54 were not covered by tests
</main>
Expand Down

0 comments on commit 74cfcae

Please sign in to comment.