diff --git a/src/App.tsx b/src/App.tsx index 2be9298..039f896 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { withRouter } from 'react-router'; import { BrowserRouter } from 'react-router-dom'; +import { createSelector } from 'reselect'; import getMuiTheme from 'material-ui/styles/getMuiTheme'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import { amber500, amber700, lightBlue500, darkBlack, white } from 'material-ui/styles/colors'; @@ -130,7 +131,7 @@ class App extends React.PureComponent { }; props: { - credentials?: store.CredentialsType; + credentials: store.CredentialsType; }; constructor(props: any) { @@ -170,7 +171,7 @@ class App extends React.PureComponent { - + @@ -178,9 +179,30 @@ class App extends React.PureComponent { } } +const credentialsSelector = createSelector( + (state: store.StoreState) => state.credentials.value, + (state: store.StoreState) => state.credentials.error, + (state: store.StoreState) => state.credentials.fetching, + (state: store.StoreState) => state.encryptionKey.key, + (value, error, fetching, encryptionKey) => { + if (value === null) { + return {value, error, fetching}; + } + + return { + error: error, + fetching: fetching, + value: { + ...value, + encryptionKey: encryptionKey, + } + }; + } +); + const mapStateToProps = (state: store.StoreState) => { return { - credentials: state.credentials, + credentials: credentialsSelector(state), }; }; diff --git a/src/LoginGate.tsx b/src/LoginGate.tsx index 6321e3f..6692261 100644 --- a/src/LoginGate.tsx +++ b/src/LoginGate.tsx @@ -1,18 +1,16 @@ import * as React from 'react'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router'; import Container from './widgets/Container'; import ExternalLink from './widgets/ExternalLink'; import SyncGate from './SyncGate'; import LoginForm from './components/LoginForm'; -import { store, StoreState, CredentialsType } from './store'; +import { store, CredentialsType } from './store'; import { fetchCredentials } from './store/actions'; import * as C from './constants'; -class Root extends React.PureComponent { +class Root extends React.Component { props: { credentials: CredentialsType; }; @@ -65,13 +63,4 @@ class Root extends React.PureComponent { } } -const mapStateToProps = (state: StoreState) => { - return { - credentials: state.credentials, - }; -}; - -// FIXME: withRouter is only needed here because of https://github.com/ReactTraining/react-router/issues/5795 -export default withRouter(connect( - mapStateToProps -)(Root)); +export default Root; diff --git a/src/store/index.ts b/src/store/index.ts index f927dc8..e165e65 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -6,13 +6,14 @@ import { createLogger } from 'redux-logger'; import promiseMiddleware from './promise-middleware'; import reducers from './reducers'; -import { CredentialsType, JournalsType, EntriesType } from './reducers'; +import { CredentialsTypeRemote, JournalsType, EntriesType } from './reducers'; export { CredentialsType, CredentialsData, JournalsType, JournalsData, EntriesType, EntriesData } from './reducers'; export interface StoreState { fetchCount: number; - credentials: CredentialsType; + credentials: CredentialsTypeRemote; + encryptionKey: {key: string}; cache: { journals: JournalsType; entries: EntriesType; diff --git a/src/store/reducers.ts b/src/store/reducers.ts index bacbdf0..ecf373a 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -17,9 +17,12 @@ interface FetchTypeInterface { error?: Error; } -export interface CredentialsData { +export interface CredentialsDataRemote { serviceApiUrl: string; credentials: EteSync.Credentials; +} + +export interface CredentialsData extends CredentialsDataRemote { encryptionKey: string; } @@ -33,6 +36,7 @@ function fetchTypeRecord() { } export type CredentialsType = FetchType; +export type CredentialsTypeRemote = FetchType; export type JournalsData = List; @@ -47,23 +51,6 @@ const EntriesFetchRecord = fetchTypeRecord(); export type EntriesTypeImmutable = Map>>; export type EntriesType = Map>; -function credentialsIdentityReducer(state: CredentialsType = {value: null}, action: any, extend: boolean = false) { - if (action.error) { - return { - value: null, - error: action.payload, - }; - } else { - const fetching = (action.payload === undefined) ? true : undefined; - const payload = (action.payload === undefined) ? null : action.payload; - let value = payload; - return { - fetching, - value, - }; - } -} - function fetchTypeIdentityReducer( state: Record> = fetchTypeRecord()(), action: any, extend: boolean = false) { if (action.error) { @@ -91,10 +78,50 @@ function fetchTypeIdentityReducer( } } +const encryptionKeyReducer = handleActions( + { + [actions.fetchCredentials.toString()]: ( + state: {key: string | null}, action: any) => { + if (action.error) { + return {key: null}; + } else if (action.payload === undefined) { + return {key: null}; + } else { + return {key: action.payload.encryptionKey}; + } + }, + [actions.logout.toString()]: (state: {key: string | null}, action: any) => { + return {out: true, key: null}; + }, + }, + {key: null} +); + const credentials = handleActions( { - [actions.fetchCredentials.toString()]: credentialsIdentityReducer, - [actions.logout.toString()]: (state: CredentialsType, action: any) => { + [actions.fetchCredentials.toString()]: ( + state: CredentialsTypeRemote, action: any, extend: boolean = false) => { + if (action.error) { + return { + value: null, + error: action.payload, + }; + } else if (action.payload === undefined) { + return { + fetching: true, + value: null, + }; + } else { + const { + encryptionKey, // We don't want to set encryption key here. + ...payload + } = action.payload; + return { + value: payload, + }; + } + }, + [actions.logout.toString()]: (state: CredentialsTypeRemote, action: any) => { return {out: true, value: null}; }, }, @@ -159,10 +186,15 @@ const fetchCount = handleAction( const credentialsPersistConfig = { key: 'credentials', - storage: session, + storage: localforage, whitelist: ['value'], }; +const encryptionKeyPersistConfig = { + key: 'encryptionKey', + storage: session, +}; + const journalsSerialize = (state: JournalsData) => { if (state === null) { return null; @@ -240,6 +272,7 @@ const cachePersistConfig = { const reducers = combineReducers({ fetchCount, credentials: persistReducer(credentialsPersistConfig, credentials), + encryptionKey: persistReducer(encryptionKeyPersistConfig, encryptionKeyReducer), cache: persistReducer(cachePersistConfig, combineReducers({ entries, journals,