diff --git a/web/source/settings-panel/lib/api/index.js b/web/source/settings-panel/lib/api/index.js index 840cbca10..fa5279eb3 100644 --- a/web/source/settings-panel/lib/api/index.js +++ b/web/source/settings-panel/lib/api/index.js @@ -24,7 +24,7 @@ const { APIError } = require("../errors"); const { setInstanceInfo } = require("../../redux/reducers/instances").actions; const oauth = require("../../redux/reducers/oauth").actions; -function apiCall(method, route, payload) { +function apiCall(method, route, payload, type="json") { return function (dispatch, getState) { const state = getState(); let base = state.oauth.instance; @@ -36,14 +36,22 @@ function apiCall(method, route, payload) { url.pathname = route; let body; - if (payload != undefined) { - body = JSON.stringify(payload); - } - let headers = { "Accept": "application/json", - "Content-Type": "application/json" }; + + if (payload != undefined) { + if (type == "json") { + headers["Content-Type"] = "application/json"; + body = JSON.stringify(payload); + } else if (type == "form") { + const formData = new FormData(); + Object.entries(payload).forEach(([key, val]) => { + formData.set(key, val); + }); + body = formData; + } + } if (auth != undefined) { headers["Authorization"] = auth; diff --git a/web/source/settings-panel/lib/api/user.js b/web/source/settings-panel/lib/api/user.js index e0a9f7dda..96ff60cde 100644 --- a/web/source/settings-panel/lib/api/user.js +++ b/web/source/settings-panel/lib/api/user.js @@ -32,6 +32,16 @@ module.exports = function({apiCall}) { return dispatch(user.setAccount(account)); }); }; - } + }, + updateAccount: function updateAccount(newAccount) { + return function (dispatch, _getSate) { + return Promise.try(() => { + return dispatch(apiCall("PATCH", "/api/v1/accounts/update_credentials", newAccount, "form")); + }).then((account) => { + console.log(account); + return dispatch(user.setAccount(account)); + }); + }; + } }; }; \ No newline at end of file diff --git a/web/source/settings-panel/style.css b/web/source/settings-panel/style.css index 7c669ebd7..cc9f1b01a 100644 --- a/web/source/settings-panel/style.css +++ b/web/source/settings-panel/style.css @@ -176,3 +176,97 @@ input, select, textarea { rgb(70, 79, 88) 20px ) !important; } + +.user-profile { + display: flex; + flex-direction: column; + gap: 1rem; + + input, textarea { + width: 100%; + line-height: 1.5rem; + } + + input[type=checkbox] { + justify-self: start; + width: initial; + } + + input:read-only { + border: none; + } + + input:invalid { + border-color: red; + } + textarea { + width: 100%; + height: 8rem; + } + + h1 { + 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; + } + + .labelinput .border { + border-radius: 0.2rem; + border: 0.15rem solid $border_accent; + padding: 0.3rem; + display: flex; + flex-direction: column; + } + + .file-input.button { + display: inline-block; + font-size: 1rem; + font-weight: normal; + padding: 0.3rem 0.3rem; + align-self: flex-start; + /* background: $border_accent; */ + margin-right: 0.2rem; + } + + .labelinput, .labelselect { + display: flex; + flex-direction: column; + gap: 0.4rem; + } + + .labelcheckbox { + display: flex; + gap: 0.4rem; + } + + .titlesave { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + } +} diff --git a/web/source/settings-panel/user/profile.js b/web/source/settings-panel/user/profile.js index 3d0a16b4e..cf070be8d 100644 --- a/web/source/settings-panel/user/profile.js +++ b/web/source/settings-panel/user/profile.js @@ -25,39 +25,37 @@ const { useErrorHandler } = require("react-error-boundary"); const Submit = require("../components/submit"); +const api = require("../lib/api"); + module.exports = function UserProfile() { + const dispatch = Redux.useDispatch(); const account = Redux.useSelector(state => state.user.account); const [errorMsg, setError] = React.useState(""); const [statusMsg, setStatus] = React.useState(""); const [headerFile, setHeaderFile] = React.useState(undefined); - const [headerSrc, setHeaderSrc] = React.useState(""); - const [avatarFile, setAvatarFile] = React.useState(undefined); - const [avatarSrc, setAvatarSrc] = React.useState(""); const [displayName, setDisplayName] = React.useState(""); const [bio, setBio] = React.useState(""); const [locked, setLocked] = React.useState(false); React.useEffect(() => { - setHeaderSrc(account.header); - setAvatarSrc(account.avatar); setDisplayName(account.display_name); setBio(account.source ? account.source.note : ""); setLocked(account.locked); - }, [account, setHeaderSrc, setAvatarSrc, setDisplayName, setBio, setLocked]); + }, []); const headerOnChange = (e) => { setHeaderFile(e.target.files[0]); - setHeaderSrc(URL.createObjectURL(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])); + // setAvatarSrc(URL.createObjectURL(e.target.files[0])); }; const submit = (e) => { @@ -66,30 +64,23 @@ module.exports = function UserProfile() { setStatus("PATCHing"); setError(""); return Promise.try(() => { - let formDataInfo = new FormData(); + let payload = { + display_name: displayName, + note: bio, + locked: locked + }; if (headerFile) { - formDataInfo.set("header", headerFile); + payload.header = headerFile; } if (avatarFile) { - formDataInfo.set("avatar", avatarFile); + payload.avatar = avatarFile; } - formDataInfo.set("display_name", displayName); - formDataInfo.set("note", bio); - formDataInfo.set("locked", locked); - - return oauth.apiRequest("/api/v1/accounts/update_credentials", "PATCH", formDataInfo, "form"); - }).then((json) => { + return dispatch(api.user.updateAccount(payload)); + }).then(() => { setStatus("Saved!"); - - setHeaderSrc(json.header); - setAvatarSrc(json.avatar); - - setDisplayName(json.display_name); - setBio(json.source.note); - setLocked(json.locked); }).catch((e) => { setError(e.message); setStatus(""); @@ -97,45 +88,43 @@ module.exports = function UserProfile() { }; return ( -
+

@{account.username}'s Profile Info

-
-
- -
- {headerSrc -
- - {headerFile ? headerFile.name : ""} -
+
+ +
+ {account.header +
+ + {headerFile ? headerFile.name : ""}
-
-
- -
- {headerSrc -
- - {avatarFile ? avatarFile.name : ""} -
+ +
+
+ +
+ {account.avatar +
+ + {avatarFile ? avatarFile.name : ""}
-
-
- - setDisplayName(e.target.value)} placeholder="A GoToSocial user"/> -
-
- -