More tslint fixes.

master
Tom Hacohen 6 years ago
parent 9913adc756
commit 7da0a6009b

@ -152,7 +152,7 @@ const AppBarWitHistory = withRouter(
private goBack() { private goBack() {
this.props.history!.goBack(); this.props.history!.goBack();
} }
}, }
); );
const IconRefreshWithSpin = withSpin(NavigationRefresh); const IconRefreshWithSpin = withSpin(NavigationRefresh);
@ -249,7 +249,7 @@ const credentialsSelector = createSelector(
encryptionKey, encryptionKey,
}, },
}; };
}, }
); );
const mapStateToProps = (state: store.StoreState) => { const mapStateToProps = (state: store.StoreState) => {
@ -261,5 +261,5 @@ const mapStateToProps = (state: store.StoreState) => {
}; };
export default connect( export default connect(
mapStateToProps, mapStateToProps
)(App); )(App);

@ -43,7 +43,7 @@ const JournalCalendar = journalView(PersistCalendar, Event);
const JournalTaskList = journalView(TaskList, Event); const JournalTaskList = journalView(TaskList, Event);
class Journal extends React.Component<PropsTypeInner> { class Journal extends React.Component<PropsTypeInner> {
state: { public state: {
tab: number, tab: number,
}; };
@ -55,7 +55,7 @@ class Journal extends React.Component<PropsTypeInner> {
}; };
} }
render() { public render() {
const { theme, isOwner, syncJournal } = this.props; const { theme, isOwner, syncJournal } = this.props;
let currentTab = this.state.tab; let currentTab = this.state.tab;
let journalOnly = false; let journalOnly = false;

@ -32,7 +32,7 @@ interface PropsTypeInner extends PropsType {
} }
class JournalEdit extends React.PureComponent<PropsTypeInner> { class JournalEdit extends React.PureComponent<PropsTypeInner> {
state = { public state = {
info: { info: {
uid: '', uid: '',
type: '', type: '',
@ -61,7 +61,7 @@ class JournalEdit extends React.PureComponent<PropsTypeInner> {
} }
} }
render() { public render() {
const { item, onDelete, onCancel } = this.props; const { item, onDelete, onCancel } = this.props;
const pageTitle = (item !== undefined) ? item.displayName : 'New Journal'; const pageTitle = (item !== undefined) ? item.displayName : 'New Journal';
@ -177,7 +177,7 @@ class JournalEdit extends React.PureComponent<PropsTypeInner> {
private onDeleteRequest() { private onDeleteRequest() {
this.setState({ this.setState({
showDeleteDialog: true showDeleteDialog: true,
}); });
} }

@ -21,7 +21,7 @@ interface PropsTypeInner extends PropsType {
} }
class JournalMembers extends React.PureComponent<PropsTypeInner> { class JournalMembers extends React.PureComponent<PropsTypeInner> {
state = { public state = {
members: null as EteSync.JournalMemberJson[] | null, members: null as EteSync.JournalMemberJson[] | null,
}; };
@ -29,7 +29,7 @@ class JournalMembers extends React.PureComponent<PropsTypeInner> {
super(props); super(props);
} }
render() { public render() {
const { info } = this.props; const { info } = this.props;
const { members } = this.state; const { members } = this.state;
@ -57,7 +57,7 @@ class JournalMembers extends React.PureComponent<PropsTypeInner> {
); );
} }
componentDidMount() { public componentDidMount() {
this.fetchMembers(); this.fetchMembers();
} }

@ -18,7 +18,7 @@ import { routeResolver } from '../App';
import { JournalsData, UserInfoData, CredentialsData } from '../store'; import { JournalsData, UserInfoData, CredentialsData } from '../store';
class JournalsList extends React.PureComponent { class JournalsList extends React.PureComponent {
props: { public props: {
etesync: CredentialsData; etesync: CredentialsData;
journals: JournalsData; journals: JournalsData;
userInfo: UserInfoData; userInfo: UserInfoData;
@ -30,7 +30,7 @@ class JournalsList extends React.PureComponent {
this.journalClicked = this.journalClicked.bind(this); this.journalClicked = this.journalClicked.bind(this);
} }
render() { public render() {
const derived = this.props.etesync.encryptionKey; const derived = this.props.etesync.encryptionKey;
let asymmetricCryptoManager: EteSync.AsymmetricCryptoManager; let asymmetricCryptoManager: EteSync.AsymmetricCryptoManager;
const journalMap = this.props.journals.reduce( const journalMap = this.props.journals.reduce(
@ -47,7 +47,7 @@ class JournalsList extends React.PureComponent {
} else { } else {
cryptoManager = new EteSync.CryptoManager(derived, journal.uid, journal.version); cryptoManager = new EteSync.CryptoManager(derived, journal.uid, journal.version);
} }
let info = journal.getInfo(cryptoManager); const info = journal.getInfo(cryptoManager);
ret[info.type] = ret[info.type] || []; ret[info.type] = ret[info.type] || [];
ret[info.type].push( ret[info.type].push(
<ListItem key={journal.uid} onClick={() => this.journalClicked(journal.uid)}> <ListItem key={journal.uid} onClick={() => this.journalClicked(journal.uid)}>
@ -59,7 +59,7 @@ class JournalsList extends React.PureComponent {
}, },
{ CALENDAR: [], { CALENDAR: [],
ADDRESS_BOOK: [], ADDRESS_BOOK: [],
TASKS: [] TASKS: [],
}); });
return ( return (
@ -94,7 +94,7 @@ class JournalsList extends React.PureComponent {
} }
private journalClicked(journalUid: string) { private journalClicked(journalUid: string) {
this.props.history.push(routeResolver.getRoute('journals._id', { journalUid: journalUid })); this.props.history.push(routeResolver.getRoute('journals._id', { journalUid }));
} }
} }

@ -16,7 +16,7 @@ import { SyncInfo } from '../SyncGate';
import * as EteSync from '../api/EteSync'; import * as EteSync from '../api/EteSync';
class Journals extends React.PureComponent { class Journals extends React.PureComponent {
props: { public props: {
etesync: CredentialsData; etesync: CredentialsData;
journals: JournalsData; journals: JournalsData;
userInfo: UserInfoData; userInfo: UserInfoData;
@ -32,7 +32,7 @@ class Journals extends React.PureComponent {
this.onItemSave = this.onItemSave.bind(this); this.onItemSave = this.onItemSave.bind(this);
} }
render() { public render() {
return ( return (
<Switch> <Switch>
<Route <Route
@ -114,7 +114,7 @@ class Journals extends React.PureComponent {
); );
} }
onItemSave(info: EteSync.CollectionInfo, originalInfo?: EteSync.CollectionInfo) { public onItemSave(info: EteSync.CollectionInfo, originalInfo?: EteSync.CollectionInfo) {
const journal = new EteSync.Journal(); const journal = new EteSync.Journal();
const cryptoManager = new EteSync.CryptoManager(this.props.etesync.encryptionKey, info.uid); const cryptoManager = new EteSync.CryptoManager(this.props.etesync.encryptionKey, info.uid);
journal.setInfo(cryptoManager, info); journal.setInfo(cryptoManager, info);
@ -130,7 +130,7 @@ class Journals extends React.PureComponent {
} }
} }
onItemDelete(info: EteSync.CollectionInfo) { public onItemDelete(info: EteSync.CollectionInfo) {
const journal = new EteSync.Journal(); const journal = new EteSync.Journal();
const cryptoManager = new EteSync.CryptoManager(this.props.etesync.encryptionKey, info.uid); const cryptoManager = new EteSync.CryptoManager(this.props.etesync.encryptionKey, info.uid);
journal.setInfo(cryptoManager, info); journal.setInfo(cryptoManager, info);
@ -140,7 +140,7 @@ class Journals extends React.PureComponent {
); );
} }
onCancel() { public onCancel() {
this.props.history.goBack(); this.props.history.goBack();
} }
} }

@ -13,7 +13,7 @@ function objValues(obj: any) {
export function journalView(JournalList: any, JournalItem: any) { export function journalView(JournalList: any, JournalItem: any) {
return withRouter(class extends React.PureComponent { return withRouter(class extends React.PureComponent {
props: { public props: {
journal: EteSync.Journal, journal: EteSync.Journal,
entries: {[key: string]: any}, entries: {[key: string]: any},
history?: History, history?: History,
@ -24,15 +24,15 @@ export function journalView(JournalList: any, JournalItem: any) {
this.itemClicked = this.itemClicked.bind(this); this.itemClicked = this.itemClicked.bind(this);
} }
itemClicked(contact: any) { public itemClicked(contact: any) {
const uid = contact.uid; const uid = contact.uid;
this.props.history!.push( this.props.history!.push(
routeResolver.getRoute('journals._id.items._id', { journalUid: this.props.journal.uid, itemUid: uid })); routeResolver.getRoute('journals._id.items._id', { journalUid: this.props.journal.uid, itemUid: uid }));
} }
render() { public render() {
let items = this.props.entries; const items = this.props.entries;
return ( return (
<Switch> <Switch>

@ -11,10 +11,10 @@ import { login, deriveKey } from './store/actions';
import * as C from './constants'; import * as C from './constants';
const SignedPagesBadge = require('./images/signed-pages-badge.svg'); import SignedPagesBadge from './images/signed-pages-badge.svg';
class LoginGate extends React.Component { class LoginGate extends React.Component {
props: { public props: {
credentials: CredentialsType; credentials: CredentialsType;
}; };
@ -24,16 +24,16 @@ class LoginGate extends React.Component {
this.onEncryptionFormSubmit = this.onEncryptionFormSubmit.bind(this); this.onEncryptionFormSubmit = this.onEncryptionFormSubmit.bind(this);
} }
onFormSubmit(username: string, password: string, encryptionPassword: string, serviceApiUrl?: string) { public onFormSubmit(username: string, password: string, encryptionPassword: string, serviceApiUrl?: string) {
serviceApiUrl = serviceApiUrl ? serviceApiUrl : C.serviceApiBase; serviceApiUrl = serviceApiUrl ? serviceApiUrl : C.serviceApiBase;
store.dispatch<any>(login(username, password, encryptionPassword, serviceApiUrl)); store.dispatch<any>(login(username, password, encryptionPassword, serviceApiUrl));
} }
onEncryptionFormSubmit(encryptionPassword: string) { public onEncryptionFormSubmit(encryptionPassword: string) {
store.dispatch(deriveKey(this.props.credentials.value!.credentials.email, encryptionPassword)); store.dispatch(deriveKey(this.props.credentials.value!.credentials.email, encryptionPassword));
} }
render() { public render() {
if (this.props.credentials.value === null) { if (this.props.credentials.value === null) {
const style = { const style = {
isSafe: { isSafe: {
@ -43,7 +43,7 @@ class LoginGate extends React.Component {
divider: { divider: {
margin: '30px 0', margin: '30px 0',
color: '#00000025', color: '#00000025',
} },
}; };
return ( return (

@ -28,16 +28,16 @@ const tasksTitle = 'Tasks';
const PersistCalendar = historyPersistor('Calendar')(Calendar); const PersistCalendar = historyPersistor('Calendar')(Calendar);
interface PropsType { interface PropsType {
contacts: Array<ContactType>; contacts: ContactType[];
events: Array<EventType>; events: EventType[];
tasks: Array<TaskType>; tasks: TaskType[];
location?: Location; location?: Location;
history?: History; history?: History;
theme: Theme; theme: Theme;
} }
class PimMain extends React.PureComponent<PropsType> { class PimMain extends React.PureComponent<PropsType> {
state: { public state: {
tab: number; tab: number;
}; };
@ -51,35 +51,35 @@ class PimMain extends React.PureComponent<PropsType> {
this.newEvent = this.newEvent.bind(this); this.newEvent = this.newEvent.bind(this);
} }
eventClicked(event: ICAL.Event) { public eventClicked(event: ICAL.Event) {
const uid = event.uid; const uid = event.uid;
this.props.history!.push( this.props.history!.push(
routeResolver.getRoute('pim.events._id', { itemUid: uid })); routeResolver.getRoute('pim.events._id', { itemUid: uid }));
} }
taskClicked(event: ICAL.Event) { public taskClicked(event: ICAL.Event) {
const uid = event.uid; const uid = event.uid;
this.props.history!.push( this.props.history!.push(
routeResolver.getRoute('pim.tasks._id', { itemUid: uid })); routeResolver.getRoute('pim.tasks._id', { itemUid: uid }));
} }
contactClicked(contact: ContactType) { public contactClicked(contact: ContactType) {
const uid = contact.uid; const uid = contact.uid;
this.props.history!.push( this.props.history!.push(
routeResolver.getRoute('pim.contacts._id', { itemUid: uid })); routeResolver.getRoute('pim.contacts._id', { itemUid: uid }));
} }
newEvent(start?: Date, end?: Date) { public newEvent(start?: Date, end?: Date) {
this.props.history!.push( this.props.history!.push(
routeResolver.getRoute('pim.events.new'), routeResolver.getRoute('pim.events.new'),
{start, end} {start, end}
); );
} }
floatingButtonClicked() { public floatingButtonClicked() {
if (this.state.tab === 0) { if (this.state.tab === 0) {
this.props.history!.push( this.props.history!.push(
routeResolver.getRoute('pim.contacts.new') routeResolver.getRoute('pim.contacts.new')
@ -93,7 +93,7 @@ class PimMain extends React.PureComponent<PropsType> {
} }
} }
render() { public render() {
const { theme } = this.props; const { theme } = this.props;
const { tab } = this.state; const { tab } = this.state;
const style = { const style = {

@ -47,9 +47,9 @@ function objValues(obj: any) {
const itemsSelector = createSelector( const itemsSelector = createSelector(
(props: {syncInfo: SyncInfo}) => props.syncInfo, (props: {syncInfo: SyncInfo}) => props.syncInfo,
(syncInfo) => { (syncInfo) => {
let collectionsAddressBook: Array<EteSync.CollectionInfo> = []; const collectionsAddressBook: EteSync.CollectionInfo[] = [];
let collectionsCalendar: Array<EteSync.CollectionInfo> = []; const collectionsCalendar: EteSync.CollectionInfo[] = [];
let collectionsTaskList: Array<EteSync.CollectionInfo> = []; const collectionsTaskList: EteSync.CollectionInfo[] = [];
let addressBookItems: {[key: string]: ContactType} = {}; let addressBookItems: {[key: string]: ContactType} = {};
let calendarItems: {[key: string]: EventType} = {}; let calendarItems: {[key: string]: EventType} = {};
let taskListItems: {[key: string]: TaskType} = {}; let taskListItems: {[key: string]: TaskType} = {};
@ -73,9 +73,9 @@ const itemsSelector = createSelector(
); );
return { return {
collectionsAddressBook, collectionsCalendar, collectionsTaskList, addressBookItems, calendarItems, taskListItems collectionsAddressBook, collectionsCalendar, collectionsTaskList, addressBookItems, calendarItems, taskListItems,
}; };
}, }
); );
const ItemChangeLog = pure((props: any) => { const ItemChangeLog = pure((props: any) => {
@ -102,7 +102,7 @@ const ItemChangeLog = pure((props: any) => {
type CollectionRoutesPropsType = RouteComponentProps<{}> & { type CollectionRoutesPropsType = RouteComponentProps<{}> & {
syncInfo: SyncInfo, syncInfo: SyncInfo,
routePrefix: string, routePrefix: string,
collections: Array<EteSync.CollectionInfo>, collections: EteSync.CollectionInfo[],
componentEdit: any, componentEdit: any,
componentView: any, componentView: any,
items: {[key: string]: PimType}, items: {[key: string]: PimType},
@ -123,7 +123,7 @@ const styles = (theme: any) => ({
const CollectionRoutes = withStyles(styles)(withRouter( const CollectionRoutes = withStyles(styles)(withRouter(
class CollectionRoutesInner extends React.PureComponent<CollectionRoutesPropsType> { class CollectionRoutesInner extends React.PureComponent<CollectionRoutesPropsType> {
render() { public render() {
const props = this.props; const props = this.props;
const { classes } = this.props; const { classes } = this.props;
const ComponentEdit = props.componentEdit; const ComponentEdit = props.componentEdit;
@ -221,7 +221,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
)); ));
class Pim extends React.PureComponent { class Pim extends React.PureComponent {
props: { public props: {
etesync: CredentialsData; etesync: CredentialsData;
userInfo: UserInfoData; userInfo: UserInfoData;
syncInfo: SyncInfo; syncInfo: SyncInfo;
@ -235,7 +235,7 @@ class Pim extends React.PureComponent {
this.onItemSave = this.onItemSave.bind(this); this.onItemSave = this.onItemSave.bind(this);
} }
onItemSave(item: PimType, journalUid: string, originalEvent?: PimType) { public onItemSave(item: PimType, journalUid: string, originalEvent?: PimType) {
const syncJournal = this.props.syncInfo.get(journalUid); const syncJournal = this.props.syncInfo.get(journalUid);
if (syncJournal === undefined) { if (syncJournal === undefined) {
@ -244,7 +244,7 @@ class Pim extends React.PureComponent {
const journal = syncJournal.journal; const journal = syncJournal.journal;
let action = (originalEvent === undefined) ? EteSync.SyncEntryAction.Add : EteSync.SyncEntryAction.Change; const action = (originalEvent === undefined) ? EteSync.SyncEntryAction.Add : EteSync.SyncEntryAction.Change;
let prevUid: string | null = null; let prevUid: string | null = null;
let last = syncJournal.journalEntries.last() as EteSync.Entry; let last = syncJournal.journalEntries.last() as EteSync.Entry;
@ -270,7 +270,7 @@ class Pim extends React.PureComponent {
}); });
} }
onItemDelete(item: PimType, journalUid: string) { public onItemDelete(item: PimType, journalUid: string) {
const syncJournal = this.props.syncInfo.get(journalUid); const syncJournal = this.props.syncInfo.get(journalUid);
if (syncJournal === undefined) { if (syncJournal === undefined) {
@ -279,7 +279,7 @@ class Pim extends React.PureComponent {
const journal = syncJournal.journal; const journal = syncJournal.journal;
let action = EteSync.SyncEntryAction.Delete; const action = EteSync.SyncEntryAction.Delete;
let prevUid: string | null = null; let prevUid: string | null = null;
let last = syncJournal.journalEntries.last() as EteSync.Entry; let last = syncJournal.journalEntries.last() as EteSync.Entry;
@ -305,11 +305,11 @@ class Pim extends React.PureComponent {
}); });
} }
onCancel() { public onCancel() {
this.props.history.goBack(); this.props.history.goBack();
} }
render() { public render() {
const { collectionsAddressBook, collectionsCalendar, collectionsTaskList, addressBookItems, calendarItems, taskListItems } = itemsSelector(this.props); const { collectionsAddressBook, collectionsCalendar, collectionsTaskList, addressBookItems, calendarItems, taskListItems } = itemsSelector(this.props);
return ( return (

@ -28,15 +28,7 @@ class Settings extends React.PureComponent<PropsTypeInner> {
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
} }
private handleChange(event: React.ChangeEvent<any>) { public render() {
const name = event.target.name;
const value = event.target.value;
const { settings } = this.props;
store.dispatch(setSettings({ ...settings, [name]: value}));
}
render() {
const { settings } = this.props; const { settings } = this.props;
return ( return (
<> <>
@ -61,9 +53,17 @@ class Settings extends React.PureComponent<PropsTypeInner> {
); );
} }
onCancel() { public onCancel() {
this.props.history.goBack(); this.props.history.goBack();
} }
private handleChange(event: React.ChangeEvent<any>) {
const name = event.target.name;
const value = event.target.value;
const { settings } = this.props;
store.dispatch(setSettings({ ...settings, [name]: value}));
}
} }
const mapStateToProps = (state: StoreState, props: PropsType) => { const mapStateToProps = (state: StoreState, props: PropsType) => {

@ -11,7 +11,7 @@ import ActionBugReport from '@material-ui/icons/BugReport';
import ActionQuestionAnswer from '@material-ui/icons/QuestionAnswer'; import ActionQuestionAnswer from '@material-ui/icons/QuestionAnswer';
import LogoutIcon from '@material-ui/icons/PowerSettingsNew'; import LogoutIcon from '@material-ui/icons/PowerSettingsNew';
const logo = require('../images/logo.svg'); import logo from '../images/logo.svg';
import { routeResolver } from '../App'; import { routeResolver } from '../App';
@ -37,12 +37,12 @@ class SideMenu extends React.PureComponent<PropsTypeInner> {
this.logout = this.logout.bind(this); this.logout = this.logout.bind(this);
} }
logout() { public logout() {
store.dispatch(logout()); store.dispatch(logout());
this.props.onCloseDrawerRequest(); this.props.onCloseDrawerRequest();
} }
render() { public render() {
const { theme } = this.props; const { theme } = this.props;
const username = (this.props.etesync && this.props.etesync.credentials.email) ? const username = (this.props.etesync && this.props.etesync.credentials.email) ?
this.props.etesync.credentials.email this.props.etesync.credentials.email

@ -78,7 +78,7 @@ const syncInfoSelector = createSelector(
const collectionInfo = journal.getInfo(cryptoManager); const collectionInfo = journal.getInfo(cryptoManager);
const syncEntries = journalEntries.value.map((entry: EteSync.Entry) => { const syncEntries = journalEntries.value.map((entry: EteSync.Entry) => {
let syncEntry = entry.getSyncEntry(cryptoManager, prevUid); const syncEntry = entry.getSyncEntry(cryptoManager, prevUid);
prevUid = entry.uid; prevUid = entry.uid;
return syncEntry; return syncEntry;
@ -93,7 +93,7 @@ const syncInfoSelector = createSelector(
}, },
Map<string, SyncInfoJournal>() Map<string, SyncInfoJournal>()
); );
}, }
); );
const PimRouter = withRouter(Pim); const PimRouter = withRouter(Pim);
@ -103,7 +103,7 @@ class SyncGate extends React.PureComponent<PropsTypeInner> {
super(props); super(props);
} }
componentDidMount() { public componentDidMount() {
const me = this.props.etesync.credentials.email; const me = this.props.etesync.credentials.email;
const syncAll = () => { const syncAll = () => {
store.dispatch<any>(fetchAll(this.props.etesync, this.props.entries)).then((haveJournals: boolean) => { store.dispatch<any>(fetchAll(this.props.etesync, this.props.entries)).then((haveJournals: boolean) => {
@ -153,7 +153,7 @@ class SyncGate extends React.PureComponent<PropsTypeInner> {
} }
} }
render() { public render() {
const entryArrays = this.props.entries; const entryArrays = this.props.entries;
const journals = this.props.journals.value; const journals = this.props.journals.value;
@ -162,7 +162,7 @@ class SyncGate extends React.PureComponent<PropsTypeInner> {
} else if (this.props.journals.error) { } else if (this.props.journals.error) {
return <PrettyError error={this.props.journals.error} />; return <PrettyError error={this.props.journals.error} />;
} else { } else {
let errors: Array<{journal: string, error: Error}> = []; const errors: Array<{journal: string, error: Error}> = [];
this.props.entries.forEach((entry, journal) => { this.props.entries.forEach((entry, journal) => {
if (entry.error) { if (entry.error) {
errors.push({journal, error: entry.error}); errors.push({journal, error: entry.error});

@ -11,8 +11,8 @@ sjcl.random.startCollectors();
export const HMAC_SIZE_BYTES = 32; export const HMAC_SIZE_BYTES = 32;
export class AsymmetricKeyPair { export class AsymmetricKeyPair {
publicKey: byte[]; public publicKey: byte[];
privateKey: byte[]; public privateKey: byte[];
constructor(publicKey: byte[], privateKey: byte[]) { constructor(publicKey: byte[], privateKey: byte[]) {
this.publicKey = publicKey; this.publicKey = publicKey;
@ -32,29 +32,29 @@ export function genUid() {
} }
function hmac256(salt: sjcl.BitArray, key: sjcl.BitArray) { function hmac256(salt: sjcl.BitArray, key: sjcl.BitArray) {
let hmac = new sjcl.misc.hmac(salt); const hmac = new sjcl.misc.hmac(salt);
return hmac.encrypt(key); return hmac.encrypt(key);
} }
export class CryptoManager { export class CryptoManager {
version: number;
key: sjcl.BitArray;
cipherKey: sjcl.BitArray;
hmacKey: sjcl.BitArray;
cipherWords = 4; public static fromDerivedKey(key: byte[], version: number = Constants.CURRENT_VERSION) {
static fromDerivedKey(key: byte[], version: number = Constants.CURRENT_VERSION) {
// FIXME: Cleanup this hack // FIXME: Cleanup this hack
const ret = new CryptoManager('', '', version); const ret = new CryptoManager('', '', version);
ret.key = sjcl.codec.bytes.toBits(key); ret.key = sjcl.codec.bytes.toBits(key);
ret._updateDerivedKeys(); ret._updateDerivedKeys();
return ret; return ret;
} }
public version: number;
public key: sjcl.BitArray;
public cipherKey: sjcl.BitArray;
public hmacKey: sjcl.BitArray;
public cipherWords = 4;
constructor(_keyBase64: base64, salt: string, version: number = Constants.CURRENT_VERSION) { constructor(_keyBase64: base64, salt: string, version: number = Constants.CURRENT_VERSION) {
this.version = version; this.version = version;
let key = sjcl.codec.base64.toBits(_keyBase64); const key = sjcl.codec.base64.toBits(_keyBase64);
// FIXME: Clean up all exeptions // FIXME: Clean up all exeptions
if (version > Constants.CURRENT_VERSION) { if (version > Constants.CURRENT_VERSION) {
throw new Error('VersionTooNewException'); throw new Error('VersionTooNewException');
@ -67,53 +67,53 @@ export class CryptoManager {
this._updateDerivedKeys(); this._updateDerivedKeys();
} }
_updateDerivedKeys() { public _updateDerivedKeys() {
this.cipherKey = hmac256(sjcl.codec.utf8String.toBits('aes'), this.key); this.cipherKey = hmac256(sjcl.codec.utf8String.toBits('aes'), this.key);
this.hmacKey = hmac256(sjcl.codec.utf8String.toBits('hmac'), this.key); this.hmacKey = hmac256(sjcl.codec.utf8String.toBits('hmac'), this.key);
} }
encryptBits(content: sjcl.BitArray): byte[] { public encryptBits(content: sjcl.BitArray): byte[] {
const iv = sjcl.random.randomWords(this.cipherWords); const iv = sjcl.random.randomWords(this.cipherWords);
let prp = new sjcl.cipher.aes(this.cipherKey); const prp = new sjcl.cipher.aes(this.cipherKey);
let cipherText = sjcl.mode.cbc.encrypt(prp, content, iv); const cipherText = sjcl.mode.cbc.encrypt(prp, content, iv);
return sjcl.codec.bytes.fromBits(iv.concat(cipherText)); return sjcl.codec.bytes.fromBits(iv.concat(cipherText));
} }
decryptBits(content: byte[]): sjcl.BitArray { public decryptBits(content: byte[]): sjcl.BitArray {
let cipherText = sjcl.codec.bytes.toBits(content); const cipherText = sjcl.codec.bytes.toBits(content);
const iv = cipherText.splice(0, this.cipherWords); const iv = cipherText.splice(0, this.cipherWords);
let prp = new sjcl.cipher.aes(this.cipherKey); const prp = new sjcl.cipher.aes(this.cipherKey);
let clearText = sjcl.mode.cbc.decrypt(prp, cipherText, iv); const clearText = sjcl.mode.cbc.decrypt(prp, cipherText, iv);
return clearText; return clearText;
} }
encryptBytes(content: byte[]): byte[] { public encryptBytes(content: byte[]): byte[] {
return this.encryptBits(sjcl.codec.bytes.toBits(content)); return this.encryptBits(sjcl.codec.bytes.toBits(content));
} }
decryptBytes(content: byte[]): byte[] { public decryptBytes(content: byte[]): byte[] {
return sjcl.codec.bytes.fromBits(this.decryptBits(content)); return sjcl.codec.bytes.fromBits(this.decryptBits(content));
} }
encrypt(content: string): byte[] { public encrypt(content: string): byte[] {
return this.encryptBits(sjcl.codec.utf8String.toBits(content)); return this.encryptBits(sjcl.codec.utf8String.toBits(content));
} }
decrypt(content: byte[]): string { public decrypt(content: byte[]): string {
return sjcl.codec.utf8String.fromBits(this.decryptBits(content)); return sjcl.codec.utf8String.fromBits(this.decryptBits(content));
} }
hmac(content: byte[]): byte[] { public hmac(content: byte[]): byte[] {
return sjcl.codec.bytes.fromBits(this.hmacBase(content)); return sjcl.codec.bytes.fromBits(this.hmacBase(content));
} }
hmac64(content: byte[]): base64 { public hmac64(content: byte[]): base64 {
return sjcl.codec.base64.fromBits(this.hmacBase(content)); return sjcl.codec.base64.fromBits(this.hmacBase(content));
} }
hmacHex(content: byte[]): string { public hmacHex(content: byte[]): string {
return sjcl.codec.hex.fromBits(this.hmacBase(content)); return sjcl.codec.hex.fromBits(this.hmacBase(content));
} }
@ -134,9 +134,8 @@ function bufferToArray(buffer: Buffer) {
} }
export class AsymmetricCryptoManager { export class AsymmetricCryptoManager {
keyPair: NodeRSA;
static generateKeyPair() { public static generateKeyPair() {
const keyPair = new NodeRSA(); const keyPair = new NodeRSA();
keyPair.generateKeyPair(3072, 65537); keyPair.generateKeyPair(3072, 65537);
const pubkey = keyPair.exportKey('pkcs8-public-der') as Buffer; const pubkey = keyPair.exportKey('pkcs8-public-der') as Buffer;
@ -144,19 +143,20 @@ export class AsymmetricCryptoManager {
return new AsymmetricKeyPair( return new AsymmetricKeyPair(
bufferToArray(pubkey), bufferToArray(privkey)); bufferToArray(pubkey), bufferToArray(privkey));
} }
public keyPair: NodeRSA;
constructor(keyPair: AsymmetricKeyPair) { constructor(keyPair: AsymmetricKeyPair) {
this.keyPair = new NodeRSA(); this.keyPair = new NodeRSA();
this.keyPair.importKey(Buffer.from(keyPair.privateKey), 'pkcs8-der'); this.keyPair.importKey(Buffer.from(keyPair.privateKey), 'pkcs8-der');
} }
encryptBytes(publicKey: byte[], content: byte[]): byte[] { public encryptBytes(publicKey: byte[], content: byte[]): byte[] {
const key = new NodeRSA(); const key = new NodeRSA();
key.importKey(Buffer.from(publicKey), 'pkcs8-public-der'); key.importKey(Buffer.from(publicKey), 'pkcs8-public-der');
return bufferToArray(key.encrypt(Buffer.from(content), 'buffer')); return bufferToArray(key.encrypt(Buffer.from(content), 'buffer'));
} }
decryptBytes(content: byte[]): byte[] { public decryptBytes(content: byte[]): byte[] {
return bufferToArray(this.keyPair.decrypt(Buffer.from(content), 'buffer')); return bufferToArray(this.keyPair.decrypt(Buffer.from(content), 'buffer'));
} }
} }

@ -9,26 +9,26 @@ import { USER, PASSWORD, keyBase64 } from './TestConstants';
let credentials: EteSync.Credentials; let credentials: EteSync.Credentials;
beforeEach(async () => { beforeEach(async () => {
let authenticator = new EteSync.Authenticator(testApiBase); const authenticator = new EteSync.Authenticator(testApiBase);
const authToken = await authenticator.getAuthToken(USER, PASSWORD); const authToken = await authenticator.getAuthToken(USER, PASSWORD);
credentials = new EteSync.Credentials(USER, authToken); credentials = new EteSync.Credentials(USER, authToken);
await fetch(testApiBase + '/reset/', { await fetch(testApiBase + '/reset/', {
method: 'post', method: 'post',
headers: { 'Authorization': 'Token ' + credentials.authToken }, headers: { Authorization: 'Token ' + credentials.authToken },
}); });
}); });
it('Simple sync', async () => { it('Simple sync', async () => {
let journalManager = new EteSync.JournalManager(credentials, testApiBase); const journalManager = new EteSync.JournalManager(credentials, testApiBase);
let journals = await journalManager.list(); let journals = await journalManager.list();
expect(journals.length).toBe(0); expect(journals.length).toBe(0);
const uid1 = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash('id1')); const uid1 = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash('id1'));
const cryptoManager = new EteSync.CryptoManager(keyBase64, USER); const cryptoManager = new EteSync.CryptoManager(keyBase64, USER);
const info1 = new EteSync.CollectionInfo({uid: uid1, content: 'test', displayName: 'Dislpay 1'}); const info1 = new EteSync.CollectionInfo({uid: uid1, content: 'test', displayName: 'Dislpay 1'});
let journal = new EteSync.Journal(); const journal = new EteSync.Journal();
journal.setInfo(cryptoManager, info1); journal.setInfo(cryptoManager, info1);
await expect(journalManager.create(journal)).resolves.toBeDefined(); await expect(journalManager.create(journal)).resolves.toBeDefined();
@ -41,7 +41,7 @@ it('Simple sync', async () => {
expect(journals[0].uid).toBe(journal.uid); expect(journals[0].uid).toBe(journal.uid);
// Update // Update
let info2 = new EteSync.CollectionInfo(info1); const info2 = new EteSync.CollectionInfo(info1);
info2.displayName = 'Display 2'; info2.displayName = 'Display 2';
journal.setInfo(cryptoManager, info2); journal.setInfo(cryptoManager, info2);
@ -59,24 +59,24 @@ it('Simple sync', async () => {
}); });
it('Journal Entry sync', async () => { it('Journal Entry sync', async () => {
let journalManager = new EteSync.JournalManager(credentials, testApiBase); const journalManager = new EteSync.JournalManager(credentials, testApiBase);
const uid1 = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash('id1')); const uid1 = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash('id1'));
const cryptoManager = new EteSync.CryptoManager(keyBase64, USER); const cryptoManager = new EteSync.CryptoManager(keyBase64, USER);
const info1 = new EteSync.CollectionInfo({uid: uid1, content: 'test', displayName: 'Dislpay 1'}); const info1 = new EteSync.CollectionInfo({uid: uid1, content: 'test', displayName: 'Dislpay 1'});
let journal = new EteSync.Journal(); const journal = new EteSync.Journal();
journal.setInfo(cryptoManager, info1); journal.setInfo(cryptoManager, info1);
await expect(journalManager.create(journal)).resolves.toBeDefined(); await expect(journalManager.create(journal)).resolves.toBeDefined();
let entryManager = new EteSync.EntryManager(credentials, testApiBase, journal.uid); const entryManager = new EteSync.EntryManager(credentials, testApiBase, journal.uid);
let entries = await entryManager.list(null); let entries = await entryManager.list(null);
expect(entries.length).toBe(0); expect(entries.length).toBe(0);
const syncEntry = new EteSync.SyncEntry({action: 'ADD', content: 'bla'}); const syncEntry = new EteSync.SyncEntry({action: 'ADD', content: 'bla'});
let prevUid = null; let prevUid = null;
let entry = new EteSync.Entry(); const entry = new EteSync.Entry();
entry.setSyncEntry(cryptoManager, syncEntry, prevUid); entry.setSyncEntry(cryptoManager, syncEntry, prevUid);
entries = [entry]; entries = [entry];
@ -120,7 +120,7 @@ it('Journal Entry sync', async () => {
expect(() => { expect(() => {
let prev = null; let prev = null;
for (let ent of entries) { for (const ent of entries) {
expect(ent.getSyncEntry(cryptoManager, prev)).toBeDefined(); expect(ent.getSyncEntry(cryptoManager, prev)).toBeDefined();
prev = ent.uid; prev = ent.uid;
} }
@ -135,7 +135,7 @@ it('User info sync', async () => {
await expect(userInfoManager.fetch(USER)).rejects.toBeInstanceOf(EteSync.HTTPError); await expect(userInfoManager.fetch(USER)).rejects.toBeInstanceOf(EteSync.HTTPError);
// Create // Create
let userInfo = new EteSync.UserInfo(USER); const userInfo = new EteSync.UserInfo(USER);
userInfo.setKeyPair(cryptoManager, new EteSync.AsymmetricKeyPair([0, 1, 2, 3], [4, 5, 6, 6])); userInfo.setKeyPair(cryptoManager, new EteSync.AsymmetricKeyPair([0, 1, 2, 3], [4, 5, 6, 6]));
await expect(userInfoManager.create(userInfo)).resolves.not.toBeNull(); await expect(userInfoManager.create(userInfo)).resolves.not.toBeNull();

@ -42,8 +42,8 @@ function hmacToHex(hmac: byte[]): string {
} }
export class Credentials { export class Credentials {
email: string; public email: string;
authToken: string; public authToken: string;
constructor(email: string, authToken: string) { constructor(email: string, authToken: string) {
this.email = email; this.email = email;
@ -52,11 +52,11 @@ export class Credentials {
} }
export class CollectionInfo { export class CollectionInfo {
uid: string; public uid: string;
type: string; public type: string;
displayName: string; public displayName: string;
description: string; public description: string;
color: number; public color: number;
constructor(json?: any) { constructor(json?: any) {
CastJson(json, this); CastJson(json, this);
@ -76,13 +76,13 @@ class BaseItem<T extends BaseItemJson> {
this._json = {} as any; this._json = {} as any;
} }
deserialize(json: T) { public deserialize(json: T) {
this._json = Object.assign({}, json); this._json = Object.assign({}, json);
this._encrypted = sjcl.codec.bytes.fromBits(sjcl.codec.base64.toBits(json.content)); this._encrypted = sjcl.codec.bytes.fromBits(sjcl.codec.base64.toBits(json.content));
this._content = undefined; this._content = undefined;
} }
serialize(): T { public serialize(): T {
return Object.assign( return Object.assign(
{}, {},
this._json, this._json,
@ -140,33 +140,33 @@ export class Journal extends BaseJournal<JournalJson> {
return this._json.version; return this._json.version;
} }
setInfo(cryptoManager: CryptoManager, info: CollectionInfo) { public setInfo(cryptoManager: CryptoManager, info: CollectionInfo) {
this._json.uid = info.uid; this._json.uid = info.uid;
this._content = info; this._content = info;
const encrypted = cryptoManager.encrypt(JSON.stringify(this._content)); const encrypted = cryptoManager.encrypt(JSON.stringify(this._content));
this._encrypted = this.calculateHmac(cryptoManager, encrypted).concat(encrypted); this._encrypted = this.calculateHmac(cryptoManager, encrypted).concat(encrypted);
} }
getInfo(cryptoManager: CryptoManager): CollectionInfo { public getInfo(cryptoManager: CryptoManager): CollectionInfo {
this.verify(cryptoManager); this.verify(cryptoManager);
if (this._content === undefined) { if (this._content === undefined) {
this._content = JSON.parse(cryptoManager.decrypt(this.encryptedContent())); this._content = JSON.parse(cryptoManager.decrypt(this.encryptedContent()));
} }
let ret = new CollectionInfo(this._content); const ret = new CollectionInfo(this._content);
ret.uid = this.uid; ret.uid = this.uid;
return ret; return ret;
} }
calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] { public calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] {
let prefix = stringToByteArray(this.uid); const prefix = stringToByteArray(this.uid);
return cryptoManager.hmac(prefix.concat(encrypted)); return cryptoManager.hmac(prefix.concat(encrypted));
} }
verify(cryptoManager: CryptoManager) { public verify(cryptoManager: CryptoManager) {
let calculated = this.calculateHmac(cryptoManager, this.encryptedContent()); const calculated = this.calculateHmac(cryptoManager, this.encryptedContent());
let hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES); const hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES);
super.verifyBase(hmac, calculated); super.verifyBase(hmac, calculated);
} }
@ -183,9 +183,9 @@ export enum SyncEntryAction {
} }
export class SyncEntry { export class SyncEntry {
uid?: string; public uid?: string;
action: SyncEntryAction; public action: SyncEntryAction;
content: string; public content: string;
constructor(json?: any, uid?: string) { constructor(json?: any, uid?: string) {
CastJson(json, this); CastJson(json, this);
@ -193,17 +193,16 @@ export class SyncEntry {
} }
} }
export interface EntryJson extends BaseJson { export type EntryJson = BaseJson;
}
export class Entry extends BaseJournal<EntryJson> { export class Entry extends BaseJournal<EntryJson> {
setSyncEntry(cryptoManager: CryptoManager, info: SyncEntry, prevUid: string | null) { public setSyncEntry(cryptoManager: CryptoManager, info: SyncEntry, prevUid: string | null) {
this._content = info; this._content = info;
this._encrypted = cryptoManager.encrypt(JSON.stringify(this._content)); this._encrypted = cryptoManager.encrypt(JSON.stringify(this._content));
this._json.uid = hmacToHex(this.calculateHmac(cryptoManager, this._encrypted, prevUid)); this._json.uid = hmacToHex(this.calculateHmac(cryptoManager, this._encrypted, prevUid));
} }
getSyncEntry(cryptoManager: CryptoManager, prevUid: string | null): SyncEntry { public getSyncEntry(cryptoManager: CryptoManager, prevUid: string | null): SyncEntry {
this.verify(cryptoManager, prevUid); this.verify(cryptoManager, prevUid);
if (this._content === undefined) { if (this._content === undefined) {
@ -213,15 +212,15 @@ export class Entry extends BaseJournal<EntryJson> {
return new SyncEntry(this._content, this.uid); return new SyncEntry(this._content, this.uid);
} }
verify(cryptoManager: CryptoManager, prevUid: string | null) { public verify(cryptoManager: CryptoManager, prevUid: string | null) {
let calculated = this.calculateHmac(cryptoManager, this._encrypted, prevUid); const calculated = this.calculateHmac(cryptoManager, this._encrypted, prevUid);
let hmac = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(this.uid)); const hmac = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(this.uid));
super.verifyBase(hmac, calculated); super.verifyBase(hmac, calculated);
} }
private calculateHmac(cryptoManager: CryptoManager, encrypted: byte[], prevUid: string | null): byte[] { private calculateHmac(cryptoManager: CryptoManager, encrypted: byte[], prevUid: string | null): byte[] {
let prefix = (prevUid !== null) ? stringToByteArray(prevUid) : []; const prefix = (prevUid !== null) ? stringToByteArray(prevUid) : [];
return cryptoManager.hmac(prefix.concat(encrypted)); return cryptoManager.hmac(prefix.concat(encrypted));
} }
} }
@ -233,7 +232,7 @@ export interface UserInfoJson extends BaseItemJson {
} }
export class UserInfo extends BaseItem<UserInfoJson> { export class UserInfo extends BaseItem<UserInfoJson> {
_owner: string; public _owner: string;
constructor(owner: string, version: number = Constants.CURRENT_VERSION) { constructor(owner: string, version: number = Constants.CURRENT_VERSION) {
super(); super();
@ -253,20 +252,20 @@ export class UserInfo extends BaseItem<UserInfoJson> {
return this._json.pubkey; return this._json.pubkey;
} }
serialize(): UserInfoJson { public serialize(): UserInfoJson {
let ret = super.serialize(); const ret = super.serialize();
ret.owner = this._owner; ret.owner = this._owner;
return ret; return ret;
} }
setKeyPair(cryptoManager: CryptoManager, keyPair: AsymmetricKeyPair) { public setKeyPair(cryptoManager: CryptoManager, keyPair: AsymmetricKeyPair) {
this._json.pubkey = sjcl.codec.base64.fromBits(sjcl.codec.bytes.toBits(keyPair.publicKey)); this._json.pubkey = sjcl.codec.base64.fromBits(sjcl.codec.bytes.toBits(keyPair.publicKey));
this._content = keyPair.privateKey; this._content = keyPair.privateKey;
const encrypted = cryptoManager.encryptBytes(keyPair.privateKey); const encrypted = cryptoManager.encryptBytes(keyPair.privateKey);
this._encrypted = this.calculateHmac(cryptoManager, encrypted).concat(encrypted); this._encrypted = this.calculateHmac(cryptoManager, encrypted).concat(encrypted);
} }
getKeyPair(cryptoManager: CryptoManager): AsymmetricKeyPair { public getKeyPair(cryptoManager: CryptoManager): AsymmetricKeyPair {
this.verify(cryptoManager); this.verify(cryptoManager);
if (this._content === undefined) { if (this._content === undefined) {
@ -277,14 +276,14 @@ export class UserInfo extends BaseItem<UserInfoJson> {
return new AsymmetricKeyPair(pubkey, this._content as byte[]); return new AsymmetricKeyPair(pubkey, this._content as byte[]);
} }
calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] { public calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] {
let postfix = sjcl.codec.bytes.fromBits(sjcl.codec.base64.toBits(this._json.pubkey)); const postfix = sjcl.codec.bytes.fromBits(sjcl.codec.base64.toBits(this._json.pubkey));
return cryptoManager.hmac(encrypted.concat(postfix)); return cryptoManager.hmac(encrypted.concat(postfix));
} }
verify(cryptoManager: CryptoManager) { public verify(cryptoManager: CryptoManager) {
let calculated = this.calculateHmac(cryptoManager, this.encryptedContent()); const calculated = this.calculateHmac(cryptoManager, this.encryptedContent());
let hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES); const hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES);
super.verifyBase(hmac, calculated); super.verifyBase(hmac, calculated);
} }
@ -297,9 +296,8 @@ export class UserInfo extends BaseItem<UserInfoJson> {
// FIXME: baseUrl and apiBase should be the right type all around. // FIXME: baseUrl and apiBase should be the right type all around.
class BaseNetwork { class BaseNetwork {
apiBase: any; // FIXME
static urlExtend(_baseUrl: URL, segments: Array<string>): URL { public static urlExtend(_baseUrl: URL, segments: string[]): URL {
let baseUrl = _baseUrl as any; let baseUrl = _baseUrl as any;
baseUrl = baseUrl.clone(); baseUrl = baseUrl.clone();
for (const segment of segments) { for (const segment of segments) {
@ -307,18 +305,19 @@ class BaseNetwork {
} }
return baseUrl.normalize(); return baseUrl.normalize();
} }
public apiBase: any; // FIXME
constructor(apiBase: string) { constructor(apiBase: string) {
this.apiBase = URI(apiBase).normalize(); this.apiBase = URI(apiBase).normalize();
} }
// FIXME: Get the correct type for extra // FIXME: Get the correct type for extra
newCall(segments: Array<string> = [], extra: any = {}, _apiBase: URL = this.apiBase): Promise<{} | Array<any>> { public newCall(segments: string[] = [], extra: any = {}, _apiBase: URL = this.apiBase): Promise<{} | any[]> {
let apiBase = BaseNetwork.urlExtend(_apiBase, segments); const apiBase = BaseNetwork.urlExtend(_apiBase, segments);
extra = Object.assign({}, extra); extra = Object.assign({}, extra);
extra.headers = Object.assign( extra.headers = Object.assign(
{ 'Accept': 'application/json' }, { Accept: 'application/json' },
extra.headers); extra.headers);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -358,17 +357,17 @@ export class Authenticator extends BaseNetwork {
this.apiBase = BaseNetwork.urlExtend(this.apiBase, ['api-token-auth', '']); this.apiBase = BaseNetwork.urlExtend(this.apiBase, ['api-token-auth', '']);
} }
getAuthToken(username: string, password: string): Promise<string> { public getAuthToken(username: string, password: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// FIXME: should be FormData but doesn't work for whatever reason // FIXME: should be FormData but doesn't work for whatever reason
let form = 'username=' + encodeURIComponent(username) + const form = 'username=' + encodeURIComponent(username) +
'&password=' + encodeURIComponent(password); '&password=' + encodeURIComponent(password);
const extra = { const extra = {
method: 'post', method: 'post',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
}, },
body: form body: form,
}; };
this.newCall([], extra).then((json: {token: string}) => { this.newCall([], extra).then((json: {token: string}) => {
@ -383,19 +382,19 @@ export class Authenticator extends BaseNetwork {
export class BaseManager extends BaseNetwork { export class BaseManager extends BaseNetwork {
protected credentials: Credentials; protected credentials: Credentials;
constructor(credentials: Credentials, apiBase: string, segments: Array<string>) { constructor(credentials: Credentials, apiBase: string, segments: string[]) {
super(apiBase); super(apiBase);
this.credentials = credentials; this.credentials = credentials;
this.apiBase = BaseNetwork.urlExtend(this.apiBase, ['api', 'v1'].concat(segments)); this.apiBase = BaseNetwork.urlExtend(this.apiBase, ['api', 'v1'].concat(segments));
} }
// FIXME: Get the correct type for extra // FIXME: Get the correct type for extra
newCall(segments: Array<string> = [], extra: any = {}, apiBase: any = this.apiBase): Promise<{} | Array<any>> { public newCall(segments: string[] = [], extra: any = {}, apiBase: any = this.apiBase): Promise<{} | any[]> {
extra = Object.assign({}, extra); extra = Object.assign({}, extra);
extra.headers = Object.assign( extra.headers = Object.assign(
{ {
'Content-Type': 'application/json;charset=UTF-8', 'Content-Type': 'application/json;charset=UTF-8',
'Authorization': 'Token ' + this.credentials.authToken 'Authorization': 'Token ' + this.credentials.authToken,
}, },
extra.headers); extra.headers);
@ -408,10 +407,10 @@ export class JournalManager extends BaseManager {
super(credentials, apiBase, ['journals', '']); super(credentials, apiBase, ['journals', '']);
} }
fetch(journalUid: string): Promise<Journal> { public fetch(journalUid: string): Promise<Journal> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.newCall([journalUid, '']).then((json: JournalJson) => { this.newCall([journalUid, '']).then((json: JournalJson) => {
let journal = new Journal(json.version); const journal = new Journal(json.version);
journal.deserialize(json); journal.deserialize(json);
resolve(journal); resolve(journal);
}).catch((error: Error) => { }).catch((error: Error) => {
@ -420,11 +419,11 @@ export class JournalManager extends BaseManager {
}); });
} }
list(): Promise<Journal[]> { public list(): Promise<Journal[]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.newCall().then((json: Array<{}>) => { this.newCall().then((json: Array<{}>) => {
resolve(json.map((val: JournalJson) => { resolve(json.map((val: JournalJson) => {
let journal = new Journal(val.version); const journal = new Journal(val.version);
journal.deserialize(val); journal.deserialize(val);
return journal; return journal;
})); }));
@ -434,7 +433,7 @@ export class JournalManager extends BaseManager {
}); });
} }
create(journal: Journal): Promise<{}> { public create(journal: Journal): Promise<{}> {
const extra = { const extra = {
method: 'post', method: 'post',
body: JSON.stringify(journal.serialize()), body: JSON.stringify(journal.serialize()),
@ -443,7 +442,7 @@ export class JournalManager extends BaseManager {
return this.newCall([], extra); return this.newCall([], extra);
} }
update(journal: Journal): Promise<{}> { public update(journal: Journal): Promise<{}> {
const extra = { const extra = {
method: 'put', method: 'put',
body: JSON.stringify(journal.serialize()), body: JSON.stringify(journal.serialize()),
@ -452,7 +451,7 @@ export class JournalManager extends BaseManager {
return this.newCall([journal.uid, ''], extra); return this.newCall([journal.uid, ''], extra);
} }
delete(journal: Journal): Promise<{}> { public delete(journal: Journal): Promise<{}> {
const extra = { const extra = {
method: 'delete', method: 'delete',
}; };
@ -466,7 +465,7 @@ export class EntryManager extends BaseManager {
super(credentials, apiBase, ['journals', journalId, 'entries', '']); super(credentials, apiBase, ['journals', journalId, 'entries', '']);
} }
list(lastUid: string | null, limit: number = 0): Promise<Entry[]> { public list(lastUid: string | null, limit: number = 0): Promise<Entry[]> {
let apiBase = this.apiBase.clone(); let apiBase = this.apiBase.clone();
apiBase = apiBase.search({ apiBase = apiBase.search({
last: (lastUid !== null) ? lastUid : undefined, last: (lastUid !== null) ? lastUid : undefined,
@ -476,7 +475,7 @@ export class EntryManager extends BaseManager {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.newCall(undefined, undefined, apiBase).then((json: Array<{}>) => { this.newCall(undefined, undefined, apiBase).then((json: Array<{}>) => {
resolve(json.map((val: any) => { resolve(json.map((val: any) => {
let entry = new Entry(); const entry = new Entry();
entry.deserialize(val); entry.deserialize(val);
return entry; return entry;
})); }));
@ -486,7 +485,7 @@ export class EntryManager extends BaseManager {
}); });
} }
create(entries: Entry[], lastUid: string | null): Promise<{}> { public create(entries: Entry[], lastUid: string | null): Promise<{}> {
let apiBase = this.apiBase.clone(); let apiBase = this.apiBase.clone();
apiBase = apiBase.search({ apiBase = apiBase.search({
last: (lastUid !== null) ? lastUid : undefined, last: (lastUid !== null) ? lastUid : undefined,
@ -511,7 +510,7 @@ export class JournalMembersManager extends BaseManager {
super(credentials, apiBase, ['journals', journalId, 'members', '']); super(credentials, apiBase, ['journals', journalId, 'members', '']);
} }
list(): Promise<JournalMemberJson[]> { public list(): Promise<JournalMemberJson[]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.newCall().then((json: Array<{}>) => { this.newCall().then((json: Array<{}>) => {
resolve(json.map((val: JournalMemberJson) => { resolve(json.map((val: JournalMemberJson) => {
@ -529,10 +528,10 @@ export class UserInfoManager extends BaseManager {
super(credentials, apiBase, ['user', '']); super(credentials, apiBase, ['user', '']);
} }
fetch(owner: string): Promise<UserInfo> { public fetch(owner: string): Promise<UserInfo> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.newCall([owner, '']).then((json: UserInfoJson) => { this.newCall([owner, '']).then((json: UserInfoJson) => {
let userInfo = new UserInfo(owner, json.version); const userInfo = new UserInfo(owner, json.version);
userInfo.deserialize(json); userInfo.deserialize(json);
resolve(userInfo); resolve(userInfo);
}).catch((error: Error) => { }).catch((error: Error) => {
@ -541,7 +540,7 @@ export class UserInfoManager extends BaseManager {
}); });
} }
create(userInfo: UserInfo): Promise<{}> { public create(userInfo: UserInfo): Promise<{}> {
const extra = { const extra = {
method: 'post', method: 'post',
body: JSON.stringify(userInfo.serialize()), body: JSON.stringify(userInfo.serialize()),
@ -550,7 +549,7 @@ export class UserInfoManager extends BaseManager {
return this.newCall([], extra); return this.newCall([], extra);
} }
update(userInfo: UserInfo): Promise<{}> { public update(userInfo: UserInfo): Promise<{}> {
const extra = { const extra = {
method: 'put', method: 'put',
body: JSON.stringify(userInfo.serialize()), body: JSON.stringify(userInfo.serialize()),
@ -559,7 +558,7 @@ export class UserInfoManager extends BaseManager {
return this.newCall([userInfo.owner, ''], extra); return this.newCall([userInfo.owner, ''], extra);
} }
delete(userInfo: UserInfo): Promise<{}> { public delete(userInfo: UserInfo): Promise<{}> {
const extra = { const extra = {
method: 'delete', method: 'delete',
}; };

@ -2,7 +2,7 @@ export type byte = number;
export type base64 = string; export type base64 = string;
export function stringToByteArray(str: string): byte[] { export function stringToByteArray(str: string): byte[] {
let ret = []; const ret = [];
for (let i = 0 ; i < str.length ; i++) { for (let i = 0 ; i < str.length ; i++) {
ret.push(str.charCodeAt(i)); ret.push(str.charCodeAt(i));
} }

@ -13,11 +13,11 @@ import PimItemHeader from './PimItemHeader';
import { ContactType } from '../pim-types'; import { ContactType } from '../pim-types';
class Contact extends React.PureComponent { class Contact extends React.PureComponent {
props: { public props: {
item?: ContactType, item?: ContactType,
}; };
render() { public render() {
if (this.props.item === undefined) { if (this.props.item === undefined) {
throw Error('Contact should be defined!'); throw Error('Contact should be defined!');
} }
@ -29,7 +29,7 @@ class Contact extends React.PureComponent {
const lastModified = (revProp) ? const lastModified = (revProp) ?
'Modified: ' + moment(revProp.getFirstValue().toJSDate()).format('LLLL') : undefined; 'Modified: ' + moment(revProp.getFirstValue().toJSDate()).format('LLLL') : undefined;
let lists = []; const lists = [];
function getAllType( function getAllType(
propName: string, propName: string,
@ -57,9 +57,9 @@ class Contact extends React.PureComponent {
'tel', 'tel',
{ {
leftIcon: <CommunicationCall />, leftIcon: <CommunicationCall />,
rightIcon: <CommunicationChatBubble /> rightIcon: <CommunicationChatBubble />,
}, },
(x) => ('tel:' + x), (x) => ('tel:' + x)
)); ));
lists.push(getAllType( lists.push(getAllType(
@ -67,44 +67,44 @@ class Contact extends React.PureComponent {
{ {
leftIcon: <CommunicationEmail />, leftIcon: <CommunicationEmail />,
}, },
(x) => ('mailto:' + x), (x) => ('mailto:' + x)
)); ));
lists.push(getAllType( lists.push(getAllType(
'impp', 'impp',
{ {
leftIcon: <CommunicationChatBubble /> leftIcon: <CommunicationChatBubble />,
}, },
(x) => x, (x) => x,
(x) => (x.substring(x.indexOf(':') + 1)), (x) => (x.substring(x.indexOf(':') + 1)),
(x) => (x.substring(0, x.indexOf(':'))), (x) => (x.substring(0, x.indexOf(':')))
)); ));
lists.push(getAllType( lists.push(getAllType(
'adr', 'adr',
{ {
leftIcon: <IconHome /> leftIcon: <IconHome />,
}, }
)); ));
lists.push(getAllType( lists.push(getAllType(
'bday', 'bday',
{ {
leftIcon: <IconDate /> leftIcon: <IconDate />,
}, },
undefined, undefined,
((x: any) => moment(x.toJSDate()).format('dddd, LL')), ((x: any) => moment(x.toJSDate()).format('dddd, LL')),
() => 'Birthday', () => 'Birthday'
)); ));
lists.push(getAllType( lists.push(getAllType(
'anniversary', 'anniversary',
{ {
leftIcon: <IconDate /> leftIcon: <IconDate />,
}, },
undefined, undefined,
((x: any) => moment(x.toJSDate()).format('dddd, LL')), ((x: any) => moment(x.toJSDate()).format('dddd, LL')),
() => 'Anniversary', () => 'Anniversary'
)); ));
const skips = ['tel', 'email', 'impp', 'adr', 'bday', 'anniversary', 'rev', const skips = ['tel', 'email', 'impp', 'adr', 'bday', 'anniversary', 'rev',
@ -126,7 +126,7 @@ class Contact extends React.PureComponent {
return values; return values;
}); });
function listIfNotEmpty(items: Array<Array<JSX.Element>>) { function listIfNotEmpty(items: JSX.Element[][]) {
if (items.length > 0) { if (items.length > 0) {
return ( return (
<React.Fragment> <React.Fragment>

@ -45,7 +45,7 @@ const imppTypes = [
]; ];
const TypeSelector = (props: any) => { const TypeSelector = (props: any) => {
const types = props.types as {type: string}[]; const types = props.types as Array<{type: string}>;
return ( return (
<Select <Select
@ -61,8 +61,8 @@ const TypeSelector = (props: any) => {
}; };
class ValueType { class ValueType {
type: string; public type: string;
value: string; public value: string;
constructor(type?: string, value?: string) { constructor(type?: string, value?: string) {
this.type = type ? type : 'home'; this.type = type ? type : 'home';
@ -74,7 +74,7 @@ interface ValueTypeComponentProps {
type?: string; type?: string;
style?: object; style?: object;
types: {type: string}[]; types: Array<{type: string}>;
name: string; name: string;
placeholder: string; placeholder: string;
value: ValueType; value: ValueType;
@ -112,16 +112,16 @@ const ValueTypeComponent = (props: ValueTypeComponentProps) => {
}; };
interface PropsType { interface PropsType {
collections: Array<EteSync.CollectionInfo>; collections: EteSync.CollectionInfo[];
initialCollection?: string; initialCollection?: string;
item?: ContactType; item?: ContactType;
onSave: (contact: ContactType, journalUid: string, originalContact?: ContactType) => void; onSave: (contact: ContactType, journalUid: string, originalContact?: ContactType) => void;
onDelete: (contact: ContactType, journalUid: string) => void; onDelete: (contact: ContactType, journalUid: string) => void;
onCancel: () => void; onCancel: () => void;
}; }
class ContactEdit extends React.PureComponent<PropsType> { class ContactEdit extends React.PureComponent<PropsType> {
state: { public state: {
uid: string, uid: string,
fn: string; fn: string;
phone: ValueType[]; phone: ValueType[];
@ -201,7 +201,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
this.onDeleteRequest = this.onDeleteRequest.bind(this); this.onDeleteRequest = this.onDeleteRequest.bind(this);
} }
componentWillReceiveProps(nextProps: any) { public componentWillReceiveProps(nextProps: any) {
if ((this.props.collections !== nextProps.collections) || if ((this.props.collections !== nextProps.collections) ||
(this.props.initialCollection !== nextProps.initialCollection)) { (this.props.initialCollection !== nextProps.initialCollection)) {
if (nextProps.initialCollection) { if (nextProps.initialCollection) {
@ -212,10 +212,10 @@ class ContactEdit extends React.PureComponent<PropsType> {
} }
} }
addValueType(name: string, _type?: string) { public addValueType(name: string, _type?: string) {
const type = _type ? _type : 'home'; const type = _type ? _type : 'home';
this.setState((prevState, props) => { this.setState((prevState, props) => {
let newArray = prevState[name].slice(0); const newArray = prevState[name].slice(0);
newArray.push(new ValueType(type)); newArray.push(new ValueType(type));
return { return {
...prevState, ...prevState,
@ -224,9 +224,9 @@ class ContactEdit extends React.PureComponent<PropsType> {
}); });
} }
removeValueType(name: string, idx: number) { public removeValueType(name: string, idx: number) {
this.setState((prevState, props) => { this.setState((prevState, props) => {
let newArray = prevState[name].slice(0); const newArray = prevState[name].slice(0);
newArray.splice(idx, 1); newArray.splice(idx, 1);
return { return {
...prevState, ...prevState,
@ -235,9 +235,9 @@ class ContactEdit extends React.PureComponent<PropsType> {
}); });
} }
handleValueTypeChange(name: string, idx: number, value: ValueType) { public handleValueTypeChange(name: string, idx: number, value: ValueType) {
this.setState((prevState, props) => { this.setState((prevState, props) => {
let newArray = prevState[name].slice(0); const newArray = prevState[name].slice(0);
newArray[idx] = value; newArray[idx] = value;
return { return {
...prevState, ...prevState,
@ -246,29 +246,29 @@ class ContactEdit extends React.PureComponent<PropsType> {
}); });
} }
handleChange(name: string, value: string) { public handleChange(name: string, value: string) {
this.setState({ this.setState({
[name]: value [name]: value,
}); });
} }
handleInputChange(contact: any) { public handleInputChange(contact: any) {
const name = contact.target.name; const name = contact.target.name;
const value = contact.target.value; const value = contact.target.value;
this.handleChange(name, value); this.handleChange(name, value);
} }
onSubmit(e: React.FormEvent<any>) { public onSubmit(e: React.FormEvent<any>) {
e.preventDefault(); e.preventDefault();
let contact = (this.props.item) ? const contact = (this.props.item) ?
this.props.item.clone() this.props.item.clone()
: :
new ContactType(new ICAL.Component(['vcard', [], []])) new ContactType(new ICAL.Component(['vcard', [], []]))
; ;
let comp = contact.comp; const comp = contact.comp;
comp.updatePropertyWithValue('prodid', '-//iCal.js EteSync Web'); comp.updatePropertyWithValue('prodid', '-//iCal.js EteSync Web');
comp.updatePropertyWithValue('version', '4.0'); comp.updatePropertyWithValue('version', '4.0');
comp.updatePropertyWithValue('uid', this.state.uid); comp.updatePropertyWithValue('uid', this.state.uid);
@ -282,7 +282,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
return; return;
} }
let prop = new ICAL.Property(name, comp); const prop = new ICAL.Property(name, comp);
prop.setParameter('type', x.type); prop.setParameter('type', x.type);
prop.setValue(x.value); prop.setValue(x.value);
comp.addProperty(prop); comp.addProperty(prop);
@ -310,13 +310,13 @@ class ContactEdit extends React.PureComponent<PropsType> {
this.props.onSave(contact, this.state.journalUid, this.props.item); this.props.onSave(contact, this.state.journalUid, this.props.item);
} }
onDeleteRequest() { public onDeleteRequest() {
this.setState({ this.setState({
showDeleteDialog: true showDeleteDialog: true,
}); });
} }
render() { public render() {
const styles = { const styles = {
form: { form: {
}, },

@ -7,12 +7,12 @@ interface FormErrors {
} }
class EncryptionLoginForm extends React.PureComponent { class EncryptionLoginForm extends React.PureComponent {
state: { public state: {
errors: FormErrors, errors: FormErrors,
encryptionPassword: string; encryptionPassword: string;
}; };
props: { public props: {
onSubmit: (encryptionPassword: string) => void; onSubmit: (encryptionPassword: string) => void;
loading?: boolean; loading?: boolean;
error?: Error; error?: Error;
@ -28,27 +28,27 @@ class EncryptionLoginForm extends React.PureComponent {
this.handleInputChange = this.handleInputChange.bind(this); this.handleInputChange = this.handleInputChange.bind(this);
} }
handleInputChange(event: React.ChangeEvent<any>) { public handleInputChange(event: React.ChangeEvent<any>) {
const name = event.target.name; const name = event.target.name;
const value = event.target.value; const value = event.target.value;
this.setState({ this.setState({
[name]: value [name]: value,
}); });
} }
generateEncryption(e: any) { public generateEncryption(e: any) {
e.preventDefault(); e.preventDefault();
const encryptionPassword = this.state.encryptionPassword; const encryptionPassword = this.state.encryptionPassword;
let errors: FormErrors = {}; const errors: FormErrors = {};
const fieldRequired = 'This field is required!'; const fieldRequired = 'This field is required!';
if (!encryptionPassword) { if (!encryptionPassword) {
errors.errorEncryptionPassword = fieldRequired; errors.errorEncryptionPassword = fieldRequired;
} }
if (Object.keys(errors).length) { if (Object.keys(errors).length) {
this.setState({errors: errors}); this.setState({errors});
return; return;
} else { } else {
this.setState({errors: {}}); this.setState({errors: {}});
@ -57,7 +57,7 @@ class EncryptionLoginForm extends React.PureComponent {
this.props.onSubmit(encryptionPassword); this.props.onSubmit(encryptionPassword);
} }
render() { public render() {
const styles = { const styles = {
form: { form: {
}, },

@ -6,7 +6,7 @@ import { IntegrityError } from '../api/EteSync';
import PrettyError from '../widgets/PrettyError'; import PrettyError from '../widgets/PrettyError';
class ErrorBoundary extends React.Component { class ErrorBoundary extends React.Component {
state: { public state: {
error?: Error; error?: Error;
}; };
@ -15,7 +15,7 @@ class ErrorBoundary extends React.Component {
this.state = { }; this.state = { };
} }
componentDidCatch(error: Error, info: any) { public componentDidCatch(error: Error, info: any) {
if (error instanceof IntegrityError) { if (error instanceof IntegrityError) {
persistor.purge(); persistor.purge();
} }
@ -23,7 +23,7 @@ class ErrorBoundary extends React.Component {
this.setState({ error }); this.setState({ error });
} }
render() { public render() {
const { error } = this.state; const { error } = this.state;
if (error && error instanceof IntegrityError) { if (error && error instanceof IntegrityError) {
return ( return (

@ -7,11 +7,11 @@ import { formatDateRange } from '../helpers';
import { EventType } from '../pim-types'; import { EventType } from '../pim-types';
class Event extends React.PureComponent { class Event extends React.PureComponent {
props: { public props: {
item?: EventType, item?: EventType,
}; };
render() { public render() {
if (this.props.item === undefined) { if (this.props.item === undefined) {
throw Error('Event should be defined!'); throw Error('Event should be defined!');
} }
@ -27,7 +27,7 @@ class Event extends React.PureComponent {
return ( return (
<React.Fragment> <React.Fragment>
<PimItemHeader text={this.props.item.summary} backgroundColor={this.props.item.color}> <PimItemHeader text={this.props.item.summary} backgroundColor={this.props.item.color}>
<div>{formatDateRange(this.props.item.startDate, this.props.item.endDate)} { timezone && <small>({timezone})</small>}</div> <div>{formatDateRange(this.props.item.startDate, this.props.item.endDate)} {timezone && <small>({timezone})</small>}</div>
<br/> <br/>
<div><u>{this.props.item.location}</u></div> <div><u>{this.props.item.location}</u></div>
</PimItemHeader> </PimItemHeader>

@ -31,17 +31,17 @@ import * as EteSync from '../api/EteSync';
import { EventType } from '../pim-types'; import { EventType } from '../pim-types';
interface PropsType { interface PropsType {
collections: Array<EteSync.CollectionInfo>; collections: EteSync.CollectionInfo[];
initialCollection?: string; initialCollection?: string;
item?: EventType; item?: EventType;
onSave: (event: EventType, journalUid: string, originalEvent?: EventType) => void; onSave: (event: EventType, journalUid: string, originalEvent?: EventType) => void;
onDelete: (event: EventType, journalUid: string) => void; onDelete: (event: EventType, journalUid: string) => void;
onCancel: () => void; onCancel: () => void;
location: Location; location: Location;
}; }
class EventEdit extends React.PureComponent<PropsType> { class EventEdit extends React.PureComponent<PropsType> {
state: { public state: {
uid: string, uid: string,
title: string; title: string;
allDay: boolean; allDay: boolean;
@ -84,7 +84,7 @@ class EventEdit extends React.PureComponent<PropsType> {
const event = this.props.item; const event = this.props.item;
const allDay = event.startDate.isDate; const allDay = event.startDate.isDate;
let endDate = event.endDate.clone(); const endDate = event.endDate.clone();
if (allDay) { if (allDay) {
endDate.adjust(-1, 0, 0, 0); endDate.adjust(-1, 0, 0, 0);
@ -114,7 +114,7 @@ class EventEdit extends React.PureComponent<PropsType> {
this.onDeleteRequest = this.onDeleteRequest.bind(this); this.onDeleteRequest = this.onDeleteRequest.bind(this);
} }
componentWillReceiveProps(nextProps: any) { public componentWillReceiveProps(nextProps: any) {
if ((this.props.collections !== nextProps.collections) || if ((this.props.collections !== nextProps.collections) ||
(this.props.initialCollection !== nextProps.initialCollection)) { (this.props.initialCollection !== nextProps.initialCollection)) {
if (nextProps.initialCollection) { if (nextProps.initialCollection) {
@ -125,24 +125,24 @@ class EventEdit extends React.PureComponent<PropsType> {
} }
} }
handleChange(name: string, value: string) { public handleChange(name: string, value: string) {
this.setState({ this.setState({
[name]: value [name]: value,
}); });
} }
handleInputChange(event: React.ChangeEvent<any>) { public handleInputChange(event: React.ChangeEvent<any>) {
const name = event.target.name; const name = event.target.name;
const value = event.target.value; const value = event.target.value;
this.handleChange(name, value); this.handleChange(name, value);
} }
toggleAllDay() { public toggleAllDay() {
this.setState({allDay: !this.state.allDay}); this.setState({allDay: !this.state.allDay});
} }
onSubmit(e: React.FormEvent<any>) { public onSubmit(e: React.FormEvent<any>) {
e.preventDefault(); e.preventDefault();
if ((!this.state.start) || (!this.state.end)) { if ((!this.state.start) || (!this.state.end)) {
@ -155,7 +155,7 @@ class EventEdit extends React.PureComponent<PropsType> {
if (!allDay) { if (!allDay) {
return ret; return ret;
} else { } else {
let data = ret.toJSON(); const data = ret.toJSON();
data.isDate = allDay; data.isDate = allDay;
return ICAL.Time.fromData(data); return ICAL.Time.fromData(data);
} }
@ -173,7 +173,7 @@ class EventEdit extends React.PureComponent<PropsType> {
return; return;
} }
let event = (this.props.item) ? const event = (this.props.item) ?
this.props.item.clone() this.props.item.clone()
: :
new EventType() new EventType()
@ -190,13 +190,13 @@ class EventEdit extends React.PureComponent<PropsType> {
this.props.onSave(event, this.state.journalUid, this.props.item); this.props.onSave(event, this.state.journalUid, this.props.item);
} }
onDeleteRequest() { public onDeleteRequest() {
this.setState({ this.setState({
showDeleteDialog: true showDeleteDialog: true,
}); });
} }
render() { public render() {
const styles = { const styles = {
form: { form: {
}, },

@ -18,15 +18,15 @@ import { TaskType, EventType, ContactType } from '../pim-types';
import * as EteSync from '../api/EteSync'; import * as EteSync from '../api/EteSync';
class JournalEntries extends React.PureComponent { class JournalEntries extends React.PureComponent {
static defaultProps = { public static defaultProps = {
prevUid: null, prevUid: null,
}; };
state: { public state: {
dialog?: string; dialog?: string;
}; };
props: { public props: {
journal: EteSync.Journal, journal: EteSync.Journal,
entries: Immutable.List<EteSync.SyncEntry>, entries: Immutable.List<EteSync.SyncEntry>,
uid?: string, uid?: string,
@ -37,7 +37,7 @@ class JournalEntries extends React.PureComponent {
this.state = {}; this.state = {};
} }
render() { public render() {
if (this.props.journal === undefined) { if (this.props.journal === undefined) {
return (<div>Loading</div>); return (<div>Loading</div>);
} }
@ -87,7 +87,7 @@ class JournalEntries extends React.PureComponent {
secondaryText={uid} secondaryText={uid}
onClick={() => { onClick={() => {
this.setState({ this.setState({
dialog: syncEntry.content dialog: syncEntry.content,
}); });
}} }}
/> />

@ -17,7 +17,7 @@ interface FormErrors {
} }
class LoginForm extends React.PureComponent { class LoginForm extends React.PureComponent {
state: { public state: {
showAdvanced: boolean; showAdvanced: boolean;
errors: FormErrors; errors: FormErrors;
@ -27,7 +27,7 @@ class LoginForm extends React.PureComponent {
encryptionPassword: string; encryptionPassword: string;
}; };
props: { public props: {
onSubmit: (username: string, password: string, encryptionPassword: string, serviceApiUrl?: string) => void; onSubmit: (username: string, password: string, encryptionPassword: string, serviceApiUrl?: string) => void;
loading?: boolean; loading?: boolean;
error?: Error; error?: Error;
@ -48,15 +48,15 @@ class LoginForm extends React.PureComponent {
this.handleInputChange = this.handleInputChange.bind(this); this.handleInputChange = this.handleInputChange.bind(this);
} }
handleInputChange(event: React.ChangeEvent<any>) { public handleInputChange(event: React.ChangeEvent<any>) {
const name = event.target.name; const name = event.target.name;
const value = event.target.value; const value = event.target.value;
this.setState({ this.setState({
[name]: value [name]: value,
}); });
} }
generateEncryption(e: any) { public generateEncryption(e: any) {
e.preventDefault(); e.preventDefault();
const server = this.state.showAdvanced ? this.state.server : undefined; const server = this.state.showAdvanced ? this.state.server : undefined;
@ -64,7 +64,7 @@ class LoginForm extends React.PureComponent {
const password = this.state.password; const password = this.state.password;
const encryptionPassword = this.state.encryptionPassword; const encryptionPassword = this.state.encryptionPassword;
let errors: FormErrors = {}; const errors: FormErrors = {};
const fieldRequired = 'This field is required!'; const fieldRequired = 'This field is required!';
if (!username) { if (!username) {
errors.errorEmail = fieldRequired; errors.errorEmail = fieldRequired;
@ -83,7 +83,7 @@ class LoginForm extends React.PureComponent {
} }
if (Object.keys(errors).length) { if (Object.keys(errors).length) {
this.setState({errors: errors}); this.setState({errors});
return; return;
} else { } else {
this.setState({errors: {}}); this.setState({errors: {}});
@ -92,11 +92,11 @@ class LoginForm extends React.PureComponent {
this.props.onSubmit(username, password, encryptionPassword, server); this.props.onSubmit(username, password, encryptionPassword, server);
} }
toggleAdvancedSettings() { public toggleAdvancedSettings() {
this.setState({showAdvanced: !this.state.showAdvanced}); this.setState({showAdvanced: !this.state.showAdvanced});
} }
render() { public render() {
const styles = { const styles = {
form: { form: {
}, },

@ -11,12 +11,12 @@ import { ContactType } from '../pim-types';
import AddressBook from '../components/AddressBook'; import AddressBook from '../components/AddressBook';
class SearchableAddressBook extends React.PureComponent { class SearchableAddressBook extends React.PureComponent {
props: { public props: {
entries: Array<ContactType>, entries: ContactType[],
onItemClick: (contact: ContactType) => void, onItemClick: (contact: ContactType) => void,
}; };
state: { public state: {
searchQuery: string; searchQuery: string;
}; };
@ -26,15 +26,15 @@ class SearchableAddressBook extends React.PureComponent {
this.handleInputChange = this.handleInputChange.bind(this); this.handleInputChange = this.handleInputChange.bind(this);
} }
handleInputChange(event: React.ChangeEvent<any>) { public handleInputChange(event: React.ChangeEvent<any>) {
const name = event.target.name; const name = event.target.name;
const value = event.target.value; const value = event.target.value;
this.setState({ this.setState({
[name]: value [name]: value,
}); });
} }
render() { public render() {
const { const {
entries, entries,
...rest ...rest
@ -52,7 +52,7 @@ class SearchableAddressBook extends React.PureComponent {
onChange={this.handleInputChange} onChange={this.handleInputChange}
/> />
{this.state.searchQuery && {this.state.searchQuery &&
<IconButton onClick={() => this.setState({'searchQuery': ''})}> <IconButton onClick={() => this.setState({searchQuery: ''})}>
<IconClear /> <IconClear />
</IconButton> </IconButton>
} }

@ -7,11 +7,11 @@ import { formatDate } from '../helpers';
import { TaskType } from '../pim-types'; import { TaskType } from '../pim-types';
class Task extends React.PureComponent { class Task extends React.PureComponent {
props: { public props: {
item?: TaskType, item?: TaskType,
}; };
render() { public render() {
if (this.props.item === undefined) { if (this.props.item === undefined) {
throw Error('Task should be defined!'); throw Error('Task should be defined!');
} }
@ -30,10 +30,10 @@ class Task extends React.PureComponent {
<React.Fragment> <React.Fragment>
<PimItemHeader text={this.props.item.summary} backgroundColor={this.props.item.color}> <PimItemHeader text={this.props.item.summary} backgroundColor={this.props.item.color}>
{ item.startDate && { item.startDate &&
<div>Start: {formatDate(item.startDate)} { timezone && <small>({timezone})</small>}</div> <div>Start: {formatDate(item.startDate)} {timezone && <small>({timezone})</small>}</div>
} }
{ item.dueDate && { item.dueDate &&
<div>Due: {formatDate(item.dueDate)} { timezone && <small>({timezone})</small>}</div> <div>Due: {formatDate(item.dueDate)} {timezone && <small>({timezone})</small>}</div>
} }
<br/> <br/>
<div><u>{this.props.item.location}</u></div> <div><u>{this.props.item.location}</u></div>

@ -31,17 +31,17 @@ import * as EteSync from '../api/EteSync';
import { TaskType, TaskStatusType } from '../pim-types'; import { TaskType, TaskStatusType } from '../pim-types';
interface PropsType { interface PropsType {
collections: Array<EteSync.CollectionInfo>; collections: EteSync.CollectionInfo[];
initialCollection?: string; initialCollection?: string;
item?: TaskType; item?: TaskType;
onSave: (item: TaskType, journalUid: string, originalItem?: TaskType) => void; onSave: (item: TaskType, journalUid: string, originalItem?: TaskType) => void;
onDelete: (item: TaskType, journalUid: string) => void; onDelete: (item: TaskType, journalUid: string) => void;
onCancel: () => void; onCancel: () => void;
location: Location; location: Location;
}; }
class TaskEdit extends React.PureComponent<PropsType> { class TaskEdit extends React.PureComponent<PropsType> {
state: { public state: {
uid: string, uid: string,
title: string; title: string;
status: TaskStatusType; status: TaskStatusType;
@ -102,7 +102,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
this.onDeleteRequest = this.onDeleteRequest.bind(this); this.onDeleteRequest = this.onDeleteRequest.bind(this);
} }
componentWillReceiveProps(nextProps: any) { public componentWillReceiveProps(nextProps: any) {
if ((this.props.collections !== nextProps.collections) || if ((this.props.collections !== nextProps.collections) ||
(this.props.initialCollection !== nextProps.initialCollection)) { (this.props.initialCollection !== nextProps.initialCollection)) {
if (nextProps.initialCollection) { if (nextProps.initialCollection) {
@ -113,24 +113,24 @@ class TaskEdit extends React.PureComponent<PropsType> {
} }
} }
handleChange(name: string, value: string) { public handleChange(name: string, value: string) {
this.setState({ this.setState({
[name]: value [name]: value,
}); });
} }
handleInputChange(event: React.ChangeEvent<any>) { public handleInputChange(event: React.ChangeEvent<any>) {
const name = event.target.name; const name = event.target.name;
const value = event.target.value; const value = event.target.value;
this.handleChange(name, value); this.handleChange(name, value);
} }
toggleAllDay() { public toggleAllDay() {
this.setState({allDay: !this.state.allDay}); this.setState({allDay: !this.state.allDay});
} }
onSubmit(e: React.FormEvent<any>) { public onSubmit(e: React.FormEvent<any>) {
e.preventDefault(); e.preventDefault();
function fromDate(date: Date | undefined, allDay: boolean) { function fromDate(date: Date | undefined, allDay: boolean) {
@ -141,7 +141,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
if (!allDay) { if (!allDay) {
return ret; return ret;
} else { } else {
let data = ret.toJSON(); const data = ret.toJSON();
data.isDate = allDay; data.isDate = allDay;
return ICAL.Time.fromData(data); return ICAL.Time.fromData(data);
} }
@ -157,7 +157,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
} }
} }
let event = (this.props.item) ? const event = (this.props.item) ?
this.props.item.clone() this.props.item.clone()
: :
new TaskType(null) new TaskType(null)
@ -178,13 +178,13 @@ class TaskEdit extends React.PureComponent<PropsType> {
this.props.onSave(event, this.state.journalUid, this.props.item); this.props.onSave(event, this.state.journalUid, this.props.item);
} }
onDeleteRequest() { public onDeleteRequest() {
this.setState({ this.setState({
showDeleteDialog: true showDeleteDialog: true,
}); });
} }
render() { public render() {
const styles = { const styles = {
form: { form: {
}, },

@ -23,7 +23,7 @@ const TaskListItem = pure((_props: any) => {
}); });
const sortSelector = createSelector( const sortSelector = createSelector(
(entries: Array<TaskType>) => entries, (entries: TaskType[]) => entries,
(entries) => { (entries) => {
return entries.sort((_a, _b) => { return entries.sort((_a, _b) => {
const a = _a.title; const a = _a.title;
@ -37,20 +37,20 @@ const sortSelector = createSelector(
return 0; return 0;
} }
}); });
}, }
); );
class TaskList extends React.PureComponent { class TaskList extends React.PureComponent {
props: { public props: {
entries: Array<TaskType>, entries: TaskType[],
onItemClick: (contact: TaskType) => void, onItemClick: (contact: TaskType) => void,
}; };
render() { public render() {
const entries = this.props.entries.filter((x) => !x.finished); const entries = this.props.entries.filter((x) => !x.finished);
const sortedEntries = sortSelector(entries); const sortedEntries = sortSelector(entries);
let itemList = sortedEntries.map((entry, idx, array) => { const itemList = sortedEntries.map((entry, idx, array) => {
const uid = entry.uid; const uid = entry.uid;
return ( return (

@ -2,12 +2,12 @@ import * as React from 'react';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
// FIXME: Should probably tie this to the history object, or at least based on the depth of the history // FIXME: Should probably tie this to the history object, or at least based on the depth of the history
let stateCache = {}; const stateCache = {};
type Constructor<T> = new(...args: any[]) => T; type Constructor<T> = new(...args: any[]) => T;
export function historyPersistor(tag: string) { export function historyPersistor(tag: string) {
return function<T extends Constructor<React.Component>>(Base: T) { return <T extends Constructor<React.Component>>(Base: T) => {
return withRouter(class extends Base { return withRouter(class extends Base {
constructor(...rest: any[]) { constructor(...rest: any[]) {
const props = rest[0]; const props = rest[0];
@ -18,14 +18,14 @@ export function historyPersistor(tag: string) {
} }
} }
componentWillUnmount() { public componentWillUnmount() {
if (super.componentWillUnmount) { if (super.componentWillUnmount) {
super.componentWillUnmount(); super.componentWillUnmount();
} }
stateCache[this.getKeyForTag(this.props, tag)] = this.state; stateCache[this.getKeyForTag(this.props, tag)] = this.state;
} }
getKeyForTag(props: any, tagName: string) { public getKeyForTag(props: any, tagName: string) {
return props.location.pathname + ':' + tagName; return props.location.pathname + ':' + tagName;
} }
}); });

@ -50,7 +50,7 @@ export default function register() {
function registerValidSW(swUrl: string) { function registerValidSW(swUrl: string) {
navigator.serviceWorker navigator.serviceWorker
.register(swUrl) .register(swUrl)
.then(registration => { .then((registration) => {
registration.onupdatefound = () => { registration.onupdatefound = () => {
const installingWorker = registration.installing; const installingWorker = registration.installing;
if (installingWorker) { if (installingWorker) {
@ -73,7 +73,7 @@ function registerValidSW(swUrl: string) {
} }
}; };
}) })
.catch(error => { .catch((error) => {
console.error('Error during service worker registration:', error); console.error('Error during service worker registration:', error);
}); });
} }
@ -81,14 +81,14 @@ function registerValidSW(swUrl: string) {
function checkValidServiceWorker(swUrl: string) { function checkValidServiceWorker(swUrl: string) {
// Check if the service worker can be found. If it can't reload the page. // Check if the service worker can be found. If it can't reload the page.
fetch(swUrl) fetch(swUrl)
.then(response => { .then((response) => {
// Ensure service worker exists, and that we really are getting a JS file. // Ensure service worker exists, and that we really are getting a JS file.
if ( if (
response.status === 404 || response.status === 404 ||
response.headers.get('content-type')!.indexOf('javascript') === -1 response.headers.get('content-type')!.indexOf('javascript') === -1
) { ) {
// No service worker found. Probably a different app. Reload the page. // No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => { registration.unregister().then(() => {
window.location.reload(); window.location.reload();
}); });
@ -107,7 +107,7 @@ function checkValidServiceWorker(swUrl: string) {
export function unregister() { export function unregister() {
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister(); registration.unregister();
}); });
} }

Loading…
Cancel
Save