diff --git a/web/source/package.json b/web/source/package.json index 4351b7651..4467265f2 100644 --- a/web/source/package.json +++ b/web/source/package.json @@ -18,6 +18,7 @@ "browserlist": "^1.0.1", "create-error": "^0.3.1", "css-extract": "^2.0.0", + "dotty": "^0.1.2", "eslint-plugin-react": "^7.24.0", "express": "^4.18.1", "factor-bundle": "^2.5.0", diff --git a/web/source/settings-panel/lib/api/user.js b/web/source/settings-panel/lib/api/user.js index 96ff60cde..2af800d41 100644 --- a/web/source/settings-panel/lib/api/user.js +++ b/web/source/settings-panel/lib/api/user.js @@ -19,10 +19,11 @@ "use strict"; const Promise = require("bluebird"); +const d = require("dotty"); const user = require("../../redux/reducers/user").actions; -module.exports = function({apiCall}) { +module.exports = function ({ apiCall }) { return { fetchAccount: function fetchAccount() { return function (dispatch, _getState) { @@ -33,10 +34,34 @@ module.exports = function({apiCall}) { }); }; }, - updateAccount: function updateAccount(newAccount) { - return function (dispatch, _getSate) { + updateAccount: function updateAccount() { + const formKeys = ["display_name", "locked"]; + const renamedKeys = [["note", "source.note"]]; + const fileKeys = ["header", "avatar"]; + + return function (dispatch, getState) { 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) => { console.log(account); return dispatch(user.setAccount(account)); diff --git a/web/source/settings-panel/redux/reducers/user.js b/web/source/settings-panel/redux/reducers/user.js index 480751290..1cc4e894e 100644 --- a/web/source/settings-panel/redux/reducers/user.js +++ b/web/source/settings-panel/redux/reducers/user.js @@ -19,6 +19,7 @@ "use strict"; const {createSlice} = require("@reduxjs/toolkit"); +const d = require("dotty"); module.exports = createSlice({ name: "user", @@ -27,6 +28,9 @@ module.exports = createSlice({ reducers: { setAccount: (state, {payload}) => { state.account = payload; + }, + setAccountVal: (state, {payload: [key, val]}) => { + d.put(state.account, key, val); } } }); \ No newline at end of file diff --git a/web/source/settings-panel/user/profile.js b/web/source/settings-panel/user/profile.js index 71028f4be..b3ac44dc3 100644 --- a/web/source/settings-panel/user/profile.js +++ b/web/source/settings-panel/user/profile.js @@ -21,10 +21,12 @@ 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 user = require("../redux/reducers/user").actions; module.exports = function UserProfile() { const dispatch = Redux.useDispatch(); @@ -33,29 +35,30 @@ module.exports = function UserProfile() { const [errorMsg, setError] = React.useState(""); const [statusMsg, setStatus] = React.useState(""); - const [headerFile, setHeaderFile] = React.useState(undefined); - const [avatarFile, setAvatarFile] = React.useState(undefined); + function onTextChange(key) { + return function (e) { + dispatch(user.setAccountVal([key, e.target.value])); + }; + } - const [displayName, setDisplayName] = React.useState(""); - const [bio, setBio] = React.useState(""); - const [locked, setLocked] = React.useState(false); + function onCheckChange(key) { + return function (e) { + dispatch(user.setAccountVal([key, e.target.checked])); + }; + } - React.useEffect(() => { - - setDisplayName(account.display_name); - setBio(account.source ? account.source.note : ""); - setLocked(account.locked); - }, []); - - const headerOnChange = (e) => { - setHeaderFile(e.target.files[0]); - // setHeaderSrc(URL.createObjectURL(e.target.files[0])); - }; - - const avatarOnChange = (e) => { - setAvatarFile(e.target.files[0]); - // setAvatarSrc(URL.createObjectURL(e.target.files[0])); - }; + 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(); @@ -63,21 +66,7 @@ module.exports = function UserProfile() { setStatus("PATCHing"); setError(""); return Promise.try(() => { - let payload = { - display_name: displayName, - note: bio, - locked: locked - }; - - if (headerFile) { - payload.header = headerFile; - } - - if (avatarFile) { - payload.avatar = avatarFile; - } - - return dispatch(api.user.updateAccount(payload)); + return dispatch(api.user.updateAccount()); }).then(() => { setStatus("Saved!"); }).catch((e) => { @@ -105,26 +94,28 @@ module.exports = function UserProfile() {

Header

- {headerFile ? headerFile.name : "no file selected"} + {account.headerFile ? account.headerFile.name : "no file selected"} +

Avatar

- {avatarFile ? avatarFile.name : "no file selected"} + {account.avatarFile ? account.avatarFile.name : "no file selected"} +
- setDisplayName(e.target.value)} placeholder="A GoToSocial user"/> +
-