More tslint fixes.
parent
9913adc756
commit
7da0a6009b
|
@ -152,7 +152,7 @@ const AppBarWitHistory = withRouter(
|
|||
private goBack() {
|
||||
this.props.history!.goBack();
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const IconRefreshWithSpin = withSpin(NavigationRefresh);
|
||||
|
@ -249,7 +249,7 @@ const credentialsSelector = createSelector(
|
|||
encryptionKey,
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const mapStateToProps = (state: store.StoreState) => {
|
||||
|
@ -261,5 +261,5 @@ const mapStateToProps = (state: store.StoreState) => {
|
|||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapStateToProps
|
||||
)(App);
|
||||
|
|
|
@ -43,7 +43,7 @@ const JournalCalendar = journalView(PersistCalendar, Event);
|
|||
const JournalTaskList = journalView(TaskList, Event);
|
||||
|
||||
class Journal extends React.Component<PropsTypeInner> {
|
||||
state: {
|
||||
public state: {
|
||||
tab: number,
|
||||
};
|
||||
|
||||
|
@ -55,7 +55,7 @@ class Journal extends React.Component<PropsTypeInner> {
|
|||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { theme, isOwner, syncJournal } = this.props;
|
||||
let currentTab = this.state.tab;
|
||||
let journalOnly = false;
|
||||
|
|
|
@ -32,7 +32,7 @@ interface PropsTypeInner extends PropsType {
|
|||
}
|
||||
|
||||
class JournalEdit extends React.PureComponent<PropsTypeInner> {
|
||||
state = {
|
||||
public state = {
|
||||
info: {
|
||||
uid: '',
|
||||
type: '',
|
||||
|
@ -61,7 +61,7 @@ class JournalEdit extends React.PureComponent<PropsTypeInner> {
|
|||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { item, onDelete, onCancel } = this.props;
|
||||
|
||||
const pageTitle = (item !== undefined) ? item.displayName : 'New Journal';
|
||||
|
@ -177,7 +177,7 @@ class JournalEdit extends React.PureComponent<PropsTypeInner> {
|
|||
|
||||
private onDeleteRequest() {
|
||||
this.setState({
|
||||
showDeleteDialog: true
|
||||
showDeleteDialog: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ interface PropsTypeInner extends PropsType {
|
|||
}
|
||||
|
||||
class JournalMembers extends React.PureComponent<PropsTypeInner> {
|
||||
state = {
|
||||
public state = {
|
||||
members: null as EteSync.JournalMemberJson[] | null,
|
||||
};
|
||||
|
||||
|
@ -29,7 +29,7 @@ class JournalMembers extends React.PureComponent<PropsTypeInner> {
|
|||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { info } = this.props;
|
||||
const { members } = this.state;
|
||||
|
||||
|
@ -57,7 +57,7 @@ class JournalMembers extends React.PureComponent<PropsTypeInner> {
|
|||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount() {
|
||||
this.fetchMembers();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import { routeResolver } from '../App';
|
|||
import { JournalsData, UserInfoData, CredentialsData } from '../store';
|
||||
|
||||
class JournalsList extends React.PureComponent {
|
||||
props: {
|
||||
public props: {
|
||||
etesync: CredentialsData;
|
||||
journals: JournalsData;
|
||||
userInfo: UserInfoData;
|
||||
|
@ -30,7 +30,7 @@ class JournalsList extends React.PureComponent {
|
|||
this.journalClicked = this.journalClicked.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const derived = this.props.etesync.encryptionKey;
|
||||
let asymmetricCryptoManager: EteSync.AsymmetricCryptoManager;
|
||||
const journalMap = this.props.journals.reduce(
|
||||
|
@ -47,7 +47,7 @@ class JournalsList extends React.PureComponent {
|
|||
} else {
|
||||
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].push(
|
||||
<ListItem key={journal.uid} onClick={() => this.journalClicked(journal.uid)}>
|
||||
|
@ -59,7 +59,7 @@ class JournalsList extends React.PureComponent {
|
|||
},
|
||||
{ CALENDAR: [],
|
||||
ADDRESS_BOOK: [],
|
||||
TASKS: []
|
||||
TASKS: [],
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -94,7 +94,7 @@ class JournalsList extends React.PureComponent {
|
|||
}
|
||||
|
||||
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';
|
||||
|
||||
class Journals extends React.PureComponent {
|
||||
props: {
|
||||
public props: {
|
||||
etesync: CredentialsData;
|
||||
journals: JournalsData;
|
||||
userInfo: UserInfoData;
|
||||
|
@ -32,7 +32,7 @@ class Journals extends React.PureComponent {
|
|||
this.onItemSave = this.onItemSave.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
return (
|
||||
<Switch>
|
||||
<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 cryptoManager = new EteSync.CryptoManager(this.props.etesync.encryptionKey, info.uid);
|
||||
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 cryptoManager = new EteSync.CryptoManager(this.props.etesync.encryptionKey, info.uid);
|
||||
journal.setInfo(cryptoManager, info);
|
||||
|
@ -140,7 +140,7 @@ class Journals extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
public onCancel() {
|
||||
this.props.history.goBack();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ function objValues(obj: any) {
|
|||
|
||||
export function journalView(JournalList: any, JournalItem: any) {
|
||||
return withRouter(class extends React.PureComponent {
|
||||
props: {
|
||||
public props: {
|
||||
journal: EteSync.Journal,
|
||||
entries: {[key: string]: any},
|
||||
history?: History,
|
||||
|
@ -24,15 +24,15 @@ export function journalView(JournalList: any, JournalItem: any) {
|
|||
this.itemClicked = this.itemClicked.bind(this);
|
||||
}
|
||||
|
||||
itemClicked(contact: any) {
|
||||
public itemClicked(contact: any) {
|
||||
const uid = contact.uid;
|
||||
|
||||
this.props.history!.push(
|
||||
routeResolver.getRoute('journals._id.items._id', { journalUid: this.props.journal.uid, itemUid: uid }));
|
||||
}
|
||||
|
||||
render() {
|
||||
let items = this.props.entries;
|
||||
public render() {
|
||||
const items = this.props.entries;
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
|
|
|
@ -11,10 +11,10 @@ import { login, deriveKey } from './store/actions';
|
|||
|
||||
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 {
|
||||
props: {
|
||||
public props: {
|
||||
credentials: CredentialsType;
|
||||
};
|
||||
|
||||
|
@ -24,16 +24,16 @@ class LoginGate extends React.Component {
|
|||
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;
|
||||
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));
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
if (this.props.credentials.value === null) {
|
||||
const style = {
|
||||
isSafe: {
|
||||
|
@ -43,7 +43,7 @@ class LoginGate extends React.Component {
|
|||
divider: {
|
||||
margin: '30px 0',
|
||||
color: '#00000025',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -28,16 +28,16 @@ const tasksTitle = 'Tasks';
|
|||
const PersistCalendar = historyPersistor('Calendar')(Calendar);
|
||||
|
||||
interface PropsType {
|
||||
contacts: Array<ContactType>;
|
||||
events: Array<EventType>;
|
||||
tasks: Array<TaskType>;
|
||||
contacts: ContactType[];
|
||||
events: EventType[];
|
||||
tasks: TaskType[];
|
||||
location?: Location;
|
||||
history?: History;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
class PimMain extends React.PureComponent<PropsType> {
|
||||
state: {
|
||||
public state: {
|
||||
tab: number;
|
||||
};
|
||||
|
||||
|
@ -51,35 +51,35 @@ class PimMain extends React.PureComponent<PropsType> {
|
|||
this.newEvent = this.newEvent.bind(this);
|
||||
}
|
||||
|
||||
eventClicked(event: ICAL.Event) {
|
||||
public eventClicked(event: ICAL.Event) {
|
||||
const uid = event.uid;
|
||||
|
||||
this.props.history!.push(
|
||||
routeResolver.getRoute('pim.events._id', { itemUid: uid }));
|
||||
}
|
||||
|
||||
taskClicked(event: ICAL.Event) {
|
||||
public taskClicked(event: ICAL.Event) {
|
||||
const uid = event.uid;
|
||||
|
||||
this.props.history!.push(
|
||||
routeResolver.getRoute('pim.tasks._id', { itemUid: uid }));
|
||||
}
|
||||
|
||||
contactClicked(contact: ContactType) {
|
||||
public contactClicked(contact: ContactType) {
|
||||
const uid = contact.uid;
|
||||
|
||||
this.props.history!.push(
|
||||
routeResolver.getRoute('pim.contacts._id', { itemUid: uid }));
|
||||
}
|
||||
|
||||
newEvent(start?: Date, end?: Date) {
|
||||
public newEvent(start?: Date, end?: Date) {
|
||||
this.props.history!.push(
|
||||
routeResolver.getRoute('pim.events.new'),
|
||||
{start, end}
|
||||
);
|
||||
}
|
||||
|
||||
floatingButtonClicked() {
|
||||
public floatingButtonClicked() {
|
||||
if (this.state.tab === 0) {
|
||||
this.props.history!.push(
|
||||
routeResolver.getRoute('pim.contacts.new')
|
||||
|
@ -93,7 +93,7 @@ class PimMain extends React.PureComponent<PropsType> {
|
|||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { theme } = this.props;
|
||||
const { tab } = this.state;
|
||||
const style = {
|
||||
|
|
|
@ -47,9 +47,9 @@ function objValues(obj: any) {
|
|||
const itemsSelector = createSelector(
|
||||
(props: {syncInfo: SyncInfo}) => props.syncInfo,
|
||||
(syncInfo) => {
|
||||
let collectionsAddressBook: Array<EteSync.CollectionInfo> = [];
|
||||
let collectionsCalendar: Array<EteSync.CollectionInfo> = [];
|
||||
let collectionsTaskList: Array<EteSync.CollectionInfo> = [];
|
||||
const collectionsAddressBook: EteSync.CollectionInfo[] = [];
|
||||
const collectionsCalendar: EteSync.CollectionInfo[] = [];
|
||||
const collectionsTaskList: EteSync.CollectionInfo[] = [];
|
||||
let addressBookItems: {[key: string]: ContactType} = {};
|
||||
let calendarItems: {[key: string]: EventType} = {};
|
||||
let taskListItems: {[key: string]: TaskType} = {};
|
||||
|
@ -73,9 +73,9 @@ const itemsSelector = createSelector(
|
|||
);
|
||||
|
||||
return {
|
||||
collectionsAddressBook, collectionsCalendar, collectionsTaskList, addressBookItems, calendarItems, taskListItems
|
||||
collectionsAddressBook, collectionsCalendar, collectionsTaskList, addressBookItems, calendarItems, taskListItems,
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const ItemChangeLog = pure((props: any) => {
|
||||
|
@ -102,7 +102,7 @@ const ItemChangeLog = pure((props: any) => {
|
|||
type CollectionRoutesPropsType = RouteComponentProps<{}> & {
|
||||
syncInfo: SyncInfo,
|
||||
routePrefix: string,
|
||||
collections: Array<EteSync.CollectionInfo>,
|
||||
collections: EteSync.CollectionInfo[],
|
||||
componentEdit: any,
|
||||
componentView: any,
|
||||
items: {[key: string]: PimType},
|
||||
|
@ -123,7 +123,7 @@ const styles = (theme: any) => ({
|
|||
|
||||
const CollectionRoutes = withStyles(styles)(withRouter(
|
||||
class CollectionRoutesInner extends React.PureComponent<CollectionRoutesPropsType> {
|
||||
render() {
|
||||
public render() {
|
||||
const props = this.props;
|
||||
const { classes } = this.props;
|
||||
const ComponentEdit = props.componentEdit;
|
||||
|
@ -221,7 +221,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
|
|||
));
|
||||
|
||||
class Pim extends React.PureComponent {
|
||||
props: {
|
||||
public props: {
|
||||
etesync: CredentialsData;
|
||||
userInfo: UserInfoData;
|
||||
syncInfo: SyncInfo;
|
||||
|
@ -235,7 +235,7 @@ class Pim extends React.PureComponent {
|
|||
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);
|
||||
|
||||
if (syncJournal === undefined) {
|
||||
|
@ -244,7 +244,7 @@ class Pim extends React.PureComponent {
|
|||
|
||||
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 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);
|
||||
|
||||
if (syncJournal === undefined) {
|
||||
|
@ -279,7 +279,7 @@ class Pim extends React.PureComponent {
|
|||
|
||||
const journal = syncJournal.journal;
|
||||
|
||||
let action = EteSync.SyncEntryAction.Delete;
|
||||
const action = EteSync.SyncEntryAction.Delete;
|
||||
|
||||
let prevUid: string | null = null;
|
||||
let last = syncJournal.journalEntries.last() as EteSync.Entry;
|
||||
|
@ -305,11 +305,11 @@ class Pim extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
public onCancel() {
|
||||
this.props.history.goBack();
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { collectionsAddressBook, collectionsCalendar, collectionsTaskList, addressBookItems, calendarItems, taskListItems } = itemsSelector(this.props);
|
||||
|
||||
return (
|
||||
|
|
|
@ -28,15 +28,7 @@ class Settings extends React.PureComponent<PropsTypeInner> {
|
|||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
|
||||
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}));
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { settings } = this.props;
|
||||
return (
|
||||
<>
|
||||
|
@ -61,9 +53,17 @@ class Settings extends React.PureComponent<PropsTypeInner> {
|
|||
);
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
public onCancel() {
|
||||
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) => {
|
||||
|
|
|
@ -11,7 +11,7 @@ import ActionBugReport from '@material-ui/icons/BugReport';
|
|||
import ActionQuestionAnswer from '@material-ui/icons/QuestionAnswer';
|
||||
import LogoutIcon from '@material-ui/icons/PowerSettingsNew';
|
||||
|
||||
const logo = require('../images/logo.svg');
|
||||
import logo from '../images/logo.svg';
|
||||
|
||||
import { routeResolver } from '../App';
|
||||
|
||||
|
@ -37,12 +37,12 @@ class SideMenu extends React.PureComponent<PropsTypeInner> {
|
|||
this.logout = this.logout.bind(this);
|
||||
}
|
||||
|
||||
logout() {
|
||||
public logout() {
|
||||
store.dispatch(logout());
|
||||
this.props.onCloseDrawerRequest();
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { theme } = this.props;
|
||||
const username = (this.props.etesync && this.props.etesync.credentials.email) ?
|
||||
this.props.etesync.credentials.email
|
||||
|
|
|
@ -78,7 +78,7 @@ const syncInfoSelector = createSelector(
|
|||
const collectionInfo = journal.getInfo(cryptoManager);
|
||||
|
||||
const syncEntries = journalEntries.value.map((entry: EteSync.Entry) => {
|
||||
let syncEntry = entry.getSyncEntry(cryptoManager, prevUid);
|
||||
const syncEntry = entry.getSyncEntry(cryptoManager, prevUid);
|
||||
prevUid = entry.uid;
|
||||
|
||||
return syncEntry;
|
||||
|
@ -93,7 +93,7 @@ const syncInfoSelector = createSelector(
|
|||
},
|
||||
Map<string, SyncInfoJournal>()
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const PimRouter = withRouter(Pim);
|
||||
|
@ -103,7 +103,7 @@ class SyncGate extends React.PureComponent<PropsTypeInner> {
|
|||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount() {
|
||||
const me = this.props.etesync.credentials.email;
|
||||
const syncAll = () => {
|
||||
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 journals = this.props.journals.value;
|
||||
|
||||
|
@ -162,7 +162,7 @@ class SyncGate extends React.PureComponent<PropsTypeInner> {
|
|||
} else if (this.props.journals.error) {
|
||||
return <PrettyError error={this.props.journals.error} />;
|
||||
} else {
|
||||
let errors: Array<{journal: string, error: Error}> = [];
|
||||
const errors: Array<{journal: string, error: Error}> = [];
|
||||
this.props.entries.forEach((entry, journal) => {
|
||||
if (entry.error) {
|
||||
errors.push({journal, error: entry.error});
|
||||
|
|
|
@ -11,8 +11,8 @@ sjcl.random.startCollectors();
|
|||
export const HMAC_SIZE_BYTES = 32;
|
||||
|
||||
export class AsymmetricKeyPair {
|
||||
publicKey: byte[];
|
||||
privateKey: byte[];
|
||||
public publicKey: byte[];
|
||||
public privateKey: byte[];
|
||||
|
||||
constructor(publicKey: byte[], privateKey: byte[]) {
|
||||
this.publicKey = publicKey;
|
||||
|
@ -32,29 +32,29 @@ export function genUid() {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
export class CryptoManager {
|
||||
version: number;
|
||||
key: sjcl.BitArray;
|
||||
cipherKey: sjcl.BitArray;
|
||||
hmacKey: sjcl.BitArray;
|
||||
|
||||
cipherWords = 4;
|
||||
|
||||
static fromDerivedKey(key: byte[], version: number = Constants.CURRENT_VERSION) {
|
||||
public static fromDerivedKey(key: byte[], version: number = Constants.CURRENT_VERSION) {
|
||||
// FIXME: Cleanup this hack
|
||||
const ret = new CryptoManager('', '', version);
|
||||
ret.key = sjcl.codec.bytes.toBits(key);
|
||||
ret._updateDerivedKeys();
|
||||
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) {
|
||||
this.version = version;
|
||||
let key = sjcl.codec.base64.toBits(_keyBase64);
|
||||
const key = sjcl.codec.base64.toBits(_keyBase64);
|
||||
// FIXME: Clean up all exeptions
|
||||
if (version > Constants.CURRENT_VERSION) {
|
||||
throw new Error('VersionTooNewException');
|
||||
|
@ -67,53 +67,53 @@ export class CryptoManager {
|
|||
this._updateDerivedKeys();
|
||||
}
|
||||
|
||||
_updateDerivedKeys() {
|
||||
public _updateDerivedKeys() {
|
||||
this.cipherKey = hmac256(sjcl.codec.utf8String.toBits('aes'), 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);
|
||||
|
||||
let prp = new sjcl.cipher.aes(this.cipherKey);
|
||||
let cipherText = sjcl.mode.cbc.encrypt(prp, content, iv);
|
||||
const prp = new sjcl.cipher.aes(this.cipherKey);
|
||||
const cipherText = sjcl.mode.cbc.encrypt(prp, content, iv);
|
||||
return sjcl.codec.bytes.fromBits(iv.concat(cipherText));
|
||||
}
|
||||
|
||||
decryptBits(content: byte[]): sjcl.BitArray {
|
||||
let cipherText = sjcl.codec.bytes.toBits(content);
|
||||
public decryptBits(content: byte[]): sjcl.BitArray {
|
||||
const cipherText = sjcl.codec.bytes.toBits(content);
|
||||
const iv = cipherText.splice(0, this.cipherWords);
|
||||
|
||||
let prp = new sjcl.cipher.aes(this.cipherKey);
|
||||
let clearText = sjcl.mode.cbc.decrypt(prp, cipherText, iv);
|
||||
const prp = new sjcl.cipher.aes(this.cipherKey);
|
||||
const clearText = sjcl.mode.cbc.decrypt(prp, cipherText, iv);
|
||||
return clearText;
|
||||
}
|
||||
|
||||
encryptBytes(content: byte[]): byte[] {
|
||||
public encryptBytes(content: byte[]): byte[] {
|
||||
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));
|
||||
}
|
||||
|
||||
encrypt(content: string): byte[] {
|
||||
public encrypt(content: string): byte[] {
|
||||
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));
|
||||
}
|
||||
|
||||
hmac(content: byte[]): byte[] {
|
||||
public hmac(content: byte[]): byte[] {
|
||||
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));
|
||||
}
|
||||
|
||||
hmacHex(content: byte[]): string {
|
||||
public hmacHex(content: byte[]): string {
|
||||
return sjcl.codec.hex.fromBits(this.hmacBase(content));
|
||||
}
|
||||
|
||||
|
@ -134,9 +134,8 @@ function bufferToArray(buffer: Buffer) {
|
|||
}
|
||||
|
||||
export class AsymmetricCryptoManager {
|
||||
keyPair: NodeRSA;
|
||||
|
||||
static generateKeyPair() {
|
||||
public static generateKeyPair() {
|
||||
const keyPair = new NodeRSA();
|
||||
keyPair.generateKeyPair(3072, 65537);
|
||||
const pubkey = keyPair.exportKey('pkcs8-public-der') as Buffer;
|
||||
|
@ -144,19 +143,20 @@ export class AsymmetricCryptoManager {
|
|||
return new AsymmetricKeyPair(
|
||||
bufferToArray(pubkey), bufferToArray(privkey));
|
||||
}
|
||||
public keyPair: NodeRSA;
|
||||
|
||||
constructor(keyPair: AsymmetricKeyPair) {
|
||||
this.keyPair = new NodeRSA();
|
||||
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();
|
||||
key.importKey(Buffer.from(publicKey), 'pkcs8-public-der');
|
||||
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'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,26 +9,26 @@ import { USER, PASSWORD, keyBase64 } from './TestConstants';
|
|||
let credentials: EteSync.Credentials;
|
||||
|
||||
beforeEach(async () => {
|
||||
let authenticator = new EteSync.Authenticator(testApiBase);
|
||||
const authenticator = new EteSync.Authenticator(testApiBase);
|
||||
const authToken = await authenticator.getAuthToken(USER, PASSWORD);
|
||||
|
||||
credentials = new EteSync.Credentials(USER, authToken);
|
||||
|
||||
await fetch(testApiBase + '/reset/', {
|
||||
method: 'post',
|
||||
headers: { 'Authorization': 'Token ' + credentials.authToken },
|
||||
headers: { Authorization: 'Token ' + credentials.authToken },
|
||||
});
|
||||
});
|
||||
|
||||
it('Simple sync', async () => {
|
||||
let journalManager = new EteSync.JournalManager(credentials, testApiBase);
|
||||
const journalManager = new EteSync.JournalManager(credentials, testApiBase);
|
||||
let journals = await journalManager.list();
|
||||
expect(journals.length).toBe(0);
|
||||
|
||||
const uid1 = sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash('id1'));
|
||||
const cryptoManager = new EteSync.CryptoManager(keyBase64, USER);
|
||||
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);
|
||||
|
||||
await expect(journalManager.create(journal)).resolves.toBeDefined();
|
||||
|
@ -41,7 +41,7 @@ it('Simple sync', async () => {
|
|||
expect(journals[0].uid).toBe(journal.uid);
|
||||
|
||||
// Update
|
||||
let info2 = new EteSync.CollectionInfo(info1);
|
||||
const info2 = new EteSync.CollectionInfo(info1);
|
||||
info2.displayName = 'Display 2';
|
||||
|
||||
journal.setInfo(cryptoManager, info2);
|
||||
|
@ -59,24 +59,24 @@ it('Simple 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 cryptoManager = new EteSync.CryptoManager(keyBase64, USER);
|
||||
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);
|
||||
|
||||
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);
|
||||
expect(entries.length).toBe(0);
|
||||
|
||||
const syncEntry = new EteSync.SyncEntry({action: 'ADD', content: 'bla'});
|
||||
let prevUid = null;
|
||||
let entry = new EteSync.Entry();
|
||||
const entry = new EteSync.Entry();
|
||||
entry.setSyncEntry(cryptoManager, syncEntry, prevUid);
|
||||
|
||||
entries = [entry];
|
||||
|
@ -120,7 +120,7 @@ it('Journal Entry sync', async () => {
|
|||
|
||||
expect(() => {
|
||||
let prev = null;
|
||||
for (let ent of entries) {
|
||||
for (const ent of entries) {
|
||||
expect(ent.getSyncEntry(cryptoManager, prev)).toBeDefined();
|
||||
prev = ent.uid;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ it('User info sync', async () => {
|
|||
await expect(userInfoManager.fetch(USER)).rejects.toBeInstanceOf(EteSync.HTTPError);
|
||||
|
||||
// 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]));
|
||||
await expect(userInfoManager.create(userInfo)).resolves.not.toBeNull();
|
||||
|
||||
|
|
|
@ -42,8 +42,8 @@ function hmacToHex(hmac: byte[]): string {
|
|||
}
|
||||
|
||||
export class Credentials {
|
||||
email: string;
|
||||
authToken: string;
|
||||
public email: string;
|
||||
public authToken: string;
|
||||
|
||||
constructor(email: string, authToken: string) {
|
||||
this.email = email;
|
||||
|
@ -52,11 +52,11 @@ export class Credentials {
|
|||
}
|
||||
|
||||
export class CollectionInfo {
|
||||
uid: string;
|
||||
type: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
color: number;
|
||||
public uid: string;
|
||||
public type: string;
|
||||
public displayName: string;
|
||||
public description: string;
|
||||
public color: number;
|
||||
|
||||
constructor(json?: any) {
|
||||
CastJson(json, this);
|
||||
|
@ -76,13 +76,13 @@ class BaseItem<T extends BaseItemJson> {
|
|||
this._json = {} as any;
|
||||
}
|
||||
|
||||
deserialize(json: T) {
|
||||
public deserialize(json: T) {
|
||||
this._json = Object.assign({}, json);
|
||||
this._encrypted = sjcl.codec.bytes.fromBits(sjcl.codec.base64.toBits(json.content));
|
||||
this._content = undefined;
|
||||
}
|
||||
|
||||
serialize(): T {
|
||||
public serialize(): T {
|
||||
return Object.assign(
|
||||
{},
|
||||
this._json,
|
||||
|
@ -140,33 +140,33 @@ export class Journal extends BaseJournal<JournalJson> {
|
|||
return this._json.version;
|
||||
}
|
||||
|
||||
setInfo(cryptoManager: CryptoManager, info: CollectionInfo) {
|
||||
public setInfo(cryptoManager: CryptoManager, info: CollectionInfo) {
|
||||
this._json.uid = info.uid;
|
||||
this._content = info;
|
||||
const encrypted = cryptoManager.encrypt(JSON.stringify(this._content));
|
||||
this._encrypted = this.calculateHmac(cryptoManager, encrypted).concat(encrypted);
|
||||
}
|
||||
|
||||
getInfo(cryptoManager: CryptoManager): CollectionInfo {
|
||||
public getInfo(cryptoManager: CryptoManager): CollectionInfo {
|
||||
this.verify(cryptoManager);
|
||||
|
||||
if (this._content === undefined) {
|
||||
this._content = JSON.parse(cryptoManager.decrypt(this.encryptedContent()));
|
||||
}
|
||||
|
||||
let ret = new CollectionInfo(this._content);
|
||||
const ret = new CollectionInfo(this._content);
|
||||
ret.uid = this.uid;
|
||||
return ret;
|
||||
}
|
||||
|
||||
calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] {
|
||||
let prefix = stringToByteArray(this.uid);
|
||||
public calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] {
|
||||
const prefix = stringToByteArray(this.uid);
|
||||
return cryptoManager.hmac(prefix.concat(encrypted));
|
||||
}
|
||||
|
||||
verify(cryptoManager: CryptoManager) {
|
||||
let calculated = this.calculateHmac(cryptoManager, this.encryptedContent());
|
||||
let hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES);
|
||||
public verify(cryptoManager: CryptoManager) {
|
||||
const calculated = this.calculateHmac(cryptoManager, this.encryptedContent());
|
||||
const hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES);
|
||||
|
||||
super.verifyBase(hmac, calculated);
|
||||
}
|
||||
|
@ -183,9 +183,9 @@ export enum SyncEntryAction {
|
|||
}
|
||||
|
||||
export class SyncEntry {
|
||||
uid?: string;
|
||||
action: SyncEntryAction;
|
||||
content: string;
|
||||
public uid?: string;
|
||||
public action: SyncEntryAction;
|
||||
public content: string;
|
||||
|
||||
constructor(json?: any, uid?: string) {
|
||||
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> {
|
||||
setSyncEntry(cryptoManager: CryptoManager, info: SyncEntry, prevUid: string | null) {
|
||||
public setSyncEntry(cryptoManager: CryptoManager, info: SyncEntry, prevUid: string | null) {
|
||||
this._content = info;
|
||||
this._encrypted = cryptoManager.encrypt(JSON.stringify(this._content));
|
||||
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);
|
||||
|
||||
if (this._content === undefined) {
|
||||
|
@ -213,15 +212,15 @@ export class Entry extends BaseJournal<EntryJson> {
|
|||
return new SyncEntry(this._content, this.uid);
|
||||
}
|
||||
|
||||
verify(cryptoManager: CryptoManager, prevUid: string | null) {
|
||||
let calculated = this.calculateHmac(cryptoManager, this._encrypted, prevUid);
|
||||
let hmac = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(this.uid));
|
||||
public verify(cryptoManager: CryptoManager, prevUid: string | null) {
|
||||
const calculated = this.calculateHmac(cryptoManager, this._encrypted, prevUid);
|
||||
const hmac = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(this.uid));
|
||||
|
||||
super.verifyBase(hmac, calculated);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +232,7 @@ export interface UserInfoJson extends BaseItemJson {
|
|||
}
|
||||
|
||||
export class UserInfo extends BaseItem<UserInfoJson> {
|
||||
_owner: string;
|
||||
public _owner: string;
|
||||
|
||||
constructor(owner: string, version: number = Constants.CURRENT_VERSION) {
|
||||
super();
|
||||
|
@ -253,20 +252,20 @@ export class UserInfo extends BaseItem<UserInfoJson> {
|
|||
return this._json.pubkey;
|
||||
}
|
||||
|
||||
serialize(): UserInfoJson {
|
||||
let ret = super.serialize();
|
||||
public serialize(): UserInfoJson {
|
||||
const ret = super.serialize();
|
||||
ret.owner = this._owner;
|
||||
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._content = keyPair.privateKey;
|
||||
const encrypted = cryptoManager.encryptBytes(keyPair.privateKey);
|
||||
this._encrypted = this.calculateHmac(cryptoManager, encrypted).concat(encrypted);
|
||||
}
|
||||
|
||||
getKeyPair(cryptoManager: CryptoManager): AsymmetricKeyPair {
|
||||
public getKeyPair(cryptoManager: CryptoManager): AsymmetricKeyPair {
|
||||
this.verify(cryptoManager);
|
||||
|
||||
if (this._content === undefined) {
|
||||
|
@ -277,14 +276,14 @@ export class UserInfo extends BaseItem<UserInfoJson> {
|
|||
return new AsymmetricKeyPair(pubkey, this._content as byte[]);
|
||||
}
|
||||
|
||||
calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] {
|
||||
let postfix = sjcl.codec.bytes.fromBits(sjcl.codec.base64.toBits(this._json.pubkey));
|
||||
public calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] {
|
||||
const postfix = sjcl.codec.bytes.fromBits(sjcl.codec.base64.toBits(this._json.pubkey));
|
||||
return cryptoManager.hmac(encrypted.concat(postfix));
|
||||
}
|
||||
|
||||
verify(cryptoManager: CryptoManager) {
|
||||
let calculated = this.calculateHmac(cryptoManager, this.encryptedContent());
|
||||
let hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES);
|
||||
public verify(cryptoManager: CryptoManager) {
|
||||
const calculated = this.calculateHmac(cryptoManager, this.encryptedContent());
|
||||
const hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES);
|
||||
|
||||
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.
|
||||
|
||||
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;
|
||||
baseUrl = baseUrl.clone();
|
||||
for (const segment of segments) {
|
||||
|
@ -307,18 +305,19 @@ class BaseNetwork {
|
|||
}
|
||||
return baseUrl.normalize();
|
||||
}
|
||||
public apiBase: any; // FIXME
|
||||
|
||||
constructor(apiBase: string) {
|
||||
this.apiBase = URI(apiBase).normalize();
|
||||
}
|
||||
|
||||
// FIXME: Get the correct type for extra
|
||||
newCall(segments: Array<string> = [], extra: any = {}, _apiBase: URL = this.apiBase): Promise<{} | Array<any>> {
|
||||
let apiBase = BaseNetwork.urlExtend(_apiBase, segments);
|
||||
public newCall(segments: string[] = [], extra: any = {}, _apiBase: URL = this.apiBase): Promise<{} | any[]> {
|
||||
const apiBase = BaseNetwork.urlExtend(_apiBase, segments);
|
||||
|
||||
extra = Object.assign({}, extra);
|
||||
extra.headers = Object.assign(
|
||||
{ 'Accept': 'application/json' },
|
||||
{ Accept: 'application/json' },
|
||||
extra.headers);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -358,17 +357,17 @@ export class Authenticator extends BaseNetwork {
|
|||
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) => {
|
||||
// FIXME: should be FormData but doesn't work for whatever reason
|
||||
let form = 'username=' + encodeURIComponent(username) +
|
||||
const form = 'username=' + encodeURIComponent(username) +
|
||||
'&password=' + encodeURIComponent(password);
|
||||
const extra = {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||
},
|
||||
body: form
|
||||
body: form,
|
||||
};
|
||||
|
||||
this.newCall([], extra).then((json: {token: string}) => {
|
||||
|
@ -383,19 +382,19 @@ export class Authenticator extends BaseNetwork {
|
|||
export class BaseManager extends BaseNetwork {
|
||||
protected credentials: Credentials;
|
||||
|
||||
constructor(credentials: Credentials, apiBase: string, segments: Array<string>) {
|
||||
constructor(credentials: Credentials, apiBase: string, segments: string[]) {
|
||||
super(apiBase);
|
||||
this.credentials = credentials;
|
||||
this.apiBase = BaseNetwork.urlExtend(this.apiBase, ['api', 'v1'].concat(segments));
|
||||
}
|
||||
|
||||
// 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.headers = Object.assign(
|
||||
{
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'Authorization': 'Token ' + this.credentials.authToken
|
||||
'Authorization': 'Token ' + this.credentials.authToken,
|
||||
},
|
||||
extra.headers);
|
||||
|
||||
|
@ -408,10 +407,10 @@ export class JournalManager extends BaseManager {
|
|||
super(credentials, apiBase, ['journals', '']);
|
||||
}
|
||||
|
||||
fetch(journalUid: string): Promise<Journal> {
|
||||
public fetch(journalUid: string): Promise<Journal> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.newCall([journalUid, '']).then((json: JournalJson) => {
|
||||
let journal = new Journal(json.version);
|
||||
const journal = new Journal(json.version);
|
||||
journal.deserialize(json);
|
||||
resolve(journal);
|
||||
}).catch((error: Error) => {
|
||||
|
@ -420,11 +419,11 @@ export class JournalManager extends BaseManager {
|
|||
});
|
||||
}
|
||||
|
||||
list(): Promise<Journal[]> {
|
||||
public list(): Promise<Journal[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.newCall().then((json: Array<{}>) => {
|
||||
resolve(json.map((val: JournalJson) => {
|
||||
let journal = new Journal(val.version);
|
||||
const journal = new Journal(val.version);
|
||||
journal.deserialize(val);
|
||||
return journal;
|
||||
}));
|
||||
|
@ -434,7 +433,7 @@ export class JournalManager extends BaseManager {
|
|||
});
|
||||
}
|
||||
|
||||
create(journal: Journal): Promise<{}> {
|
||||
public create(journal: Journal): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'post',
|
||||
body: JSON.stringify(journal.serialize()),
|
||||
|
@ -443,7 +442,7 @@ export class JournalManager extends BaseManager {
|
|||
return this.newCall([], extra);
|
||||
}
|
||||
|
||||
update(journal: Journal): Promise<{}> {
|
||||
public update(journal: Journal): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'put',
|
||||
body: JSON.stringify(journal.serialize()),
|
||||
|
@ -452,7 +451,7 @@ export class JournalManager extends BaseManager {
|
|||
return this.newCall([journal.uid, ''], extra);
|
||||
}
|
||||
|
||||
delete(journal: Journal): Promise<{}> {
|
||||
public delete(journal: Journal): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'delete',
|
||||
};
|
||||
|
@ -466,7 +465,7 @@ export class EntryManager extends BaseManager {
|
|||
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();
|
||||
apiBase = apiBase.search({
|
||||
last: (lastUid !== null) ? lastUid : undefined,
|
||||
|
@ -476,7 +475,7 @@ export class EntryManager extends BaseManager {
|
|||
return new Promise((resolve, reject) => {
|
||||
this.newCall(undefined, undefined, apiBase).then((json: Array<{}>) => {
|
||||
resolve(json.map((val: any) => {
|
||||
let entry = new Entry();
|
||||
const entry = new Entry();
|
||||
entry.deserialize(val);
|
||||
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();
|
||||
apiBase = apiBase.search({
|
||||
last: (lastUid !== null) ? lastUid : undefined,
|
||||
|
@ -511,7 +510,7 @@ export class JournalMembersManager extends BaseManager {
|
|||
super(credentials, apiBase, ['journals', journalId, 'members', '']);
|
||||
}
|
||||
|
||||
list(): Promise<JournalMemberJson[]> {
|
||||
public list(): Promise<JournalMemberJson[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.newCall().then((json: Array<{}>) => {
|
||||
resolve(json.map((val: JournalMemberJson) => {
|
||||
|
@ -529,10 +528,10 @@ export class UserInfoManager extends BaseManager {
|
|||
super(credentials, apiBase, ['user', '']);
|
||||
}
|
||||
|
||||
fetch(owner: string): Promise<UserInfo> {
|
||||
public fetch(owner: string): Promise<UserInfo> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.newCall([owner, '']).then((json: UserInfoJson) => {
|
||||
let userInfo = new UserInfo(owner, json.version);
|
||||
const userInfo = new UserInfo(owner, json.version);
|
||||
userInfo.deserialize(json);
|
||||
resolve(userInfo);
|
||||
}).catch((error: Error) => {
|
||||
|
@ -541,7 +540,7 @@ export class UserInfoManager extends BaseManager {
|
|||
});
|
||||
}
|
||||
|
||||
create(userInfo: UserInfo): Promise<{}> {
|
||||
public create(userInfo: UserInfo): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'post',
|
||||
body: JSON.stringify(userInfo.serialize()),
|
||||
|
@ -550,7 +549,7 @@ export class UserInfoManager extends BaseManager {
|
|||
return this.newCall([], extra);
|
||||
}
|
||||
|
||||
update(userInfo: UserInfo): Promise<{}> {
|
||||
public update(userInfo: UserInfo): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'put',
|
||||
body: JSON.stringify(userInfo.serialize()),
|
||||
|
@ -559,7 +558,7 @@ export class UserInfoManager extends BaseManager {
|
|||
return this.newCall([userInfo.owner, ''], extra);
|
||||
}
|
||||
|
||||
delete(userInfo: UserInfo): Promise<{}> {
|
||||
public delete(userInfo: UserInfo): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'delete',
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ export type byte = number;
|
|||
export type base64 = string;
|
||||
|
||||
export function stringToByteArray(str: string): byte[] {
|
||||
let ret = [];
|
||||
const ret = [];
|
||||
for (let i = 0 ; i < str.length ; i++) {
|
||||
ret.push(str.charCodeAt(i));
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ import PimItemHeader from './PimItemHeader';
|
|||
import { ContactType } from '../pim-types';
|
||||
|
||||
class Contact extends React.PureComponent {
|
||||
props: {
|
||||
public props: {
|
||||
item?: ContactType,
|
||||
};
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
if (this.props.item === undefined) {
|
||||
throw Error('Contact should be defined!');
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class Contact extends React.PureComponent {
|
|||
const lastModified = (revProp) ?
|
||||
'Modified: ' + moment(revProp.getFirstValue().toJSDate()).format('LLLL') : undefined;
|
||||
|
||||
let lists = [];
|
||||
const lists = [];
|
||||
|
||||
function getAllType(
|
||||
propName: string,
|
||||
|
@ -57,9 +57,9 @@ class Contact extends React.PureComponent {
|
|||
'tel',
|
||||
{
|
||||
leftIcon: <CommunicationCall />,
|
||||
rightIcon: <CommunicationChatBubble />
|
||||
rightIcon: <CommunicationChatBubble />,
|
||||
},
|
||||
(x) => ('tel:' + x),
|
||||
(x) => ('tel:' + x)
|
||||
));
|
||||
|
||||
lists.push(getAllType(
|
||||
|
@ -67,44 +67,44 @@ class Contact extends React.PureComponent {
|
|||
{
|
||||
leftIcon: <CommunicationEmail />,
|
||||
},
|
||||
(x) => ('mailto:' + x),
|
||||
(x) => ('mailto:' + x)
|
||||
));
|
||||
|
||||
lists.push(getAllType(
|
||||
'impp',
|
||||
{
|
||||
leftIcon: <CommunicationChatBubble />
|
||||
leftIcon: <CommunicationChatBubble />,
|
||||
},
|
||||
(x) => x,
|
||||
(x) => (x.substring(x.indexOf(':') + 1)),
|
||||
(x) => (x.substring(0, x.indexOf(':'))),
|
||||
(x) => (x.substring(0, x.indexOf(':')))
|
||||
));
|
||||
|
||||
lists.push(getAllType(
|
||||
'adr',
|
||||
{
|
||||
leftIcon: <IconHome />
|
||||
},
|
||||
leftIcon: <IconHome />,
|
||||
}
|
||||
));
|
||||
|
||||
lists.push(getAllType(
|
||||
'bday',
|
||||
{
|
||||
leftIcon: <IconDate />
|
||||
leftIcon: <IconDate />,
|
||||
},
|
||||
undefined,
|
||||
((x: any) => moment(x.toJSDate()).format('dddd, LL')),
|
||||
() => 'Birthday',
|
||||
() => 'Birthday'
|
||||
));
|
||||
|
||||
lists.push(getAllType(
|
||||
'anniversary',
|
||||
{
|
||||
leftIcon: <IconDate />
|
||||
leftIcon: <IconDate />,
|
||||
},
|
||||
undefined,
|
||||
((x: any) => moment(x.toJSDate()).format('dddd, LL')),
|
||||
() => 'Anniversary',
|
||||
() => 'Anniversary'
|
||||
));
|
||||
|
||||
const skips = ['tel', 'email', 'impp', 'adr', 'bday', 'anniversary', 'rev',
|
||||
|
@ -126,7 +126,7 @@ class Contact extends React.PureComponent {
|
|||
return values;
|
||||
});
|
||||
|
||||
function listIfNotEmpty(items: Array<Array<JSX.Element>>) {
|
||||
function listIfNotEmpty(items: JSX.Element[][]) {
|
||||
if (items.length > 0) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
|
|
@ -45,7 +45,7 @@ const imppTypes = [
|
|||
];
|
||||
|
||||
const TypeSelector = (props: any) => {
|
||||
const types = props.types as {type: string}[];
|
||||
const types = props.types as Array<{type: string}>;
|
||||
|
||||
return (
|
||||
<Select
|
||||
|
@ -61,8 +61,8 @@ const TypeSelector = (props: any) => {
|
|||
};
|
||||
|
||||
class ValueType {
|
||||
type: string;
|
||||
value: string;
|
||||
public type: string;
|
||||
public value: string;
|
||||
|
||||
constructor(type?: string, value?: string) {
|
||||
this.type = type ? type : 'home';
|
||||
|
@ -74,7 +74,7 @@ interface ValueTypeComponentProps {
|
|||
type?: string;
|
||||
style?: object;
|
||||
|
||||
types: {type: string}[];
|
||||
types: Array<{type: string}>;
|
||||
name: string;
|
||||
placeholder: string;
|
||||
value: ValueType;
|
||||
|
@ -112,16 +112,16 @@ const ValueTypeComponent = (props: ValueTypeComponentProps) => {
|
|||
};
|
||||
|
||||
interface PropsType {
|
||||
collections: Array<EteSync.CollectionInfo>;
|
||||
collections: EteSync.CollectionInfo[];
|
||||
initialCollection?: string;
|
||||
item?: ContactType;
|
||||
onSave: (contact: ContactType, journalUid: string, originalContact?: ContactType) => void;
|
||||
onDelete: (contact: ContactType, journalUid: string) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
class ContactEdit extends React.PureComponent<PropsType> {
|
||||
state: {
|
||||
public state: {
|
||||
uid: string,
|
||||
fn: string;
|
||||
phone: ValueType[];
|
||||
|
@ -201,7 +201,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
|
|||
this.onDeleteRequest = this.onDeleteRequest.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: any) {
|
||||
public componentWillReceiveProps(nextProps: any) {
|
||||
if ((this.props.collections !== nextProps.collections) ||
|
||||
(this.props.initialCollection !== 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';
|
||||
this.setState((prevState, props) => {
|
||||
let newArray = prevState[name].slice(0);
|
||||
const newArray = prevState[name].slice(0);
|
||||
newArray.push(new ValueType(type));
|
||||
return {
|
||||
...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) => {
|
||||
let newArray = prevState[name].slice(0);
|
||||
const newArray = prevState[name].slice(0);
|
||||
newArray.splice(idx, 1);
|
||||
return {
|
||||
...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) => {
|
||||
let newArray = prevState[name].slice(0);
|
||||
const newArray = prevState[name].slice(0);
|
||||
newArray[idx] = value;
|
||||
return {
|
||||
...prevState,
|
||||
|
@ -246,29 +246,29 @@ class ContactEdit extends React.PureComponent<PropsType> {
|
|||
});
|
||||
}
|
||||
|
||||
handleChange(name: string, value: string) {
|
||||
public handleChange(name: string, value: string) {
|
||||
this.setState({
|
||||
[name]: value
|
||||
[name]: value,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
handleInputChange(contact: any) {
|
||||
public handleInputChange(contact: any) {
|
||||
const name = contact.target.name;
|
||||
const value = contact.target.value;
|
||||
this.handleChange(name, value);
|
||||
}
|
||||
|
||||
onSubmit(e: React.FormEvent<any>) {
|
||||
public onSubmit(e: React.FormEvent<any>) {
|
||||
e.preventDefault();
|
||||
|
||||
let contact = (this.props.item) ?
|
||||
const contact = (this.props.item) ?
|
||||
this.props.item.clone()
|
||||
:
|
||||
new ContactType(new ICAL.Component(['vcard', [], []]))
|
||||
;
|
||||
|
||||
let comp = contact.comp;
|
||||
const comp = contact.comp;
|
||||
comp.updatePropertyWithValue('prodid', '-//iCal.js EteSync Web');
|
||||
comp.updatePropertyWithValue('version', '4.0');
|
||||
comp.updatePropertyWithValue('uid', this.state.uid);
|
||||
|
@ -282,7 +282,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
|
|||
return;
|
||||
}
|
||||
|
||||
let prop = new ICAL.Property(name, comp);
|
||||
const prop = new ICAL.Property(name, comp);
|
||||
prop.setParameter('type', x.type);
|
||||
prop.setValue(x.value);
|
||||
comp.addProperty(prop);
|
||||
|
@ -310,13 +310,13 @@ class ContactEdit extends React.PureComponent<PropsType> {
|
|||
this.props.onSave(contact, this.state.journalUid, this.props.item);
|
||||
}
|
||||
|
||||
onDeleteRequest() {
|
||||
public onDeleteRequest() {
|
||||
this.setState({
|
||||
showDeleteDialog: true
|
||||
showDeleteDialog: true,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const styles = {
|
||||
form: {
|
||||
},
|
||||
|
|
|
@ -7,12 +7,12 @@ interface FormErrors {
|
|||
}
|
||||
|
||||
class EncryptionLoginForm extends React.PureComponent {
|
||||
state: {
|
||||
public state: {
|
||||
errors: FormErrors,
|
||||
encryptionPassword: string;
|
||||
};
|
||||
|
||||
props: {
|
||||
public props: {
|
||||
onSubmit: (encryptionPassword: string) => void;
|
||||
loading?: boolean;
|
||||
error?: Error;
|
||||
|
@ -28,27 +28,27 @@ class EncryptionLoginForm extends React.PureComponent {
|
|||
this.handleInputChange = this.handleInputChange.bind(this);
|
||||
}
|
||||
|
||||
handleInputChange(event: React.ChangeEvent<any>) {
|
||||
public handleInputChange(event: React.ChangeEvent<any>) {
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
this.setState({
|
||||
[name]: value
|
||||
[name]: value,
|
||||
});
|
||||
}
|
||||
|
||||
generateEncryption(e: any) {
|
||||
public generateEncryption(e: any) {
|
||||
e.preventDefault();
|
||||
|
||||
const encryptionPassword = this.state.encryptionPassword;
|
||||
|
||||
let errors: FormErrors = {};
|
||||
const errors: FormErrors = {};
|
||||
const fieldRequired = 'This field is required!';
|
||||
if (!encryptionPassword) {
|
||||
errors.errorEncryptionPassword = fieldRequired;
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length) {
|
||||
this.setState({errors: errors});
|
||||
this.setState({errors});
|
||||
return;
|
||||
} else {
|
||||
this.setState({errors: {}});
|
||||
|
@ -57,7 +57,7 @@ class EncryptionLoginForm extends React.PureComponent {
|
|||
this.props.onSubmit(encryptionPassword);
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const styles = {
|
||||
form: {
|
||||
},
|
||||
|
|
|
@ -6,7 +6,7 @@ import { IntegrityError } from '../api/EteSync';
|
|||
import PrettyError from '../widgets/PrettyError';
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
state: {
|
||||
public state: {
|
||||
error?: Error;
|
||||
};
|
||||
|
||||
|
@ -15,7 +15,7 @@ class ErrorBoundary extends React.Component {
|
|||
this.state = { };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: any) {
|
||||
public componentDidCatch(error: Error, info: any) {
|
||||
if (error instanceof IntegrityError) {
|
||||
persistor.purge();
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class ErrorBoundary extends React.Component {
|
|||
this.setState({ error });
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { error } = this.state;
|
||||
if (error && error instanceof IntegrityError) {
|
||||
return (
|
||||
|
|
|
@ -7,11 +7,11 @@ import { formatDateRange } from '../helpers';
|
|||
import { EventType } from '../pim-types';
|
||||
|
||||
class Event extends React.PureComponent {
|
||||
props: {
|
||||
public props: {
|
||||
item?: EventType,
|
||||
};
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
if (this.props.item === undefined) {
|
||||
throw Error('Event should be defined!');
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class Event extends React.PureComponent {
|
|||
return (
|
||||
<React.Fragment>
|
||||
<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/>
|
||||
<div><u>{this.props.item.location}</u></div>
|
||||
</PimItemHeader>
|
||||
|
|
|
@ -31,17 +31,17 @@ import * as EteSync from '../api/EteSync';
|
|||
import { EventType } from '../pim-types';
|
||||
|
||||
interface PropsType {
|
||||
collections: Array<EteSync.CollectionInfo>;
|
||||
collections: EteSync.CollectionInfo[];
|
||||
initialCollection?: string;
|
||||
item?: EventType;
|
||||
onSave: (event: EventType, journalUid: string, originalEvent?: EventType) => void;
|
||||
onDelete: (event: EventType, journalUid: string) => void;
|
||||
onCancel: () => void;
|
||||
location: Location;
|
||||
};
|
||||
}
|
||||
|
||||
class EventEdit extends React.PureComponent<PropsType> {
|
||||
state: {
|
||||
public state: {
|
||||
uid: string,
|
||||
title: string;
|
||||
allDay: boolean;
|
||||
|
@ -84,7 +84,7 @@ class EventEdit extends React.PureComponent<PropsType> {
|
|||
const event = this.props.item;
|
||||
|
||||
const allDay = event.startDate.isDate;
|
||||
let endDate = event.endDate.clone();
|
||||
const endDate = event.endDate.clone();
|
||||
|
||||
if (allDay) {
|
||||
endDate.adjust(-1, 0, 0, 0);
|
||||
|
@ -114,7 +114,7 @@ class EventEdit extends React.PureComponent<PropsType> {
|
|||
this.onDeleteRequest = this.onDeleteRequest.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: any) {
|
||||
public componentWillReceiveProps(nextProps: any) {
|
||||
if ((this.props.collections !== nextProps.collections) ||
|
||||
(this.props.initialCollection !== 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({
|
||||
[name]: value
|
||||
[name]: value,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
handleInputChange(event: React.ChangeEvent<any>) {
|
||||
public handleInputChange(event: React.ChangeEvent<any>) {
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
this.handleChange(name, value);
|
||||
}
|
||||
|
||||
toggleAllDay() {
|
||||
public toggleAllDay() {
|
||||
this.setState({allDay: !this.state.allDay});
|
||||
}
|
||||
|
||||
onSubmit(e: React.FormEvent<any>) {
|
||||
public onSubmit(e: React.FormEvent<any>) {
|
||||
e.preventDefault();
|
||||
|
||||
if ((!this.state.start) || (!this.state.end)) {
|
||||
|
@ -155,7 +155,7 @@ class EventEdit extends React.PureComponent<PropsType> {
|
|||
if (!allDay) {
|
||||
return ret;
|
||||
} else {
|
||||
let data = ret.toJSON();
|
||||
const data = ret.toJSON();
|
||||
data.isDate = allDay;
|
||||
return ICAL.Time.fromData(data);
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ class EventEdit extends React.PureComponent<PropsType> {
|
|||
return;
|
||||
}
|
||||
|
||||
let event = (this.props.item) ?
|
||||
const event = (this.props.item) ?
|
||||
this.props.item.clone()
|
||||
:
|
||||
new EventType()
|
||||
|
@ -190,13 +190,13 @@ class EventEdit extends React.PureComponent<PropsType> {
|
|||
this.props.onSave(event, this.state.journalUid, this.props.item);
|
||||
}
|
||||
|
||||
onDeleteRequest() {
|
||||
public onDeleteRequest() {
|
||||
this.setState({
|
||||
showDeleteDialog: true
|
||||
showDeleteDialog: true,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const styles = {
|
||||
form: {
|
||||
},
|
||||
|
|
|
@ -18,15 +18,15 @@ import { TaskType, EventType, ContactType } from '../pim-types';
|
|||
import * as EteSync from '../api/EteSync';
|
||||
|
||||
class JournalEntries extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
public static defaultProps = {
|
||||
prevUid: null,
|
||||
};
|
||||
|
||||
state: {
|
||||
public state: {
|
||||
dialog?: string;
|
||||
};
|
||||
|
||||
props: {
|
||||
public props: {
|
||||
journal: EteSync.Journal,
|
||||
entries: Immutable.List<EteSync.SyncEntry>,
|
||||
uid?: string,
|
||||
|
@ -37,7 +37,7 @@ class JournalEntries extends React.PureComponent {
|
|||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
if (this.props.journal === undefined) {
|
||||
return (<div>Loading</div>);
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ class JournalEntries extends React.PureComponent {
|
|||
secondaryText={uid}
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
dialog: syncEntry.content
|
||||
dialog: syncEntry.content,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -17,7 +17,7 @@ interface FormErrors {
|
|||
}
|
||||
|
||||
class LoginForm extends React.PureComponent {
|
||||
state: {
|
||||
public state: {
|
||||
showAdvanced: boolean;
|
||||
errors: FormErrors;
|
||||
|
||||
|
@ -27,7 +27,7 @@ class LoginForm extends React.PureComponent {
|
|||
encryptionPassword: string;
|
||||
};
|
||||
|
||||
props: {
|
||||
public props: {
|
||||
onSubmit: (username: string, password: string, encryptionPassword: string, serviceApiUrl?: string) => void;
|
||||
loading?: boolean;
|
||||
error?: Error;
|
||||
|
@ -48,15 +48,15 @@ class LoginForm extends React.PureComponent {
|
|||
this.handleInputChange = this.handleInputChange.bind(this);
|
||||
}
|
||||
|
||||
handleInputChange(event: React.ChangeEvent<any>) {
|
||||
public handleInputChange(event: React.ChangeEvent<any>) {
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
this.setState({
|
||||
[name]: value
|
||||
[name]: value,
|
||||
});
|
||||
}
|
||||
|
||||
generateEncryption(e: any) {
|
||||
public generateEncryption(e: any) {
|
||||
e.preventDefault();
|
||||
const server = this.state.showAdvanced ? this.state.server : undefined;
|
||||
|
||||
|
@ -64,7 +64,7 @@ class LoginForm extends React.PureComponent {
|
|||
const password = this.state.password;
|
||||
const encryptionPassword = this.state.encryptionPassword;
|
||||
|
||||
let errors: FormErrors = {};
|
||||
const errors: FormErrors = {};
|
||||
const fieldRequired = 'This field is required!';
|
||||
if (!username) {
|
||||
errors.errorEmail = fieldRequired;
|
||||
|
@ -83,7 +83,7 @@ class LoginForm extends React.PureComponent {
|
|||
}
|
||||
|
||||
if (Object.keys(errors).length) {
|
||||
this.setState({errors: errors});
|
||||
this.setState({errors});
|
||||
return;
|
||||
} else {
|
||||
this.setState({errors: {}});
|
||||
|
@ -92,11 +92,11 @@ class LoginForm extends React.PureComponent {
|
|||
this.props.onSubmit(username, password, encryptionPassword, server);
|
||||
}
|
||||
|
||||
toggleAdvancedSettings() {
|
||||
public toggleAdvancedSettings() {
|
||||
this.setState({showAdvanced: !this.state.showAdvanced});
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const styles = {
|
||||
form: {
|
||||
},
|
||||
|
|
|
@ -11,12 +11,12 @@ import { ContactType } from '../pim-types';
|
|||
import AddressBook from '../components/AddressBook';
|
||||
|
||||
class SearchableAddressBook extends React.PureComponent {
|
||||
props: {
|
||||
entries: Array<ContactType>,
|
||||
public props: {
|
||||
entries: ContactType[],
|
||||
onItemClick: (contact: ContactType) => void,
|
||||
};
|
||||
|
||||
state: {
|
||||
public state: {
|
||||
searchQuery: string;
|
||||
};
|
||||
|
||||
|
@ -26,15 +26,15 @@ class SearchableAddressBook extends React.PureComponent {
|
|||
this.handleInputChange = this.handleInputChange.bind(this);
|
||||
}
|
||||
|
||||
handleInputChange(event: React.ChangeEvent<any>) {
|
||||
public handleInputChange(event: React.ChangeEvent<any>) {
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
this.setState({
|
||||
[name]: value
|
||||
[name]: value,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const {
|
||||
entries,
|
||||
...rest
|
||||
|
@ -52,7 +52,7 @@ class SearchableAddressBook extends React.PureComponent {
|
|||
onChange={this.handleInputChange}
|
||||
/>
|
||||
{this.state.searchQuery &&
|
||||
<IconButton onClick={() => this.setState({'searchQuery': ''})}>
|
||||
<IconButton onClick={() => this.setState({searchQuery: ''})}>
|
||||
<IconClear />
|
||||
</IconButton>
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ import { formatDate } from '../helpers';
|
|||
import { TaskType } from '../pim-types';
|
||||
|
||||
class Task extends React.PureComponent {
|
||||
props: {
|
||||
public props: {
|
||||
item?: TaskType,
|
||||
};
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
if (this.props.item === undefined) {
|
||||
throw Error('Task should be defined!');
|
||||
}
|
||||
|
@ -30,10 +30,10 @@ class Task extends React.PureComponent {
|
|||
<React.Fragment>
|
||||
<PimItemHeader text={this.props.item.summary} backgroundColor={this.props.item.color}>
|
||||
{ item.startDate &&
|
||||
<div>Start: {formatDate(item.startDate)} { timezone && <small>({timezone})</small>}</div>
|
||||
<div>Start: {formatDate(item.startDate)} {timezone && <small>({timezone})</small>}</div>
|
||||
}
|
||||
{ item.dueDate &&
|
||||
<div>Due: {formatDate(item.dueDate)} { timezone && <small>({timezone})</small>}</div>
|
||||
<div>Due: {formatDate(item.dueDate)} {timezone && <small>({timezone})</small>}</div>
|
||||
}
|
||||
<br/>
|
||||
<div><u>{this.props.item.location}</u></div>
|
||||
|
|
|
@ -31,17 +31,17 @@ import * as EteSync from '../api/EteSync';
|
|||
import { TaskType, TaskStatusType } from '../pim-types';
|
||||
|
||||
interface PropsType {
|
||||
collections: Array<EteSync.CollectionInfo>;
|
||||
collections: EteSync.CollectionInfo[];
|
||||
initialCollection?: string;
|
||||
item?: TaskType;
|
||||
onSave: (item: TaskType, journalUid: string, originalItem?: TaskType) => void;
|
||||
onDelete: (item: TaskType, journalUid: string) => void;
|
||||
onCancel: () => void;
|
||||
location: Location;
|
||||
};
|
||||
}
|
||||
|
||||
class TaskEdit extends React.PureComponent<PropsType> {
|
||||
state: {
|
||||
public state: {
|
||||
uid: string,
|
||||
title: string;
|
||||
status: TaskStatusType;
|
||||
|
@ -102,7 +102,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
|
|||
this.onDeleteRequest = this.onDeleteRequest.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: any) {
|
||||
public componentWillReceiveProps(nextProps: any) {
|
||||
if ((this.props.collections !== nextProps.collections) ||
|
||||
(this.props.initialCollection !== 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({
|
||||
[name]: value
|
||||
[name]: value,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
handleInputChange(event: React.ChangeEvent<any>) {
|
||||
public handleInputChange(event: React.ChangeEvent<any>) {
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
this.handleChange(name, value);
|
||||
}
|
||||
|
||||
toggleAllDay() {
|
||||
public toggleAllDay() {
|
||||
this.setState({allDay: !this.state.allDay});
|
||||
}
|
||||
|
||||
onSubmit(e: React.FormEvent<any>) {
|
||||
public onSubmit(e: React.FormEvent<any>) {
|
||||
e.preventDefault();
|
||||
|
||||
function fromDate(date: Date | undefined, allDay: boolean) {
|
||||
|
@ -141,7 +141,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
|
|||
if (!allDay) {
|
||||
return ret;
|
||||
} else {
|
||||
let data = ret.toJSON();
|
||||
const data = ret.toJSON();
|
||||
data.isDate = allDay;
|
||||
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()
|
||||
:
|
||||
new TaskType(null)
|
||||
|
@ -178,13 +178,13 @@ class TaskEdit extends React.PureComponent<PropsType> {
|
|||
this.props.onSave(event, this.state.journalUid, this.props.item);
|
||||
}
|
||||
|
||||
onDeleteRequest() {
|
||||
public onDeleteRequest() {
|
||||
this.setState({
|
||||
showDeleteDialog: true
|
||||
showDeleteDialog: true,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const styles = {
|
||||
form: {
|
||||
},
|
||||
|
|
|
@ -23,7 +23,7 @@ const TaskListItem = pure((_props: any) => {
|
|||
});
|
||||
|
||||
const sortSelector = createSelector(
|
||||
(entries: Array<TaskType>) => entries,
|
||||
(entries: TaskType[]) => entries,
|
||||
(entries) => {
|
||||
return entries.sort((_a, _b) => {
|
||||
const a = _a.title;
|
||||
|
@ -37,20 +37,20 @@ const sortSelector = createSelector(
|
|||
return 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
class TaskList extends React.PureComponent {
|
||||
props: {
|
||||
entries: Array<TaskType>,
|
||||
public props: {
|
||||
entries: TaskType[],
|
||||
onItemClick: (contact: TaskType) => void,
|
||||
};
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const entries = this.props.entries.filter((x) => !x.finished);
|
||||
const sortedEntries = sortSelector(entries);
|
||||
|
||||
let itemList = sortedEntries.map((entry, idx, array) => {
|
||||
const itemList = sortedEntries.map((entry, idx, array) => {
|
||||
const uid = entry.uid;
|
||||
|
||||
return (
|
||||
|
|
|
@ -2,12 +2,12 @@ import * as React from 'react';
|
|||
import { withRouter } from 'react-router';
|
||||
|
||||
// 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;
|
||||
|
||||
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 {
|
||||
constructor(...rest: any[]) {
|
||||
const props = rest[0];
|
||||
|
@ -18,14 +18,14 @@ export function historyPersistor(tag: string) {
|
|||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount() {
|
||||
if (super.componentWillUnmount) {
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
stateCache[this.getKeyForTag(this.props, tag)] = this.state;
|
||||
}
|
||||
|
||||
getKeyForTag(props: any, tagName: string) {
|
||||
public getKeyForTag(props: any, tagName: string) {
|
||||
return props.location.pathname + ':' + tagName;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -50,7 +50,7 @@ export default function register() {
|
|||
function registerValidSW(swUrl: string) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
.then((registration) => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker) {
|
||||
|
@ -73,7 +73,7 @@ function registerValidSW(swUrl: string) {
|
|||
}
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
@ -81,14 +81,14 @@ function registerValidSW(swUrl: string) {
|
|||
function checkValidServiceWorker(swUrl: string) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if (
|
||||
response.status === 404 ||
|
||||
response.headers.get('content-type')!.indexOf('javascript') === -1
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
|
@ -107,7 +107,7 @@ function checkValidServiceWorker(swUrl: string) {
|
|||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue