diff --git a/web/source/css/_colors.css b/web/source/css/_colors.css
index f8c266539..055572ed9 100644
--- a/web/source/css/_colors.css
+++ b/web/source/css/_colors.css
@@ -44,6 +44,9 @@ $blue1: #3a9fde; /* darker blue for smaller elements (borders), can only be used
$blue2: #66befe; /* all-round accent color, can be used with $gray1 (6.8), $gray2 (5.5), $gray3 (4.9), $gray4 (4.5) */
$blue3: #89caff; /* hover/selected accent to $blue2, can be used with $gray1 (7.9), $gray2 (6.3), $gray3 (5.6), $gray4 (5.2), $gray5 (4.7) */
+$error1: #860000; /* Error border/foreground text, can be used with $error2 (5.0), $white1 (10), $white2 (5.1) */
+$error2: #ff9796; /* Error background text, can be used with $error1 (5.0), $gray1 (6.6), $gray2 (5.3), $gray3 (4.8) */
+
$fg: $white1;
$bg: $gray1;
diff --git a/web/source/package.json b/web/source/package.json
index 41c7260e7..51a49a478 100644
--- a/web/source/package.json
+++ b/web/source/package.json
@@ -38,7 +38,9 @@
"react-error-boundary": "^3.1.4",
"react-redux": "^8.0.2",
"reactify": "^1.1.1",
+ "redux-devtools-extension": "^2.13.9",
"redux-persist": "^6.0.0",
+ "redux-thunk": "^2.4.1",
"uglifyify": "^5.0.2",
"wouter": "^2.8.0-alpha.2"
},
diff --git a/web/source/settings-panel/components/login.jsx b/web/source/settings-panel/components/login.jsx
index 180ab738f..636a7f326 100644
--- a/web/source/settings-panel/components/login.jsx
+++ b/web/source/settings-panel/components/login.jsx
@@ -18,6 +18,76 @@
"use strict";
+const Promise = require("bluebird");
+const React = require("react");
+const Redux = require("react-redux");
+
+const { setInstance } = require("../redux/reducers/instances").actions;
+const { updateInstance, updateRegistration } = require("../lib/api");
+
module.exports = function Login() {
- return (null);
+ const dispatch = Redux.useDispatch();
+ const [ instanceField, setInstanceField ] = React.useState("");
+ const [ errorMsg, setErrorMsg ] = React.useState();
+ const instanceFieldRef = React.useRef("");
+
+ React.useEffect(() => {
+ // check if current domain runs an instance
+ Promise.try(() => {
+ console.log("trying", window.location.origin);
+ return dispatch(updateInstance(window.location.origin));
+ }).then((json) => {
+ if (instanceFieldRef.current.length == 0) { // user hasn't started typing yet
+ dispatch(setInstance(json.uri));
+ instanceFieldRef.current = json.uri;
+ setInstanceField(json.uri);
+ }
+ }).catch((e) => {
+ console.log("Current domain does not host a valid instance: ", e);
+ });
+ }, []);
+
+ function tryInstance() {
+ Promise.try(() => {
+ return dispatch(updateInstance(instanceFieldRef.current)).catch((e) => {
+ // TODO: clearer error messages for common errors
+ console.log(e);
+ throw e;
+ });
+ }).then((instance) => {
+ // return dispatch(updateRegistration);
+ }).catch((e) => {
+ setErrorMsg(
+ <>
+ {e.type}
+ {e.message}
+ >
+ );
+ });
+ }
+
+ function updateInstanceField(e) {
+ if (e.key == "Enter") {
+ tryInstance(instanceField);
+ } else {
+ setInstanceField(e.target.value);
+ instanceFieldRef.current = e.target.value;
+ }
+ }
+
+ return (
+
+ );
};
\ No newline at end of file
diff --git a/web/source/settings-panel/index.js b/web/source/settings-panel/index.js
index 01b8eeb03..bca2ead4e 100644
--- a/web/source/settings-panel/index.js
+++ b/web/source/settings-panel/index.js
@@ -33,7 +33,7 @@ const ErrorFallback = require("./components/error");
const oauthLib = require("./lib/oauth");
-// require("./style.css");
+require("./style.css");
// TODO: nested categories?
const nav = {
@@ -90,7 +90,7 @@ function App() {
{sidebar}
{/* */}
-
+
{panelRouter}
@@ -98,7 +98,9 @@ function App() {
>
);
} else {
- return ;
+ return (
+
+ );
}
}
diff --git a/web/source/settings-panel/lib/api.js b/web/source/settings-panel/lib/api.js
new file mode 100644
index 000000000..e330ed64b
--- /dev/null
+++ b/web/source/settings-panel/lib/api.js
@@ -0,0 +1,94 @@
+/*
+ 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 .
+*/
+
+"use strict";
+
+const Promise = require("bluebird");
+const { setRegistration } = require("../redux/reducers/oauth").actions;
+const { setInstanceInfo } = require("../redux/reducers/instances").actions;
+
+function apiCall(base, method, route, {payload, headers={}}) {
+ return Promise.try(() => {
+ let url = new URL(base);
+ url.pathname = route;
+ let body;
+
+ if (payload != undefined) {
+ body = JSON.stringify(payload);
+ }
+
+ let fetchHeaders = {
+ "Content-Type": "application/json",
+ ...headers
+ };
+
+ return fetch(url.toString(), {
+ method: method,
+ headers: fetchHeaders,
+ body: body
+ });
+ }).then((res) => {
+ if (res.status == 200) {
+ return res.json();
+ } else {
+ throw res;
+ }
+ });
+}
+
+function getCurrentUrl() {
+ return `${window.location.origin}${window.location.pathname}`;
+}
+
+function updateInstance(domain) {
+ return function(dispatch, getState) {
+ /* check if domain is valid instance, then register client if needed */
+
+ return Promise.try(() => {
+ return apiCall(domain, "GET", "/api/v1/instance", {
+ headers: {
+ "Content-Type": "text/plain"
+ }
+ });
+ }).then((json) => {
+ if (json && json.uri) { // TODO: validate instance json more?
+ dispatch(setInstanceInfo(json.uri, json));
+ return json;
+ }
+ });
+ };
+}
+
+function updateRegistration() {
+ return function(dispatch, getState) {
+ let base = getState().oauth.instance;
+ return Promise.try(() => {
+ return apiCall(base, "POST", "/api/v1/apps", {
+ client_name: "GoToSocial Settings",
+ scopes: "write admin",
+ redirect_uris: getCurrentUrl(),
+ website: getCurrentUrl()
+ });
+ }).then((json) => {
+ console.log(json);
+ dispatch(setRegistration(base, json));
+ });
+ };
+}
+
+module.exports = { updateInstance, updateRegistration };
\ No newline at end of file
diff --git a/web/source/settings-panel/redux/index.js b/web/source/settings-panel/redux/index.js
index 12ad9d9b8..422bfab11 100644
--- a/web/source/settings-panel/redux/index.js
+++ b/web/source/settings-panel/redux/index.js
@@ -18,22 +18,27 @@
"use strict";
-const { createStore, combineReducers } = require("redux");
+const { createStore, combineReducers, applyMiddleware } = require("redux");
const { persistStore, persistReducer } = require("redux-persist");
+const thunk = require("redux-thunk").default;
+const { composeWithDevTools } = require("redux-devtools-extension");
const persistConfig = {
key: "gotosocial-settings",
storage: require("redux-persist/lib/storage").default,
- stateReconciler: require("redux-persist/lib/stateReconciler/autoMergeLevel2").default
+ stateReconciler: require("redux-persist/lib/stateReconciler/autoMergeLevel2").default,
+ whitelist: ['oauth']
};
const combinedReducers = combineReducers({
- oauth: require("./reducers/oauth").reducer
+ oauth: require("./reducers/oauth").reducer,
+ instances: require("./reducers/instances").reducer,
});
const persistedReducer = persistReducer(persistConfig, combinedReducers);
+const composedEnhancer = composeWithDevTools(applyMiddleware(thunk));
-const store = createStore(persistedReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
+const store = createStore(persistedReducer, composedEnhancer);
const persistor = persistStore(store);
module.exports = { store, persistor };
\ No newline at end of file
diff --git a/web/source/settings-panel/redux/reducers/instances.js b/web/source/settings-panel/redux/reducers/instances.js
new file mode 100644
index 000000000..92c7e982c
--- /dev/null
+++ b/web/source/settings-panel/redux/reducers/instances.js
@@ -0,0 +1,38 @@
+/*
+ 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 .
+*/
+
+"use strict";
+
+const {createSlice} = require("@reduxjs/toolkit");
+
+module.exports = createSlice({
+ name: "instances",
+ initialState: {
+ info: {},
+ current: undefined
+ },
+ reducers: {
+ setInstance: (state, {payload}) => {
+ state.current = payload;
+ },
+ setInstanceInfo: (state, {payload}) => {
+ let [key, info] = payload;
+ state.info[key] = info;
+ },
+ }
+});
\ No newline at end of file
diff --git a/web/source/settings-panel/redux/reducers/oauth.js b/web/source/settings-panel/redux/reducers/oauth.js
index d2186392c..a3202703e 100644
--- a/web/source/settings-panel/redux/reducers/oauth.js
+++ b/web/source/settings-panel/redux/reducers/oauth.js
@@ -23,20 +23,13 @@ const {createSlice} = require("@reduxjs/toolkit");
module.exports = createSlice({
name: "oauth",
initialState: {
- loggedIn: false
+ loggedIn: false,
+ registrations: {}
},
reducers: {
- setInstance: (state, {payload}) => {
- return {
- ...state,
- instance: payload
- };
- },
setRegistration: (state, {payload}) => {
- return {
- ...state,
-
- }
+ let [key, info] = payload;
+ state.instanceRegistration[key] = info;
}
}
});
\ No newline at end of file
diff --git a/web/source/settings-panel/style.css b/web/source/settings-panel/style.css
index 051038c58..a44e5d547 100644
--- a/web/source/settings-panel/style.css
+++ b/web/source/settings-panel/style.css
@@ -32,7 +32,7 @@ section {
display: grid;
grid-template-columns: 1fr min(92%, 90ch) 1fr;
- section {
+ section.with-sidebar {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
@@ -128,10 +128,16 @@ input, select, textarea {
}
.error {
+ background: $error2;
+ border: 1px solid $error1;
+ border-radius: $br;
+ color: $error1;
font-weight: bold;
+ padding: 0.5rem;
pre {
background: $bg;
+ color: $fg;
padding: 1rem;
overflow: auto;
margin: 0;
diff --git a/web/source/yarn.lock b/web/source/yarn.lock
index 4f3cfd647..dd8056b87 100644
--- a/web/source/yarn.lock
+++ b/web/source/yarn.lock
@@ -4854,6 +4854,11 @@ recast@^0.11.17:
private "~0.1.5"
source-map "~0.5.0"
+redux-devtools-extension@^2.13.9:
+ version "2.13.9"
+ resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz#6b764e8028b507adcb75a1cae790f71e6be08ae7"
+ integrity sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==
+
redux-persist@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"