You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
4.6 KiB
TypeScript

import { createStore, combineReducers, applyMiddleware } from 'redux';
import { persistReducer, persistStore } from 'redux-persist';
import { createActions, handleAction, handleActions } from 'redux-actions';
import session from 'redux-persist/lib/storage/session';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';
import promiseMiddleware from './promise-middleware';
import * as EteSync from './api/EteSync';
const loggerMiddleware = createLogger();
export interface FetchType<T> {
value: T | null;
fetching?: boolean;
error?: Error;
}
export interface CredentialsData {
serviceApiUrl: string;
credentials: EteSync.Credentials;
encryptionKey: string;
}
export type CredentialsType = FetchType<CredentialsData>;
export type JournalsData = Array<EteSync.Journal>;
export type JournalsType = FetchType<JournalsData>;
export type EntriesData = Array<EteSync.Entry>;
export type EntriesType = {[key: string]: FetchType<EntriesData>};
export interface StoreState {
fetchCount: number;
credentials: CredentialsType;
cache: {
journals: JournalsType;
entries: EntriesType;
};
}
function fetchTypeIdentityReducer(state: FetchType<any>, action: any) {
if (action.error) {
return {
value: null,
error: action.payload,
};
} else {
const fetching = (action.payload === undefined) ? true : undefined;
return {
fetching,
value: (action.payload === undefined) ? null : action.payload,
};
}
}
export const { fetchCredentials, logout } = createActions({
FETCH_CREDENTIALS: (username: string, password: string, encryptionPassword: string, server: string) => {
const authenticator = new EteSync.Authenticator(server);
return new Promise((resolve, reject) => {
authenticator.getAuthToken(username, password).then(
(authToken) => {
const creds = new EteSync.Credentials(username, authToken);
const derived = EteSync.deriveKey(username, encryptionPassword);
const context = {
serviceApiUrl: server,
credentials: creds,
encryptionKey: derived,
};
resolve(context);
},
(error) => {
reject(error);
}
);
});
},
LOGOUT: () => undefined,
});
export const { fetchJournals, fetchEntries } = createActions({
FETCH_JOURNALS: (etesync: CredentialsData) => {
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
let journalManager = new EteSync.JournalManager(creds, apiBase);
return journalManager.list();
},
FETCH_ENTRIES: [
(etesync: CredentialsData, journalUid: string, prevUid: string | null) => {
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
let entryManager = new EteSync.EntryManager(creds, apiBase, journalUid);
return entryManager.list(prevUid) as any;
},
(etesync: CredentialsData, journalUid: string, prevUid: string | null) => {
return { journal: journalUid, prevUid };
}
]
});
const credentials = handleActions(
{
[fetchCredentials.toString()]: fetchTypeIdentityReducer,
[logout.toString()]: (state: CredentialsType, action: any) => {
return {out: true, value: null};
},
},
{value: null}
);
const entries = handleAction(
fetchEntries,
(state: EntriesType, action: any) => {
if (action.error) {
return { ...state,
[action.meta.journal]: {
value: null,
error: action.payload,
},
};
} else {
const fetching = (action.payload === undefined) ? true : undefined;
return { ...state,
[action.meta.journal]: {
fetching,
value: (action.payload === undefined) ? null : action.payload,
},
};
}
},
{}
);
const journals = handleAction(
fetchJournals,
fetchTypeIdentityReducer,
{value: null}
);
function fetchCount(state: number = 0, action: any) {
switch (action.type) {
case fetchCredentials.toString():
case fetchJournals.toString():
case fetchEntries.toString():
if (action.payload === undefined) {
return state + 1;
} else {
return state - 1;
}
default:
return state;
}
}
const credentialsPersistConfig = {
key: 'credentials',
storage: session,
whitelist: ['value'],
};
const reducers = combineReducers({
fetchCount,
credentials: persistReducer(credentialsPersistConfig, credentials),
cache: combineReducers({
journals,
entries,
})
});
export const store = createStore(
reducers,
applyMiddleware(
thunkMiddleware,
promiseMiddleware,
loggerMiddleware
)
);
export const persistor = persistStore(store);