Cleanup the calendar and render in the main page.

master
Tom Hacohen 7 years ago
parent 425923aa6a
commit fc42566486

@ -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<ICAL.Component>,
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 (
<div style={{width: '100%', height: 500}}>
<BigCalendar
events={entries}
{...{culture: 'en-GB'}}
date={this.state.currentDate}
onNavigate={(currentDate: Date) => { this.setState({currentDate}); }}
/>
</div>
);
}
}
export default Calendar;

@ -10,7 +10,7 @@ import JournalAddressBook from './JournalAddressBook';
import JournalCalendar from './JournalCalendar'; import JournalCalendar from './JournalCalendar';
import LoadingIndicator from './LoadingIndicator'; import LoadingIndicator from './LoadingIndicator';
import { syncEntriesToItemMap } from './journal-processors'; import { syncEntriesToItemMap, syncEntriesToCalendarItemMap } from './journal-processors';
import { store, StoreState, JournalsData, EntriesType, CredentialsData, fetchEntries } from './store'; import { store, StoreState, JournalsData, EntriesType, CredentialsData, fetchEntries } from './store';
@ -70,7 +70,7 @@ class Journal extends React.Component {
let itemsTitle: string; let itemsTitle: string;
let itemsView: JSX.Element; let itemsView: JSX.Element;
if (collectionInfo.type === 'CALENDAR') { if (collectionInfo.type === 'CALENDAR') {
itemsView = <JournalCalendar journal={journal} entries={syncEntries} />; itemsView = <JournalCalendar journal={journal} entries={syncEntriesToCalendarItemMap(syncEntries)} />;
itemsTitle = 'Events'; itemsTitle = 'Events';
} else if (collectionInfo.type === 'ADDRESS_BOOK') { } else if (collectionInfo.type === 'ADDRESS_BOOK') {
itemsView = <JournalAddressBook journal={journal} entries={syncEntriesToItemMap(syncEntries)} />; itemsView = <JournalAddressBook journal={journal} entries={syncEntriesToItemMap(syncEntries)} />;

@ -30,10 +30,6 @@ class JournalAddressBook extends React.Component {
} }
render() { render() {
if (this.props.journal === undefined) {
return (<div>Loading</div>);
}
let items = this.props.entries; let items = this.props.entries;
return ( return (

@ -1,101 +1,34 @@
import * as React from 'react'; import * as React from 'react';
import BigCalendar from 'react-big-calendar';
import 'react-big-calendar/lib/css/react-big-calendar.css'; 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 ICAL from 'ical.js';
import * as EteSync from './api/EteSync'; 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 { class JournalCalendar extends React.Component {
static defaultProps = {
prevUid: null,
};
state: {
currentDate?: Date;
};
props: { props: {
journal: EteSync.Journal, journal: EteSync.Journal,
entries: Array<EteSync.SyncEntry>, entries: Map<string, ICAL.Component>,
history?: any,
}; };
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = {}; this.eventClicked = this.eventClicked.bind(this);
}
render() {
if (this.props.journal === undefined) {
return (<div>Loading</div>);
}
let items: Map<string, ICAL.Component> = 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'); eventClicked(contact: ICAL.Component) {
// FIXME: do something
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);
}
} }
let entries = Array.from(items.values()).map((value) => ( render() {
new EventWrapper(new ICAL.Event(value)) let items = this.props.entries;
)).sort((a, b) => {
if (a.summary < b.summary) {
return -1;
} else if (a.summary > b.summary) {
return 1;
} else {
return 0;
}
});
return ( return (
<div style={{width: '100%', height: 500}}> <div style={{width: '100%', height: 500}}>
<BigCalendar <Calendar entries={Array.from(items.values())} onItemClick={this.eventClicked} />
events={entries}
{...{culture: 'en-GB'}}
date={this.state.currentDate}
onNavigate={(currentDate: Date) => { this.setState({currentDate}); }}
/>
</div> </div>
); );
} }

@ -1,15 +1,18 @@
import * as React from 'react'; import * as React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { Tabs, Tab } from 'material-ui/Tabs';
import LoadingIndicator from './LoadingIndicator'; import LoadingIndicator from './LoadingIndicator';
import * as EteSync from './api/EteSync'; import * as EteSync from './api/EteSync';
import AddressBook from './AddressBook'; import AddressBook from './AddressBook';
import Calendar from './Calendar';
import { store, JournalsType, EntriesType, fetchJournals, fetchEntries, StoreState, CredentialsData } from './store'; import { store, JournalsType, EntriesType, fetchJournals, fetchEntries, StoreState, CredentialsData } from './store';
import { syncEntriesToItemMap } from './journal-processors'; import { syncEntriesToItemMap, syncEntriesToCalendarItemMap } from './journal-processors';
interface PropsType { interface PropsType {
etesync: CredentialsData; etesync: CredentialsData;
@ -25,9 +28,14 @@ class SyncGate extends React.Component {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.eventClicked = this.eventClicked.bind(this);
this.contactClicked = this.contactClicked.bind(this); this.contactClicked = this.contactClicked.bind(this);
} }
eventClicked(contact: any) {
// FIXME
}
contactClicked(contact: any) { contactClicked(contact: any) {
// FIXME // FIXME
} }
@ -56,7 +64,8 @@ class SyncGate extends React.Component {
const derived = this.props.etesync.encryptionKey; 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) { for (const journal of this.props.journals.value) {
const journalEntries = this.props.entries[journal.uid].value; const journalEntries = this.props.entries[journal.uid].value;
const cryptoManager = new EteSync.CryptoManager(derived, journal.uid, journal.version); const cryptoManager = new EteSync.CryptoManager(derived, journal.uid, journal.version);
@ -74,23 +83,33 @@ class SyncGate extends React.Component {
const collectionInfo = journal.getInfo(cryptoManager); const collectionInfo = journal.getInfo(cryptoManager);
if (collectionInfo.type !== 'ADDRESS_BOOK') { const syncEntries = journalEntries.map((entry) => {
continue;
}
syncEntries = syncEntries.concat(journalEntries.map((entry) => {
let syncEntry = entry.getSyncEntry(cryptoManager, prevUid); let syncEntry = entry.getSyncEntry(cryptoManager, prevUid);
prevUid = entry.uid; prevUid = entry.uid;
return syncEntry; 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 ( return (
<AddressBook entries={Array.from(items.values())} onItemClick={this.contactClicked} /> <Tabs>
<Tab label="Address Book">
<AddressBook entries={Array.from(addressBookItems.values())} onItemClick={this.contactClicked} />
</Tab>
<Tab label="Calendar">
<Calendar entries={Array.from(calendarItems.values())} onItemClick={this.eventClicked} />
</Tab>
</Tabs>
); );
} }
} }

@ -20,3 +20,26 @@ export function syncEntriesToItemMap(entries: EteSync.SyncEntry[]) {
return items; return items;
} }
export function syncEntriesToCalendarItemMap(entries: EteSync.SyncEntry[]) {
let items: Map<string, ICAL.Component> = 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;
}

Loading…
Cancel
Save