mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-01 06:50:00 +00:00
use redux state for profile form
This commit is contained in:
parent
3ce9dfe7d6
commit
2bd18790cf
5 changed files with 71 additions and 45 deletions
|
@ -18,6 +18,7 @@
|
||||||
"browserlist": "^1.0.1",
|
"browserlist": "^1.0.1",
|
||||||
"create-error": "^0.3.1",
|
"create-error": "^0.3.1",
|
||||||
"css-extract": "^2.0.0",
|
"css-extract": "^2.0.0",
|
||||||
|
"dotty": "^0.1.2",
|
||||||
"eslint-plugin-react": "^7.24.0",
|
"eslint-plugin-react": "^7.24.0",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"factor-bundle": "^2.5.0",
|
"factor-bundle": "^2.5.0",
|
||||||
|
|
|
@ -19,10 +19,11 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const Promise = require("bluebird");
|
const Promise = require("bluebird");
|
||||||
|
const d = require("dotty");
|
||||||
|
|
||||||
const user = require("../../redux/reducers/user").actions;
|
const user = require("../../redux/reducers/user").actions;
|
||||||
|
|
||||||
module.exports = function({apiCall}) {
|
module.exports = function ({ apiCall }) {
|
||||||
return {
|
return {
|
||||||
fetchAccount: function fetchAccount() {
|
fetchAccount: function fetchAccount() {
|
||||||
return function (dispatch, _getState) {
|
return function (dispatch, _getState) {
|
||||||
|
@ -33,10 +34,34 @@ module.exports = function({apiCall}) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateAccount: function updateAccount(newAccount) {
|
updateAccount: function updateAccount() {
|
||||||
return function (dispatch, _getSate) {
|
const formKeys = ["display_name", "locked"];
|
||||||
|
const renamedKeys = [["note", "source.note"]];
|
||||||
|
const fileKeys = ["header", "avatar"];
|
||||||
|
|
||||||
|
return function (dispatch, getState) {
|
||||||
return Promise.try(() => {
|
return Promise.try(() => {
|
||||||
return dispatch(apiCall("PATCH", "/api/v1/accounts/update_credentials", newAccount, "form"));
|
const { account } = getState().user;
|
||||||
|
|
||||||
|
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) => {
|
}).then((account) => {
|
||||||
console.log(account);
|
console.log(account);
|
||||||
return dispatch(user.setAccount(account));
|
return dispatch(user.setAccount(account));
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {createSlice} = require("@reduxjs/toolkit");
|
const {createSlice} = require("@reduxjs/toolkit");
|
||||||
|
const d = require("dotty");
|
||||||
|
|
||||||
module.exports = createSlice({
|
module.exports = createSlice({
|
||||||
name: "user",
|
name: "user",
|
||||||
|
@ -27,6 +28,9 @@ module.exports = createSlice({
|
||||||
reducers: {
|
reducers: {
|
||||||
setAccount: (state, {payload}) => {
|
setAccount: (state, {payload}) => {
|
||||||
state.account = payload;
|
state.account = payload;
|
||||||
|
},
|
||||||
|
setAccountVal: (state, {payload: [key, val]}) => {
|
||||||
|
d.put(state.account, key, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -21,10 +21,12 @@
|
||||||
const Promise = require("bluebird");
|
const Promise = require("bluebird");
|
||||||
const React = require("react");
|
const React = require("react");
|
||||||
const Redux = require("react-redux");
|
const Redux = require("react-redux");
|
||||||
|
const d = require("dotty");
|
||||||
|
|
||||||
const Submit = require("../components/submit");
|
const Submit = require("../components/submit");
|
||||||
|
|
||||||
const api = require("../lib/api");
|
const api = require("../lib/api");
|
||||||
|
const user = require("../redux/reducers/user").actions;
|
||||||
|
|
||||||
module.exports = function UserProfile() {
|
module.exports = function UserProfile() {
|
||||||
const dispatch = Redux.useDispatch();
|
const dispatch = Redux.useDispatch();
|
||||||
|
@ -33,29 +35,30 @@ module.exports = function UserProfile() {
|
||||||
const [errorMsg, setError] = React.useState("");
|
const [errorMsg, setError] = React.useState("");
|
||||||
const [statusMsg, setStatus] = React.useState("");
|
const [statusMsg, setStatus] = React.useState("");
|
||||||
|
|
||||||
const [headerFile, setHeaderFile] = React.useState(undefined);
|
function onTextChange(key) {
|
||||||
const [avatarFile, setAvatarFile] = React.useState(undefined);
|
return function (e) {
|
||||||
|
dispatch(user.setAccountVal([key, e.target.value]));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const [displayName, setDisplayName] = React.useState("");
|
function onCheckChange(key) {
|
||||||
const [bio, setBio] = React.useState("");
|
return function (e) {
|
||||||
const [locked, setLocked] = React.useState(false);
|
dispatch(user.setAccountVal([key, e.target.checked]));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
function onFileChange(key) {
|
||||||
|
return function (e) {
|
||||||
setDisplayName(account.display_name);
|
let old = d.get(account, key);
|
||||||
setBio(account.source ? account.source.note : "");
|
if (old != undefined) {
|
||||||
setLocked(account.locked);
|
URL.revokeObjectURL(old); // no error revoking a non-Object URL as provided by instance
|
||||||
}, []);
|
}
|
||||||
|
let file = e.target.files[0];
|
||||||
const headerOnChange = (e) => {
|
let objectURL = URL.createObjectURL(file);
|
||||||
setHeaderFile(e.target.files[0]);
|
dispatch(user.setAccountVal([key, objectURL]));
|
||||||
// setHeaderSrc(URL.createObjectURL(e.target.files[0]));
|
dispatch(user.setAccountVal([`${key}File`, file]));
|
||||||
};
|
};
|
||||||
|
}
|
||||||
const avatarOnChange = (e) => {
|
|
||||||
setAvatarFile(e.target.files[0]);
|
|
||||||
// setAvatarSrc(URL.createObjectURL(e.target.files[0]));
|
|
||||||
};
|
|
||||||
|
|
||||||
const submit = (e) => {
|
const submit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -63,21 +66,7 @@ module.exports = function UserProfile() {
|
||||||
setStatus("PATCHing");
|
setStatus("PATCHing");
|
||||||
setError("");
|
setError("");
|
||||||
return Promise.try(() => {
|
return Promise.try(() => {
|
||||||
let payload = {
|
return dispatch(api.user.updateAccount());
|
||||||
display_name: displayName,
|
|
||||||
note: bio,
|
|
||||||
locked: locked
|
|
||||||
};
|
|
||||||
|
|
||||||
if (headerFile) {
|
|
||||||
payload.header = headerFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avatarFile) {
|
|
||||||
payload.avatar = avatarFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dispatch(api.user.updateAccount(payload));
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
setStatus("Saved!");
|
setStatus("Saved!");
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
|
@ -105,26 +94,28 @@ module.exports = function UserProfile() {
|
||||||
<div>
|
<div>
|
||||||
<h3>Header</h3>
|
<h3>Header</h3>
|
||||||
<label htmlFor="header" className="file-input button">Browse…</label>
|
<label htmlFor="header" className="file-input button">Browse…</label>
|
||||||
<span>{headerFile ? headerFile.name : "no file selected"}</span>
|
<span>{account.headerFile ? account.headerFile.name : "no file selected"}</span>
|
||||||
|
<input className="hidden" id="header" type="file" accept="image/*" onChange={onFileChange("header")}/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>Avatar</h3>
|
<h3>Avatar</h3>
|
||||||
<label htmlFor="avatar" className="file-input button">Browse…</label>
|
<label htmlFor="avatar" className="file-input button">Browse…</label>
|
||||||
<span>{avatarFile ? avatarFile.name : "no file selected"}</span>
|
<span>{account.avatarFile ? account.avatarFile.name : "no file selected"}</span>
|
||||||
|
<input className="hidden" id="avatar" type="file" accept="image/*" onChange={onFileChange("avatar")}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="labelinput">
|
<div className="labelinput">
|
||||||
<label htmlFor="displayname">Name</label>
|
<label htmlFor="displayname">Name</label>
|
||||||
<input id="displayname" type="text" value={displayName} onChange={(e) => setDisplayName(e.target.value)} placeholder="A GoToSocial user"/>
|
<input id="displayname" type="text" value={account.display_name} onChange={onTextChange("display_name")} placeholder="A GoToSocial user"/>
|
||||||
</div>
|
</div>
|
||||||
<div className="labelinput">
|
<div className="labelinput">
|
||||||
<label htmlFor="bio">Bio</label>
|
<label htmlFor="bio">Bio</label>
|
||||||
<textarea id="bio" value={bio} onChange={(e) => setBio(e.target.value)} 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>
|
||||||
<div className="labelcheckbox">
|
<div className="labelcheckbox">
|
||||||
<label htmlFor="locked">Manually approve follow requests?</label>
|
<label htmlFor="locked">Manually approve follow requests?</label>
|
||||||
<input id="locked" type="checkbox" checked={locked} onChange={(e) => setLocked(e.target.checked)}/>
|
<input id="locked" type="checkbox" checked={account.locked} onChange={onCheckChange("locked")}/>
|
||||||
</div>
|
</div>
|
||||||
<Submit onClick={submit} label="Save profile info" errorMsg={errorMsg} statusMsg={statusMsg}/>
|
<Submit onClick={submit} label="Save profile info" errorMsg={errorMsg} statusMsg={statusMsg}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2528,6 +2528,11 @@ domain-browser@^1.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
|
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
|
||||||
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
|
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
|
||||||
|
|
||||||
|
dotty@^0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/dotty/-/dotty-0.1.2.tgz#512d44cc4111a724931226259297f235e8484f6f"
|
||||||
|
integrity sha512-V0EWmKeH3DEhMwAZ+8ZB2Ao4OK6p++Z0hsDtZq3N0+0ZMVqkzrcEGROvOnZpLnvBg5PTNG23JEDLAm64gPaotQ==
|
||||||
|
|
||||||
duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2, duplexer2@~0.1.4:
|
duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2, duplexer2@~0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
|
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
|
||||||
|
|
Loading…
Reference in a new issue