diff --git a/src/Calendar.tsx b/src/Calendar.tsx new file mode 100644 index 0000000..3f86884 --- /dev/null +++ b/src/Calendar.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import BigCalendar from 'react-big-calendar'; +import 'react-big-calendar/lib/css/react-big-calendar.css'; +import * as moment from 'moment'; + +import * as ICAL from 'ical.js'; + +BigCalendar.momentLocalizer(moment); + +class EventWrapper { + event: ICAL.Event; + + constructor(event: ICAL.Event) { + this.event = event; + } + + get summary() { + return this.event.summary; + } + + get title() { + return this.summary; + } + + get start() { + return this.event.startDate.toJSDate(); + } + + get end() { + return this.event.endDate.toJSDate(); + } +} + +class Calendar extends React.Component { + state: { + currentDate?: Date; + }; + + props: { + entries: Array, + onItemClick: (contact: ICAL.Component) => void, + }; + + constructor(props: any) { + super(props); + this.state = {}; + } + + render() { + let entries = this.props.entries.map((value) => ( + new EventWrapper(new ICAL.Event(value)) + )).sort((a, b) => { + if (a.summary < b.summary) { + return -1; + } else if (a.summary > b.summary) { + return 1; + } else { + return 0; + } + }); + + return ( +
+ { this.setState({currentDate}); }} + /> +
+ ); + } +} + +export default Calendar; diff --git a/src/Journal.tsx b/src/Journal.tsx index fdecf3f..2b466bb 100644 --- a/src/Journal.tsx +++ b/src/Journal.tsx @@ -10,7 +10,7 @@ import JournalAddressBook from './JournalAddressBook'; import JournalCalendar from './JournalCalendar'; import LoadingIndicator from './LoadingIndicator'; -import { syncEntriesToItemMap } from './journal-processors'; +import { syncEntriesToItemMap, syncEntriesToCalendarItemMap } from './journal-processors'; import { store, StoreState, JournalsData, EntriesType, CredentialsData, fetchEntries } from './store'; @@ -70,7 +70,7 @@ class Journal extends React.Component { let itemsTitle: string; let itemsView: JSX.Element; if (collectionInfo.type === 'CALENDAR') { - itemsView = ; + itemsView = ; itemsTitle = 'Events'; } else if (collectionInfo.type === 'ADDRESS_BOOK') { itemsView = ; diff --git a/src/JournalAddressBook.tsx b/src/JournalAddressBook.tsx index 43bf5c3..a74c6d7 100644 --- a/src/JournalAddressBook.tsx +++ b/src/JournalAddressBook.tsx @@ -30,10 +30,6 @@ class JournalAddressBook extends React.Component { } render() { - if (this.props.journal === undefined) { - return (
Loading
); - } - let items = this.props.entries; return ( diff --git a/src/JournalCalendar.tsx b/src/JournalCalendar.tsx index caae320..7a762f4 100644 --- a/src/JournalCalendar.tsx +++ b/src/JournalCalendar.tsx @@ -1,101 +1,34 @@ import * as React from 'react'; -import BigCalendar from 'react-big-calendar'; import 'react-big-calendar/lib/css/react-big-calendar.css'; -import * as moment from 'moment'; + +import Calendar from './Calendar'; import * as ICAL from 'ical.js'; import * as EteSync from './api/EteSync'; -BigCalendar.momentLocalizer(moment); - -class EventWrapper { - event: ICAL.Event; - - constructor(event: ICAL.Event) { - this.event = event; - } - - get summary() { - return this.event.summary; - } - - get title() { - return this.summary; - } - - get start() { - return this.event.startDate.toJSDate(); - } - - get end() { - return this.event.endDate.toJSDate(); - } -} - class JournalCalendar extends React.Component { - static defaultProps = { - prevUid: null, - }; - - state: { - currentDate?: Date; - }; - props: { journal: EteSync.Journal, - entries: Array, + entries: Map, + history?: any, }; constructor(props: any) { super(props); - this.state = {}; + this.eventClicked = this.eventClicked.bind(this); } - render() { - if (this.props.journal === undefined) { - return (
Loading
); - } - - let items: Map = new Map(); - - for (const syncEntry of this.props.entries) { - let comp = new ICAL.Component(ICAL.parse(syncEntry.content)).getFirstSubcomponent('vevent'); - - if (comp === null) { - continue; - } - - const uid = comp.getFirstPropertyValue('uid'); - - if ((syncEntry.action === EteSync.SyncEntryAction.Add) || - (syncEntry.action === EteSync.SyncEntryAction.Change)) { - items.set(uid, comp); - } else if (syncEntry.action === EteSync.SyncEntryAction.Delete) { - items.delete(uid); - } - } + eventClicked(contact: ICAL.Component) { + // FIXME: do something + } - let entries = Array.from(items.values()).map((value) => ( - new EventWrapper(new ICAL.Event(value)) - )).sort((a, b) => { - if (a.summary < b.summary) { - return -1; - } else if (a.summary > b.summary) { - return 1; - } else { - return 0; - } - }); + render() { + let items = this.props.entries; return (
- { this.setState({currentDate}); }} - /> +
); } diff --git a/src/SyncGate.tsx b/src/SyncGate.tsx index d1d2d03..0733209 100644 --- a/src/SyncGate.tsx +++ b/src/SyncGate.tsx @@ -1,15 +1,18 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { withRouter } from 'react-router'; +import { Tabs, Tab } from 'material-ui/Tabs'; + import LoadingIndicator from './LoadingIndicator'; import * as EteSync from './api/EteSync'; import AddressBook from './AddressBook'; +import Calendar from './Calendar'; import { store, JournalsType, EntriesType, fetchJournals, fetchEntries, StoreState, CredentialsData } from './store'; -import { syncEntriesToItemMap } from './journal-processors'; +import { syncEntriesToItemMap, syncEntriesToCalendarItemMap } from './journal-processors'; interface PropsType { etesync: CredentialsData; @@ -25,9 +28,14 @@ class SyncGate extends React.Component { constructor(props: any) { super(props); + this.eventClicked = this.eventClicked.bind(this); this.contactClicked = this.contactClicked.bind(this); } + eventClicked(contact: any) { + // FIXME + } + contactClicked(contact: any) { // FIXME } @@ -56,7 +64,8 @@ class SyncGate extends React.Component { const derived = this.props.etesync.encryptionKey; - let syncEntries: EteSync.SyncEntry[] = []; + let syncEntriesCalendar: EteSync.SyncEntry[] = []; + let syncEntriesAddressBook: EteSync.SyncEntry[] = []; for (const journal of this.props.journals.value) { const journalEntries = this.props.entries[journal.uid].value; const cryptoManager = new EteSync.CryptoManager(derived, journal.uid, journal.version); @@ -74,23 +83,33 @@ class SyncGate extends React.Component { const collectionInfo = journal.getInfo(cryptoManager); - if (collectionInfo.type !== 'ADDRESS_BOOK') { - continue; - } - - syncEntries = syncEntries.concat(journalEntries.map((entry) => { + const syncEntries = journalEntries.map((entry) => { let syncEntry = entry.getSyncEntry(cryptoManager, prevUid); prevUid = entry.uid; return syncEntry; - })); + }); + + if (collectionInfo.type === 'ADDRESS_BOOK') { + syncEntriesAddressBook = syncEntriesAddressBook.concat(syncEntries); + } else if (collectionInfo.type === 'CALENDAR') { + syncEntriesCalendar = syncEntriesCalendar.concat(syncEntries); + } } - let items = syncEntriesToItemMap(syncEntries); + let addressBookItems = syncEntriesToItemMap(syncEntriesAddressBook); + let calendarItems = syncEntriesToCalendarItemMap(syncEntriesCalendar); return ( - + + + + + + + + ); } } diff --git a/src/journal-processors.tsx b/src/journal-processors.tsx index 8c054d8..d2f2d1c 100644 --- a/src/journal-processors.tsx +++ b/src/journal-processors.tsx @@ -20,3 +20,26 @@ export function syncEntriesToItemMap(entries: EteSync.SyncEntry[]) { return items; } + +export function syncEntriesToCalendarItemMap(entries: EteSync.SyncEntry[]) { + let items: Map = new Map(); + + for (const syncEntry of entries) { + let comp = new ICAL.Component(ICAL.parse(syncEntry.content)).getFirstSubcomponent('vevent'); + + if (comp === null) { + continue; + } + + const uid = comp.getFirstPropertyValue('uid'); + + if ((syncEntry.action === EteSync.SyncEntryAction.Add) || + (syncEntry.action === EteSync.SyncEntryAction.Change)) { + items.set(uid, comp); + } else if (syncEntry.action === EteSync.SyncEntryAction.Delete) { + items.delete(uid); + } + } + + return items; +}