Small UX updates assistants (#872)
Browse files
src/lib/stores/settings.ts
CHANGED
@@ -17,8 +17,12 @@ type SettingsStore = {
|
|
17 |
assistants: Array<ObjectId | string>;
|
18 |
};
|
19 |
|
|
|
|
|
|
|
|
|
20 |
export function useSettingsStore() {
|
21 |
-
return getContext<
|
22 |
}
|
23 |
|
24 |
export function createSettingsStore(initialValue: Omit<SettingsStore, "recentlySaved">) {
|
@@ -64,14 +68,35 @@ export function createSettingsStore(initialValue: Omit<SettingsStore, "recentlyS
|
|
64 |
// debounce server calls by 300ms
|
65 |
}
|
66 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
const newStore = {
|
69 |
subscribe: baseStore.subscribe,
|
70 |
set: setSettings,
|
|
|
71 |
update: (fn: (s: SettingsStore) => SettingsStore) => {
|
72 |
setSettings(fn(get(baseStore)));
|
73 |
},
|
74 |
-
} satisfies
|
75 |
|
76 |
setContext("settings", newStore);
|
77 |
|
|
|
17 |
assistants: Array<ObjectId | string>;
|
18 |
};
|
19 |
|
20 |
+
type SettingsStoreWritable = Writable<SettingsStore> & {
|
21 |
+
instantSet: (settings: Partial<SettingsStore>) => Promise<void>;
|
22 |
+
};
|
23 |
+
|
24 |
export function useSettingsStore() {
|
25 |
+
return getContext<SettingsStoreWritable>("settings");
|
26 |
}
|
27 |
|
28 |
export function createSettingsStore(initialValue: Omit<SettingsStore, "recentlySaved">) {
|
|
|
68 |
// debounce server calls by 300ms
|
69 |
}
|
70 |
}
|
71 |
+
async function instantSet(settings: Partial<SettingsStore>) {
|
72 |
+
baseStore.update((s) => ({
|
73 |
+
...s,
|
74 |
+
...settings,
|
75 |
+
}));
|
76 |
+
|
77 |
+
if (browser) {
|
78 |
+
await fetch(`${base}/settings`, {
|
79 |
+
method: "POST",
|
80 |
+
headers: {
|
81 |
+
"Content-Type": "application/json",
|
82 |
+
},
|
83 |
+
body: JSON.stringify({
|
84 |
+
...get(baseStore),
|
85 |
+
...settings,
|
86 |
+
}),
|
87 |
+
});
|
88 |
+
invalidate(UrlDependency.ConversationList);
|
89 |
+
}
|
90 |
+
}
|
91 |
|
92 |
const newStore = {
|
93 |
subscribe: baseStore.subscribe,
|
94 |
set: setSettings,
|
95 |
+
instantSet,
|
96 |
update: (fn: (s: SettingsStore) => SettingsStore) => {
|
97 |
setSettings(fn(get(baseStore)));
|
98 |
},
|
99 |
+
} satisfies SettingsStoreWritable;
|
100 |
|
101 |
setContext("settings", newStore);
|
102 |
|
src/routes/+layout.svelte
CHANGED
@@ -116,7 +116,9 @@
|
|
116 |
if ($settings.activeModel === $page.url.searchParams.get("model")) {
|
117 |
goto(`${base}/?`);
|
118 |
}
|
119 |
-
|
|
|
|
|
120 |
}
|
121 |
|
122 |
$: mobileNavTitle = ["/models", "/assistants", "/privacy"].includes($page.route.id ?? "")
|
|
|
116 |
if ($settings.activeModel === $page.url.searchParams.get("model")) {
|
117 |
goto(`${base}/?`);
|
118 |
}
|
119 |
+
settings.instantSet({
|
120 |
+
activeModel: $page.url.searchParams.get("model") ?? $settings.activeModel,
|
121 |
+
});
|
122 |
}
|
123 |
|
124 |
$: mobileNavTitle = ["/models", "/assistants", "/privacy"].includes($page.route.id ?? "")
|
src/routes/assistant/[assistantId]/+page.svelte
CHANGED
@@ -8,6 +8,7 @@
|
|
8 |
import { applyAction, enhance } from "$app/forms";
|
9 |
import { PUBLIC_APP_NAME, PUBLIC_ORIGIN } from "$env/static/public";
|
10 |
import { page } from "$app/stores";
|
|
|
11 |
|
12 |
export let data: PageData;
|
13 |
|
@@ -47,6 +48,35 @@
|
|
47 |
}}
|
48 |
class="z-10 flex flex-col content-center items-center gap-x-10 gap-y-3 overflow-hidden rounded-2xl bg-white p-4 pt-6 text-center shadow-2xl outline-none max-sm:w-[85dvw] max-sm:px-6 md:w-96 md:grid-cols-3 md:grid-rows-[auto,1fr] md:p-8"
|
49 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
{#if data.assistant.avatar}
|
51 |
<img
|
52 |
class="size-16 flex-none rounded-full object-cover sm:size-24"
|
|
|
8 |
import { applyAction, enhance } from "$app/forms";
|
9 |
import { PUBLIC_APP_NAME, PUBLIC_ORIGIN } from "$env/static/public";
|
10 |
import { page } from "$app/stores";
|
11 |
+
import IconGear from "~icons/bi/gear-fill";
|
12 |
|
13 |
export let data: PageData;
|
14 |
|
|
|
48 |
}}
|
49 |
class="z-10 flex flex-col content-center items-center gap-x-10 gap-y-3 overflow-hidden rounded-2xl bg-white p-4 pt-6 text-center shadow-2xl outline-none max-sm:w-[85dvw] max-sm:px-6 md:w-96 md:grid-cols-3 md:grid-rows-[auto,1fr] md:p-8"
|
50 |
>
|
51 |
+
<div class="absolute right-0 top-0 m-6">
|
52 |
+
<form
|
53 |
+
method="POST"
|
54 |
+
action="{base}/settings/assistants/{data.assistant._id}?/subscribe"
|
55 |
+
class="w-full"
|
56 |
+
use:enhance={() => {
|
57 |
+
return async ({ result }) => {
|
58 |
+
// `result` is an `ActionResult` object
|
59 |
+
if (result.type === "success") {
|
60 |
+
$settings.activeModel = data.assistant._id;
|
61 |
+
await goto(`${base}/settings/assistants/${data.assistant._id}`, {
|
62 |
+
invalidateAll: true,
|
63 |
+
});
|
64 |
+
} else {
|
65 |
+
await applyAction(result);
|
66 |
+
}
|
67 |
+
};
|
68 |
+
}}
|
69 |
+
>
|
70 |
+
<button
|
71 |
+
class="flex items-center rounded-full border border-gray-200 px-2.5 py-1 text-sm text-gray-900 hover:bg-gray-100"
|
72 |
+
name="Settings"
|
73 |
+
type="submit"
|
74 |
+
>
|
75 |
+
<IconGear class="mr-1.5 text-xxs" />
|
76 |
+
Settings
|
77 |
+
</button>
|
78 |
+
</form>
|
79 |
+
</div>
|
80 |
{#if data.assistant.avatar}
|
81 |
<img
|
82 |
class="size-16 flex-none rounded-full object-cover sm:size-24"
|
src/routes/assistants/+page.svelte
CHANGED
@@ -17,6 +17,7 @@
|
|
17 |
import Pagination from "$lib/components/Pagination.svelte";
|
18 |
import { formatUserCount } from "$lib/utils/formatUserCount";
|
19 |
import { getHref } from "$lib/utils/getHref";
|
|
|
20 |
|
21 |
export let data: PageData;
|
22 |
|
@@ -30,6 +31,8 @@
|
|
30 |
});
|
31 |
goto(newUrl);
|
32 |
};
|
|
|
|
|
33 |
</script>
|
34 |
|
35 |
<svelte:head>
|
@@ -143,9 +146,16 @@
|
|
143 |
|
144 |
<div class="mt-8 grid grid-cols-2 gap-3 sm:gap-5 md:grid-cols-3 lg:grid-cols-4">
|
145 |
{#each data.assistants as assistant (assistant._id)}
|
146 |
-
<
|
147 |
-
href="{base}/assistant/{assistant._id}"
|
148 |
class="relative flex flex-col items-center justify-center overflow-hidden text-balance rounded-xl border bg-gray-50/50 px-4 py-6 text-center shadow hover:bg-gray-50 hover:shadow-inner max-sm:px-4 sm:h-64 sm:pb-4 xl:pt-8 dark:border-gray-800/70 dark:bg-gray-950/20 dark:hover:bg-gray-950/40"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
>
|
150 |
{#if assistant.userCount && assistant.userCount > 1}
|
151 |
<div
|
@@ -186,7 +196,7 @@
|
|
186 |
</a>
|
187 |
</p>
|
188 |
{/if}
|
189 |
-
</
|
190 |
{:else}
|
191 |
No assistants found
|
192 |
{/each}
|
|
|
17 |
import Pagination from "$lib/components/Pagination.svelte";
|
18 |
import { formatUserCount } from "$lib/utils/formatUserCount";
|
19 |
import { getHref } from "$lib/utils/getHref";
|
20 |
+
import { useSettingsStore } from "$lib/stores/settings";
|
21 |
|
22 |
export let data: PageData;
|
23 |
|
|
|
31 |
});
|
32 |
goto(newUrl);
|
33 |
};
|
34 |
+
|
35 |
+
const settings = useSettingsStore();
|
36 |
</script>
|
37 |
|
38 |
<svelte:head>
|
|
|
146 |
|
147 |
<div class="mt-8 grid grid-cols-2 gap-3 sm:gap-5 md:grid-cols-3 lg:grid-cols-4">
|
148 |
{#each data.assistants as assistant (assistant._id)}
|
149 |
+
<button
|
|
|
150 |
class="relative flex flex-col items-center justify-center overflow-hidden text-balance rounded-xl border bg-gray-50/50 px-4 py-6 text-center shadow hover:bg-gray-50 hover:shadow-inner max-sm:px-4 sm:h-64 sm:pb-4 xl:pt-8 dark:border-gray-800/70 dark:bg-gray-950/20 dark:hover:bg-gray-950/40"
|
151 |
+
on:click={() => {
|
152 |
+
if (data.settings.assistants.includes(assistant._id.toString())) {
|
153 |
+
settings.instantSet({ activeModel: assistant._id.toString() });
|
154 |
+
goto(`${base}` || "/");
|
155 |
+
} else {
|
156 |
+
goto(`${base}/assistant/${assistant._id}`);
|
157 |
+
}
|
158 |
+
}}
|
159 |
>
|
160 |
{#if assistant.userCount && assistant.userCount > 1}
|
161 |
<div
|
|
|
196 |
</a>
|
197 |
</p>
|
198 |
{/if}
|
199 |
+
</button>
|
200 |
{:else}
|
201 |
No assistants found
|
202 |
{/each}
|