mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-10-31 22:40:01 +00:00
implement old/user/profile.js
This commit is contained in:
parent
10faf67b51
commit
43f2e988b9
11 changed files with 269 additions and 253 deletions
|
@ -24,6 +24,7 @@
|
|||
"factor-bundle": "^2.5.0",
|
||||
"from2-string": "^1.1.0",
|
||||
"icssify": "^2.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"js-file-download": "^0.4.12",
|
||||
"modern-normalize": "^1.1.0",
|
||||
"photoswipe": "^5.3.0",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"use strict";
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const { isPlainObject } = require("is-plain-object");
|
||||
|
||||
const { APIError } = require("../errors");
|
||||
const { setInstanceInfo } = require("../../redux/reducers/instances").actions;
|
||||
|
@ -47,7 +48,13 @@ function apiCall(method, route, payload, type="json") {
|
|||
} else if (type == "form") {
|
||||
const formData = new FormData();
|
||||
Object.entries(payload).forEach(([key, val]) => {
|
||||
formData.set(key, val);
|
||||
if (isPlainObject(val)) {
|
||||
Object.entries(val).forEach(([key2, val2]) => {
|
||||
formData.set(`${key}[${key2}]`, val2);
|
||||
});
|
||||
} else {
|
||||
formData.set(key, val);
|
||||
}
|
||||
});
|
||||
body = formData;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,38 @@ const d = require("dotty");
|
|||
const user = require("../../redux/reducers/user").actions;
|
||||
|
||||
module.exports = function ({ apiCall }) {
|
||||
function updateCredentials(selector, {formKeys=[], renamedKeys=[], fileKeys=[]}) {
|
||||
return function (dispatch, getState) {
|
||||
return Promise.try(() => {
|
||||
const state = selector(getState());
|
||||
|
||||
const update = {};
|
||||
|
||||
formKeys.forEach((key) => {
|
||||
d.put(update, key, d.get(state, key));
|
||||
});
|
||||
|
||||
renamedKeys.forEach(([sendKey, intKey]) => {
|
||||
d.put(update, sendKey, d.get(state, intKey));
|
||||
});
|
||||
|
||||
fileKeys.forEach((key) => {
|
||||
let file = d.get(state, `${key}File`);
|
||||
if (file != undefined) {
|
||||
d.put(update, key, file);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(update);
|
||||
|
||||
return dispatch(apiCall("PATCH", "/api/v1/accounts/update_credentials", update, "form"));
|
||||
}).then((account) => {
|
||||
console.log(account);
|
||||
return dispatch(user.setAccount(account));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
fetchAccount: function fetchAccount() {
|
||||
return function (dispatch, _getState) {
|
||||
|
@ -34,39 +66,17 @@ module.exports = function ({ apiCall }) {
|
|||
});
|
||||
};
|
||||
},
|
||||
updateAccount: function updateAccount() {
|
||||
const formKeys = ["display_name", "locked"];
|
||||
updateProfile: function updateProfile() {
|
||||
const formKeys = ["display_name", "locked", "source"];
|
||||
const renamedKeys = [["note", "source.note"]];
|
||||
const fileKeys = ["header", "avatar"];
|
||||
|
||||
return function (dispatch, getState) {
|
||||
return Promise.try(() => {
|
||||
const { account } = getState().user;
|
||||
return updateCredentials((state) => state.user.profile, {formKeys, renamedKeys, fileKeys});
|
||||
},
|
||||
updateSettings: function updateProfile() {
|
||||
const formKeys = ["source"];
|
||||
|
||||
const update = {};
|
||||
|
||||
formKeys.forEach((key) => {
|
||||
d.put(update, key, d.get(account, key));
|
||||
update[key] = account[key];
|
||||
});
|
||||
|
||||
renamedKeys.forEach(([sendKey, intKey]) => {
|
||||
d.put(update, sendKey, d.get(account, intKey));
|
||||
});
|
||||
|
||||
fileKeys.forEach((key) => {
|
||||
let file = d.get(account, `${key}File`);
|
||||
if (file != undefined) {
|
||||
d.put(update, key, file);
|
||||
}
|
||||
});
|
||||
|
||||
return dispatch(apiCall("PATCH", "/api/v1/accounts/update_credentials", update, "form"));
|
||||
}).then((account) => {
|
||||
console.log(account);
|
||||
return dispatch(user.setAccount(account));
|
||||
});
|
||||
};
|
||||
return updateCredentials((state) => state.user.settings, {formKeys});
|
||||
}
|
||||
};
|
||||
};
|
50
web/source/settings-panel/lib/form-fields.js
Normal file
50
web/source/settings-panel/lib/form-fields.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
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 d = require("dotty");
|
||||
|
||||
module.exports = function(dispatch, setter, obj) {
|
||||
return {
|
||||
onTextChange: function (key) {
|
||||
return function (e) {
|
||||
dispatch(setter([key, e.target.value]));
|
||||
};
|
||||
},
|
||||
|
||||
onCheckChange: function (key) {
|
||||
return function (e) {
|
||||
dispatch(setter([key, e.target.checked]));
|
||||
};
|
||||
},
|
||||
|
||||
onFileChange: function (key) {
|
||||
return function (e) {
|
||||
let old = d.get(obj, key);
|
||||
if (old != undefined) {
|
||||
URL.revokeObjectURL(old); // no error revoking a non-Object URL as provided by instance
|
||||
}
|
||||
let file = e.target.files[0];
|
||||
let objectURL = URL.createObjectURL(file);
|
||||
dispatch(setter([key, objectURL]));
|
||||
dispatch(setter([`${key}File`, file]));
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
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 Promise = require("bluebird");
|
||||
|
||||
const Languages = require("./languages");
|
||||
const Submit = require("../../lib/submit");
|
||||
|
||||
module.exports = function Posts({oauth, account}) {
|
||||
const [errorMsg, setError] = React.useState("");
|
||||
const [statusMsg, setStatus] = React.useState("");
|
||||
|
||||
const [language, setLanguage] = React.useState("");
|
||||
const [privacy, setPrivacy] = React.useState("");
|
||||
const [format, setFormat] = React.useState("");
|
||||
const [sensitive, setSensitive] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (account.source) {
|
||||
setLanguage(account.source.language.toUpperCase());
|
||||
setPrivacy(account.source.privacy);
|
||||
setSensitive(account.source.sensitive ? account.source.sensitive : false);
|
||||
setFormat(account.source.status_format ? account.source.status_format : "plain");
|
||||
}
|
||||
|
||||
}, [account, setSensitive, setPrivacy]);
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
setStatus("PATCHing");
|
||||
setError("");
|
||||
return Promise.try(() => {
|
||||
let formDataInfo = new FormData();
|
||||
|
||||
formDataInfo.set("source[language]", language);
|
||||
formDataInfo.set("source[privacy]", privacy);
|
||||
formDataInfo.set("source[sensitive]", sensitive);
|
||||
formDataInfo.set("source[status_format]", format);
|
||||
|
||||
return oauth.apiRequest("/api/v1/accounts/update_credentials", "PATCH", formDataInfo, "form");
|
||||
}).then((json) => {
|
||||
setStatus("Saved!");
|
||||
setLanguage(json.source.language.toUpperCase());
|
||||
setPrivacy(json.source.privacy);
|
||||
setSensitive(json.source.sensitive ? json.source.sensitive : false);
|
||||
setFormat(json.source.status_format ? json.source.status_format : "plain");
|
||||
}).catch((e) => {
|
||||
setError(e.message);
|
||||
setStatus("");
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="posts">
|
||||
<h1>Post Settings</h1>
|
||||
<form>
|
||||
<div className="labelselect">
|
||||
<label htmlFor="language">Default post language</label>
|
||||
<select id="language" autoComplete="language" value={language} onChange={(e) => setLanguage(e.target.value)}>
|
||||
<Languages />
|
||||
</select>
|
||||
</div>
|
||||
<div className="labelselect">
|
||||
<label htmlFor="privacy">Default post privacy</label>
|
||||
<select id="privacy" value={privacy} onChange={(e) => setPrivacy(e.target.value)}>
|
||||
<option value="private">Private / followers-only)</option>
|
||||
<option value="unlisted">Unlisted</option>
|
||||
<option value="public">Public</option>
|
||||
</select>
|
||||
<a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#privacy-settings" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post privacy settings (opens in a new tab)</a>
|
||||
</div>
|
||||
<div className="labelselect">
|
||||
<label htmlFor="format">Default post format</label>
|
||||
<select id="format" value={format} onChange={(e) => setFormat(e.target.value)}>
|
||||
<option value="plain">Plain (default)</option>
|
||||
<option value="markdown">Markdown</option>
|
||||
</select>
|
||||
<a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#input-types" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post format settings (opens in a new tab)</a>
|
||||
</div>
|
||||
<div className="labelcheckbox">
|
||||
<label htmlFor="sensitive">Mark my posts as sensitive by default</label>
|
||||
<input id="sensitive" type="checkbox" checked={sensitive} onChange={(e) => setSensitive(e.target.checked)}/>
|
||||
</div>
|
||||
<Submit onClick={submit} label="Save post settings" errorMsg={errorMsg} statusMsg={statusMsg}/>
|
||||
</form>
|
||||
</section>
|
||||
);
|
||||
};
|
|
@ -24,13 +24,22 @@ const d = require("dotty");
|
|||
module.exports = createSlice({
|
||||
name: "user",
|
||||
initialState: {
|
||||
profile: {},
|
||||
settings: {}
|
||||
},
|
||||
reducers: {
|
||||
setAccount: (state, {payload}) => {
|
||||
state.account = payload;
|
||||
state.profile = payload;
|
||||
// /user/settings only needs a copy of the 'source' obj
|
||||
state.settings = {
|
||||
source: payload.source
|
||||
};
|
||||
},
|
||||
setAccountVal: (state, {payload: [key, val]}) => {
|
||||
d.put(state.account, key, val);
|
||||
setProfileVal: (state, {payload: [key, val]}) => {
|
||||
d.put(state.profile, key, val);
|
||||
},
|
||||
setSettingsVal: (state, {payload: [key, val]}) => {
|
||||
d.put(state.settings, key, val);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -177,48 +177,11 @@ input, select, textarea {
|
|||
) !important;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
section.with-sidebar > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
.overview {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
.basic {
|
||||
margin-top: -4.5rem;
|
||||
|
||||
.avatar {
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
}
|
||||
|
||||
.displayname {
|
||||
font-size: 1.3rem;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
margin-top: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.files {
|
||||
padding: 1rem;
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div:first-child {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
span {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
line-height: 1.5rem;
|
||||
|
@ -236,6 +199,7 @@ input, select, textarea {
|
|||
input:invalid {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 8rem;
|
||||
|
@ -245,29 +209,6 @@ input, select, textarea {
|
|||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
img {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: $boxshadow_border;
|
||||
box-shadow: $box-shadow;
|
||||
object-fit: cover;
|
||||
border-radius: 0.2rem;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.avatarpreview {
|
||||
height: 8.5rem;
|
||||
width: 8.5rem;
|
||||
}
|
||||
|
||||
.headerpreview {
|
||||
width: 100%;
|
||||
aspect-ratio: 3 / 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.moreinfolink {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
@ -307,3 +248,60 @@ input, select, textarea {
|
|||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
.overview {
|
||||
display: grid;
|
||||
grid-template-columns: 70% 30%;
|
||||
|
||||
.basic {
|
||||
margin-top: -4.5rem;
|
||||
|
||||
.avatar {
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
}
|
||||
|
||||
.displayname {
|
||||
font-size: 1.3rem;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
margin-top: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.files {
|
||||
margin: 1rem;
|
||||
margin-right: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
div.picker {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding: 0.3rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
div:first-child {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
span {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,103 +21,82 @@
|
|||
const Promise = require("bluebird");
|
||||
const React = require("react");
|
||||
const Redux = require("react-redux");
|
||||
const d = require("dotty");
|
||||
|
||||
const Submit = require("../components/submit");
|
||||
|
||||
const api = require("../lib/api");
|
||||
const formFields = require("../lib/form-fields");
|
||||
const user = require("../redux/reducers/user").actions;
|
||||
|
||||
module.exports = function UserProfile() {
|
||||
const dispatch = Redux.useDispatch();
|
||||
const account = Redux.useSelector(state => state.user.account);
|
||||
const account = Redux.useSelector(state => state.user.profile);
|
||||
|
||||
const { onTextChange, onCheckChange, onFileChange } = formFields(dispatch, user.setProfileVal, account);
|
||||
|
||||
const [errorMsg, setError] = React.useState("");
|
||||
const [statusMsg, setStatus] = React.useState("");
|
||||
|
||||
function onTextChange(key) {
|
||||
return function (e) {
|
||||
dispatch(user.setAccountVal([key, e.target.value]));
|
||||
};
|
||||
}
|
||||
|
||||
function onCheckChange(key) {
|
||||
return function (e) {
|
||||
dispatch(user.setAccountVal([key, e.target.checked]));
|
||||
};
|
||||
}
|
||||
|
||||
function onFileChange(key) {
|
||||
return function (e) {
|
||||
let old = d.get(account, key);
|
||||
if (old != undefined) {
|
||||
URL.revokeObjectURL(old); // no error revoking a non-Object URL as provided by instance
|
||||
}
|
||||
let file = e.target.files[0];
|
||||
let objectURL = URL.createObjectURL(file);
|
||||
dispatch(user.setAccountVal([key, objectURL]));
|
||||
dispatch(user.setAccountVal([`${key}File`, file]));
|
||||
};
|
||||
}
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
function submit() {
|
||||
setStatus("PATCHing");
|
||||
setError("");
|
||||
return Promise.try(() => {
|
||||
return dispatch(api.user.updateAccount());
|
||||
return dispatch(api.user.updateProfile());
|
||||
}).then(() => {
|
||||
setStatus("Saved!");
|
||||
}).catch((e) => {
|
||||
setError(e.message);
|
||||
setStatus("");
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="user-profile">
|
||||
<h1>Profile</h1>
|
||||
<div className="overview">
|
||||
<div className="profile">
|
||||
<div className="headerimage">
|
||||
<img className="headerpreview" src={account.header} alt={account.header ? `header image for ${account.username}` : "None set"}/>
|
||||
</div>
|
||||
<div className="basic">
|
||||
<div id="profile-basic-filler2"></div>
|
||||
<span className="avatar"><img className="avatarpreview" src={account.avatar} alt={account.avatar ? `avatar image for ${account.username}` : "None set"}/></span>
|
||||
<div className="displayname">{account.display_name.trim().length > 0 ? account.display_name : account.username}</div>
|
||||
<div className="username"><span>@{account.username}</span></div>
|
||||
</div>
|
||||
<div className="headerimage">
|
||||
<img className="headerpreview" src={account.header} alt={account.header ? `header image for ${account.username}` : "None set"} />
|
||||
</div>
|
||||
<div className="basic">
|
||||
<div id="profile-basic-filler2"></div>
|
||||
<span className="avatar"><img className="avatarpreview" src={account.avatar} alt={account.avatar ? `avatar image for ${account.username}` : "None set"} /></span>
|
||||
<div className="displayname">{account.display_name.trim().length > 0 ? account.display_name : account.username}</div>
|
||||
<div className="username"><span>@{account.username}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="files">
|
||||
<div>
|
||||
<h3>Header</h3>
|
||||
<label htmlFor="header" className="file-input button">Browse…</label>
|
||||
<span>{account.headerFile ? account.headerFile.name : "no file selected"}</span>
|
||||
<input className="hidden" id="header" type="file" accept="image/*" onChange={onFileChange("header")}/>
|
||||
<div className="picker">
|
||||
<label htmlFor="header" className="file-input button">Browse</label>
|
||||
<span>{account.headerFile ? account.headerFile.name : "no file selected"}</span>
|
||||
</div>
|
||||
<input className="hidden" id="header" type="file" accept="image/*" onChange={onFileChange("header")} />
|
||||
</div>
|
||||
<div>
|
||||
<h3>Avatar</h3>
|
||||
<label htmlFor="avatar" className="file-input button">Browse…</label>
|
||||
<span>{account.avatarFile ? account.avatarFile.name : "no file selected"}</span>
|
||||
<input className="hidden" id="avatar" type="file" accept="image/*" onChange={onFileChange("avatar")}/>
|
||||
<div className="picker">
|
||||
<label htmlFor="avatar" className="file-input button">Browse</label>
|
||||
<span>{account.avatarFile ? account.avatarFile.name : "no file selected"}</span>
|
||||
</div>
|
||||
<input className="hidden" id="avatar" type="file" accept="image/*" onChange={onFileChange("avatar")} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="labelinput">
|
||||
<label htmlFor="displayname">Name</label>
|
||||
<input id="displayname" type="text" value={account.display_name} onChange={onTextChange("display_name")} placeholder="A GoToSocial user"/>
|
||||
<input id="displayname" type="text" value={account.display_name} onChange={onTextChange("display_name")} placeholder="A GoToSocial user" />
|
||||
</div>
|
||||
<div className="labelinput">
|
||||
<label htmlFor="bio">Bio</label>
|
||||
<textarea id="bio" value={account.source.note} onChange={onTextChange("source.note")} placeholder="Just trying out GoToSocial, my pronouns are they/them and I like sloths."/>
|
||||
<textarea id="bio" value={account.source.note} onChange={onTextChange("source.note")} placeholder="Just trying out GoToSocial, my pronouns are they/them and I like sloths." />
|
||||
</div>
|
||||
<div className="labelcheckbox">
|
||||
<label htmlFor="locked">Manually approve follow requests?</label>
|
||||
<input id="locked" type="checkbox" checked={account.locked} onChange={onCheckChange("locked")}/>
|
||||
<input id="locked" type="checkbox" checked={account.locked} onChange={onCheckChange("locked")} />
|
||||
</div>
|
||||
<Submit onClick={submit} label="Save profile info" errorMsg={errorMsg} statusMsg={statusMsg}/>
|
||||
<Submit onClick={submit} label="Save profile info" errorMsg={errorMsg} statusMsg={statusMsg} />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -18,6 +18,70 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const React = require("react");
|
||||
const Redux = require("react-redux");
|
||||
|
||||
const api = require("../lib/api");
|
||||
const formFields = require("../lib/form-fields");
|
||||
const user = require("../redux/reducers/user").actions;
|
||||
|
||||
const Languages = require("../components/languages");
|
||||
const Submit = require("../components/submit");
|
||||
|
||||
module.exports = function UserSettings() {
|
||||
return "user settings";
|
||||
const dispatch = Redux.useDispatch();
|
||||
const account = Redux.useSelector(state => state.user.settings);
|
||||
|
||||
const { onTextChange, onCheckChange } = formFields(dispatch, user.setSettingsVal, account);
|
||||
|
||||
const [errorMsg, setError] = React.useState("");
|
||||
const [statusMsg, setStatus] = React.useState("");
|
||||
|
||||
function submit() {
|
||||
setStatus("PATCHing");
|
||||
setError("");
|
||||
return Promise.try(() => {
|
||||
return dispatch(api.user.updateSettings());
|
||||
}).then(() => {
|
||||
setStatus("Saved!");
|
||||
}).catch((e) => {
|
||||
setError(e.message);
|
||||
setStatus("");
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="user-settings">
|
||||
<h1>Post settings</h1>
|
||||
<div className="labelselect">
|
||||
<label htmlFor="language">Default post language</label>
|
||||
<select id="language" autoComplete="language" value={account.source.language.toUpperCase()} onChange={onTextChange("source.language")}>
|
||||
<Languages />
|
||||
</select>
|
||||
</div>
|
||||
<div className="labelselect">
|
||||
<label htmlFor="privacy">Default post privacy</label>
|
||||
<select id="privacy" value={account.source.privacy} onChange={onTextChange("source.privacy")}>
|
||||
<option value="private">Private / followers-only)</option>
|
||||
<option value="unlisted">Unlisted</option>
|
||||
<option value="public">Public</option>
|
||||
</select>
|
||||
<a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#privacy-settings" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post privacy settings (opens in a new tab)</a>
|
||||
</div>
|
||||
<div className="labelselect">
|
||||
<label htmlFor="format">Default post format</label>
|
||||
<select id="format" value={account.source.format} onChange={onTextChange("source.format")}>
|
||||
<option value="plain">Plain (default)</option>
|
||||
<option value="markdown">Markdown</option>
|
||||
</select>
|
||||
<a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#input-types" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post format settings (opens in a new tab)</a>
|
||||
</div>
|
||||
<div className="labelcheckbox">
|
||||
<label htmlFor="sensitive">Mark my posts as sensitive by default</label>
|
||||
<input id="sensitive" type="checkbox" checked={account.source.sensitive} onChange={onCheckChange("source.sensitive")}/>
|
||||
</div>
|
||||
<Submit onClick={submit} label="Save post settings" errorMsg={errorMsg} statusMsg={statusMsg}/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -3670,6 +3670,11 @@ is-plain-obj@^2.0.0, is-plain-obj@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
||||
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
|
||||
|
||||
is-plain-object@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
||||
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
|
||||
|
||||
is-regex@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
|
||||
|
|
Loading…
Reference in a new issue