SettingsModal.svelte 11.3 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
<script lang="ts">
2
	import { getContext } from 'svelte';
Jannik Streidl's avatar
Jannik Streidl committed
3
	import { toast } from 'svelte-sonner';
Timothy J. Baek's avatar
Timothy J. Baek committed
4
	import { models, settings, user } from '$lib/stores';
5

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
6
	import { getModels as _getModels } from '$lib/apis';
Timothy J. Baek's avatar
Timothy J. Baek committed
7
8

	import Modal from '../common/Modal.svelte';
9
	import Account from './Settings/Account.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
10
11
12
	import About from './Settings/About.svelte';
	import General from './Settings/General.svelte';
	import Interface from './Settings/Interface.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
13
	import Audio from './Settings/Audio.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
14
	import Chats from './Settings/Chats.svelte';
15
16
	import User from '../icons/User.svelte';
	import Personalization from './Settings/Personalization.svelte';
17
	import { updateUserSettings } from '$lib/apis/users';
Timothy J. Baek's avatar
Timothy J. Baek committed
18
	import { goto } from '$app/navigation';
19

20
21
	const i18n = getContext('i18n');

Timothy J. Baek's avatar
Timothy J. Baek committed
22
	export let show = false;
23
24
25
26

	const saveSettings = async (updated) => {
		console.log(updated);
		await settings.set({ ...$settings, ...updated });
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
27
		await models.set(await getModels());
28
		await updateUserSettings(localStorage.token, { ui: $settings });
29
	};
30

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
31
32
	const getModels = async () => {
		return await _getModels(localStorage.token);
33
34
	};

35
	let selectedTab = 'general';
Timothy J. Baek's avatar
Timothy J. Baek committed
36
37
38
</script>

<Modal bind:show>
Timothy J. Baek's avatar
Timothy J. Baek committed
39
	<div class="text-gray-700 dark:text-gray-100">
Timothy J. Baek's avatar
Timothy J. Baek committed
40
		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
