[Frontend] Settings for profile fields (#1885)
* get max emoji size from instance settings * expose (hardcoded) max amount of profile fields in instance api * basic profile field setting * fix profile field hook structure for updates * *twirls mustache* fix ze tests --------- Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
This commit is contained in:
parent
4990099fde
commit
8fb5a7e7f8
|
@ -118,7 +118,8 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"allow_custom_css": true,
|
"allow_custom_css": true,
|
||||||
"max_featured_tags": 10
|
"max_featured_tags": 10,
|
||||||
|
"max_profile_fields": 6
|
||||||
},
|
},
|
||||||
"emojis": {
|
"emojis": {
|
||||||
"emoji_size_limit": 51200
|
"emoji_size_limit": 51200
|
||||||
|
@ -221,7 +222,8 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"allow_custom_css": true,
|
"allow_custom_css": true,
|
||||||
"max_featured_tags": 10
|
"max_featured_tags": 10,
|
||||||
|
"max_profile_fields": 6
|
||||||
},
|
},
|
||||||
"emojis": {
|
"emojis": {
|
||||||
"emoji_size_limit": 51200
|
"emoji_size_limit": 51200
|
||||||
|
@ -324,7 +326,8 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"allow_custom_css": true,
|
"allow_custom_css": true,
|
||||||
"max_featured_tags": 10
|
"max_featured_tags": 10,
|
||||||
|
"max_profile_fields": 6
|
||||||
},
|
},
|
||||||
"emojis": {
|
"emojis": {
|
||||||
"emoji_size_limit": 51200
|
"emoji_size_limit": 51200
|
||||||
|
@ -478,7 +481,8 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() {
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"allow_custom_css": true,
|
"allow_custom_css": true,
|
||||||
"max_featured_tags": 10
|
"max_featured_tags": 10,
|
||||||
|
"max_profile_fields": 6
|
||||||
},
|
},
|
||||||
"emojis": {
|
"emojis": {
|
||||||
"emoji_size_limit": 51200
|
"emoji_size_limit": 51200
|
||||||
|
@ -603,7 +607,8 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() {
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"allow_custom_css": true,
|
"allow_custom_css": true,
|
||||||
"max_featured_tags": 10
|
"max_featured_tags": 10,
|
||||||
|
"max_profile_fields": 6
|
||||||
},
|
},
|
||||||
"emojis": {
|
"emojis": {
|
||||||
"emoji_size_limit": 51200
|
"emoji_size_limit": 51200
|
||||||
|
@ -743,7 +748,8 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() {
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"allow_custom_css": true,
|
"allow_custom_css": true,
|
||||||
"max_featured_tags": 10
|
"max_featured_tags": 10,
|
||||||
|
"max_profile_fields": 6
|
||||||
},
|
},
|
||||||
"emojis": {
|
"emojis": {
|
||||||
"emoji_size_limit": 51200
|
"emoji_size_limit": 51200
|
||||||
|
|
|
@ -54,6 +54,9 @@ type InstanceConfigurationAccounts struct {
|
||||||
// The maximum number of featured tags allowed for each account.
|
// The maximum number of featured tags allowed for each account.
|
||||||
// Currently not implemented, so this is hardcoded to 10.
|
// Currently not implemented, so this is hardcoded to 10.
|
||||||
MaxFeaturedTags int `json:"max_featured_tags"`
|
MaxFeaturedTags int `json:"max_featured_tags"`
|
||||||
|
// The maximum number of profile fields allowed for each account.
|
||||||
|
// Currently not configurable, so this is hardcoded to 6. (https://github.com/superseriousbusiness/gotosocial/issues/1876)
|
||||||
|
MaxProfileFields int `json:"max_profile_fields"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstanceConfigurationStatuses models instance status config parameters.
|
// InstanceConfigurationStatuses models instance status config parameters.
|
||||||
|
|
|
@ -43,6 +43,7 @@ const (
|
||||||
instancePollsMinExpiration = 300 // seconds
|
instancePollsMinExpiration = 300 // seconds
|
||||||
instancePollsMaxExpiration = 2629746 // seconds
|
instancePollsMaxExpiration = 2629746 // seconds
|
||||||
instanceAccountsMaxFeaturedTags = 10
|
instanceAccountsMaxFeaturedTags = 10
|
||||||
|
instanceAccountsMaxProfileFields = 6 // FIXME: https://github.com/superseriousbusiness/gotosocial/issues/1876
|
||||||
instanceSourceURL = "https://github.com/superseriousbusiness/gotosocial"
|
instanceSourceURL = "https://github.com/superseriousbusiness/gotosocial"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -756,6 +757,7 @@ func (c *converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins
|
||||||
instance.Configuration.Polls.MaxExpiration = instancePollsMaxExpiration
|
instance.Configuration.Polls.MaxExpiration = instancePollsMaxExpiration
|
||||||
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
||||||
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
||||||
|
instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields
|
||||||
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
|
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
|
||||||
|
|
||||||
// URLs
|
// URLs
|
||||||
|
@ -882,6 +884,7 @@ func (c *converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins
|
||||||
instance.Configuration.Polls.MaxExpiration = instancePollsMaxExpiration
|
instance.Configuration.Polls.MaxExpiration = instancePollsMaxExpiration
|
||||||
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
||||||
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
||||||
|
instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields
|
||||||
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
|
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
|
||||||
|
|
||||||
// registrations
|
// registrations
|
||||||
|
|
|
@ -647,7 +647,8 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() {
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"allow_custom_css": true,
|
"allow_custom_css": true,
|
||||||
"max_featured_tags": 10
|
"max_featured_tags": 10,
|
||||||
|
"max_profile_fields": 6
|
||||||
},
|
},
|
||||||
"emojis": {
|
"emojis": {
|
||||||
"emoji_size_limit": 51200
|
"emoji_size_limit": 51200
|
||||||
|
@ -730,7 +731,8 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() {
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"allow_custom_css": true,
|
"allow_custom_css": true,
|
||||||
"max_featured_tags": 10
|
"max_featured_tags": 10,
|
||||||
|
"max_profile_fields": 6
|
||||||
},
|
},
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"max_characters": 5000,
|
"max_characters": 5000,
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"match-sorter": "^6.3.1",
|
"match-sorter": "^6.3.1",
|
||||||
"modern-normalize": "^1.1.0",
|
"modern-normalize": "^1.1.0",
|
||||||
"nanoid": "^4.0.0",
|
"nanoid": "^4.0.0",
|
||||||
|
"object-to-formdata": "^4.4.2",
|
||||||
"papaparse": "^5.3.2",
|
"papaparse": "^5.3.2",
|
||||||
"photoswipe": "^5.3.3",
|
"photoswipe": "^5.3.3",
|
||||||
"photoswipe-dynamic-caption-plugin": "^1.2.7",
|
"photoswipe-dynamic-caption-plugin": "^1.2.7",
|
||||||
|
|
|
@ -42,9 +42,14 @@ const MutationButton = require("../../../components/form/mutation-button");
|
||||||
module.exports = function NewEmojiForm() {
|
module.exports = function NewEmojiForm() {
|
||||||
const shortcode = useShortcode();
|
const shortcode = useShortcode();
|
||||||
|
|
||||||
|
const { data: instance } = query.useInstanceQuery();
|
||||||
|
const emojiMaxSize = React.useMemo(() => {
|
||||||
|
return instance?.configuration?.emojis?.emoji_size_limit ?? 50 * 1024;
|
||||||
|
}, [instance]);
|
||||||
|
|
||||||
const image = useFileInput("image", {
|
const image = useFileInput("image", {
|
||||||
withPreview: true,
|
withPreview: true,
|
||||||
maxSize: 50 * 1024 // TODO: get from instance api?
|
maxSize: emojiMaxSize
|
||||||
});
|
});
|
||||||
|
|
||||||
const category = useComboBoxInput("category");
|
const category = useComboBoxInput("category");
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const React = require("react");
|
||||||
|
|
||||||
|
const FormContext = React.createContext({});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
FormContext,
|
||||||
|
useWithFormContext(index, form) {
|
||||||
|
const formContainer = React.useContext(FormContext);
|
||||||
|
formContainer[index] = form;
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const React = require("react");
|
||||||
|
|
||||||
|
const getFormMutations = require("./get-form-mutations");
|
||||||
|
|
||||||
|
function parseFields(entries, length) {
|
||||||
|
const fields = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (entries[i] != undefined) {
|
||||||
|
fields[i] = Object.assign({}, entries[i]);
|
||||||
|
} else {
|
||||||
|
fields[i] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function useArrayInput({ name, _Name }, { initialValue, length = 0 }) {
|
||||||
|
const fields = React.useRef({});
|
||||||
|
|
||||||
|
const value = React.useMemo(() => parseFields(initialValue, length), [initialValue, length]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
ctx: fields.current,
|
||||||
|
maxLength: length,
|
||||||
|
selectedValues() {
|
||||||
|
// if any form field changed, we need to re-send everything
|
||||||
|
const hasUpdate = Object.values(fields.current).some((fieldSet) => {
|
||||||
|
const { updatedFields } = getFormMutations(fieldSet, { changedOnly: true });
|
||||||
|
return updatedFields.length > 0;
|
||||||
|
});
|
||||||
|
if (hasUpdate) {
|
||||||
|
return Object.values(fields.current).map((fieldSet) => {
|
||||||
|
return getFormMutations(fieldSet, { changedOnly: false }).mutationData;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const syncpipe = require("syncpipe");
|
||||||
|
|
||||||
|
module.exports = function getFormMutations(form, { changedOnly }) {
|
||||||
|
let updatedFields = [];
|
||||||
|
return {
|
||||||
|
updatedFields,
|
||||||
|
mutationData: syncpipe(form, [
|
||||||
|
(_) => Object.values(_),
|
||||||
|
(_) => _.map((field) => {
|
||||||
|
if (field.selectedValues != undefined) {
|
||||||
|
let selected = field.selectedValues();
|
||||||
|
if (!changedOnly || selected.length > 0) {
|
||||||
|
updatedFields.push(field);
|
||||||
|
return [field.name, selected];
|
||||||
|
}
|
||||||
|
} else if (!changedOnly || field.hasChanged()) {
|
||||||
|
updatedFields.push(field);
|
||||||
|
return [field.name, field.value];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
(_) => _.filter((value) => value != null),
|
||||||
|
(_) => Object.fromEntries(_)
|
||||||
|
])
|
||||||
|
};
|
||||||
|
};
|
|
@ -74,6 +74,7 @@ module.exports = {
|
||||||
useRadioInput: makeHook(require("./radio")),
|
useRadioInput: makeHook(require("./radio")),
|
||||||
useComboBoxInput: makeHook(require("./combo-box")),
|
useComboBoxInput: makeHook(require("./combo-box")),
|
||||||
useCheckListInput: makeHook(require("./check-list")),
|
useCheckListInput: makeHook(require("./check-list")),
|
||||||
|
useFieldArrayInput: makeHook(require("./field-array")),
|
||||||
useValue: function (name, value) {
|
useValue: function (name, value) {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
const Promise = require("bluebird");
|
const Promise = require("bluebird");
|
||||||
const React = require("react");
|
const React = require("react");
|
||||||
const syncpipe = require("syncpipe");
|
const getFormMutations = require("./get-form-mutations");
|
||||||
|
|
||||||
module.exports = function useFormSubmit(form, mutationQuery, { changedOnly = true, onFinish } = {}) {
|
module.exports = function useFormSubmit(form, mutationQuery, { changedOnly = true, onFinish } = {}) {
|
||||||
if (!Array.isArray(mutationQuery)) {
|
if (!Array.isArray(mutationQuery)) {
|
||||||
|
@ -44,25 +44,12 @@ module.exports = function useFormSubmit(form, mutationQuery, { changedOnly = tru
|
||||||
}
|
}
|
||||||
usedAction.current = action;
|
usedAction.current = action;
|
||||||
// transform the field definitions into an object with just their values
|
// transform the field definitions into an object with just their values
|
||||||
let updatedFields = [];
|
|
||||||
const mutationData = syncpipe(form, [
|
const { mutationData, updatedFields } = getFormMutations(form, { changedOnly });
|
||||||
(_) => Object.values(_),
|
|
||||||
(_) => _.map((field) => {
|
if (updatedFields.length == 0) {
|
||||||
if (field.selectedValues != undefined) {
|
return;
|
||||||
let selected = field.selectedValues();
|
}
|
||||||
if (!changedOnly || selected.length > 0) {
|
|
||||||
updatedFields.push(field);
|
|
||||||
return [field.name, selected];
|
|
||||||
}
|
|
||||||
} else if (!changedOnly || field.hasChanged()) {
|
|
||||||
updatedFields.push(field);
|
|
||||||
return [field.name, field.value];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
(_) => _.filter((value) => value != null),
|
|
||||||
(_) => Object.fromEntries(_)
|
|
||||||
]);
|
|
||||||
|
|
||||||
mutationData.action = action;
|
mutationData.action = action;
|
||||||
|
|
||||||
|
|
|
@ -20,23 +20,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { createApi, fetchBaseQuery } = require("@reduxjs/toolkit/query/react");
|
const { createApi, fetchBaseQuery } = require("@reduxjs/toolkit/query/react");
|
||||||
const { isPlainObject } = require("is-plain-object");
|
const { serialize: serializeForm } = require("object-to-formdata");
|
||||||
|
|
||||||
function convertToForm(obj) {
|
|
||||||
const formData = new FormData();
|
|
||||||
Object.entries(obj).forEach(([key, val]) => {
|
|
||||||
if (isPlainObject(val)) {
|
|
||||||
Object.entries(val).forEach(([key2, val2]) => {
|
|
||||||
if (val2 != undefined) {
|
|
||||||
formData.set(`${key}[${key2}]`, val2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (val != undefined) {
|
|
||||||
formData.set(key, val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return formData;
|
|
||||||
}
|
|
||||||
|
|
||||||
function instanceBasedQuery(args, api, extraOptions) {
|
function instanceBasedQuery(args, api, extraOptions) {
|
||||||
const state = api.getState();
|
const state = api.getState();
|
||||||
|
@ -55,7 +39,9 @@ function instanceBasedQuery(args, api, extraOptions) {
|
||||||
|
|
||||||
if (args.asForm) {
|
if (args.asForm) {
|
||||||
delete args.asForm;
|
delete args.asForm;
|
||||||
args.body = convertToForm(args.body);
|
args.body = serializeForm(args.body, {
|
||||||
|
indices: true, // Array indices, for profile fields
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetchBaseQuery({
|
return fetchBaseQuery({
|
||||||
|
|
|
@ -439,6 +439,17 @@ section.with-sidebar > div, section.with-sidebar > form {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fields {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
|
|
|
@ -26,10 +26,12 @@ const query = require("../lib/query");
|
||||||
const {
|
const {
|
||||||
useTextInput,
|
useTextInput,
|
||||||
useFileInput,
|
useFileInput,
|
||||||
useBoolInput
|
useBoolInput,
|
||||||
|
useFieldArrayInput
|
||||||
} = require("../lib/form");
|
} = require("../lib/form");
|
||||||
|
|
||||||
const useFormSubmit = require("../lib/form/submit");
|
const useFormSubmit = require("../lib/form/submit");
|
||||||
|
const { useWithFormContext, FormContext } = require("../lib/form/context");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
TextInput,
|
TextInput,
|
||||||
|
@ -65,8 +67,11 @@ function UserProfileForm({ data: profile }) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { data: instance } = query.useInstanceQuery();
|
const { data: instance } = query.useInstanceQuery();
|
||||||
const allowCustomCSS = React.useMemo(() => {
|
const instanceConfig = React.useMemo(() => {
|
||||||
return instance?.configuration?.accounts?.allow_custom_css === true;
|
return {
|
||||||
|
allowCustomCSS: instance?.configuration?.accounts?.allow_custom_css === true,
|
||||||
|
maxPinnedFields: instance?.configuration?.accounts?.max_profile_fields ?? 6
|
||||||
|
};
|
||||||
}, [instance]);
|
}, [instance]);
|
||||||
|
|
||||||
const form = {
|
const form = {
|
||||||
|
@ -78,9 +83,18 @@ function UserProfileForm({ data: profile }) {
|
||||||
bot: useBoolInput("bot", { source: profile }),
|
bot: useBoolInput("bot", { source: profile }),
|
||||||
locked: useBoolInput("locked", { source: profile }),
|
locked: useBoolInput("locked", { source: profile }),
|
||||||
enableRSS: useBoolInput("enable_rss", { source: profile }),
|
enableRSS: useBoolInput("enable_rss", { source: profile }),
|
||||||
|
fields: useFieldArrayInput("fields_attributes", {
|
||||||
|
defaultValue: profile?.source?.fields,
|
||||||
|
length: instanceConfig.maxPinnedFields
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const [submitForm, result] = useFormSubmit(form, query.useUpdateCredentialsMutation());
|
const [submitForm, result] = useFormSubmit(form, query.useUpdateCredentialsMutation(), {
|
||||||
|
onFinish: () => {
|
||||||
|
form.avatar.reset();
|
||||||
|
form.header.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="user-profile" onSubmit={submitForm}>
|
<form className="user-profile" onSubmit={submitForm}>
|
||||||
|
@ -129,7 +143,11 @@ function UserProfileForm({ data: profile }) {
|
||||||
field={form.enableRSS}
|
field={form.enableRSS}
|
||||||
label="Enable RSS feed of Public posts"
|
label="Enable RSS feed of Public posts"
|
||||||
/>
|
/>
|
||||||
{!allowCustomCSS ? null :
|
<b>Profile fields</b>
|
||||||
|
<ProfileFields
|
||||||
|
field={form.fields}
|
||||||
|
/>
|
||||||
|
{!instanceConfig.allowCustomCSS ? null :
|
||||||
<TextArea
|
<TextArea
|
||||||
field={form.customCSS}
|
field={form.customCSS}
|
||||||
label="Custom CSS"
|
label="Custom CSS"
|
||||||
|
@ -142,4 +160,40 @@ function UserProfileForm({ data: profile }) {
|
||||||
<MutationButton label="Save profile info" result={result} />
|
<MutationButton label="Save profile info" result={result} />
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProfileFields({ field: formField }) {
|
||||||
|
return (
|
||||||
|
<div className="fields">
|
||||||
|
<FormContext.Provider value={formField.ctx}>
|
||||||
|
{formField.value.map((data, i) => (
|
||||||
|
<Field
|
||||||
|
key={i}
|
||||||
|
index={i}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</FormContext.Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Field({ index, data }) {
|
||||||
|
const form = useWithFormContext(index, {
|
||||||
|
name: useTextInput("name", { defaultValue: data.name }),
|
||||||
|
value: useTextInput("value", { defaultValue: data.value })
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="entry">
|
||||||
|
<TextInput
|
||||||
|
field={form.name}
|
||||||
|
placeholder="Name"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
field={form.value}
|
||||||
|
placeholder="Value"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
|
@ -4137,6 +4137,11 @@ object-keys@^1.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
||||||
|
|
||||||
|
object-to-formdata@^4.4.2:
|
||||||
|
version "4.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-to-formdata/-/object-to-formdata-4.4.2.tgz#f89013f90493c58cb5f6ab9f50b7aeec30745ea6"
|
||||||
|
integrity sha512-fu6UDjsqIfFUu/B3GXJ2IFnNAL/YbsC1PPzqDIFXcfkhdYjTD3K4zqhyD/lZ6+KdP9O/64YIPckIOiS5ouXwLA==
|
||||||
|
|
||||||
object.assign@^4.1.3, object.assign@^4.1.4:
|
object.assign@^4.1.3, object.assign@^4.1.4:
|
||||||
version "4.1.4"
|
version "4.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
|
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
|
||||||
|
|
Loading…
Reference in New Issue