login flow up till app registration
This commit is contained in:
parent
cc69e65a72
commit
a4bb869d0f
|
@ -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;
|
||||
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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(
|
||||
<>
|
||||
<b>{e.type}</b>
|
||||
<span>{e.message}</span>
|
||||
</>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function updateInstanceField(e) {
|
||||
if (e.key == "Enter") {
|
||||
tryInstance(instanceField);
|
||||
} else {
|
||||
setInstanceField(e.target.value);
|
||||
instanceFieldRef.current = e.target.value;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="login">
|
||||
<h1>OAUTH Login:</h1>
|
||||
<form onSubmit={(e) => e.preventDefault()}>
|
||||
<label htmlFor="instance">Instance: </label>
|
||||
<input value={instanceField} onChange={updateInstanceField} id="instance"/>
|
||||
{errorMsg &&
|
||||
<div className="error">
|
||||
{errorMsg}
|
||||
</div>
|
||||
}
|
||||
<button onClick={tryInstance}>Authenticate</button>
|
||||
</form>
|
||||
</section>
|
||||
);
|
||||
};
|
|
@ -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}
|
||||
{/* <button className="logout" onClick={oauth.logout}>Log out</button> */}
|
||||
</div>
|
||||
<section>
|
||||
<section className="with-sidebar">
|
||||
<Switch>
|
||||
{panelRouter}
|
||||
</Switch>
|
||||
|
@ -98,7 +98,9 @@ function App() {
|
|||
</>
|
||||
);
|
||||
} else {
|
||||
return <Login />;
|
||||
return (
|
||||
<Login />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
"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 };
|
|
@ -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 };
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
"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;
|
||||
},
|
||||
}
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue