Repository navigation
Expand file tree
/
Copy pathApp.tsx
More file actions
141 lines (131 loc) · 3.84 KB
/
Copy pathApp.tsx
File metadata and controls
141 lines (131 loc) · 3.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import React, {
useEffect,
useState
} from 'react';
import type {ReadTransaction, WriteTransaction} from 'replicache';
import add from 'date-fns/add'
import setDay from 'date-fns/setDay';
import areIntervalsOverlapping from 'date-fns/areIntervalsOverlapping';
import format from 'date-fns/format';
import uuid from 'uuid-random';
import './App.css';
import type {
Event,
NormalizedEvent,
EventDate
} from './types';
import {
rep,
addEvent,
updateEvent,
deleteEvent,
} from './data';
export default App;
function App() {
const events = useSubscribe(async (tx: ReadTransaction) => {
// TODO: Scan API needs improvement.
// See https://github.com/rocicorp/replicache-sdk-js/issues/30.
const res = Array.from(await tx.scan({prefix: '/event/', limit: 50000}));
const result = res.map(item => {
let event = item.value as Event;
if (!event.start || !event.end) {
return null;
}
let normalized = item.value as unknown as NormalizedEvent;
normalized.start = eventDate(event.start);
normalized.end = eventDate(event.end);
return normalized;
}).filter(item => {
if (!item) {
return false;
}
const now = new Date();
const viewInterval = {
start: setDay(new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime(), 0),
end: setDay(new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999).getTime(), 6),
};
return areIntervalsOverlapping(item, viewInterval);
}) as NormalizedEvent[];
result.sort((a, b) => a.start.getTime() - b.start.getTime());
return result;
}, [], []);
return (
<div className="App">
<header className="App-header">
This Week's Agenda<br/>
<SyncButton/>
<NewEventButton/>
</header>
<List events={events} />
</div>
);
}
function List({events}: {events: NormalizedEvent[]}) {
return (
<ul className="App-list">
{events.map(event => (
<ListItem key={event.id} event={event} />
))}
</ul>
);
}
function ListItem({event}: {event: NormalizedEvent}) {
const [summaryEdit, setSummaryEdit] = useState<string|null>(null);
const handleSummaryBlur = async () => {
if (summaryEdit == null) {
return;
}
// Wait for the local edit to go through then clear the edit state.
await updateEvent({
id: event.id,
summary: summaryEdit,
});
setSummaryEdit(null);
};
const handleDelete = () => {
deleteEvent(event);
};
return <div style={{marginBottom:'1em'}}>
<span style={{color:'#999'}}>{`${format(event.start, 'PPpp')}`}</span><br/>
<input value={summaryEdit ?? event.summary}
onChange={e => setSummaryEdit(e.target.value)}
onBlur={handleSummaryBlur} />
<button onClick={handleDelete}>🗑</button>
</div>;
}
function SyncButton() {
const [syncing, setSyncing] = useState<boolean>(false);
useEffect(() => {
rep.onSync = (syncing) => setSyncing(syncing);
}, []);
return <button onClick={() => rep.sync()} disabled={syncing}>
{syncing ? "Syncing..." : "Sync"}
</button>
}
function NewEventButton() {
const handleClick = async () => {
const start = add(new Date(), {hours:1});
const end = add(start, {hours:1});
addEvent({
id: uuid().replace(/\-/g, ''),
summary: 'Untitled Event',
start: {
dateTime: start.toISOString(),
},
end: {
dateTime: end.toISOString(),
},
});
};
return <button onClick={() => handleClick()}>New Event</button>
}
function useSubscribe<R>(query: (tx: ReadTransaction) => Promise<R>, def: R, deps?: any[]) {
const [snapshot, setSnapshot] = useState<R>(def);
useEffect(() => {
return rep.subscribe(query, {onData: setSnapshot});
}, deps);
return snapshot;
}
function eventDate(d: EventDate) {
return new Date(Date.parse(d.dateTime || d.date || ''));
}