Toast errors (#91)
Browse filesCo-authored-by: Julien Chaumond <[email protected]>
Co-authored-by: Adrien Denat <[email protected]>
- src/lib/components/Toast.svelte +19 -0
- src/lib/components/icons/IconDazzled.svelte +36 -0
- src/lib/shareConversation.ts +4 -2
- src/lib/stores/errors.ts +7 -0
- src/routes/+layout.svelte +34 -3
- src/routes/+page.svelte +9 -6
- src/routes/conversation/[id]/+page.svelte +3 -1
- src/routes/r/[id]/+page.svelte +5 -2
src/lib/components/Toast.svelte
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { fade } from "svelte/transition";
|
3 |
+
|
4 |
+
import IconDazzled from "$lib/components/icons/IconDazzled.svelte";
|
5 |
+
|
6 |
+
export let message = "";
|
7 |
+
</script>
|
8 |
+
|
9 |
+
<div
|
10 |
+
transition:fade={{ duration: 300 }}
|
11 |
+
class="fixed right-0 top-12 md:top-0 bg-gradient-to-bl from-red-500/20 via-red-500/0 to-red-500/0 pt-2 md:pt-5 pr-2 md:pr-8 pl-36 pb-36 z-20 pointer-events-none"
|
12 |
+
>
|
13 |
+
<div
|
14 |
+
class="flex items-center bg-white/90 dark:bg-gray-900/80 rounded-full py-1 px-3 shadow-sm pointer-events-auto"
|
15 |
+
>
|
16 |
+
<IconDazzled classNames="text-2xl mr-2" />
|
17 |
+
<h2 class="font-semibold">{message}</h2>
|
18 |
+
</div>
|
19 |
+
</div>
|
src/lib/components/icons/IconDazzled.svelte
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
xmlns="http://www.w3.org/2000/svg"
|
7 |
+
width="1em"
|
8 |
+
height="1em"
|
9 |
+
class={classNames}
|
10 |
+
fill="none"
|
11 |
+
viewBox="0 0 26 23"
|
12 |
+
>
|
13 |
+
<path
|
14 |
+
fill="url(#a)"
|
15 |
+
d="M.93 10.65A10.17 10.17 0 0 1 11.11.48h4.67a9.45 9.45 0 0 1 0 18.89H4.53L1.62 22.2a.38.38 0 0 1-.69-.28V10.65Z"
|
16 |
+
/>
|
17 |
+
<path
|
18 |
+
fill="#000"
|
19 |
+
fill-rule="evenodd"
|
20 |
+
d="M11.52 7.4a1.86 1.86 0 1 1-3.72 0 1.86 1.86 0 0 1 3.72 0Zm7.57 0a1.86 1.86 0 1 1-3.73 0 1.86 1.86 0 0 1 3.73 0ZM8.9 12.9a.55.55 0 0 0-.11.35.76.76 0 0 1-1.51 0c0-.95.67-1.94 1.76-1.94 1.09 0 1.76 1 1.76 1.94H9.3a.55.55 0 0 0-.12-.35c-.06-.07-.1-.08-.13-.08s-.08 0-.14.08Zm4.04 0a.55.55 0 0 0-.12.35h-1.51c0-.95.68-1.94 1.76-1.94 1.1 0 1.77 1 1.77 1.94h-1.51a.55.55 0 0 0-.12-.35c-.06-.07-.11-.08-.14-.08-.02 0-.07 0-.13.08Zm-1.89.79c-.02 0-.07-.01-.13-.08a.55.55 0 0 1-.12-.36h-1.5c0 .95.67 1.95 1.75 1.95 1.1 0 1.77-1 1.77-1.95h-1.51c0 .16-.06.28-.12.36-.06.07-.11.08-.14.08Zm4.04 0c-.03 0-.08-.01-.14-.08a.55.55 0 0 1-.12-.36h-1.5c0 .95.67 1.95 1.76 1.95 1.08 0 1.76-1 1.76-1.95h-1.51c0 .16-.06.28-.12.36-.06.07-.11.08-.13.08Zm1.76-.44c0-.16.05-.28.12-.35.06-.07.1-.08.13-.08s.08 0 .14.08c.06.07.11.2.11.35a.76.76 0 0 0 1.51 0c0-.95-.67-1.94-1.76-1.94-1.09 0-1.76 1-1.76 1.94h1.5Z"
|
21 |
+
clip-rule="evenodd"
|
22 |
+
/>
|
23 |
+
<defs>
|
24 |
+
<radialGradient
|
25 |
+
id="a"
|
26 |
+
cx="0"
|
27 |
+
cy="0"
|
28 |
+
r="1"
|
29 |
+
gradientTransform="matrix(0 31.37 -34.85 0 13.08 -9.02)"
|
30 |
+
gradientUnits="userSpaceOnUse"
|
31 |
+
>
|
32 |
+
<stop stop-color="#FFD21E" />
|
33 |
+
<stop offset="1" stop-color="red" />
|
34 |
+
</radialGradient>
|
35 |
+
</defs>
|
36 |
+
</svg>
|
src/lib/shareConversation.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
import { base } from "$app/paths";
|
|
|
2 |
|
3 |
export async function shareConversation(id: string, title: string) {
|
4 |
try {
|
@@ -10,7 +11,8 @@ export async function shareConversation(id: string, title: string) {
|
|
10 |
});
|
11 |
|
12 |
if (!res.ok) {
|
13 |
-
|
|
|
14 |
return;
|
15 |
}
|
16 |
|
@@ -26,7 +28,7 @@ export async function shareConversation(id: string, title: string) {
|
|
26 |
prompt("Copy this public url to share:", url);
|
27 |
}
|
28 |
} catch (err) {
|
|
|
29 |
console.error(err);
|
30 |
-
alert(String(err));
|
31 |
}
|
32 |
}
|
|
|
1 |
import { base } from "$app/paths";
|
2 |
+
import { ERROR_MESSAGES, error } from "$lib/stores/errors";
|
3 |
|
4 |
export async function shareConversation(id: string, title: string) {
|
5 |
try {
|
|
|
11 |
});
|
12 |
|
13 |
if (!res.ok) {
|
14 |
+
error.set("Error while sharing conversation, try again.");
|
15 |
+
console.error("Error while sharing conversation: " + (await res.text()));
|
16 |
return;
|
17 |
}
|
18 |
|
|
|
28 |
prompt("Copy this public url to share:", url);
|
29 |
}
|
30 |
} catch (err) {
|
31 |
+
error.set(ERROR_MESSAGES.default);
|
32 |
console.error(err);
|
|
|
33 |
}
|
34 |
}
|
src/lib/stores/errors.ts
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { writable } from "svelte/store";
|
2 |
+
|
3 |
+
export const ERROR_MESSAGES = {
|
4 |
+
default: "Oops, something went wrong.",
|
5 |
+
};
|
6 |
+
|
7 |
+
export const error = writable<string | null>(null);
|
src/routes/+layout.svelte
CHANGED
@@ -1,19 +1,41 @@
|
|
1 |
<script lang="ts">
|
|
|
2 |
import { goto, invalidate } from "$app/navigation";
|
3 |
import { page } from "$app/stores";
|
4 |
import "../styles/main.css";
|
5 |
import type { LayoutData } from "./$types";
|
6 |
import { base } from "$app/paths";
|
|
|
|
|
7 |
import { shareConversation } from "$lib/shareConversation";
|
8 |
import { UrlDependency } from "$lib/types/UrlDependency";
|
|
|
9 |
|
10 |
import MobileNav from "$lib/components/MobileNav.svelte";
|
11 |
import NavMenu from "$lib/components/NavMenu.svelte";
|
12 |
-
import
|
13 |
|
14 |
export let data: LayoutData;
|
15 |
|
16 |
let isNavOpen = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
async function deleteConversation(id: string) {
|
19 |
try {
|
@@ -25,7 +47,7 @@
|
|
25 |
});
|
26 |
|
27 |
if (!res.ok) {
|
28 |
-
|
29 |
return;
|
30 |
}
|
31 |
|
@@ -36,9 +58,15 @@
|
|
36 |
}
|
37 |
} catch (err) {
|
38 |
console.error(err);
|
39 |
-
|
40 |
}
|
41 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
</script>
|
43 |
|
44 |
<svelte:head>
|
@@ -70,5 +98,8 @@
|
|
70 |
on:deleteConversation={(ev) => deleteConversation(ev.detail)}
|
71 |
/>
|
72 |
</nav>
|
|
|
|
|
|
|
73 |
<slot />
|
74 |
</div>
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { onDestroy } from "svelte";
|
3 |
import { goto, invalidate } from "$app/navigation";
|
4 |
import { page } from "$app/stores";
|
5 |
import "../styles/main.css";
|
6 |
import type { LayoutData } from "./$types";
|
7 |
import { base } from "$app/paths";
|
8 |
+
import { PUBLIC_ORIGIN } from "$env/static/public";
|
9 |
+
|
10 |
import { shareConversation } from "$lib/shareConversation";
|
11 |
import { UrlDependency } from "$lib/types/UrlDependency";
|
12 |
+
import { error } from "$lib/stores/errors";
|
13 |
|
14 |
import MobileNav from "$lib/components/MobileNav.svelte";
|
15 |
import NavMenu from "$lib/components/NavMenu.svelte";
|
16 |
+
import Toast from "$lib/components/Toast.svelte";
|
17 |
|
18 |
export let data: LayoutData;
|
19 |
|
20 |
let isNavOpen = false;
|
21 |
+
let errorToastTimeout: NodeJS.Timeout;
|
22 |
+
let currentError: string | null;
|
23 |
+
|
24 |
+
async function onError() {
|
25 |
+
// If a new different error comes, wait for the current error to hide first
|
26 |
+
if ($error && currentError && $error !== currentError) {
|
27 |
+
clearTimeout(errorToastTimeout);
|
28 |
+
currentError = null;
|
29 |
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
30 |
+
}
|
31 |
+
|
32 |
+
currentError = $error;
|
33 |
+
|
34 |
+
errorToastTimeout = setTimeout(() => {
|
35 |
+
$error = null;
|
36 |
+
currentError = null;
|
37 |
+
}, 3000);
|
38 |
+
}
|
39 |
|
40 |
async function deleteConversation(id: string) {
|
41 |
try {
|
|
|
47 |
});
|
48 |
|
49 |
if (!res.ok) {
|
50 |
+
$error = "Error while deleting conversation, try again.";
|
51 |
return;
|
52 |
}
|
53 |
|
|
|
58 |
}
|
59 |
} catch (err) {
|
60 |
console.error(err);
|
61 |
+
$error = String(err);
|
62 |
}
|
63 |
}
|
64 |
+
|
65 |
+
onDestroy(() => {
|
66 |
+
clearTimeout(errorToastTimeout);
|
67 |
+
});
|
68 |
+
|
69 |
+
$: if ($error) onError();
|
70 |
</script>
|
71 |
|
72 |
<svelte:head>
|
|
|
98 |
on:deleteConversation={(ev) => deleteConversation(ev.detail)}
|
99 |
/>
|
100 |
</nav>
|
101 |
+
{#if currentError}
|
102 |
+
<Toast message={currentError} />
|
103 |
+
{/if}
|
104 |
<slot />
|
105 |
</div>
|
src/routes/+page.svelte
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import { goto } from
|
3 |
-
import { base } from
|
4 |
-
import ChatWindow from
|
5 |
-
import {
|
|
|
6 |
|
7 |
let loading = false;
|
8 |
|
@@ -17,7 +18,8 @@
|
|
17 |
});
|
18 |
|
19 |
if (!res.ok) {
|
20 |
-
|
|
|
21 |
return;
|
22 |
}
|
23 |
|
@@ -29,7 +31,8 @@
|
|
29 |
// invalidateAll to update list of conversations
|
30 |
await goto(`${base}/conversation/${conversationId}`, { invalidateAll: true });
|
31 |
} catch (err) {
|
32 |
-
|
|
|
33 |
} finally {
|
34 |
loading = false;
|
35 |
}
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { goto } from "$app/navigation";
|
3 |
+
import { base } from "$app/paths";
|
4 |
+
import ChatWindow from "$lib/components/chat/ChatWindow.svelte";
|
5 |
+
import { ERROR_MESSAGES, error } from "$lib/stores/errors";
|
6 |
+
import { pendingMessage } from "$lib/stores/pendingMessage";
|
7 |
|
8 |
let loading = false;
|
9 |
|
|
|
18 |
});
|
19 |
|
20 |
if (!res.ok) {
|
21 |
+
error.set("Error while creating conversation, try again.");
|
22 |
+
console.error("Error while creating conversation: " + (await res.text()));
|
23 |
return;
|
24 |
}
|
25 |
|
|
|
31 |
// invalidateAll to update list of conversations
|
32 |
await goto(`${base}/conversation/${conversationId}`, { invalidateAll: true });
|
33 |
} catch (err) {
|
34 |
+
error.set(ERROR_MESSAGES.default);
|
35 |
+
console.error(err);
|
36 |
} finally {
|
37 |
loading = false;
|
38 |
}
|
src/routes/conversation/[id]/+page.svelte
CHANGED
@@ -10,6 +10,7 @@
|
|
10 |
import { PUBLIC_MAX_INPUT_TOKENS } from "$env/static/public";
|
11 |
import { shareConversation } from "$lib/shareConversation";
|
12 |
import { UrlDependency } from "$lib/types/UrlDependency";
|
|
|
13 |
|
14 |
export let data: PageData;
|
15 |
|
@@ -105,8 +106,9 @@
|
|
105 |
await invalidate(UrlDependency.ConversationList);
|
106 |
}
|
107 |
} catch (err) {
|
|
|
|
|
108 |
console.error(err);
|
109 |
-
alert(String(err));
|
110 |
} finally {
|
111 |
loading = false;
|
112 |
}
|
|
|
10 |
import { PUBLIC_MAX_INPUT_TOKENS } from "$env/static/public";
|
11 |
import { shareConversation } from "$lib/shareConversation";
|
12 |
import { UrlDependency } from "$lib/types/UrlDependency";
|
13 |
+
import { error } from "$lib/stores/errors";
|
14 |
|
15 |
export let data: PageData;
|
16 |
|
|
|
106 |
await invalidate(UrlDependency.ConversationList);
|
107 |
}
|
108 |
} catch (err) {
|
109 |
+
// TODO: Should prob check if this is really a TooManyRequests error
|
110 |
+
$error = "Too much traffic, please try again.";
|
111 |
console.error(err);
|
|
|
112 |
} finally {
|
113 |
loading = false;
|
114 |
}
|
src/routes/r/[id]/+page.svelte
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
import { base } from "$app/paths";
|
4 |
import { page } from "$app/stores";
|
5 |
import ChatWindow from "$lib/components/chat/ChatWindow.svelte";
|
|
|
6 |
import { pendingMessage } from "$lib/stores/pendingMessage";
|
7 |
import type { PageData } from "./$types";
|
8 |
|
@@ -24,7 +25,8 @@
|
|
24 |
});
|
25 |
|
26 |
if (!res.ok) {
|
27 |
-
|
|
|
28 |
return;
|
29 |
}
|
30 |
|
@@ -36,7 +38,8 @@
|
|
36 |
// invalidateAll to update list of conversations
|
37 |
await goto(`${base}/conversation/${conversationId}`, { invalidateAll: true });
|
38 |
} catch (err) {
|
39 |
-
|
|
|
40 |
} finally {
|
41 |
loading = false;
|
42 |
}
|
|
|
3 |
import { base } from "$app/paths";
|
4 |
import { page } from "$app/stores";
|
5 |
import ChatWindow from "$lib/components/chat/ChatWindow.svelte";
|
6 |
+
import { ERROR_MESSAGES, error } from "$lib/stores/errors";
|
7 |
import { pendingMessage } from "$lib/stores/pendingMessage";
|
8 |
import type { PageData } from "./$types";
|
9 |
|
|
|
25 |
});
|
26 |
|
27 |
if (!res.ok) {
|
28 |
+
error.set("Error while creating conversation, try again.");
|
29 |
+
console.error("Error while creating conversation: " + (await res.text()));
|
30 |
return;
|
31 |
}
|
32 |
|
|
|
38 |
// invalidateAll to update list of conversations
|
39 |
await goto(`${base}/conversation/${conversationId}`, { invalidateAll: true });
|
40 |
} catch (err) {
|
41 |
+
error.set(ERROR_MESSAGES.default);
|
42 |
+
console.error(String(err));
|
43 |
} finally {
|
44 |
loading = false;
|
45 |
}
|