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) */
|
$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) */
|
$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;
|
$fg: $white1;
|
||||||
$bg: $gray1;
|
$bg: $gray1;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,9 @@
|
||||||
"react-error-boundary": "^3.1.4",
|
"react-error-boundary": "^3.1.4",
|
||||||
"react-redux": "^8.0.2",
|
"react-redux": "^8.0.2",
|
||||||
"reactify": "^1.1.1",
|
"reactify": "^1.1.1",
|
||||||
|
"redux-devtools-extension": "^2.13.9",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
|
"redux-thunk": "^2.4.1",
|
||||||
"uglifyify": "^5.0.2",
|
"uglifyify": "^5.0.2",
|
||||||
"wouter": "^2.8.0-alpha.2"
|
"wouter": "^2.8.0-alpha.2"
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,6 +18,76 @@
|
||||||
|
|
||||||
"use strict";
|
"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() {
|
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");
|
const oauthLib = require("./lib/oauth");
|
||||||
|
|
||||||
// require("./style.css");
|
require("./style.css");
|
||||||
|
|
||||||
// TODO: nested categories?
|
// TODO: nested categories?
|
||||||
const nav = {
|
const nav = {
|
||||||
|
@ -90,7 +90,7 @@ function App() {
|
||||||
{sidebar}
|
{sidebar}
|
||||||
{/* <button className="logout" onClick={oauth.logout}>Log out</button> */}
|
{/* <button className="logout" onClick={oauth.logout}>Log out</button> */}
|
||||||
</div>
|
</div>
|
||||||
<section>
|
<section className="with-sidebar">
|
||||||
<Switch>
|
<Switch>
|
||||||
{panelRouter}
|
{panelRouter}
|
||||||
</Switch>
|
</Switch>
|
||||||
|
@ -98,7 +98,9 @@ function App() {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} 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";
|
"use strict";
|
||||||
|
|
||||||
const { createStore, combineReducers } = require("redux");
|
const { createStore, combineReducers, applyMiddleware } = require("redux");
|
||||||
const { persistStore, persistReducer } = require("redux-persist");
|
const { persistStore, persistReducer } = require("redux-persist");
|
||||||
|
const thunk = require("redux-thunk").default;
|
||||||
|
const { composeWithDevTools } = require("redux-devtools-extension");
|
||||||
|
|
||||||
const persistConfig = {
|
const persistConfig = {
|
||||||
key: "gotosocial-settings",
|
key: "gotosocial-settings",
|
||||||
storage: require("redux-persist/lib/storage").default,
|
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({
|
const combinedReducers = combineReducers({
|
||||||
oauth: require("./reducers/oauth").reducer
|
oauth: require("./reducers/oauth").reducer,
|
||||||
|
instances: require("./reducers/instances").reducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const persistedReducer = persistReducer(persistConfig, combinedReducers);
|
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);
|
const persistor = persistStore(store);
|
||||||
|
|
||||||
module.exports = { store, persistor };
|
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({
|
module.exports = createSlice({
|
||||||
name: "oauth",
|
name: "oauth",
|
||||||
initialState: {
|
initialState: {
|
||||||
loggedIn: false
|
loggedIn: false,
|
||||||
|
registrations: {}
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
setInstance: (state, {payload}) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
instance: payload
|
|
||||||
};
|
|
||||||
},
|
|
||||||
setRegistration: (state, {payload}) => {
|
setRegistration: (state, {payload}) => {
|
||||||
return {
|
let [key, info] = payload;
|
||||||
...state,
|
state.instanceRegistration[key] = info;
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -32,7 +32,7 @@ section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr min(92%, 90ch) 1fr;
|
grid-template-columns: 1fr min(92%, 90ch) 1fr;
|
||||||
|
|
||||||
section {
|
section.with-sidebar {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
|
@ -128,10 +128,16 @@ input, select, textarea {
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
|
background: $error2;
|
||||||
|
border: 1px solid $error1;
|
||||||
|
border-radius: $br;
|
||||||
|
color: $error1;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
padding: 0.5rem;
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background: $bg;
|
background: $bg;
|
||||||
|
color: $fg;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
|
@ -4854,6 +4854,11 @@ recast@^0.11.17:
|
||||||
private "~0.1.5"
|
private "~0.1.5"
|
||||||
source-map "~0.5.0"
|
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:
|
redux-persist@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
|
resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
|
||||||
|
|
Loading…
Reference in New Issue