mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 07:12:25 -05: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:
parent
b7b42e832a
commit
8953f57d88
32 changed files with 1230 additions and 28 deletions
92
web/assets/themes/blurple-dark.css
Normal file
92
web/assets/themes/blurple-dark.css
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
theme-title: Blurple (dark)
|
||||
theme-description: Official dark blurple theme
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Define our nice blurple palette */
|
||||
--blurple1: #ffffff;
|
||||
--blurple2: #ebe6f8;
|
||||
--blurple3: #d6cceb;
|
||||
--blurple4: #c2b3e1;
|
||||
--blurple5: #ad99d7;
|
||||
--blurple6: #9980cd;
|
||||
--blurple7: #8566c2;
|
||||
--blurple8: #704db8;
|
||||
--blurple9: #5c33ae;
|
||||
--blurple10: #471aa4;
|
||||
--blurple11: #33009a;
|
||||
--blurple12: #170044;
|
||||
|
||||
/* Restyle basic colors to use blurple */
|
||||
--blue1: var(--blurple1);
|
||||
--blue2: var(--blurple2);
|
||||
--blue3: var(--blurple3);
|
||||
|
||||
/* Basic page styling (background + foreground) */
|
||||
--bg: var(--blurple12);
|
||||
--bg-accent: var(--blurple11);
|
||||
--fg: var(--blurple1);
|
||||
--fg-reduced: var(--blurple3);
|
||||
|
||||
/* Profile page styling (light) */
|
||||
--profile-bg: var(--blurple11);
|
||||
|
||||
/* Blurpleize buttons */
|
||||
--button-bg: var(--blurple2);
|
||||
--button-fg: var(--blurple11);
|
||||
|
||||
/* Blurpleize statuses */
|
||||
--status-bg: var(--blurple11);
|
||||
--status-focus-bg: var(--blurple11);
|
||||
--status-info-bg: var(--blurple9);
|
||||
--status-focus-info-bg: var(--blurple9);
|
||||
|
||||
/* Used around statuses + other items */
|
||||
--boxshadow-border: 0.08rem solid black;
|
||||
}
|
||||
|
||||
/* Scroll bar */
|
||||
html, body {
|
||||
scrollbar-color: var(--blurple8) var(--blurple12);
|
||||
}
|
||||
|
||||
/* Profile fields */
|
||||
.profile .about-user .fields .field {
|
||||
border-bottom: 0.1rem solid var(--blurple8);
|
||||
}
|
||||
.profile .about-user .fields .field:first-child {
|
||||
border-top: 0.1rem solid var(--blurple8);
|
||||
}
|
||||
|
||||
/* Status media */
|
||||
.status .media .media-wrapper {
|
||||
border: 0.08rem solid var(--blurple9);
|
||||
}
|
||||
.status .media .media-wrapper details .unknown-attachment .placeholder {
|
||||
color: var(--blue2);
|
||||
}
|
||||
.status .media .media-wrapper details video.plyr-video {
|
||||
background: var(--blurple11);
|
||||
}
|
||||
|
||||
/* Status polls */
|
||||
.status .text .poll {
|
||||
background-color: var(--bg);
|
||||
}
|
||||
.status .text .poll .poll-info {
|
||||
background-color: var(--blurple11);
|
||||
}
|
||||
|
||||
/* Code snippets */
|
||||
pre, pre[class*="language-"],
|
||||
code, code[class*="language-"] {
|
||||
background-color: var(--blurple12);
|
||||
color: var(--fg-reduced);
|
||||
}
|
||||
|
||||
/* Block quotes */
|
||||
blockquote {
|
||||
background-color: var(--blurple12);
|
||||
color: var(--fg-reduced);
|
||||
}
|
||||
94
web/assets/themes/blurple-light.css
Normal file
94
web/assets/themes/blurple-light.css
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
theme-title: Blurple (light)
|
||||
theme-description: Official light blurple theme
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Define our nice blurple palette */
|
||||
--blurple1: #ffffff;
|
||||
--blurple2: #ebe6f8;
|
||||
--blurple3: #d6cceb;
|
||||
--blurple4: #c2b3e1;
|
||||
--blurple5: #ad99d7;
|
||||
--blurple6: #9980cd;
|
||||
--blurple7: #8566c2;
|
||||
--blurple8: #704db8;
|
||||
--blurple9: #5c33ae;
|
||||
--blurple10: #471aa4;
|
||||
--blurple11: #33009a;
|
||||
--blurple12: #170044;
|
||||
|
||||
/* Restyle basic colors to use blurple */
|
||||
--white1: var(--blurple2);
|
||||
--white2: var(--blurple3);
|
||||
--blue1: var(--blurple6);
|
||||
--blue2: var(--blurple8);
|
||||
--blue3: var(--blurple10);
|
||||
|
||||
/* Basic page styling (background + foreground) */
|
||||
--bg: linear-gradient(var(--blurple2), var(--blurple1));
|
||||
--bg-accent: var(--white2);
|
||||
--fg: var(--gray1);
|
||||
--fg-reduced: var(--gray2);
|
||||
|
||||
/* Profile page styling (light) */
|
||||
--profile-bg: var(--white2);
|
||||
|
||||
/* Blurpleize buttons */
|
||||
--button-bg: var(--blue2);
|
||||
--button-fg: var(--white1);
|
||||
|
||||
/* Blurpleize statuses */
|
||||
--status-bg: var(--white1);
|
||||
--status-focus-bg: var(--white1);
|
||||
--status-info-bg: var(--white2);
|
||||
--status-focus-info-bg: var(--white2);
|
||||
|
||||
/* Used around statuses + other items */
|
||||
--boxshadow-border: 0.08rem solid var(--blurple10);
|
||||
}
|
||||
|
||||
/* Scroll bar */
|
||||
html, body {
|
||||
scrollbar-color: var(--blurple8) var(--blurple2);
|
||||
}
|
||||
|
||||
/* Profile fields */
|
||||
.profile .about-user .fields .field {
|
||||
border-bottom: 0.1rem solid var(--blurple10);
|
||||
}
|
||||
.profile .about-user .fields .field:first-child {
|
||||
border-top: 0.1rem solid var(--blurple10);
|
||||
}
|
||||
|
||||
/* Status media */
|
||||
.status .media .media-wrapper {
|
||||
border: 0.08rem solid var(--blurple10);
|
||||
}
|
||||
.status .media .media-wrapper details .unknown-attachment .placeholder {
|
||||
color: var(--blue2);
|
||||
}
|
||||
.status .media .media-wrapper details video.plyr-video {
|
||||
background: var(--blurple2);
|
||||
}
|
||||
|
||||
/* Status polls */
|
||||
.status .text .poll {
|
||||
background-color: var(--white2);
|
||||
}
|
||||
.status .text .poll .poll-info {
|
||||
background-color: var(--white1);
|
||||
}
|
||||
|
||||
/* Code snippets */
|
||||
pre, pre[class*="language-"],
|
||||
code, code[class*="language-"] {
|
||||
background-color: var(--blurple12);
|
||||
color: var(--blurple2);
|
||||
}
|
||||
|
||||
/* Block quotes */
|
||||
blockquote {
|
||||
background-color: var(--blurple1);
|
||||
color: var(--blurple12);
|
||||
}
|
||||
159
web/assets/themes/midnight-trip.css
Normal file
159
web/assets/themes/midnight-trip.css
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
theme-title: Midnight Trip
|
||||
theme-description: Woah
|
||||
*/
|
||||
|
||||
/* Theme colors */
|
||||
:root {
|
||||
--acid-green: rgb(63, 255, 0);
|
||||
--acid-green-light: #79FF4D;
|
||||
--acid-green-dark: #269900;
|
||||
--magenta: rgb(153, 50, 204);
|
||||
--darkred: rgb(58, 0, 15);
|
||||
--darkblue: rgb(0, 0, 58);
|
||||
--darkmagenta: rgb(47, 1, 65);
|
||||
|
||||
/* Override */
|
||||
--orange2: var(--acid-green);
|
||||
--gray1: rgb(20, 21, 23);
|
||||
--blue1: var(--acid-green-dark);
|
||||
--blue2: var(--acid-green-light);
|
||||
--blue3: var(--acid-green);
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(-45deg, black, var(--darkmagenta), var(--darkblue), var(--darkred));
|
||||
background-size: 400% 400%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media not (prefers-reduced-motion) {
|
||||
body {
|
||||
animation: gradient 30s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html, body {
|
||||
/* Funky scroll bar */
|
||||
scrollbar-color: var(--acid-green) var(--gray1);
|
||||
}
|
||||
|
||||
/* Instance display name */
|
||||
.page-header {
|
||||
grid-column: 2;
|
||||
align-self: start;
|
||||
margin: 1rem 0 1rem 0;
|
||||
background-color: var(--gray1);
|
||||
border: 0.25rem solid var(--magenta);
|
||||
border-radius: var(--br);
|
||||
}
|
||||
|
||||
/* Header card */
|
||||
.profile .profile-header {
|
||||
background-color: var(--gray1);
|
||||
border: 0.25rem solid var(--magenta);
|
||||
}
|
||||
|
||||
/* About + Pinned posts headers */
|
||||
.profile .col-header {
|
||||
background: var(--gray1);
|
||||
border: 0.25rem solid var(--magenta);
|
||||
}
|
||||
|
||||
.profile .about-user .col-header {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Make about sections transparent */
|
||||
.profile .about-user .fields, .profile .about-user .bio, .profile .about-user .accountstats {
|
||||
background: var(--gray1);
|
||||
border-left: 0.25rem solid var(--magenta);
|
||||
border-right: 0.25rem solid var(--magenta);
|
||||
}
|
||||
|
||||
/* Fiddle around with borders on about sections */
|
||||
.profile .about-user .fields .field:first-child {
|
||||
border-top: 0.25rem dashed var(--magenta);
|
||||
}
|
||||
.profile .about-user .fields .field {
|
||||
border-bottom: 0.25rem dashed var(--magenta);
|
||||
}
|
||||
.profile .about-user .accountstats {
|
||||
border-top: 0.25rem dashed var(--magenta);
|
||||
border-bottom: 0.25rem solid var(--magenta);
|
||||
}
|
||||
|
||||
/* Statuses + threads */
|
||||
|
||||
/* Thread column header */
|
||||
.thread .col-header {
|
||||
background: var(--gray1);
|
||||
border: 0.25rem solid var(--magenta);
|
||||
}
|
||||
|
||||
/* Main status body */
|
||||
.status, .status.expanded {
|
||||
background: var(--gray1);
|
||||
border: 0.25rem solid var(--magenta);
|
||||
}
|
||||
|
||||
/* Code snippets */
|
||||
.status .text .content pre, .status .text .content code {
|
||||
background: black;
|
||||
color: var(--white2);
|
||||
}
|
||||
|
||||
/* Block quotes */
|
||||
.status .text .content blockquote {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
/* Media wrapper for attachments */
|
||||
.status .media .media-wrapper {
|
||||
background: var(--bg-nearly-opaque);
|
||||
}
|
||||
.status .media .media-wrapper details .unknown-attachment .placeholder {
|
||||
border: 0.2rem dashed var(--magenta);
|
||||
}
|
||||
|
||||
/* Polls */
|
||||
.status .text .poll {
|
||||
background-color: black;
|
||||
border: 0.25rem solid var(--magenta);
|
||||
}
|
||||
|
||||
.status .text .poll .poll-info {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
/* Status info bars */
|
||||
.status .status-info, .status.expanded .status-info {
|
||||
background: black;
|
||||
}
|
||||
|
||||
/* Back + next links */
|
||||
.backnextlinks {
|
||||
background: var(--gray1);
|
||||
padding: 0.5rem;
|
||||
border: 0.25rem solid var(--magenta);
|
||||
border-radius: var(--br);
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
margin-top: 2rem;
|
||||
background-color: var(--gray1);
|
||||
border-top: 0.25rem solid var(--magenta);
|
||||
}
|
||||
124
web/assets/themes/soft.css
Normal file
124
web/assets/themes/soft.css
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
theme-title: Soft
|
||||
theme-description: Pastel pink and blue with dark magenta trim
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Define our palette */
|
||||
--soft-pink: rgb(255, 199, 234);
|
||||
--soft-pink-translucent: rgb(255, 199, 234, 30%);
|
||||
--soft-lilac: #D8B4F8;
|
||||
--soft-lilac-translucent: rgb(216, 180, 248, 30%);
|
||||
--soft-blue: #d6f1ff;
|
||||
|
||||
/* Override */
|
||||
--blue1: #7f16de;
|
||||
--blue2: #7514cc;
|
||||
--blue3: #6b12ba;
|
||||
--orange2: var(--blue1);
|
||||
--br: 0.8rem;
|
||||
--br-inner: 0.4rem;
|
||||
|
||||
/* Basic page styling (background + foreground) */
|
||||
--bg: linear-gradient(-90deg, var(--soft-blue), var(--soft-pink), white, var(--soft-pink), var(--soft-blue));
|
||||
--bg-accent: var(--soft-pink-translucent);
|
||||
--fg: var(--gray1);
|
||||
--fg-reduced: var(--gray3);
|
||||
|
||||
/* Profile page styling (light) */
|
||||
--profile-bg: var(--soft-pink-translucent);
|
||||
|
||||
/* Statuses */
|
||||
--status-bg: var(--soft-pink-translucent);
|
||||
--status-focus-bg: var(--soft-pink-translucent);
|
||||
--status-info-bg: var(--soft-lilac-translucent);
|
||||
--status-focus-info-bg: var(--soft-lilac-translucent);
|
||||
|
||||
/* Boot-on */
|
||||
--button-fg: var(--white1);
|
||||
|
||||
/* Used around statuses + other items */
|
||||
--boxshadow-border: 0.08rem solid var(--gray8);
|
||||
}
|
||||
|
||||
/* Scroll bar */
|
||||
html, body {
|
||||
scrollbar-color: var(--orange2) var(--soft-pink);
|
||||
}
|
||||
|
||||
/* Header card */
|
||||
.profile .profile-header {
|
||||
border: var(--boxshadow-border);
|
||||
}
|
||||
|
||||
.profile .profile-header .basic-info .namerole .role {
|
||||
border: var(--boxshadow-border);
|
||||
}
|
||||
|
||||
/* About + Pinned posts headers */
|
||||
.profile .col-header {
|
||||
border: var(--boxshadow-border);
|
||||
}
|
||||
|
||||
.profile .about-user .col-header {
|
||||
margin-bottom: initial;
|
||||
border-bottom: none;
|
||||
border-top: var(--boxshadow-border);
|
||||
border-left: var(--boxshadow-border);
|
||||
border-right: var(--boxshadow-border);
|
||||
}
|
||||
|
||||
/* Profile fields + bio */
|
||||
.profile .about-user .fields {
|
||||
border-left: var(--boxshadow-border);
|
||||
border-right: var(--boxshadow-border);
|
||||
}
|
||||
.profile .about-user .fields .field {
|
||||
border-bottom: 0.1rem dashed var(--blue3);
|
||||
}
|
||||
.profile .about-user .fields .field:first-child {
|
||||
border-top: 0.1rem dashed var(--blue3);
|
||||
}
|
||||
.profile .about-user .bio {
|
||||
border-left: var(--boxshadow-border);
|
||||
border-right: var(--boxshadow-border);
|
||||
}
|
||||
.profile .about-user .accountstats {
|
||||
background: var(--soft-lilac-translucent);
|
||||
border-bottom: var(--boxshadow-border);
|
||||
border-left: var(--boxshadow-border);
|
||||
border-right: var(--boxshadow-border);
|
||||
border-bottom-left-radius: var(--br);
|
||||
border-bottom-right-radius: var(--br);
|
||||
}
|
||||
|
||||
/* Status media */
|
||||
.status .media .media-wrapper {
|
||||
border: 0.08rem solid var(--blue3);
|
||||
}
|
||||
.status .media .media-wrapper details .unknown-attachment .placeholder {
|
||||
color: var(--blue2);
|
||||
}
|
||||
.status .media .media-wrapper details video.plyr-video {
|
||||
background: var(--soft-pink-translucent);
|
||||
}
|
||||
|
||||
/* Status polls */
|
||||
.status .text .poll {
|
||||
background-color: var(--soft-lilac-translucent);
|
||||
}
|
||||
.status .text .poll .poll-info {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* Code snippets */
|
||||
pre, pre[class*="language-"],
|
||||
code, code[class*="language-"] {
|
||||
background-color: var(--gray1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Block quotes */
|
||||
blockquote {
|
||||
background-color: var(--soft-lilac-translucent);
|
||||
}
|
||||
95
web/assets/themes/sunset-light.css
Normal file
95
web/assets/themes/sunset-light.css
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
theme-title: Sunset (light)
|
||||
theme-description: Official light orange/yellow theme.
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Define our palette */
|
||||
--eggshell: #fff6eb;
|
||||
--yellow: #FFAF45;
|
||||
--orange: #FB6D48;
|
||||
--pink: #D74B76;
|
||||
--eggplant1: #5c385e;
|
||||
--eggplant2: #523254;
|
||||
--eggplant3: #482c49;
|
||||
--eggplant4: #29192a;
|
||||
|
||||
/* Restyle basic colors */
|
||||
--white1: var(--eggshell);
|
||||
--white2: var(--yellow);
|
||||
--blue1: var(--eggplant1);
|
||||
--blue2: var(--eggplant2);
|
||||
--blue3: var(--eggplant3);
|
||||
--orange2: var(--pink);
|
||||
|
||||
/* Basic page styling (background + foreground) */
|
||||
--bg: linear-gradient(var(--eggplant1), var(--pink), var(--orange), var(--yellow), var(--eggshell));
|
||||
--bg-accent: var(--white2);
|
||||
--fg: var(--eggplant4);
|
||||
--fg-reduced: var(--eggplant3);
|
||||
|
||||
/* Profile page styling (light) */
|
||||
--profile-bg: var(--white2);
|
||||
|
||||
/* Buttons */
|
||||
--button-bg: var(--blue2);
|
||||
--button-fg: var(--white1);
|
||||
|
||||
/* Statuses */
|
||||
--status-bg: var(--white1);
|
||||
--status-focus-bg: var(--white1);
|
||||
--status-info-bg: var(--white2);
|
||||
--status-focus-info-bg: var(--white2);
|
||||
|
||||
/* Used around statuses + other items */
|
||||
--boxshadow-border: 0.08rem solid var(--orange);
|
||||
}
|
||||
|
||||
/* Scroll bar */
|
||||
html, body {
|
||||
scrollbar-color: var(--pink) var(--eggshell);
|
||||
}
|
||||
|
||||
.page-header a h1 {
|
||||
color: var(--eggshell);
|
||||
}
|
||||
|
||||
/* Profile fields */
|
||||
.profile .about-user .fields .field {
|
||||
border-bottom: 0.1rem solid var(--orange);
|
||||
}
|
||||
.profile .about-user .fields .field:first-child {
|
||||
border-top: 0.1rem solid var(--orange);
|
||||
}
|
||||
|
||||
/* Status media */
|
||||
.status .media .media-wrapper {
|
||||
border: 0.08rem solid var(--orange);
|
||||
}
|
||||
.status .media .media-wrapper details .unknown-attachment .placeholder {
|
||||
color: var(--blue2);
|
||||
}
|
||||
.status .media .media-wrapper details video.plyr-video {
|
||||
background: var(--eggshell);
|
||||
}
|
||||
|
||||
/* Status polls */
|
||||
.status .text .poll {
|
||||
background-color: var(--white2);
|
||||
}
|
||||
.status .text .poll .poll-info {
|
||||
background-color: var(--white1);
|
||||
}
|
||||
|
||||
/* Code snippets */
|
||||
pre, pre[class*="language-"],
|
||||
code, code[class*="language-"] {
|
||||
background-color: var(--eggplant4);
|
||||
color: var(--white1);
|
||||
}
|
||||
|
||||
/* Block quotes */
|
||||
blockquote {
|
||||
background-color: var(--yellow);
|
||||
color: var(--eggplant4);
|
||||
}
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue