-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
book.extra-3.js
146 lines (136 loc) · 4.18 KB
/
book.extra-3.js
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
142
143
144
145
146
/** @jsx jsx */
/** @jsxFrag React.Fragment */
import {jsx} from '@emotion/core'
import React from 'react'
import debounceFn from 'debounce-fn'
import {FaRegCalendarAlt} from 'react-icons/fa'
import Tooltip from '@reach/tooltip'
import {useParams} from 'react-router-dom'
import {useBook} from 'utils/books'
import {formatDate} from 'utils/misc'
import {useListItem, useUpdateListItem} from 'utils/list-items'
import * as mq from 'styles/media-queries'
import * as colors from 'styles/colors'
import {Spinner, Textarea, ErrorMessage} from 'components/lib'
import {Rating} from 'components/rating'
import {Profiler} from 'components/profiler'
import {StatusButtons} from 'components/status-buttons'
function BookScreen() {
const {bookId} = useParams()
const book = useBook(bookId)
const listItem = useListItem(bookId)
const {title, author, coverImageUrl, publisher, synopsis} = book
return (
<Profiler id="Book Screen" data={{bookId, listItemId: listItem?.id}}>
<div>
<div
css={{
display: 'grid',
gridTemplateColumns: '1fr 2fr',
gridGap: '2em',
marginBottom: '1em',
[mq.small]: {
display: 'flex',
flexDirection: 'column',
},
}}
>
<img
src={coverImageUrl}
alt={`${title} book cover`}
css={{width: '100%', maxWidth: '14rem'}}
/>
<div>
<div css={{display: 'flex', position: 'relative'}}>
<div css={{flex: 1, justifyContent: 'space-between'}}>
<h1>{title}</h1>
<div>
<i>{author}</i>
<span css={{marginRight: 6, marginLeft: 6}}>|</span>
<i>{publisher}</i>
</div>
</div>
<div
css={{
right: 0,
color: colors.gray80,
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-around',
minHeight: 100,
}}
>
{book.loadingBook ? null : <StatusButtons book={book} />}
</div>
</div>
<div css={{marginTop: 10, minHeight: 46}}>
{listItem?.finishDate ? <Rating listItem={listItem} /> : null}
{listItem ? <ListItemTimeframe listItem={listItem} /> : null}
</div>
<br />
<p>{synopsis}</p>
</div>
</div>
{!book.loadingBook && listItem ? (
<NotesTextarea listItem={listItem} />
) : null}
</div>
</Profiler>
)
}
function ListItemTimeframe({listItem}) {
const timeframeLabel = listItem.finishDate
? 'Start and finish date'
: 'Start date'
return (
<Tooltip label={timeframeLabel}>
<div aria-label={timeframeLabel} css={{marginTop: 6}}>
<FaRegCalendarAlt css={{marginTop: -2, marginRight: 5}} />
<span>
{formatDate(listItem.startDate)}{' '}
{listItem.finishDate ? `— ${formatDate(listItem.finishDate)}` : null}
</span>
</div>
</Tooltip>
)
}
function NotesTextarea({listItem}) {
const [mutate, {status, error}] = useUpdateListItem({throwOnError: false})
const debouncedMutate = React.useCallback(debounceFn(mutate, {wait: 300}), [])
function handleNotesChange(e) {
debouncedMutate({id: listItem.id, notes: e.target.value})
}
return (
<React.Fragment>
<div>
<label
htmlFor="notes"
css={{
display: 'inline-block',
marginRight: 10,
marginTop: '0',
marginBottom: '0.5rem',
fontWeight: 'bold',
}}
>
Notes
</label>
{error ? (
<ErrorMessage
variant="inline"
error={error}
css={{fontSize: '0.7em'}}
/>
) : null}
{status === 'loading' ? <Spinner /> : null}
</div>
<Textarea
id="notes"
defaultValue={listItem.notes}
onChange={handleNotesChange}
css={{width: '100%', minHeight: 300}}
/>
</React.Fragment>
)
}
export {BookScreen}