diff --git a/src/SyncGate.tsx b/src/SyncGate.tsx index dce0a79..e6dc82e 100644 --- a/src/SyncGate.tsx +++ b/src/SyncGate.tsx @@ -17,7 +17,7 @@ import * as EteSync from './api/EteSync'; import { CURRENT_VERSION } from './api/Constants'; import { store, JournalsType, EntriesType, StoreState, CredentialsData, UserInfoType } from './store'; -import { fetchAll, fetchUserInfo, createUserInfo } from './store/actions'; +import { createJournal, fetchAll, fetchEntries, fetchUserInfo, createUserInfo } from './store/actions'; export interface SyncInfoJournal { journal: EteSync.Journal; @@ -101,7 +101,25 @@ class SyncGate extends React.PureComponent { componentDidMount() { const me = this.props.etesync.credentials.email; const syncAll = () => { - store.dispatch(fetchAll(this.props.etesync, this.props.entries)); + store.dispatch(fetchAll(this.props.etesync, this.props.entries)).then((haveJournals: boolean) => { + if (haveJournals) { + return; + } + + ['ADDRESS_BOOK', 'CALENDAR'].forEach((collectionType) => { + const collection = new EteSync.CollectionInfo(); + collection.uid = EteSync.genUid(); + collection.type = collectionType; + collection.displayName = 'Default'; + + const journal = new EteSync.Journal(); + const cryptoManager = new EteSync.CryptoManager(this.props.etesync.encryptionKey, collection.uid); + journal.setInfo(cryptoManager, collection); + store.dispatch(createJournal(this.props.etesync, journal)).then(() => { + store.dispatch(fetchEntries(this.props.etesync, collection.uid)); + }); + }); + }); }; const sync = () => { diff --git a/src/api/Crypto.ts b/src/api/Crypto.ts index fe46e38..2fb717e 100644 --- a/src/api/Crypto.ts +++ b/src/api/Crypto.ts @@ -26,6 +26,11 @@ export function deriveKey(salt: string, password: string): string { return sjcl.codec.base64.fromBits((sjcl.misc as any).scrypt(password, salt, 16384, 8, 1, keySize)); } +export function genUid() { + const rand = sjcl.random.randomWords(4); + return sjcl.codec.hex.fromBits(hmac256(rand, rand)); +} + function hmac256(salt: sjcl.BitArray, key: sjcl.BitArray) { let hmac = new sjcl.misc.hmac(salt); return hmac.encrypt(key); diff --git a/src/api/EteSync.ts b/src/api/EteSync.ts index bfb3d9e..77eda10 100644 --- a/src/api/EteSync.ts +++ b/src/api/EteSync.ts @@ -7,7 +7,7 @@ import * as fetch from 'isomorphic-fetch'; import { byte, base64, stringToByteArray } from './Helpers'; import { CryptoManager, AsymmetricKeyPair, HMAC_SIZE_BYTES } from './Crypto'; -export { CryptoManager, AsymmetricCryptoManager, AsymmetricKeyPair, deriveKey } from './Crypto'; +export { CryptoManager, AsymmetricCryptoManager, AsymmetricKeyPair, deriveKey, genUid } from './Crypto'; class ExtendableError extends Error { constructor(message: any) { diff --git a/src/store/actions.ts b/src/store/actions.ts index f358c08..6597c9a 100644 --- a/src/store/actions.ts +++ b/src/store/actions.ts @@ -54,6 +54,20 @@ export const { fetchJournals } = createActions({ }, }); +export const createJournal = createAction( + 'CREATE_JOURNAL', + (etesync: CredentialsData, journal: EteSync.Journal) => { + const creds = etesync.credentials; + const apiBase = etesync.serviceApiUrl; + let journalManager = new EteSync.JournalManager(creds, apiBase); + + return journalManager.create(journal); + }, + (etesync: CredentialsData, journal: EteSync.Journal) => { + return { journal }; + }, +); + export const { fetchEntries, createEntries } = createActions({ FETCH_ENTRIES: [ (etesync: CredentialsData, journalUid: string, prevUid: string | null) => { diff --git a/src/store/reducers.ts b/src/store/reducers.ts index 96eeef5..1971baa 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -138,31 +138,62 @@ export const entries = handleAction( Map({}) ); -const journals = handleAction( - actions.fetchJournals, - (state: JournalsTypeImmutable, action: any) => { - const newState = fetchTypeIdentityReducer(state, action); - // Compare the states and see if they are really different - const oldJournals = state.get('value', null); - const newJournals = newState.get('value', null); - - if (!oldJournals || !newJournals || (oldJournals.size !== newJournals.size)) { - return newState; - } +const journals = handleActions( + { + [actions.fetchJournals.toString()]: (state: JournalsTypeImmutable, action: any) => { + const newState = fetchTypeIdentityReducer(state, action); + // Compare the states and see if they are really different + const oldJournals = state.get('value', null); + const newJournals = newState.get('value', null); + + if (!oldJournals || !newJournals || (oldJournals.size !== newJournals.size)) { + return newState; + } - let oldJournalHash = {}; - oldJournals.forEach((x) => { - oldJournalHash[x.uid] = x.serialize(); - }); + let oldJournalHash = {}; + oldJournals.forEach((x) => { + oldJournalHash[x.uid] = x.serialize(); + }); - if (newJournals.every((journal: EteSync.Journal) => ( - (journal.uid in oldJournalHash) && - (journal.serialize().content === oldJournalHash[journal.uid].content) - ))) { - return state; - } else { - return newState; - } + if (newJournals.every((journal: EteSync.Journal) => ( + (journal.uid in oldJournalHash) && + (journal.serialize().content === oldJournalHash[journal.uid].content) + ))) { + return state; + } else { + return newState; + } + }, + [actions.createJournal.toString()]: (state: JournalsTypeImmutable, _action: any) => { + const action = { ..._action }; + if (action.payload) { + action.payload = (action.meta === undefined) ? action.payload : action.meta.journal; + action.payload = [ action.payload ]; + } + + const newState = fetchTypeIdentityReducer(state, action, true); + // Compare the states and see if they are really different + const oldJournals = state.get('value', null); + const newJournals = newState.get('value', null); + + if (!oldJournals || !newJournals || (oldJournals.size !== newJournals.size)) { + return newState; + } + + let oldJournalHash = {}; + oldJournals.forEach((x) => { + oldJournalHash[x.uid] = x.serialize(); + }); + + if (newJournals.every((journal: EteSync.Journal) => ( + (journal.uid in oldJournalHash) && + (journal.serialize().content === oldJournalHash[journal.uid].content) + ))) { + return state; + } else { + return newState; + } + }, }, new JournalsFetchRecord(), );