diff --git a/web/source/settings-panel/components/login.jsx b/web/source/settings-panel/components/login.jsx index c4bebd063..86d9c6a12 100644 --- a/web/source/settings-panel/components/login.jsx +++ b/web/source/settings-panel/components/login.jsx @@ -36,7 +36,7 @@ module.exports = function Login({error}) { let currentDomain = window.location.origin; Promise.try(() => { console.log("trying", currentDomain); - return dispatch(api.instance.fetch(currentDomain)); + return dispatch(api.instance.fetchWithoutStore(currentDomain)); }).then(() => { if (instanceFieldRef.current.length == 0) { // user hasn't started typing yet dispatch(setInstance(currentDomain)); @@ -51,7 +51,7 @@ module.exports = function Login({error}) { function tryInstance() { let domain = instanceFieldRef.current; Promise.try(() => { - return dispatch(api.instance.fetch(domain)).catch((e) => { + return dispatch(api.instance.fetchWithoutStore(domain)).catch((e) => { // TODO: clearer error messages for common errors console.log(e); throw e; diff --git a/web/source/settings-panel/index.js b/web/source/settings-panel/index.js index d59dc7237..e4b050055 100644 --- a/web/source/settings-panel/index.js +++ b/web/source/settings-panel/index.js @@ -40,7 +40,6 @@ const nav = { entries: { "Profile": require("./user/profile.js"), "Settings": require("./user/settings.js"), - "Customization": require("./user/customization.js") } }, "Admin": { @@ -76,6 +75,9 @@ function App() { return dispatch(api.oauth.tokenize(code)); } } + }).then(() => { + // Fetch current instance info + return dispatch(api.instance.fetch()); }).then(() => { // Check currently stored auth token for validity if available if (loginState == "callback" || loginState == "login") { diff --git a/web/source/settings-panel/lib/api/index.js b/web/source/settings-panel/lib/api/index.js index 3bd361d78..6a99ed1e9 100644 --- a/web/source/settings-panel/lib/api/index.js +++ b/web/source/settings-panel/lib/api/index.js @@ -22,7 +22,7 @@ const Promise = require("bluebird"); const { isPlainObject } = require("is-plain-object"); const { APIError } = require("../errors"); -const { setInstanceInfo } = require("../../redux/reducers/instances").actions; +const { setInstanceInfo, setNamedInstanceInfo } = require("../../redux/reducers/instances").actions; const oauth = require("../../redux/reducers/oauth").actions; function apiCall(method, route, payload, type="json") { @@ -95,7 +95,7 @@ function getCurrentUrl() { return `${window.location.origin}${window.location.pathname}`; } -function fetchInstance(domain) { +function fetchInstanceWithoutStore(domain) { return function(dispatch, getState) { return Promise.try(() => { let lookup = getState().instances.info[domain]; @@ -113,7 +113,20 @@ function fetchInstance(domain) { return apiCall("GET", "/api/v1/instance")(dispatch, () => fakeState); }).then((json) => { if (json && json.uri) { // TODO: validate instance json more? - dispatch(setInstanceInfo([domain, json])); + dispatch(setNamedInstanceInfo([domain, json])); + return json; + } + }); + }; +} + +function fetchInstance() { + return function(dispatch, _getState) { + return Promise.try(() => { + return dispatch(apiCall("GET", "/api/v1/instance")); + }).then((json) => { + if (json && json.uri) { + dispatch(setInstanceInfo(json)); return json; } }); @@ -122,6 +135,7 @@ function fetchInstance(domain) { module.exports = { instance: { + fetchWithoutStore: fetchInstanceWithoutStore, fetch: fetchInstance }, oauth: require("./oauth")({apiCall, getCurrentUrl}), diff --git a/web/source/settings-panel/lib/api/user.js b/web/source/settings-panel/lib/api/user.js index 94bbeb920..a24bccaff 100644 --- a/web/source/settings-panel/lib/api/user.js +++ b/web/source/settings-panel/lib/api/user.js @@ -64,7 +64,7 @@ module.exports = function ({ apiCall }) { }; }, updateProfile: function updateProfile() { - const formKeys = ["display_name", "locked", "source"]; + const formKeys = ["display_name", "locked", "source", "custom_css"]; const renamedKeys = [["note", "source.note"]]; const fileKeys = ["header", "avatar"]; diff --git a/web/source/settings-panel/redux/reducers/instances.js b/web/source/settings-panel/redux/reducers/instances.js index 668035d6f..de874662b 100644 --- a/web/source/settings-panel/redux/reducers/instances.js +++ b/web/source/settings-panel/redux/reducers/instances.js @@ -26,9 +26,12 @@ module.exports = createSlice({ info: {}, }, reducers: { - setInstanceInfo: (state, {payload}) => { + setNamedInstanceInfo: (state, {payload}) => { let [key, info] = payload; state.info[key] = info; }, + setInstanceInfo: (state, {payload}) => { + state.current = payload; + } } }); \ No newline at end of file diff --git a/web/source/settings-panel/user/customization.js b/web/source/settings-panel/user/customization.js deleted file mode 100644 index 8a029e305..000000000 --- a/web/source/settings-panel/user/customization.js +++ /dev/null @@ -1,23 +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"; - -module.exports = function UserCustomization() { - return "user customization"; -}; \ 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 25e22c4cf..f06f0e667 100644 --- a/web/source/settings-panel/user/profile.js +++ b/web/source/settings-panel/user/profile.js @@ -31,6 +31,9 @@ const user = require("../redux/reducers/user").actions; module.exports = function UserProfile() { const dispatch = Redux.useDispatch(); const account = Redux.useSelector(state => state.user.profile); + const instance = Redux.useSelector(state => state.instances.current); + + const allowCustomCSS = instance.configuration.accounts.allow_custom_css; const { onTextChange, onCheckChange, onFileChange } = formFields(dispatch, user.setProfileVal, account); @@ -106,6 +109,13 @@ module.exports = function UserProfile() { <label htmlFor="locked">Manually approve follow requests?</label> <input id="locked" type="checkbox" checked={account.locked} onChange={onCheckChange("locked")} /> </div> + { !allowCustomCSS ? null : + <div className="labelinput"> + <label htmlFor="customcss">Custom CSS</label> + <textarea className="mono" id="customcss" value={account.custom_css} onChange={onTextChange("custom_css")}/> + <a href="https://docs.gotosocial.org/en/latest/user_guide/custom_css" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about custom CSS (opens in a new tab)</a> + </div> + } <Submit onClick={submit} label="Save profile info" errorMsg={errorMsg} statusMsg={statusMsg} /> </div> );