mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-11-30 02:32:58 +05:30
Move App into shell dir. Decouple ContextProviders. Improved type coverage for reducers
This commit is contained in:
parent
5c70021456
commit
128f327ec4
@ -1,34 +0,0 @@
|
||||
import React from 'react';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
import { Provider as ReduxProvider } from 'react-redux';
|
||||
import { Router, Route, Switch } from 'react-router-dom';
|
||||
import { IntlProvider } from 'app/components/i18n';
|
||||
import { Store } from 'redux';
|
||||
import AuthFlowRoute from 'app/containers/AuthFlowRoute';
|
||||
import RootPage from 'app/pages/root/RootPage';
|
||||
import SuccessOauthPage from 'app/pages/auth/SuccessOauthPage';
|
||||
|
||||
const App = ({
|
||||
store,
|
||||
browserHistory,
|
||||
}: {
|
||||
store: Store;
|
||||
browserHistory: any;
|
||||
}) => (
|
||||
<ReduxProvider store={store}>
|
||||
<IntlProvider>
|
||||
<Router history={browserHistory}>
|
||||
<Switch>
|
||||
<Route path="/oauth2/code/success" component={SuccessOauthPage} />
|
||||
<AuthFlowRoute
|
||||
path="/oauth2/:version(v\d+)/:clientId?"
|
||||
component={() => null}
|
||||
/>
|
||||
<Route path="/" component={RootPage} />
|
||||
</Switch>
|
||||
</Router>
|
||||
</IntlProvider>
|
||||
</ReduxProvider>
|
||||
);
|
||||
|
||||
export default hot(App);
|
@ -1,4 +1,5 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import { RootState } from 'app/reducers';
|
||||
|
||||
import {
|
||||
ERROR,
|
||||
@ -27,9 +28,25 @@ export interface Client {
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface OAuthState {
|
||||
clientId: string;
|
||||
redirectUrl: string;
|
||||
responseType: string;
|
||||
description?: string;
|
||||
scope: string;
|
||||
prompt: string;
|
||||
loginHint: string;
|
||||
state: string;
|
||||
success?: boolean;
|
||||
code?: string;
|
||||
displayCode?: string;
|
||||
acceptRequired?: boolean;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
credentials: Credentials;
|
||||
error:
|
||||
| null
|
||||
| string
|
||||
| {
|
||||
type: string;
|
||||
@ -38,25 +55,11 @@ export interface State {
|
||||
isLoading: boolean;
|
||||
isSwitcherEnabled: boolean;
|
||||
client: Client | null;
|
||||
login: string;
|
||||
oauth: {
|
||||
clientId: string;
|
||||
redirectUrl: string;
|
||||
responseType: string;
|
||||
description: string;
|
||||
scope: string;
|
||||
prompt: string;
|
||||
loginHint: string;
|
||||
state: string;
|
||||
success?: boolean;
|
||||
code?: string;
|
||||
displayCode?: string;
|
||||
acceptRequired?: boolean;
|
||||
} | null;
|
||||
oauth: OAuthState | null;
|
||||
scopes: string[];
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
export default combineReducers<State>({
|
||||
credentials,
|
||||
error,
|
||||
isLoading,
|
||||
@ -66,7 +69,10 @@ export default combineReducers({
|
||||
scopes,
|
||||
});
|
||||
|
||||
function error(state = null, { type, payload = null, error = false }) {
|
||||
function error(
|
||||
state = null,
|
||||
{ type, payload = null, error = false },
|
||||
): State['error'] {
|
||||
switch (type) {
|
||||
case ERROR:
|
||||
if (!error) {
|
||||
@ -89,7 +95,7 @@ function credentials(
|
||||
type: string;
|
||||
payload: Credentials | null;
|
||||
},
|
||||
) {
|
||||
): State['credentials'] {
|
||||
if (type === SET_CREDENTIALS) {
|
||||
if (payload && typeof payload === 'object') {
|
||||
return {
|
||||
@ -103,7 +109,10 @@ function credentials(
|
||||
return state;
|
||||
}
|
||||
|
||||
function isSwitcherEnabled(state = true, { type, payload = false }) {
|
||||
function isSwitcherEnabled(
|
||||
state = true,
|
||||
{ type, payload = false },
|
||||
): State['isSwitcherEnabled'] {
|
||||
switch (type) {
|
||||
case SET_SWITCHER:
|
||||
if (typeof payload !== 'boolean') {
|
||||
@ -117,7 +126,10 @@ function isSwitcherEnabled(state = true, { type, payload = false }) {
|
||||
}
|
||||
}
|
||||
|
||||
function isLoading(state = false, { type, payload = null }) {
|
||||
function isLoading(
|
||||
state = false,
|
||||
{ type, payload = null },
|
||||
): State['isLoading'] {
|
||||
switch (type) {
|
||||
case SET_LOADING_STATE:
|
||||
return !!payload;
|
||||
@ -127,7 +139,7 @@ function isLoading(state = false, { type, payload = null }) {
|
||||
}
|
||||
}
|
||||
|
||||
function client(state = null, { type, payload }) {
|
||||
function client(state = null, { type, payload }): State['client'] {
|
||||
switch (type) {
|
||||
case SET_CLIENT:
|
||||
return {
|
||||
@ -141,7 +153,10 @@ function client(state = null, { type, payload }) {
|
||||
}
|
||||
}
|
||||
|
||||
function oauth(state: State | null = null, { type, payload }) {
|
||||
function oauth(
|
||||
state: State['oauth'] = null,
|
||||
{ type, payload },
|
||||
): State['oauth'] {
|
||||
switch (type) {
|
||||
case SET_OAUTH:
|
||||
return {
|
||||
@ -156,7 +171,7 @@ function oauth(state: State | null = null, { type, payload }) {
|
||||
|
||||
case SET_OAUTH_RESULT:
|
||||
return {
|
||||
...state,
|
||||
...(state as OAuthState),
|
||||
success: payload.success,
|
||||
code: payload.code,
|
||||
displayCode: payload.displayCode,
|
||||
@ -164,7 +179,7 @@ function oauth(state: State | null = null, { type, payload }) {
|
||||
|
||||
case REQUIRE_PERMISSIONS_ACCEPT:
|
||||
return {
|
||||
...state,
|
||||
...(state as OAuthState),
|
||||
acceptRequired: true,
|
||||
};
|
||||
|
||||
@ -173,7 +188,7 @@ function oauth(state: State | null = null, { type, payload }) {
|
||||
}
|
||||
}
|
||||
|
||||
function scopes(state = [], { type, payload = [] }) {
|
||||
function scopes(state = [], { type, payload = [] }): State['scopes'] {
|
||||
switch (type) {
|
||||
case SET_SCOPES:
|
||||
return payload;
|
||||
@ -183,10 +198,10 @@ function scopes(state = [], { type, payload = [] }) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getLogin(state: { [key: string]: any }): string | null {
|
||||
export function getLogin(state: RootState): string | null {
|
||||
return state.auth.credentials.login || null;
|
||||
}
|
||||
|
||||
export function getCredentials(state: { [key: string]: any }): Credentials {
|
||||
export function getCredentials(state: RootState): Credentials {
|
||||
return state.auth.credentials;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { BSOD } from './actions';
|
||||
|
||||
export default function(state = false, { type }) {
|
||||
export type State = boolean;
|
||||
|
||||
export default function(state: State = false, { type }): State {
|
||||
if (type === BSOD) {
|
||||
return true;
|
||||
}
|
@ -14,7 +14,7 @@ import history, { browserHistory } from 'app/services/history';
|
||||
import i18n from 'app/services/i18n';
|
||||
import { loadScript, debounce } from 'app/functions';
|
||||
|
||||
import App from './App';
|
||||
import App from './shell';
|
||||
|
||||
const win: { [key: string]: any } = window as any;
|
||||
|
||||
@ -35,7 +35,7 @@ Promise.all([
|
||||
i18n.ensureIntl(), // ensure, that intl is polyfilled before any rendering
|
||||
]).then(() => {
|
||||
ReactDOM.render(
|
||||
<App store={store} browserHistory={browserHistory} />,
|
||||
<App store={store} history={browserHistory} />,
|
||||
document.getElementById('app'),
|
||||
);
|
||||
|
||||
|
@ -7,13 +7,14 @@ import accounts, {
|
||||
} from 'app/components/accounts/reducer';
|
||||
import i18n, { State as I18nState } from 'app/components/i18n/reducer';
|
||||
import popup, { State as PopupState } from 'app/components/ui/popup/reducer';
|
||||
import bsod from 'app/components/ui/bsod/reducer';
|
||||
import bsod, { State as BsodState } from 'app/components/ui/bsod/reducer';
|
||||
import apps, { Apps } from 'app/components/dev/apps/reducer';
|
||||
import { ThunkDispatch, ThunkAction as ReduxThunkAction } from 'redux-thunk';
|
||||
import { Store as ReduxStore } from 'redux';
|
||||
|
||||
export interface RootState {
|
||||
auth: AuthState;
|
||||
bsod: BsodState;
|
||||
accounts: AccountsState;
|
||||
user: User;
|
||||
popup: PopupState;
|
||||
@ -41,7 +42,7 @@ export type Store = ReduxStore<RootState> & {
|
||||
dispatch: Dispatch;
|
||||
};
|
||||
|
||||
export default combineReducers({
|
||||
export default combineReducers<RootState>({
|
||||
bsod,
|
||||
auth,
|
||||
user,
|
||||
|
@ -33,7 +33,7 @@ type OauthRequestData = {
|
||||
client_id: string;
|
||||
redirect_uri: string;
|
||||
response_type: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
scope: string;
|
||||
prompt: string;
|
||||
login_hint?: string;
|
||||
@ -44,7 +44,7 @@ export type OauthData = {
|
||||
clientId: string;
|
||||
redirectUrl: string;
|
||||
responseType: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
scope: string;
|
||||
prompt: string; // comma separated list of 'none' | 'consent' | 'select_account';
|
||||
loginHint?: string;
|
||||
|
24
packages/app/shell/App.tsx
Normal file
24
packages/app/shell/App.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { Store } from 'app/reducers';
|
||||
import AuthFlowRoute from 'app/containers/AuthFlowRoute';
|
||||
import RootPage from 'app/pages/root/RootPage';
|
||||
import SuccessOauthPage from 'app/pages/auth/SuccessOauthPage';
|
||||
|
||||
import ContextProvider from './ContextProvider';
|
||||
|
||||
const App = ({ store, history }: { store: Store; history: any }) => (
|
||||
<ContextProvider store={store} history={history}>
|
||||
<Switch>
|
||||
<Route path="/oauth2/code/success" component={SuccessOauthPage} />
|
||||
<AuthFlowRoute
|
||||
path="/oauth2/:version(v\d+)/:clientId?"
|
||||
component={() => null}
|
||||
/>
|
||||
<Route path="/" component={RootPage} />
|
||||
</Switch>
|
||||
</ContextProvider>
|
||||
);
|
||||
|
||||
export default hot(App);
|
25
packages/app/shell/ContextProvider.tsx
Normal file
25
packages/app/shell/ContextProvider.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { Provider as ReduxProvider } from 'react-redux';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { IntlProvider } from 'app/components/i18n';
|
||||
import { Store } from 'app/reducers';
|
||||
|
||||
function ContextProvider({
|
||||
children,
|
||||
store,
|
||||
history,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
store: Store;
|
||||
history: any;
|
||||
}) {
|
||||
return (
|
||||
<ReduxProvider store={store}>
|
||||
<IntlProvider>
|
||||
<Router history={history}>{children}</Router>
|
||||
</IntlProvider>
|
||||
</ReduxProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContextProvider;
|
2
packages/app/shell/index.ts
Normal file
2
packages/app/shell/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default } from './App';
|
||||
export { default as ContextProvider } from './ContextProvider';
|
@ -6,26 +6,25 @@ import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import persistState from 'redux-localstorage';
|
||||
|
||||
import reducers from 'app/reducers';
|
||||
import reducers, { Store } from 'app/reducers';
|
||||
|
||||
export default function storeFactory() {
|
||||
export default function storeFactory(): Store {
|
||||
const middlewares = applyMiddleware(thunk);
|
||||
const persistStateEnhancer = persistState(['accounts', 'user'], {
|
||||
key: 'redux-storage',
|
||||
});
|
||||
|
||||
/* global process: false */
|
||||
let enhancer;
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
enhancer = compose(middlewares, persistStateEnhancer);
|
||||
} else {
|
||||
const composeEnhancers =
|
||||
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
enhancer = composeEnhancers(middlewares, persistStateEnhancer);
|
||||
}
|
||||
|
||||
const store = createStore(reducers, {}, enhancer);
|
||||
const store = createStore(reducers, {}, enhancer) as Store;
|
||||
|
||||
// Hot reload reducers
|
||||
if (module.hot && typeof module.hot.accept === 'function') {
|
Loading…
Reference in New Issue
Block a user