mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[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
This commit is contained in:
@ -23,6 +23,7 @@ import type {
|
||||
MoveAccountFormData,
|
||||
UpdateAliasesFormData
|
||||
} from "../../types/migration";
|
||||
import type { Theme } from "../../types/theme";
|
||||
|
||||
const extended = gtsApi.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
@ -66,6 +67,11 @@ const extended = gtsApi.injectEndpoints({
|
||||
url: `/api/v1/accounts/move`,
|
||||
body: data
|
||||
})
|
||||
}),
|
||||
accountThemes: build.query<Theme[], void>({
|
||||
query: () => ({
|
||||
url: `/api/v1/accounts/themes`
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
@ -75,4 +81,5 @@ export const {
|
||||
usePasswordChangeMutation,
|
||||
useAliasAccountMutation,
|
||||
useMoveAccountMutation,
|
||||
useAccountThemesQuery,
|
||||
} = extended;
|
||||
|
24
web/source/settings/lib/types/theme.ts
Normal file
24
web/source/settings/lib/types/theme.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface Theme {
|
||||
title: string;
|
||||
description: string;
|
||||
file_name: string;
|
||||
}
|
@ -439,7 +439,7 @@ section.with-sidebar > div, section.with-sidebar > form {
|
||||
display: grid;
|
||||
max-width: 60rem;
|
||||
grid-template-columns: 70% 30%;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-rows: auto;
|
||||
gap: 1rem;
|
||||
|
||||
.files {
|
||||
@ -465,6 +465,12 @@ section.with-sidebar > div, section.with-sidebar > form {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.theme, .form-field.radio {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.migration-details {
|
||||
|
@ -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">
|
||||
|
Reference in New Issue
Block a user