gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

profile.js (5201B)


      1 /*
      2 	GoToSocial
      3 	Copyright (C) GoToSocial Authors admin@gotosocial.org
      4 	SPDX-License-Identifier: AGPL-3.0-or-later
      5 
      6 	This program is free software: you can redistribute it and/or modify
      7 	it under the terms of the GNU Affero General Public License as published by
      8 	the Free Software Foundation, either version 3 of the License, or
      9 	(at your option) any later version.
     10 
     11 	This program is distributed in the hope that it will be useful,
     12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 	GNU Affero General Public License for more details.
     15 
     16 	You should have received a copy of the GNU Affero General Public License
     17 	along with this program.  If not, see <http://www.gnu.org/licenses/>.
     18 */
     19 
     20 "use strict";
     21 
     22 const React = require("react");
     23 
     24 const query = require("../lib/query");
     25 
     26 const {
     27 	useTextInput,
     28 	useFileInput,
     29 	useBoolInput,
     30 	useFieldArrayInput
     31 } = require("../lib/form");
     32 
     33 const useFormSubmit = require("../lib/form/submit");
     34 const { useWithFormContext, FormContext } = require("../lib/form/context");
     35 
     36 const {
     37 	TextInput,
     38 	TextArea,
     39 	FileInput,
     40 	Checkbox
     41 } = require("../components/form/inputs");
     42 
     43 const FormWithData = require("../lib/form/form-with-data");
     44 const FakeProfile = require("../components/fake-profile");
     45 const MutationButton = require("../components/form/mutation-button");
     46 
     47 module.exports = function UserProfile() {
     48 	return (
     49 		<FormWithData
     50 			dataQuery={query.useVerifyCredentialsQuery}
     51 			DataForm={UserProfileForm}
     52 		/>
     53 	);
     54 };
     55 
     56 function UserProfileForm({ data: profile }) {
     57 	/*
     58 		User profile update form keys
     59 		- bool bot
     60 		- bool locked
     61 		- string display_name
     62 		- string note
     63 		- file avatar
     64 		- file header
     65 		- bool enable_rss
     66 		- string custom_css (if enabled)
     67 	*/
     68 
     69 	const { data: instance } = query.useInstanceQuery();
     70 	const instanceConfig = React.useMemo(() => {
     71 		return {
     72 			allowCustomCSS: instance?.configuration?.accounts?.allow_custom_css === true,
     73 			maxPinnedFields: instance?.configuration?.accounts?.max_profile_fields ?? 6
     74 		};
     75 	}, [instance]);
     76 
     77 	const form = {
     78 		avatar: useFileInput("avatar", { withPreview: true }),
     79 		header: useFileInput("header", { withPreview: true }),
     80 		displayName: useTextInput("display_name", { source: profile }),
     81 		note: useTextInput("note", { source: profile, valueSelector: (p) => p.source?.note }),
     82 		customCSS: useTextInput("custom_css", { source: profile }),
     83 		bot: useBoolInput("bot", { source: profile }),
     84 		locked: useBoolInput("locked", { source: profile }),
     85 		enableRSS: useBoolInput("enable_rss", { source: profile }),
     86 		fields: useFieldArrayInput("fields_attributes", {
     87 			defaultValue: profile?.source?.fields,
     88 			length: instanceConfig.maxPinnedFields
     89 		}),
     90 	};
     91 
     92 	const [submitForm, result] = useFormSubmit(form, query.useUpdateCredentialsMutation(), {
     93 		onFinish: () => {
     94 			form.avatar.reset();
     95 			form.header.reset();
     96 		}
     97 	});
     98 
     99 	return (
    100 		<form className="user-profile" onSubmit={submitForm}>
    101 			<h1>Profile</h1>
    102 			<div className="overview">
    103 				<FakeProfile
    104 					avatar={form.avatar.previewValue ?? profile.avatar}
    105 					header={form.header.previewValue ?? profile.header}
    106 					display_name={form.displayName.value ?? profile.username}
    107 					username={profile.username}
    108 					role={profile.role}
    109 				/>
    110 				<div className="files">
    111 					<div>
    112 						<h3>Header</h3>
    113 						<FileInput
    114 							field={form.header}
    115 							accept="image/*"
    116 						/>
    117 					</div>
    118 					<div>
    119 						<h3>Avatar</h3>
    120 						<FileInput
    121 							field={form.avatar}
    122 							accept="image/*"
    123 						/>
    124 					</div>
    125 				</div>
    126 			</div>
    127 			<TextInput
    128 				field={form.displayName}
    129 				label="Name"
    130 				placeholder="A GoToSocial user"
    131 			/>
    132 			<TextArea
    133 				field={form.note}
    134 				label="Bio"
    135 				placeholder="Just trying out GoToSocial, my pronouns are they/them and I like sloths."
    136 				rows={8}
    137 			/>
    138 			<Checkbox
    139 				field={form.locked}
    140 				label="Manually approve follow requests"
    141 			/>
    142 			<Checkbox
    143 				field={form.enableRSS}
    144 				label="Enable RSS feed of Public posts"
    145 			/>
    146 			<b>Profile fields</b>
    147 			<ProfileFields
    148 				field={form.fields}
    149 			/>
    150 			{!instanceConfig.allowCustomCSS ? null :
    151 				<TextArea
    152 					field={form.customCSS}
    153 					label="Custom CSS"
    154 					className="monospace"
    155 					rows={8}
    156 				>
    157 					<a href="https://docs.gotosocial.org/en/latest/user_guide/custom_css" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about custom profile CSS (opens in a new tab)</a>
    158 				</TextArea>
    159 			}
    160 			<MutationButton label="Save profile info" result={result} />
    161 		</form>
    162 	);
    163 }
    164 
    165 function ProfileFields({ field: formField }) {
    166 	return (
    167 		<div className="fields">
    168 			<FormContext.Provider value={formField.ctx}>
    169 				{formField.value.map((data, i) => (
    170 					<Field
    171 						key={i}
    172 						index={i}
    173 						data={data}
    174 					/>
    175 				))}
    176 			</FormContext.Provider>
    177 		</div>
    178 	);
    179 }
    180 
    181 function Field({ index, data }) {
    182 	const form = useWithFormContext(index, {
    183 		name: useTextInput("name", { defaultValue: data.name }),
    184 		value: useTextInput("value", { defaultValue: data.value })
    185 	});
    186 
    187 	return (
    188 		<div className="entry">
    189 			<TextInput
    190 				field={form.name}
    191 				placeholder="Name"
    192 			/>
    193 			<TextInput
    194 				field={form.value}
    195 				placeholder="Value"
    196 			/>
    197 		</div>
    198 	);
    199 }