From 8953f57d887c060c3b58f83c38d2010d27a45ef3 Mon Sep 17 00:00:00 2001
From: tobi <31960611+tsmethurst@users.noreply.github.com>
Date: Mon, 25 Mar 2024 18:32:24 +0100
Subject: [feature] User-selectable preset CSS themes for accounts (#2777)

* [feature] User-selectable preset themes

* docs, more theme stuff

* lint, tests

* fix css name

* correct some little issues

* add another theme

* fix poll background

* okay last theme i swear

* make retrieval of apimodel themes more conventional

* preallocate stylesheet slices
---
 web/source/settings/user/profile.tsx | 39 ++++++++++++++++++++++++++++++++----
 1 file changed, 35 insertions(+), 4 deletions(-)

(limited to 'web/source/settings/user/profile.tsx')

diff --git a/web/source/settings/user/profile.tsx b/web/source/settings/user/profile.tsx
index a03d4d247..cd4b17227 100644
--- a/web/source/settings/user/profile.tsx
+++ b/web/source/settings/user/profile.tsx
@@ -23,7 +23,8 @@ import {
 	useTextInput,
 	useFileInput,
 	useBoolInput,
-	useFieldArrayInput
+	useFieldArrayInput,
+	useRadioInput
 } from "../lib/form";
 
 import useFormSubmit from "../lib/form/submit";
@@ -33,14 +34,15 @@ import {
 	TextInput,
 	TextArea,
 	FileInput,
-	Checkbox
+	Checkbox,
+	RadioGroup
 } from "../components/form/inputs";
 
 import FormWithData from "../lib/form/form-with-data";
 import FakeProfile from "../components/fake-profile";
 import MutationButton from "../components/form/mutation-button";
 
-import { useInstanceV1Query } from "../lib/query";
+import { useAccountThemesQuery, useInstanceV1Query } from "../lib/query";
 import { useUpdateCredentialsMutation } from "../lib/query/user";
 import { useVerifyCredentialsQuery } from "../lib/query/oauth";
 
@@ -64,6 +66,7 @@ function UserProfileForm({ data: profile }) {
 		- file header
 		- bool enable_rss
 		- string custom_css (if enabled)
+		- string theme
 	*/
 
 	const { data: instance } = useInstanceV1Query();
@@ -73,13 +76,24 @@ function UserProfileForm({ data: profile }) {
 			maxPinnedFields: instance?.configuration?.accounts?.max_profile_fields ?? 6
 		};
 	}, [instance]);
+	
+	// Parse out available theme options into nice format.
+	const { data: themes } = useAccountThemesQuery();
+	let themeOptions = { "": "Default" };
+	themes?.forEach((theme) => {
+		let key = theme.file_name;
+		let value = theme.title;
+		if (theme.description) {
+			value += " - " + theme.description;
+		}
+		themeOptions[key] = value;
+	});
 
 	const form = {
 		avatar: useFileInput("avatar", { withPreview: true }),
 		header: useFileInput("header", { withPreview: true }),
 		displayName: useTextInput("display_name", { source: profile }),
 		note: useTextInput("note", { source: profile, valueSelector: (p) => p.source?.note }),
-		customCSS: useTextInput("custom_css", { source: profile, nosubmit: !instanceConfig.allowCustomCSS }),
 		bot: useBoolInput("bot", { source: profile }),
 		locked: useBoolInput("locked", { source: profile }),
 		discoverable: useBoolInput("discoverable", { source: profile}),
@@ -88,6 +102,11 @@ function UserProfileForm({ data: profile }) {
 			defaultValue: profile?.source?.fields,
 			length: instanceConfig.maxPinnedFields
 		}),
+		customCSS: useTextInput("custom_css", { source: profile, nosubmit: !instanceConfig.allowCustomCSS }),
+		theme: useRadioInput("theme", {
+			source: profile,
+			options: themeOptions,
+		}),
 	};
 
 	const [submitForm, result] = useFormSubmit(form, useUpdateCredentialsMutation(), {
@@ -125,6 +144,18 @@ function UserProfileForm({ data: profile }) {
 						/>
 					</div>
 				</div>
+
+				<div className="theme">
+					<div>
+						<b id="theme-label">Theme</b>
+						<br/>
+						<span>After choosing theme and saving, <a href={profile.url} target="_blank">open your profile</a> and refresh to see changes.</span>
+					</div>
+					<RadioGroup
+						aria-labelledby="theme-label"
+						field={form.theme}
+					/>
+				</div>
 			</div>
 
 			<div className="form-section-docs">
-- 
cgit v1.2.3