41
			<div class=" text-lg font-medium self-center">{$i18n.t('Settings')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
			<button
				class="self-center"
				on:click={() => {
					show = false;
				}}
			>
				<svg
					xmlns="http://www.w3.org/2000/svg"
					viewBox="0 0 20 20"
					fill="currentColor"
					class="w-5 h-5"
				>
					<path
						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
					/>
				</svg>
			</button>
		</div>

Timothy J. Baek's avatar
Timothy J. Baek committed
61
62
		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
			<div
Timothy J. Baek's avatar
Timothy J. Baek committed
63
				class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
Timothy J. Baek's avatar
Timothy J. Baek committed
64
65
			>
				<button
66
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
Timothy J. Baek's avatar
Timothy J. Baek committed
67
					'general'
Timothy J. Baek's avatar
Timothy J. Baek committed
68
69
						? 'bg-gray-200 dark:bg-gray-700'
						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
Timothy J. Baek's avatar
Timothy J. Baek committed
70
					on:click={() => {
71
						selectedTab = 'general';
Timothy J. Baek's avatar
Timothy J. Baek committed
72
					}}
Timothy J. Baek's avatar
Timothy J. Baek committed
73
				>
Timothy J. Baek's avatar
Timothy J. Baek committed
74
75
76
77
78
79
80
81
82
83
84
85
86
87
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 20 20"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M8.34 1.804A1 1 0 019.32 1h1.36a1 1 0 01.98.804l.295 1.473c.497.144.971.342 1.416.587l1.25-.834a1 1 0 011.262.125l.962.962a1 1 0 01.125 1.262l-.834 1.25c.245.445.443.919.587 1.416l1.473.294a1 1 0 01.804.98v1.361a1 1 0 01-.804.98l-1.473.295a6.95 6.95 0 01-.587 1.416l.834 1.25a1 1 0 01-.125 1.262l-.962.962a1 1 0 01-1.262.125l-1.25-.834a6.953 6.953 0 01-1.416.587l-.294 1.473a1 1 0 01-.98.804H9.32a1 1 0 01-.98-.804l-.295-1.473a6.957 6.957 0 01-1.416-.587l-1.25.834a1 1 0 01-1.262-.125l-.962-.962a1 1 0 01-.125-1.262l.834-1.25a6.957 6.957 0 01-.587-1.416l-1.473-.294A1 1 0 011 10.68V9.32a1 1 0 01.804-.98l1.473-.295c.144-.497.342-.971.587-1.416l-.834-1.25a1 1 0 01.125-1.262l.962-.962A1 1 0 015.38 3.03l1.25.834a6.957 6.957 0 011.416-.587l.294-1.473zM13 10a3 3 0 11-6 0 3 3 0 016 0z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
88
					<div class=" self-center">{$i18n.t('General')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
89
90
				</button>

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
				{#if $user.role === 'admin'}
					<button
						class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
						'admin'
							? 'bg-gray-200 dark:bg-gray-700'
							: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
						on:click={async () => {
							await goto('/admin/settings');
							show = false;
						}}
					>
						<div class=" self-center mr-2">
							<svg
								xmlns="http://www.w3.org/2000/svg"
								viewBox="0 0 24 24"
								fill="currentColor"
								class="size-4"
							>
								<path
									fill-rule="evenodd"
									d="M4.5 3.75a3 3 0 0 0-3 3v10.5a3 3 0 0 0 3 3h15a3 3 0 0 0 3-3V6.75a3 3 0 0 0-3-3h-15Zm4.125 3a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Zm-3.873 8.703a4.126 4.126 0 0 1 7.746 0 .75.75 0 0 1-.351.92 7.47 7.47 0 0 1-3.522.877 7.47 7.47 0 0 1-3.522-.877.75.75 0 0 1-.351-.92ZM15 8.25a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 0-1.5H15ZM14.25 12a.75.75 0 0 1 .75-.75h3.75a.75.75 0 0 1 0 1.5H15a.75.75 0 0 1-.75-.75Zm.75 2.25a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 0-1.5H15Z"
									clip-rule="evenodd"
								/>
							</svg>
						</div>
						<div class=" self-center">{$i18n.t('Admin Settings')}</div>
					</button>
				{/if}

120
				<button
121
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
Timothy J. Baek's avatar
Timothy J. Baek committed
122
					'interface'
Timothy J. Baek's avatar
Timothy J. Baek committed
123
124
						? 'bg-gray-200 dark:bg-gray-700'
						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
125
					on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
126
						selectedTab = 'interface';
127
128
129
130
131
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
Timothy J. Baek's avatar
Timothy J. Baek committed
132
							viewBox="0 0 16 16"
133
134
135
136
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
Timothy J. Baek's avatar
Timothy J. Baek committed
137
								fill-rule="evenodd"
Timothy J. Baek's avatar
Timothy J. Baek committed
138
								d="M2 4.25A2.25 2.25 0 0 1 4.25 2h7.5A2.25 2.25 0 0 1 14 4.25v5.5A2.25 2.25 0 0 1 11.75 12h-1.312c.1.128.21.248.328.36a.75.75 0 0 1 .234.545v.345a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-.345a.75.75 0 0 1 .234-.545c.118-.111.228-.232.328-.36H4.25A2.25 2.25 0 0 1 2 9.75v-5.5Zm2.25-.75a.75.75 0 0 0-.75.75v4.5c0 .414.336.75.75.75h7.5a.75.75 0 0 0 .75-.75v-4.5a.75.75 0 0 0-.75-.75h-7.5Z"
Timothy J. Baek's avatar
Timothy J. Baek committed
139
140
141
142
								clip-rule="evenodd"
							/>
						</svg>
					</div>
143
					<div class=" self-center">{$i18n.t('Interface')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
144
145
				</button>

Timothy J. Baek's avatar
Timothy J. Baek committed
146
147
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
148
					'personalization'
Timothy J. Baek's avatar
Timothy J. Baek committed
149
150
151
						? 'bg-gray-200 dark:bg-gray-700'
						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
					on:click={() => {
152
						selectedTab = 'personalization';
Timothy J. Baek's avatar
Timothy J. Baek committed
153
154
155
					}}
				>
					<div class=" self-center mr-2">
156
						<User />
Timothy J. Baek's avatar
Timothy J. Baek committed
157
					</div>
158
					<div class=" self-center">{$i18n.t('Personalization')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
159
160
				</button>

Timothy J. Baek's avatar
Timothy J. Baek committed
161
162
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
Timothy J. Baek's avatar
Timothy J. Baek committed
163
					'audio'
Timothy J. Baek's avatar
Timothy J. Baek committed
164
165
166
						? 'bg-gray-200 dark:bg-gray-700'
						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
					on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
167
						selectedTab = 'audio';
Timothy J. Baek's avatar
Timothy J. Baek committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 16 16"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								d="M7.557 2.066A.75.75 0 0 1 8 2.75v10.5a.75.75 0 0 1-1.248.56L3.59 11H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h1.59l3.162-2.81a.75.75 0 0 1 .805-.124ZM12.95 3.05a.75.75 0 1 0-1.06 1.06 5.5 5.5 0 0 1 0 7.78.75.75 0 1 0 1.06 1.06 7 7 0 0 0 0-9.9Z"
							/>
							<path
								d="M10.828 5.172a.75.75 0 1 0-1.06 1.06 2.5 2.5 0 0 1 0 3.536.75.75 0 1 0 1.06 1.06 4 4 0 0 0 0-5.656Z"
182
183
184
							/>
						</svg>
					</div>
185
					<div class=" self-center">{$i18n.t('Audio')}</div>
186
				</button>
Timothy J. Baek's avatar
Timothy J. Baek committed
187

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
					'chats'
						? 'bg-gray-200 dark:bg-gray-700'
						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
					on:click={() => {
						selectedTab = 'chats';
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 16 16"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M8 2C4.262 2 1 4.57 1 8c0 1.86.98 3.486 2.455 4.566a3.472 3.472 0 0 1-.469 1.26.75.75 0 0 0 .713 1.14 6.961 6.961 0 0 0 3.06-1.06c.403.062.818.094 1.241.094 3.738 0 7-2.57 7-6s-3.262-6-7-6ZM5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Zm7-1a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM8 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
211
					<div class=" self-center">{$i18n.t('Chats')}</div>
212
213
				</button>

Timothy J. Baek's avatar
Timothy J. Baek committed
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
					'account'
						? 'bg-gray-200 dark:bg-gray-700'
						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
					on:click={() => {
						selectedTab = 'account';
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 16 16"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M15 8A7 7 0 1 1 1 8a7 7 0 0 1 14 0Zm-5-2a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM8 9c-1.825 0-3.422.977-4.295 2.437A5.49 5.49 0 0 0 8 13.5a5.49 5.49 0 0 0 4.294-2.063A4.997 4.997 0 0 0 8 9Z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
237
					<div class=" self-center">{$i18n.t('Account')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
238
239
				</button>

Timothy J. Baek's avatar
Timothy J. Baek committed
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
					'about'
						? 'bg-gray-200 dark:bg-gray-700'
						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
					on:click={() => {
						selectedTab = 'about';
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 20 20"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
263
					<div class=" self-center">{$i18n.t('About')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
264
				</button>
Timothy J. Baek's avatar
Timothy J. Baek committed
265
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
266
			<div class="flex-1 md:min-h-[28rem]">
267
				{#if selectedTab === 'general'}
Timothy J. Baek's avatar
Timothy J. Baek committed
268
					<General
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
269
						{getModels}
Timothy J. Baek's avatar
Timothy J. Baek committed
270
271
						{saveSettings}
						on:save={() => {
272
							toast.success($i18n.t('Settings saved successfully!'));
Timothy J. Baek's avatar
Timothy J. Baek committed
273
274
						}}
					/>
275
				{:else if selectedTab === 'interface'}
Timothy J. Baek's avatar
Timothy J. Baek committed
276
					<Interface
Timothy J. Baek's avatar
Timothy J. Baek committed
277
						{saveSettings}
Timothy J. Baek's avatar
Timothy J. Baek committed
278
						on:save={() => {
279
							toast.success($i18n.t('Settings saved successfully!'));
280
						}}
Timothy J. Baek's avatar
Timothy J. Baek committed
281
					/>
282
283
284
285
286
287
288
				{:else if selectedTab === 'personalization'}
					<Personalization
						{saveSettings}
						on:save={() => {
							toast.success($i18n.t('Settings saved successfully!'));
						}}
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
289
290
				{:else if selectedTab === 'audio'}
					<Audio
Timothy J. Baek's avatar
Timothy J. Baek committed
291
292
						{saveSettings}
						on:save={() => {
293
							toast.success($i18n.t('Settings saved successfully!'));
Timothy J. Baek's avatar
Timothy J. Baek committed
294
						}}
Timothy J. Baek's avatar
Timothy J. Baek committed
295
					/>
296
				{:else if selectedTab === 'chats'}
Timothy J. Baek's avatar
Timothy J. Baek committed
297
					<Chats {saveSettings} />
298
				{:else if selectedTab === 'account'}
299
300
					<Account
						saveHandler={() => {
301
							toast.success($i18n.t('Settings saved successfully!'));
302
						}}
303
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
304
				{:else if selectedTab === 'about'}
Timothy J. Baek's avatar
Timothy J. Baek committed
305
					<About />
Timothy J. Baek's avatar
Timothy J. Baek committed
306
307
				{/if}
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
308
		</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
309
310
	</div>
</Modal>
311
312
313
314
315
316
317
318
319

<style>
	input::-webkit-outer-spin-button,
	input::-webkit-inner-spin-button {
		/* display: none; <- Crashes Chrome on hover */
		-webkit-appearance: none;
		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
	}

320
321
322
323
324
325
326
327
328
	.tabs::-webkit-scrollbar {
		display: none; /* for Chrome, Safari and Opera */
	}

	.tabs {
		-ms-overflow-style: none; /* IE and Edge */
		scrollbar-width: none; /* Firefox */
	}

329
330
331
332
	input[type='number'] {
		-moz-appearance: textfield; /* Firefox */
	}
</style>