@@ -15,9 +15,10 @@ import ChapterCard from 'components/manga/ChapterCard';
15
15
import ResumeFab from 'components/manga/ResumeFAB' ;
16
16
import { filterAndSortChapters , useChapterOptions } from 'components/manga/util' ;
17
17
import EmptyView from 'components/util/EmptyView' ;
18
- import { pluralize } from 'components/util/helpers' ;
18
+ import { interpolate } from 'components/util/helpers' ;
19
19
import makeToast from 'components/util/Toast' ;
20
20
import React , {
21
+ ComponentProps ,
21
22
useEffect , useMemo , useRef , useState ,
22
23
} from 'react' ;
23
24
import { Virtuoso } from 'react-virtuoso' ;
@@ -37,6 +38,39 @@ const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({
37
38
} ,
38
39
} ) ) ;
39
40
41
+ const actionsStrings = {
42
+ download : {
43
+ success : { one : 'Download added' , many : '%count% downloads added' } ,
44
+ error : { one : 'Error adding download' , many : 'Error adding downloads' } ,
45
+ } ,
46
+ delete : {
47
+ success : { one : 'Chapter deleted' , many : '%count% chapters deleted' } ,
48
+ error : { one : 'Error deleting chapter' , many : 'Error deleting chapters' } ,
49
+ } ,
50
+ bookmark : {
51
+ success : { one : 'Chapter bookmarked' , many : '%count% chapters bookmarked' } ,
52
+ error : { one : 'Error bookmarking chapter' , many : 'Error bookmarking chapters' } ,
53
+ } ,
54
+ unbookmark : {
55
+ success : { one : 'Chapter bookmark removed' , many : '%count% chapter bookmarks removed' } ,
56
+ error : { one : 'Error removing bookmark' , many : 'Error removing bookmarks' } ,
57
+ } ,
58
+ mark_as_read : {
59
+ success : { one : 'Chapter marked as read' , many : '%count% chapters marked as read' } ,
60
+ error : { one : 'Error marking chapter as read' , many : 'Error marking chapters as read' } ,
61
+ } ,
62
+ mark_as_unread : {
63
+ success : { one : 'Chapter marked as unread' , many : '%count% chapters marked as unread' } ,
64
+ error : { one : 'Error marking chapter as unread' , many : 'Error marking chapters as unread' } ,
65
+ } ,
66
+ } ;
67
+
68
+ export interface IChapterWithMeta {
69
+ chapter : IChapter
70
+ downloadChapter : IDownloadChapter | undefined
71
+ selected : boolean | null
72
+ }
73
+
40
74
interface IProps {
41
75
mangaId : string
42
76
}
@@ -81,11 +115,6 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
81
115
. find ( ( c ) => c . read === false ) ,
82
116
[ visibleChapters ] ) ;
83
117
84
- const selectedChapters = useMemo ( ( ) => {
85
- if ( selection === null ) return null ;
86
- return visibleChapters . filter ( ( chap ) => selection . includes ( chap . id ) ) ;
87
- } , [ visibleChapters , selection ] ) ;
88
-
89
118
const handleSelection = ( index : number ) => {
90
119
const chapter = visibleChapters [ index ] ;
91
120
if ( ! chapter ) return ;
@@ -110,16 +139,30 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
110
139
setSelection ( null ) ;
111
140
} ;
112
141
113
- const handleFabAction = ( action : 'download' ) => {
114
- if ( ! selectedChapters || selectedChapters . length === 0 ) return ;
115
- const chapterIds = selectedChapters . map ( ( c ) => c . id ) ;
142
+ const handleFabAction : ComponentProps < typeof SelectionFAB > [ 'onAction' ] = ( action , actionChapters ) => {
143
+ if ( actionChapters . length === 0 ) return ;
144
+ const chapterIds = actionChapters . map ( ( { chapter } ) => chapter . id ) ;
145
+
146
+ let actionPromise : Promise < any > ;
116
147
117
148
if ( action === 'download' ) {
118
- client . post ( '/api/v1/download/batch' , { chapterIds } )
119
- . then ( ( ) => makeToast ( `${ chapterIds . length } ${ pluralize ( chapterIds . length , 'download' ) } added` , 'success' ) )
120
- . then ( ( ) => mutate ( ) )
121
- . catch ( ( ) => makeToast ( 'Error adding downloads' , 'error' ) ) ;
149
+ actionPromise = client . post ( '/api/v1/download/batch' , { chapterIds } ) ;
150
+ } else {
151
+ const change : BatchChaptersChange = { } ;
152
+
153
+ if ( action === 'delete' ) change . delete = true ;
154
+ else if ( action === 'bookmark' ) change . isBookmarked = true ;
155
+ else if ( action === 'unbookmark' ) change . isBookmarked = false ;
156
+ else if ( action === 'mark_as_read' ) change . isRead = true ;
157
+ else if ( action === 'mark_as_unread' ) change . isRead = false ;
158
+
159
+ actionPromise = client . post ( '/api/v1/chapter/batch' , { chapterIds, change } ) ;
122
160
}
161
+
162
+ actionPromise
163
+ . then ( ( ) => makeToast ( interpolate ( chapterIds . length , actionsStrings [ action ] . success ) , 'success' ) )
164
+ . then ( ( ) => mutate ( ) )
165
+ . catch ( ( ) => makeToast ( interpolate ( chapterIds . length , actionsStrings [ action ] . error ) , 'error' ) ) ;
123
166
} ;
124
167
125
168
if ( loading ) {
@@ -138,7 +181,7 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
138
181
const noChaptersFound = chapters . length === 0 ;
139
182
const noChaptersMatchingFilter = ! noChaptersFound && visibleChapters . length === 0 ;
140
183
141
- const scrollCache = visibleChapters . map ( ( chapter ) => {
184
+ const chaptersWithMeta : IChapterWithMeta [ ] = visibleChapters . map ( ( chapter ) => {
142
185
const downloadChapter = queue ?. find (
143
186
( cd ) => cd . chapterIndex === chapter . index
144
187
&& cd . mangaId === chapter . mangaId ,
@@ -151,6 +194,10 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
151
194
} ;
152
195
} ) ;
153
196
197
+ const selectedChapters = ( selection === null )
198
+ ? null
199
+ : chaptersWithMeta . filter ( ( { chapter } ) => selection . includes ( chapter . id ) ) ;
200
+
154
201
return (
155
202
< >
156
203
< Stack direction = "column" sx = { { position : 'relative' } } >
@@ -193,7 +240,7 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
193
240
itemContent = { ( index :number ) => (
194
241
< ChapterCard
195
242
// eslint-disable-next-line react/jsx-props-no-spreading
196
- { ...scrollCache [ index ] }
243
+ { ...chaptersWithMeta [ index ] }
197
244
showChapterNumber = { options . showChapterNumber }
198
245
triggerChaptersUpdate = { ( ) => mutate ( ) }
199
246
onSelect = { ( ) => handleSelection ( index ) }
0 commit comments