diff options
author | 2023-10-05 16:06:19 +0200 | |
---|---|---|
committer | 2023-10-05 16:06:19 +0200 | |
commit | d173fcdfa3ad6f6aee721c8553f25f4db38fa302 (patch) | |
tree | 722cdaf93e090edcf83769ae763731008daf5fd3 /web/source/settings | |
parent | updates markdown parsing to reduce allocations in the same way as the plain t... (diff) | |
download | gotosocial-d173fcdfa3ad6f6aee721c8553f25f4db38fa302.tar.xz |
[chore] Convert some settings / admin panel JS to TypeScript (#2247)
* initial conversion of STUFF to typescript
* more stuff
* update babel deps, include commonjs transform
* update bundler & eslint configuration
* eslint --fix
* upgrade deps
* update docs, build stuff, peripheral stuff
---------
Co-authored-by: f0x <f0x@cthu.lu>
Diffstat (limited to 'web/source/settings')
72 files changed, 542 insertions, 476 deletions
diff --git a/web/source/settings/admin/accounts/detail.jsx b/web/source/settings/admin/accounts/detail.jsx index 189c07e8d..0e906cd1c 100644 --- a/web/source/settings/admin/accounts/detail.jsx +++ b/web/source/settings/admin/accounts/detail.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { useRoute, Redirect } = require("wouter"); diff --git a/web/source/settings/admin/accounts/index.jsx b/web/source/settings/admin/accounts/index.jsx index 1dea36fb5..c642d903e 100644 --- a/web/source/settings/admin/accounts/index.jsx +++ b/web/source/settings/admin/accounts/index.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Switch, Route, Link } = require("wouter"); diff --git a/web/source/settings/admin/actions/keys/expireremote.jsx b/web/source/settings/admin/actions/keys/expireremote.jsx index b9045a7ed..172f65bc3 100644 --- a/web/source/settings/admin/actions/keys/expireremote.jsx +++ b/web/source/settings/admin/actions/keys/expireremote.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../../../lib/query"); diff --git a/web/source/settings/admin/actions/keys/index.jsx b/web/source/settings/admin/actions/keys/index.jsx index b40835c12..f6a851e70 100644 --- a/web/source/settings/admin/actions/keys/index.jsx +++ b/web/source/settings/admin/actions/keys/index.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const ExpireRemote = require("./expireremote"); diff --git a/web/source/settings/admin/actions/media/cleanup.jsx b/web/source/settings/admin/actions/media/cleanup.jsx index 61ee15258..8b0e628f6 100644 --- a/web/source/settings/admin/actions/media/cleanup.jsx +++ b/web/source/settings/admin/actions/media/cleanup.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../../../lib/query"); diff --git a/web/source/settings/admin/actions/media/index.jsx b/web/source/settings/admin/actions/media/index.jsx index c5167506a..c904eb047 100644 --- a/web/source/settings/admin/actions/media/index.jsx +++ b/web/source/settings/admin/actions/media/index.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const Cleanup = require("./cleanup"); diff --git a/web/source/settings/admin/emoji/category-select.jsx b/web/source/settings/admin/emoji/category-select.jsx index bc4c280d5..da2604602 100644 --- a/web/source/settings/admin/emoji/category-select.jsx +++ b/web/source/settings/admin/emoji/category-select.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const splitFilterN = require("split-filter-n"); const syncpipe = require('syncpipe'); diff --git a/web/source/settings/admin/emoji/local/detail.js b/web/source/settings/admin/emoji/local/detail.js index 6b583b0b9..daf7a2dac 100644 --- a/web/source/settings/admin/emoji/local/detail.js +++ b/web/source/settings/admin/emoji/local/detail.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { useRoute, Link, Redirect } = require("wouter"); diff --git a/web/source/settings/admin/emoji/local/index.js b/web/source/settings/admin/emoji/local/index.js index 56695094c..008bd7a61 100644 --- a/web/source/settings/admin/emoji/local/index.js +++ b/web/source/settings/admin/emoji/local/index.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Switch, Route } = require("wouter"); diff --git a/web/source/settings/admin/emoji/local/new-emoji.js b/web/source/settings/admin/emoji/local/new-emoji.js index de796fe30..439d09e62 100644 --- a/web/source/settings/admin/emoji/local/new-emoji.js +++ b/web/source/settings/admin/emoji/local/new-emoji.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../../../lib/query"); diff --git a/web/source/settings/admin/emoji/local/overview.js b/web/source/settings/admin/emoji/local/overview.js index e8d35ff54..38dc1feba 100644 --- a/web/source/settings/admin/emoji/local/overview.js +++ b/web/source/settings/admin/emoji/local/overview.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Link } = require("wouter"); const syncpipe = require("syncpipe"); diff --git a/web/source/settings/admin/emoji/local/use-shortcode.js b/web/source/settings/admin/emoji/local/use-shortcode.js index c9c27cdf2..7e1bae0ad 100644 --- a/web/source/settings/admin/emoji/local/use-shortcode.js +++ b/web/source/settings/admin/emoji/local/use-shortcode.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../../../lib/query"); diff --git a/web/source/settings/admin/emoji/remote/index.js b/web/source/settings/admin/emoji/remote/index.js index bf7113b49..e877efb89 100644 --- a/web/source/settings/admin/emoji/remote/index.js +++ b/web/source/settings/admin/emoji/remote/index.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const ParseFromToot = require("./parse-from-toot"); diff --git a/web/source/settings/admin/emoji/remote/parse-from-toot.js b/web/source/settings/admin/emoji/remote/parse-from-toot.js index 3bf5fdd90..e6438a4d2 100644 --- a/web/source/settings/admin/emoji/remote/parse-from-toot.js +++ b/web/source/settings/admin/emoji/remote/parse-from-toot.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../../../lib/query"); diff --git a/web/source/settings/admin/federation/detail.js b/web/source/settings/admin/federation/detail.js index a3bbfcac1..7bdee66cf 100644 --- a/web/source/settings/admin/federation/detail.js +++ b/web/source/settings/admin/federation/detail.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { useRoute, Redirect, useLocation } = require("wouter"); diff --git a/web/source/settings/admin/federation/import-export/export-format-table.jsx b/web/source/settings/admin/federation/import-export/export-format-table.jsx index 062a169a4..7fcffa348 100644 --- a/web/source/settings/admin/federation/import-export/export-format-table.jsx +++ b/web/source/settings/admin/federation/import-export/export-format-table.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); module.exports = function ExportFormatTable() { diff --git a/web/source/settings/admin/federation/import-export/form.jsx b/web/source/settings/admin/federation/import-export/form.jsx index a6967b8f0..2086739e3 100644 --- a/web/source/settings/admin/federation/import-export/form.jsx +++ b/web/source/settings/admin/federation/import-export/form.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../../../lib/query"); diff --git a/web/source/settings/admin/federation/import-export/index.jsx b/web/source/settings/admin/federation/import-export/index.jsx index 7126b4e89..bff14b939 100644 --- a/web/source/settings/admin/federation/import-export/index.jsx +++ b/web/source/settings/admin/federation/import-export/index.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Switch, Route, Redirect, useLocation } = require("wouter"); diff --git a/web/source/settings/admin/federation/import-export/process.jsx b/web/source/settings/admin/federation/import-export/process.jsx index b78dc8ed9..b39410605 100644 --- a/web/source/settings/admin/federation/import-export/process.jsx +++ b/web/source/settings/admin/federation/import-export/process.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../../../lib/query"); diff --git a/web/source/settings/admin/federation/index.js b/web/source/settings/admin/federation/index.js index 081d34113..ec536c0be 100644 --- a/web/source/settings/admin/federation/index.js +++ b/web/source/settings/admin/federation/index.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Switch, Route } = require("wouter"); diff --git a/web/source/settings/admin/federation/overview.js b/web/source/settings/admin/federation/overview.js index 626794455..c09289284 100644 --- a/web/source/settings/admin/federation/overview.js +++ b/web/source/settings/admin/federation/overview.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Link, useLocation } = require("wouter"); const { matchSorter } = require("match-sorter"); diff --git a/web/source/settings/admin/reports/detail.jsx b/web/source/settings/admin/reports/detail.jsx index e85b345b0..6b85872c4 100644 --- a/web/source/settings/admin/reports/detail.jsx +++ b/web/source/settings/admin/reports/detail.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { useRoute, Redirect } = require("wouter"); diff --git a/web/source/settings/admin/reports/index.jsx b/web/source/settings/admin/reports/index.jsx index 62a71c3e3..2f7a09517 100644 --- a/web/source/settings/admin/reports/index.jsx +++ b/web/source/settings/admin/reports/index.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Link, Switch, Route } = require("wouter"); diff --git a/web/source/settings/admin/reports/username.jsx b/web/source/settings/admin/reports/username.jsx index eca3570d8..9754c2dd5 100644 --- a/web/source/settings/admin/reports/username.jsx +++ b/web/source/settings/admin/reports/username.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Link } = require("wouter"); diff --git a/web/source/settings/admin/settings/index.jsx b/web/source/settings/admin/settings/index.jsx index dab476433..5ea227fb1 100644 --- a/web/source/settings/admin/settings/index.jsx +++ b/web/source/settings/admin/settings/index.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../../lib/query"); diff --git a/web/source/settings/admin/settings/rules.jsx b/web/source/settings/admin/settings/rules.jsx index 330bc07fd..7c78e6601 100644 --- a/web/source/settings/admin/settings/rules.jsx +++ b/web/source/settings/admin/settings/rules.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Switch, Route, Link, Redirect, useRoute } = require("wouter"); diff --git a/web/source/settings/components/authorization/index.jsx b/web/source/settings/components/authorization/index.tsx index d38e160da..321bb03eb 100644 --- a/web/source/settings/components/authorization/index.jsx +++ b/web/source/settings/components/authorization/index.tsx @@ -17,23 +17,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; +import { useVerifyCredentialsQuery } from "../../lib/query/oauth"; +import { store } from "../../redux/store"; -const React = require("react"); -const Redux = require("react-redux"); +import React from "react"; -const query = require("../../lib/query"); +import Login from "./login"; +import Loading from "../loading"; +import { Error } from "../error"; -const Login = require("./login"); -const Loading = require("../loading"); -const { Error } = require("../error"); +export function Authorization({ App }) { + const { loginState, expectingRedirect } = store.getState().oauth; + const skip = (loginState == "none" || loginState == "logout" || expectingRedirect); -module.exports = function Authorization({ App }) { - const { loginState, expectingRedirect } = Redux.useSelector((state) => state.oauth); - - const { isLoading, isSuccess, data: account, error } = query.useVerifyCredentialsQuery(undefined, { - skip: loginState == "none" || loginState == "logout" || expectingRedirect - }); + const { + isLoading, + isSuccess, + data: account, + error, + } = useVerifyCredentialsQuery(null, { skip: skip }); let showLogin = true; let content = null; @@ -73,4 +75,4 @@ module.exports = function Authorization({ App }) { </section> ); } -};
\ No newline at end of file +} diff --git a/web/source/settings/components/authorization/login.jsx b/web/source/settings/components/authorization/login.tsx index dbeb6c047..76bfccf43 100644 --- a/web/source/settings/components/authorization/login.jsx +++ b/web/source/settings/components/authorization/login.tsx @@ -17,18 +17,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; +import React from "react"; -const React = require("react"); +import { useAuthorizeFlowMutation } from "../../lib/query/oauth"; +import { useTextInput, useValue } from "../../lib/form"; +import useFormSubmit from "../../lib/form/submit"; +import { TextInput } from "../form/inputs"; +import MutationButton from "../form/mutation-button"; +import Loading from "../loading"; -const query = require("../../lib/query"); -const { useTextInput, useValue } = require("../../lib/form"); -const useFormSubmit = require("../../lib/form/submit"); -const { TextInput } = require("../form/inputs"); -const MutationButton = require("../form/mutation-button"); -const Loading = require("../loading"); - -module.exports = function Login({ }) { +export default function Login({ }) { const form = { instance: useTextInput("instance", { defaultValue: window.location.origin @@ -38,8 +36,11 @@ module.exports = function Login({ }) { const [formSubmit, result] = useFormSubmit( form, - query.useAuthorizeFlowMutation(), - { changedOnly: false } + useAuthorizeFlowMutation(), + { + changedOnly: false, + onFinish: undefined, + } ); if (result.isLoading) { @@ -63,7 +64,11 @@ module.exports = function Login({ }) { label="Instance" name="instance" /> - <MutationButton label="Login" result={result} /> + <MutationButton + label="Login" + result={result} + disabled={false} + /> </form> ); -};
\ No newline at end of file +}
\ No newline at end of file diff --git a/web/source/settings/components/back-button.jsx b/web/source/settings/components/back-button.jsx index 13a7a757b..05306e2ea 100644 --- a/web/source/settings/components/back-button.jsx +++ b/web/source/settings/components/back-button.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Link } = require("wouter"); diff --git a/web/source/settings/components/check-list.jsx b/web/source/settings/components/check-list.jsx index 492e58e5f..de42a56a5 100644 --- a/web/source/settings/components/check-list.jsx +++ b/web/source/settings/components/check-list.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); module.exports = function CheckList({ field, header = "All", EntryComponent, getExtraProps }) { diff --git a/web/source/settings/components/combo-box.jsx b/web/source/settings/components/combo-box.jsx index 030bd5c30..fc0133583 100644 --- a/web/source/settings/components/combo-box.jsx +++ b/web/source/settings/components/combo-box.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { diff --git a/web/source/settings/components/error.jsx b/web/source/settings/components/error.jsx index 283083481..3205cd5e3 100644 --- a/web/source/settings/components/error.jsx +++ b/web/source/settings/components/error.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); function ErrorFallback({ error, resetErrorBoundary }) { diff --git a/web/source/settings/components/fake-profile.jsx b/web/source/settings/components/fake-profile.jsx index 04424a5ed..d1f28f36c 100644 --- a/web/source/settings/components/fake-profile.jsx +++ b/web/source/settings/components/fake-profile.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); module.exports = function FakeProfile({ avatar, header, display_name, username, role }) { diff --git a/web/source/settings/components/fake-toot.jsx b/web/source/settings/components/fake-toot.jsx index b71831a95..7c2e40454 100644 --- a/web/source/settings/components/fake-toot.jsx +++ b/web/source/settings/components/fake-toot.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../lib/query"); diff --git a/web/source/settings/components/form/inputs.jsx b/web/source/settings/components/form/inputs.jsx index 378879525..f7a6beeda 100644 --- a/web/source/settings/components/form/inputs.jsx +++ b/web/source/settings/components/form/inputs.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); function TextInput({ label, field, ...inputProps }) { diff --git a/web/source/settings/components/form/mutation-button.jsx b/web/source/settings/components/form/mutation-button.jsx index 5f7218db4..0eaf33912 100644 --- a/web/source/settings/components/form/mutation-button.jsx +++ b/web/source/settings/components/form/mutation-button.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Error } = require("../error"); diff --git a/web/source/settings/components/languages.jsx b/web/source/settings/components/languages.jsx index 51006098b..61c76e03a 100644 --- a/web/source/settings/components/languages.jsx +++ b/web/source/settings/components/languages.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const langs = require("langs"); diff --git a/web/source/settings/components/loading.jsx b/web/source/settings/components/loading.jsx index 27ba17499..c62c72a0a 100644 --- a/web/source/settings/components/loading.jsx +++ b/web/source/settings/components/loading.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); module.exports = function Loading() { diff --git a/web/source/settings/components/user-logout-card.jsx b/web/source/settings/components/user-logout-card.jsx index 902d545bc..de77f0485 100644 --- a/web/source/settings/components/user-logout-card.jsx +++ b/web/source/settings/components/user-logout-card.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../lib/query"); diff --git a/web/source/settings/index.js b/web/source/settings/index.js index 9758e89e6..bc3a925c2 100644 --- a/web/source/settings/index.js +++ b/web/source/settings/index.js @@ -17,17 +17,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const ReactDom = require("react-dom/client"); const { Provider } = require("react-redux"); const { PersistGate } = require("redux-persist/integration/react"); -const { store, persistor } = require("./redux"); +const { store, persistor } = require("./redux/store"); const { createNavigation, Menu, Item } = require("./lib/navigation"); -const AuthorizationGate = require("./components/authorization"); +const { Authorization } = require("./components/authorization"); const Loading = require("./components/loading"); const UserLogoutCard = require("./components/user-logout-card"); const { RoleContext } = require("./lib/navigation/util"); @@ -90,7 +88,7 @@ function Main() { return ( <Provider store={store}> <PersistGate loading={<section><Loading /></section>} persistor={persistor}> - <AuthorizationGate App={App} /> + <Authorization App={App} /> </PersistGate> </Provider> ); diff --git a/web/source/settings/lib/domain-block.js b/web/source/settings/lib/domain-block.js index 98691d79d..e1cbd4c22 100644 --- a/web/source/settings/lib/domain-block.js +++ b/web/source/settings/lib/domain-block.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const isValidDomain = require("is-valid-domain"); const psl = require("psl"); diff --git a/web/source/settings/lib/form/bool.jsx b/web/source/settings/lib/form/bool.jsx index d7d916602..47a4bbd1b 100644 --- a/web/source/settings/lib/form/bool.jsx +++ b/web/source/settings/lib/form/bool.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const _default = false; diff --git a/web/source/settings/lib/form/check-list.jsx b/web/source/settings/lib/form/check-list.jsx index fb1fdd32b..2f649dba6 100644 --- a/web/source/settings/lib/form/check-list.jsx +++ b/web/source/settings/lib/form/check-list.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const syncpipe = require("syncpipe"); const { createSlice } = require("@reduxjs/toolkit"); diff --git a/web/source/settings/lib/form/combo-box.jsx b/web/source/settings/lib/form/combo-box.jsx index 8506d72f0..985c262d8 100644 --- a/web/source/settings/lib/form/combo-box.jsx +++ b/web/source/settings/lib/form/combo-box.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { useComboboxState } = require("ariakit/combobox"); diff --git a/web/source/settings/lib/form/context.jsx b/web/source/settings/lib/form/context.jsx index b25bb11b7..1eb24a931 100644 --- a/web/source/settings/lib/form/context.jsx +++ b/web/source/settings/lib/form/context.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const FormContext = React.createContext({}); diff --git a/web/source/settings/lib/form/field-array.jsx b/web/source/settings/lib/form/field-array.jsx index beea0bc9b..f2d7bc7ce 100644 --- a/web/source/settings/lib/form/field-array.jsx +++ b/web/source/settings/lib/form/field-array.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const getFormMutations = require("./get-form-mutations"); diff --git a/web/source/settings/lib/form/file.jsx b/web/source/settings/lib/form/file.jsx index b28361c61..a9e96dc97 100644 --- a/web/source/settings/lib/form/file.jsx +++ b/web/source/settings/lib/form/file.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const prettierBytes = require("prettier-bytes"); diff --git a/web/source/settings/lib/form/form-with-data.jsx b/web/source/settings/lib/form/form-with-data.jsx index 73799da70..ef05c46c0 100644 --- a/web/source/settings/lib/form/form-with-data.jsx +++ b/web/source/settings/lib/form/form-with-data.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Error } = require("../../components/error"); diff --git a/web/source/settings/lib/form/get-form-mutations.js b/web/source/settings/lib/form/get-form-mutations.js index 6bdc3e4cd..b0ae6e9b0 100644 --- a/web/source/settings/lib/form/get-form-mutations.js +++ b/web/source/settings/lib/form/get-form-mutations.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const syncpipe = require("syncpipe"); module.exports = function getFormMutations(form, { changedOnly }) { diff --git a/web/source/settings/lib/form/index.js b/web/source/settings/lib/form/index.js index 3d5f5238b..99537ae7f 100644 --- a/web/source/settings/lib/form/index.js +++ b/web/source/settings/lib/form/index.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const getByDot = require("get-by-dot").default; diff --git a/web/source/settings/lib/form/radio.jsx b/web/source/settings/lib/form/radio.jsx index c4bb77e7a..4bb061f4b 100644 --- a/web/source/settings/lib/form/radio.jsx +++ b/web/source/settings/lib/form/radio.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const _default = ""; diff --git a/web/source/settings/lib/form/submit.js b/web/source/settings/lib/form/submit.js index 2d1d42b3a..ab2945812 100644 --- a/web/source/settings/lib/form/submit.js +++ b/web/source/settings/lib/form/submit.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const Promise = require("bluebird"); const React = require("react"); const getFormMutations = require("./get-form-mutations"); diff --git a/web/source/settings/lib/form/text.jsx b/web/source/settings/lib/form/text.jsx index 0d7753bef..f9c096ac8 100644 --- a/web/source/settings/lib/form/text.jsx +++ b/web/source/settings/lib/form/text.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const _default = ""; diff --git a/web/source/settings/lib/navigation/components.jsx b/web/source/settings/lib/navigation/components.jsx index 365ebff89..64ed160b6 100644 --- a/web/source/settings/lib/navigation/components.jsx +++ b/web/source/settings/lib/navigation/components.jsx @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { Link, Route, Redirect, Switch, useLocation, useRouter } = require("wouter"); const syncpipe = require("syncpipe"); diff --git a/web/source/settings/lib/navigation/index.js b/web/source/settings/lib/navigation/index.js index c1331d0ec..2e8f062f4 100644 --- a/web/source/settings/lib/navigation/index.js +++ b/web/source/settings/lib/navigation/index.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const { nanoid } = require("nanoid"); const { Redirect } = require("wouter"); diff --git a/web/source/settings/lib/navigation/util.js b/web/source/settings/lib/navigation/util.js index f0b29d7a1..2c6c4968f 100644 --- a/web/source/settings/lib/navigation/util.js +++ b/web/source/settings/lib/navigation/util.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const RoleContext = React.createContext([]); const BaseUrlContext = React.createContext(null); diff --git a/web/source/settings/lib/query/admin/custom-emoji.js b/web/source/settings/lib/query/admin/custom-emoji.js index c84155efd..6e7c772a2 100644 --- a/web/source/settings/lib/query/admin/custom-emoji.js +++ b/web/source/settings/lib/query/admin/custom-emoji.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const Promise = require("bluebird"); const { unwrapRes } = require("../lib"); diff --git a/web/source/settings/lib/query/admin/import-export.js b/web/source/settings/lib/query/admin/import-export.js index 7c44d8280..9a04438c2 100644 --- a/web/source/settings/lib/query/admin/import-export.js +++ b/web/source/settings/lib/query/admin/import-export.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const Promise = require("bluebird"); const fileDownload = require("js-file-download"); const csv = require("papaparse"); diff --git a/web/source/settings/lib/query/admin/index.js b/web/source/settings/lib/query/admin/index.js index 7b46e6ba4..7a55389d3 100644 --- a/web/source/settings/lib/query/admin/index.js +++ b/web/source/settings/lib/query/admin/index.js @@ -17,15 +17,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const { replaceCacheOnMutation, removeFromCacheOnMutation, domainListToObject, idListToObject } = require("../lib"); -const base = require("../base"); +const { gtsApi } = require("../gts-api"); const endpoints = (build) => ({ updateInstance: build.mutation({ @@ -164,4 +162,4 @@ const endpoints = (build) => ({ ...require("./reports")(build) }); -module.exports = base.injectEndpoints({ endpoints });
\ No newline at end of file +module.exports = gtsApi.injectEndpoints({ endpoints });
\ No newline at end of file diff --git a/web/source/settings/lib/query/admin/reports.js b/web/source/settings/lib/query/admin/reports.js index 28940872c..1c45bb7bc 100644 --- a/web/source/settings/lib/query/admin/reports.js +++ b/web/source/settings/lib/query/admin/reports.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - module.exports = (build) => ({ listReports: build.query({ query: (params = {}) => ({ diff --git a/web/source/settings/lib/query/base.js b/web/source/settings/lib/query/base.js deleted file mode 100644 index ba02d4e07..000000000 --- a/web/source/settings/lib/query/base.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - 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 { createApi, fetchBaseQuery } = require("@reduxjs/toolkit/query/react"); -const { serialize: serializeForm } = require("object-to-formdata"); - -function instanceBasedQuery(args, api, extraOptions) { - const state = api.getState(); - const { instance, token } = state.oauth; - - if (args.baseUrl == undefined) { - args.baseUrl = instance; - } - - if (args.discardEmpty) { - if (args.body == undefined || Object.keys(args.body).length == 0) { - return { data: null }; - } - delete args.discardEmpty; - } - - if (args.asForm) { - delete args.asForm; - args.body = serializeForm(args.body, { - indices: true, // Array indices, for profile fields - }); - } - - return fetchBaseQuery({ - baseUrl: args.baseUrl, - prepareHeaders: (headers) => { - if (token != undefined) { - headers.set('Authorization', token); - } - headers.set("Accept", "application/json"); - return headers; - }, - })(args, api, extraOptions); -} - -module.exports = createApi({ - reducerPath: "api", - baseQuery: instanceBasedQuery, - tagTypes: ["Auth", "Emoji", "Reports", "Account", "InstanceRules"], - endpoints: (build) => ({ - instance: build.query({ - query: () => ({ - url: `/api/v1/instance` - }) - }) - }) -});
\ No newline at end of file diff --git a/web/source/settings/lib/query/gts-api.ts b/web/source/settings/lib/query/gts-api.ts new file mode 100644 index 000000000..9e043137c --- /dev/null +++ b/web/source/settings/lib/query/gts-api.ts @@ -0,0 +1,149 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + 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/>. +*/ + +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; +import type { + BaseQueryFn, + FetchArgs, + FetchBaseQueryError, +} from '@reduxjs/toolkit/query/react'; +import { serialize as serializeForm } from "object-to-formdata"; + +import type { RootState } from '../../redux/store'; + +/** + * GTSFetchArgs extends standard FetchArgs used by + * RTK Query with a couple helpers of our own. + */ +export interface GTSFetchArgs extends FetchArgs { + /** + * If provided, will be used as base URL. Else, + * will fall back to authorized instance as baseUrl. + */ + baseUrl?: string; + /** + * If true, and no args.body is set, or args.body is empty, + * then a null response will be returned from the API call. + */ + discardEmpty?: boolean; + /** + * If true, then args.body will be serialized + * as FormData before submission. + */ + asForm?: boolean; +} + +/** + * gtsBaseQuery wraps the redux toolkit fetchBaseQuery with some helper functionality. + * + * For an explainer of what's happening in this function, see: + * - https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#customizing-queries-with-basequery + * - https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#constructing-a-dynamic-base-url-using-redux-state + * + * @param args + * @param api + * @param extraOptions + * @returns + */ +const gtsBaseQuery: BaseQueryFn< + string | GTSFetchArgs, + any, + FetchBaseQueryError +> = async (args, api, extraOptions) => { + // Retrieve state at the moment + // this function was called. + const state = api.getState() as RootState; + const { instanceUrl, token } = state.oauth; + + // Derive baseUrl dynamically. + let baseUrl: string; + + // Check if simple string baseUrl provided + // as args, or if more complex args provided. + if (typeof args === "string") { + baseUrl = args; + } else { + if (args.baseUrl != undefined) { + baseUrl = args.baseUrl; + } else { + baseUrl = instanceUrl; + } + + if (args.discardEmpty) { + if (args.body == undefined || Object.keys(args.body).length == 0) { + return { data: null }; + } + } + + if (args.asForm) { + args.body = serializeForm(args.body, { + // Array indices, for profile fields. + indices: true, + }); + } + + // Delete any of our extended arguments + // to avoid confusing fetchBaseQuery. + delete args.baseUrl; + delete args.discardEmpty; + delete args.asForm; + } + + if (!baseUrl) { + return { + error: { + status: 400, + statusText: 'Bad Request', + data: {"error":"No baseUrl set for request"}, + }, + }; + } + + return fetchBaseQuery({ + baseUrl: baseUrl, + prepareHeaders: (headers) => { + if (token != undefined) { + headers.set('Authorization', token); + } + headers.set("Accept", "application/json"); + return headers; + }, + })(args, api, extraOptions); +}; + +export const gtsApi = createApi({ + reducerPath: "api", + baseQuery: gtsBaseQuery, + tagTypes: [ + "Auth", + "Emoji", + "Reports", + "Account", + "InstanceRules", + ], + endpoints: (builder) => ({ + instance: builder.query<any, void>({ + query: () => ({ + url: `/api/v1/instance` + }) + }) + }) +}); + +export const { useInstanceQuery } = gtsApi; diff --git a/web/source/settings/lib/query/index.js b/web/source/settings/lib/query/index.js index dab896f80..aeaa4a1d7 100644 --- a/web/source/settings/lib/query/index.js +++ b/web/source/settings/lib/query/index.js @@ -17,11 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - module.exports = { - ...require("./base"), + ...require("./gts-api"), ...require("./oauth"), ...require("./user"), ...require("./admin") -};
\ No newline at end of file +}; diff --git a/web/source/settings/lib/query/lib.js b/web/source/settings/lib/query/lib.js index 56ce05478..1025ca3a7 100644 --- a/web/source/settings/lib/query/lib.js +++ b/web/source/settings/lib/query/lib.js @@ -17,10 +17,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const syncpipe = require("syncpipe"); -const base = require("./base"); +const { gtsApi } = require("./gts-api"); module.exports = { unwrapRes(res) { @@ -70,7 +68,7 @@ function makeCacheMutation(action) { return { onQueryStarted: (_, { dispatch, queryFulfilled }) => { queryFulfilled.then(({ data: newData }) => { - dispatch(base.util.updateQueryData(queryName, arg, (draft) => { + dispatch(gtsApi.util.updateQueryData(queryName, arg, (draft) => { if (findKey != undefined) { key = findKey(draft, newData); } diff --git a/web/source/settings/lib/query/oauth.js b/web/source/settings/lib/query/oauth.js deleted file mode 100644 index 7284ee856..000000000 --- a/web/source/settings/lib/query/oauth.js +++ /dev/null @@ -1,160 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - 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 Promise = require("bluebird"); - -const base = require("./base"); -const { unwrapRes } = require("./lib"); -const oauth = require("../../redux/oauth").actions; - -function getSettingsURL() { - /* needed in case the settings interface isn't hosted at /settings but - some subpath like /gotosocial/settings. Other parts of the code don't - take this into account yet so mostly future-proofing. - - Also drops anything past /settings/, because authorization urls that are too long - get rejected by GTS. - */ - let [pre, _past] = window.location.pathname.split("/settings"); - return `${window.location.origin}${pre}/settings`; -} - -const SETTINGS_URL = getSettingsURL(); - -const endpoints = (build) => ({ - verifyCredentials: build.query({ - providesTags: (_res, error) => - error == undefined - ? ["Auth"] - : [], - queryFn: (_arg, api, _extraOpts, baseQuery) => { - const state = api.getState(); - - return Promise.try(() => { - // Process callback code first, if available - if (state.oauth.loginState == "callback") { - let urlParams = new URLSearchParams(window.location.search); - let code = urlParams.get("code"); - - if (code == undefined) { - throw { - message: "Waiting for callback, but no ?code= provided in url." - }; - } else { - let app = state.oauth.registration; - - if (app == undefined || app.client_id == undefined) { - throw { - message: "No stored registration data, can't finish login flow." - }; - } - - return baseQuery({ - method: "POST", - url: "/oauth/token", - body: { - client_id: app.client_id, - client_secret: app.client_secret, - redirect_uri: SETTINGS_URL, - grant_type: "authorization_code", - code: code - } - }).then(unwrapRes).then((token) => { - // remove ?code= from url - window.history.replaceState({}, document.title, window.location.pathname); - api.dispatch(oauth.setToken(token)); - }); - } - } - }).then(() => { - return baseQuery({ - url: `/api/v1/accounts/verify_credentials` - }); - }).catch((e) => { - return { error: e }; - }); - } - }), - authorizeFlow: build.mutation({ - queryFn: (formData, api, _extraOpts, baseQuery) => { - let instance; - const state = api.getState(); - - return Promise.try(() => { - if (!formData.instance.startsWith("http")) { - formData.instance = `https://${formData.instance}`; - } - instance = new URL(formData.instance).origin; - - const stored = state.oauth.instance; - if (stored?.instance == instance && stored.registration) { - return stored.registration; - } - - return baseQuery({ - method: "POST", - baseUrl: instance, - url: "/api/v1/apps", - body: { - client_name: "GoToSocial Settings", - scopes: formData.scopes, - redirect_uris: SETTINGS_URL, - website: SETTINGS_URL - } - }).then(unwrapRes).then((app) => { - app.scopes = formData.scopes; - - api.dispatch(oauth.authorize({ - instance: instance, - registration: app, - loginState: "callback", - expectingRedirect: true - })); - - return app; - }); - }).then((app) => { - let url = new URL(instance); - url.pathname = "/oauth/authorize"; - url.searchParams.set("client_id", app.client_id); - url.searchParams.set("redirect_uri", SETTINGS_URL); - url.searchParams.set("response_type", "code"); - url.searchParams.set("scope", app.scopes); - - let redirectURL = url.toString(); - window.location.assign(redirectURL); - - return { data: null }; - }).catch((e) => { - return { error: e }; - }); - }, - }), - logout: build.mutation({ - queryFn: (_arg, api) => { - api.dispatch(oauth.remove()); - return { data: null }; - }, - invalidatesTags: ["Auth"] - }) -}); - -module.exports = base.injectEndpoints({ endpoints });
\ No newline at end of file diff --git a/web/source/settings/lib/query/oauth/index.ts b/web/source/settings/lib/query/oauth/index.ts new file mode 100644 index 000000000..9af2dd5fb --- /dev/null +++ b/web/source/settings/lib/query/oauth/index.ts @@ -0,0 +1,204 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + 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/>. +*/ + +import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'; + +import { gtsApi } from "../gts-api"; +import { + setToken as oauthSetToken, + remove as oauthRemove, + authorize as oauthAuthorize, +} from "../../../redux/oauth"; +import { RootState } from '../../../redux/store'; + +export interface OauthTokenRequestBody { + client_id: string; + client_secret: string; + redirect_uri: string; + grant_type: string; + code: string; +} + +function getSettingsURL() { + /* + needed in case the settings interface isn't hosted at /settings but + some subpath like /gotosocial/settings. Other parts of the code don't + take this into account yet so mostly future-proofing. + + Also drops anything past /settings/, because authorization urls that are too long + get rejected by GTS. + */ + let [pre, _past] = window.location.pathname.split("/settings"); + return `${window.location.origin}${pre}/settings`; +} + +const SETTINGS_URL = (getSettingsURL()); + +// Couple auth functions here require multiple requests as +// part of an OAuth token 'flow'. To keep things simple for +// callers of these query functions, the multiple requests +// are chained within one query. +// +// https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#performing-multiple-requests-with-a-single-query +const extended = gtsApi.injectEndpoints({ + endpoints: (builder) => ({ + verifyCredentials: builder.query<any, void>({ + providesTags: (_res, error) => + error == undefined ? ["Auth"] : [], + async queryFn(_arg, api, _extraOpts, fetchWithBQ) { + const state = api.getState() as RootState; + const oauthState = state.oauth; + + // If we're not in the middle of an auth/callback, + // we may already have an auth token, so just + // return a standard verify_credentials query. + if (oauthState.loginState != 'callback') { + return fetchWithBQ({ + url: `/api/v1/accounts/verify_credentials` + }); + } + + // We're in the middle of an auth/callback flow. + // Try to retrieve callback code from URL query. + let urlParams = new URLSearchParams(window.location.search); + let code = urlParams.get("code"); + if (code == undefined) { + return { + error: { + status: 400, + statusText: 'Bad Request', + data: {"error":"Waiting for callback, but no ?code= provided in url."}, + }, + }; + } + + // Retrieve app with which the + // callback code was generated. + let app = oauthState.app; + if (app == undefined || app.client_id == undefined) { + return { + error: { + status: 400, + statusText: 'Bad Request', + data: {"error":"No stored app registration data, can't finish login flow."}, + }, + }; + } + + // Use the provided code and app + // secret to request an auth token. + const tokenReqBody: OauthTokenRequestBody = { + client_id: app.client_id, + client_secret: app.client_secret, + redirect_uri: SETTINGS_URL, + grant_type: "authorization_code", + code: code + }; + + const tokenResult = await fetchWithBQ({ + method: "POST", + url: "/oauth/token", + body: tokenReqBody, + }); + if (tokenResult.error) { + return { error: tokenResult.error as FetchBaseQueryError }; + } + + // Remove ?code= query param from + // url, we don't want it anymore. + window.history.replaceState({}, document.title, window.location.pathname); + + // Store returned token in redux. + api.dispatch(oauthSetToken(tokenResult.data)); + + // We're now authed! So return + // standard verify_credentials query. + return fetchWithBQ({ + url: `/api/v1/accounts/verify_credentials` + }); + } + }), + + authorizeFlow: builder.mutation({ + async queryFn(formData, api, _extraOpts, fetchWithBQ) { + const state = api.getState() as RootState; + const oauthState = state.oauth; + + let instanceUrl: string; + if (!formData.instance.startsWith("http")) { + formData.instance = `https://${formData.instance}`; + } + + instanceUrl = new URL(formData.instance).origin; + if (oauthState?.instanceUrl == instanceUrl && oauthState.app) { + return { data: oauthState.app }; + } + + const appResult = await fetchWithBQ({ + method: "POST", + baseUrl: instanceUrl, + url: "/api/v1/apps", + body: { + client_name: "GoToSocial Settings", + scopes: formData.scopes, + redirect_uris: SETTINGS_URL, + website: SETTINGS_URL + } + }); + if (appResult.error) { + return { error: appResult.error as FetchBaseQueryError }; + } + + let app = appResult.data as any; + + app.scopes = formData.scopes; + api.dispatch(oauthAuthorize({ + instanceUrl: instanceUrl, + app: app, + loginState: "callback", + expectingRedirect: true + })); + + let url = new URL(instanceUrl); + url.pathname = "/oauth/authorize"; + url.searchParams.set("client_id", app.client_id); + url.searchParams.set("redirect_uri", SETTINGS_URL); + url.searchParams.set("response_type", "code"); + url.searchParams.set("scope", app.scopes); + + let redirectURL = url.toString(); + window.location.assign(redirectURL); + return { data: null }; + }, + }), + logout: builder.mutation({ + queryFn: (_arg, api) => { + api.dispatch(oauthRemove()); + return { data: null }; + }, + invalidatesTags: ["Auth"] + }) + }) +}); + +export const { + useVerifyCredentialsQuery, + useAuthorizeFlowMutation, + useLogoutMutation, +} = extended;
\ No newline at end of file diff --git a/web/source/settings/lib/query/user.js b/web/source/settings/lib/query/user/index.ts index a88e16035..751e38e5b 100644 --- a/web/source/settings/lib/query/user.js +++ b/web/source/settings/lib/query/user/index.ts @@ -17,29 +17,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; +import { replaceCacheOnMutation } from "../lib"; +import { gtsApi } from "../gts-api"; -const { replaceCacheOnMutation } = require("./lib"); -const base = require("./base"); - -const endpoints = (build) => ({ - updateCredentials: build.mutation({ - query: (formData) => ({ - method: "PATCH", - url: `/api/v1/accounts/update_credentials`, - asForm: true, - body: formData, - discardEmpty: true +const extended = gtsApi.injectEndpoints({ + endpoints: (builder) => ({ + updateCredentials: builder.mutation({ + query: (formData) => ({ + method: "PATCH", + url: `/api/v1/accounts/update_credentials`, + asForm: true, + body: formData, + discardEmpty: true + }), + ...replaceCacheOnMutation("verifyCredentials") }), - ...replaceCacheOnMutation("verifyCredentials") - }), - passwordChange: build.mutation({ - query: (data) => ({ - method: "POST", - url: `/api/v1/user/password_change`, - body: data + passwordChange: builder.mutation({ + query: (data) => ({ + method: "POST", + url: `/api/v1/user/password_change`, + body: data + }) }) }) }); -module.exports = base.injectEndpoints({ endpoints });
\ No newline at end of file +export const { + useUpdateCredentialsMutation, + usePasswordChangeMutation, +} = extended; diff --git a/web/source/settings/redux/oauth.js b/web/source/settings/redux/oauth.js deleted file mode 100644 index ca0be15d0..000000000 --- a/web/source/settings/redux/oauth.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - 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 { createSlice } = require("@reduxjs/toolkit"); - -module.exports = createSlice({ - name: "oauth", - initialState: { - loginState: 'none', - expectingRedirect: false - }, - reducers: { - authorize: (state, { payload }) => { - return payload; // overrides state - }, - setToken: (state, { payload }) => { - state.token = `${payload.token_type} ${payload.access_token}`; - state.loginState = "login"; - }, - remove: (state, { _payload }) => { - delete state.token; - delete state.registration; - state.loginState = "logout"; - } - } -});
\ No newline at end of file diff --git a/web/source/settings/redux/oauth.ts b/web/source/settings/redux/oauth.ts new file mode 100644 index 000000000..1d6bf9bb1 --- /dev/null +++ b/web/source/settings/redux/oauth.ts @@ -0,0 +1,89 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + 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/>. +*/ + +import { PayloadAction, createSlice } from "@reduxjs/toolkit"; + +/** + * OAuthToken represents a response + * to an OAuth token request. + */ +export interface OAuthToken { + /** + * Most likely to be 'Bearer' + * but may be something else. + */ + token_type: string; + /** + * The actual token. Can be passed in to + * authenticate further requests using the + * Authorization header and the token type. + */ + access_token: string; +} + +export interface OAuthApp { + client_id: string; + client_secret: string; +} + +export interface OAuthState { + instanceUrl?: string; + loginState: "none" | "callback" | "login" | "logout"; + expectingRedirect: boolean; + /** + * Token stored in easy-to-use format. + * Will look something like: + * "Authorization: Bearer BLAHBLAHBLAH" + */ + token?: string; + app?: OAuthApp; +} + +const initialState: OAuthState = { + loginState: 'none', + expectingRedirect: false, +}; + +export const oauthSlice = createSlice({ + name: "oauth", + initialState: initialState, + reducers: { + authorize: (_state, action: PayloadAction<OAuthState>) => { + // Overrides state with payload. + return action.payload; + }, + setToken: (state, action: PayloadAction<OAuthToken>) => { + // Mark us as logged in by storing token. + state.token = `${action.payload.token_type} ${action.payload.access_token}`; + state.loginState = "login"; + }, + remove: (state) => { + // Mark us as logged out by clearing auth. + delete state.token; + delete state.app; + state.loginState = "logout"; + } + } +}); + +export const { + authorize, + setToken, + remove, +} = oauthSlice.actions; diff --git a/web/source/settings/redux/index.js b/web/source/settings/redux/store.ts index fe367a672..0c1285187 100644 --- a/web/source/settings/redux/index.js +++ b/web/source/settings/redux/store.ts @@ -17,11 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - -const { combineReducers } = require("redux"); -const { configureStore } = require("@reduxjs/toolkit"); -const { +import { combineReducers } from "redux"; +import { configureStore } from "@reduxjs/toolkit"; +import { persistStore, persistReducer, FLUSH, @@ -30,14 +28,14 @@ const { PERSIST, PURGE, REGISTER, -} = require("redux-persist"); +} from "redux-persist"; -const query = require("../lib/query/base"); -const { Promise } = require("bluebird"); +import { oauthSlice } from "./oauth"; +import { gtsApi } from "../lib/query/gts-api"; const combinedReducers = combineReducers({ - oauth: require("./oauth").reducer, - [query.reducerPath]: query.reducer + [gtsApi.reducerPath]: gtsApi.reducer, + oauth: oauthSlice.reducer, }); const persistedReducer = persistReducer({ @@ -45,27 +43,41 @@ const persistedReducer = persistReducer({ storage: require("redux-persist/lib/storage").default, stateReconciler: require("redux-persist/lib/stateReconciler/autoMergeLevel1").default, whitelist: ["oauth"], - migrate: (state) => { - return Promise.try(() => { - if (state?.oauth != undefined) { - state.oauth.expectingRedirect = false; - } + migrate: async (state) => { + if (state == undefined) { return state; - }); + } + + // This is a cheeky workaround for + // redux-persist being a stickler. + let anyState = state as any; + if (anyState?.oauth != undefined) { + anyState.oauth.expectingRedirect = false; + } + + return anyState; } }, combinedReducers); -const store = configureStore({ +export const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => { return getDefaultMiddleware({ serializableCheck: { - ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER] + ignoredActions: [ + FLUSH, + REHYDRATE, + PAUSE, + PERSIST, + PURGE, + REGISTER, + ] } - }).concat(query.middleware); + }).concat(gtsApi.middleware); } }); -const persistor = persistStore(store); +export const persistor = persistStore(store); -module.exports = { store, persistor };
\ No newline at end of file +export type AppDispatch = typeof store.dispatch; +export type RootState = ReturnType<typeof store.getState>; diff --git a/web/source/settings/user/profile.js b/web/source/settings/user/profile.js index dd600cafc..6c26bb406 100644 --- a/web/source/settings/user/profile.js +++ b/web/source/settings/user/profile.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../lib/query"); diff --git a/web/source/settings/user/settings.js b/web/source/settings/user/settings.js index 68483f105..2a23a87e0 100644 --- a/web/source/settings/user/settings.js +++ b/web/source/settings/user/settings.js @@ -17,8 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -"use strict"; - const React = require("react"); const query = require("../lib/query"); |