@@ -3,8 +3,9 @@ import { ArticleList } from '@/client/Home/_components/Article/ArticleList';
3
3
import { ArticleListLayout , keyConstructorGenerator } from './common' ;
4
4
import { fetcher } from '@/features/swr/fetcher' ;
5
5
import { Stack , Button , Text } from '@mantine/core' ;
6
- import type { InboxItem , InboxItemWithArticle } from '@read-stack/openapi' ;
6
+ import type { Article , InboxItem } from '@read-stack/openapi' ;
7
7
import {
8
+ archiveInboxItemResponseSchema ,
8
9
getInboxItemsResponseSchema ,
9
10
moveInboxItemToClipResponseSchema ,
10
11
} from '@read-stack/openapi' ;
@@ -14,12 +15,15 @@ import Image from 'next/image';
14
15
import { toast } from 'react-toastify' ;
15
16
import { IconChevronRight } from '@tabler/icons-react' ;
16
17
import { useMutators } from './useMutators' ;
17
- import type { UnreadClipAdditionalProps } from './UnreadClipList' ;
18
+
19
+ import type { ReadClipAdditionalProps } from './ReadClipList' ;
18
20
// eslint-disable-next-line import/no-cycle -- しゃーなし
21
+ import { readClipsFetcher , readClipsKeyConstructor } from './ReadClipList' ;
19
22
import {
20
23
unreadClipsFetcher ,
21
24
unreadClipsKeyConstructor ,
22
25
} from './UnreadClipList' ;
26
+ import type { UnreadClipAdditionalProps } from './UnreadClipList' ;
23
27
24
28
export interface InboxItemAdditionalProps {
25
29
item : InboxItem ;
@@ -47,90 +51,187 @@ const NoContentComponent = (
47
51
</ Stack >
48
52
) ;
49
53
50
- interface ActionSectionProps {
51
- item : InboxItemWithArticle ;
52
- }
53
-
54
- const ActionSection : FC < ActionSectionProps > = ( { item } ) => {
54
+ const useReducers = ( ) => {
55
55
const mutators = useMutators ( ) ;
56
56
57
- const moveToClip = useCallback ( async ( ) => {
58
- try {
59
- const mutating = fetch (
60
- `/api/v1/users/me/inboxes/${ item . id } /move-to-clip` ,
61
- { method : 'POST' } ,
62
- )
63
- . then ( ( res ) => res . json ( ) )
64
- . then ( ( json ) => moveInboxItemToClipResponseSchema . parse ( json ) . clip ) ;
65
-
66
- void mutators . inboxItem ?.(
67
- async ( ) => {
68
- await mutating ;
69
- const result = await inboxFetcher ( inboxKeyConstructor ( 1 ) ) ;
70
-
71
- return [ result ] ;
72
- } ,
73
- {
74
- optimisticData : ( prev ) => {
75
- if ( prev === undefined ) return [ ] ;
76
-
77
- const result = prev . map ( ( r ) => ( {
78
- ...r ,
79
- articles : r . articles . filter ( ( a ) => a . id !== item . articleId ) ,
80
- } ) ) ;
81
-
82
- return result ;
57
+ const moveToClip = useCallback (
58
+ async ( article : Article & InboxItemAdditionalProps ) => {
59
+ try {
60
+ const mutating = fetch (
61
+ `/api/v1/users/me/inboxes/${ article . item . id } /move-to-clip` ,
62
+ { method : 'POST' } ,
63
+ )
64
+ . then ( ( res ) => res . json ( ) )
65
+ . then ( ( json ) => moveInboxItemToClipResponseSchema . parse ( json ) . clip ) ;
66
+
67
+ void mutators . inboxItem ?.(
68
+ async ( ) => {
69
+ await mutating ;
70
+ const result = await inboxFetcher ( inboxKeyConstructor ( 1 ) ) ;
71
+
72
+ return [ result ] ;
83
73
} ,
84
- } ,
85
- ) ;
86
-
87
- const clip = await mutating ;
88
- void mutators . unreadClip ?.(
89
- async ( ) => {
90
- await mutating ;
91
- const result = await unreadClipsFetcher ( unreadClipsKeyConstructor ( 1 ) ) ;
92
- return [ result ] ;
93
- } ,
94
- {
95
- optimisticData : ( prev ) => {
96
- if ( prev === undefined ) return [ ] ;
97
-
98
- const newResult : FetchArticleResult < UnreadClipAdditionalProps > = {
99
- articles : [ { ...item . article , clip } ] ,
100
- finished : false ,
101
- } ;
102
-
103
- return [ newResult , ...prev ] ;
74
+ {
75
+ optimisticData : ( prev ) => {
76
+ if ( prev === undefined ) return [ ] ;
77
+
78
+ const result = prev . map ( ( r ) => ( {
79
+ ...r ,
80
+ articles : r . articles . filter ( ( a ) => a . id !== article . id ) ,
81
+ } ) ) ;
82
+
83
+ return result ;
84
+ } ,
104
85
} ,
105
- } ,
106
- ) ;
107
- } catch ( err ) {
108
- console . error ( err ) ;
109
- toast ( '記事の移動に失敗しました' , { type : 'error' } ) ;
110
- }
111
- } , [ item . article , item . articleId , item . id , mutators ] ) ;
112
- return (
113
- < Button
114
- fullWidth
115
- onClick = { moveToClip }
116
- rightIcon = { < IconChevronRight /> }
117
- type = "button"
118
- variant = "light"
119
- >
120
- スタックに積む
121
- </ Button >
86
+ ) ;
87
+
88
+ const clip = await mutating ;
89
+ void mutators . unreadClip ?.(
90
+ async ( ) => {
91
+ await mutating ;
92
+ const result = await unreadClipsFetcher (
93
+ unreadClipsKeyConstructor ( 1 ) ,
94
+ ) ;
95
+ return [ result ] ;
96
+ } ,
97
+ {
98
+ optimisticData : ( prev ) => {
99
+ if ( prev === undefined ) return [ ] ;
100
+
101
+ const newResult : FetchArticleResult < UnreadClipAdditionalProps > = {
102
+ articles : [ { ...article , clip } ] ,
103
+ finished : false ,
104
+ } ;
105
+
106
+ return [ newResult , ...prev ] ;
107
+ } ,
108
+ } ,
109
+ ) ;
110
+ } catch ( err ) {
111
+ console . error ( err ) ;
112
+ toast ( '記事の移動に失敗しました' , { type : 'error' } ) ;
113
+ }
114
+ } ,
115
+ [ mutators ] ,
116
+ ) ;
117
+
118
+ const archive = useCallback (
119
+ async ( article : Article & InboxItemAdditionalProps ) => {
120
+ try {
121
+ const mutating = fetch (
122
+ `/api/v1/users/me/inboxes/${ article . item . id } /archive` ,
123
+ { method : 'POST' } ,
124
+ )
125
+ . then ( ( res ) => res . json ( ) )
126
+ . then ( ( json ) => archiveInboxItemResponseSchema . parse ( json ) . clip ) ;
127
+
128
+ void mutators . inboxItem ?.(
129
+ async ( ) => {
130
+ await mutating ;
131
+ const result = await inboxFetcher ( inboxKeyConstructor ( 1 ) ) ;
132
+
133
+ return [ result ] ;
134
+ } ,
135
+ {
136
+ optimisticData : ( prev ) => {
137
+ if ( prev === undefined ) return [ ] ;
138
+
139
+ const result = prev . map ( ( r ) => ( {
140
+ ...r ,
141
+ articles : r . articles . filter ( ( a ) => a . id !== article . id ) ,
142
+ } ) ) ;
143
+
144
+ return result ;
145
+ } ,
146
+ } ,
147
+ ) ;
148
+
149
+ const clip = await mutating ;
150
+ console . log ( 'clip' , clip ) ;
151
+
152
+ void mutators . readClip ?.(
153
+ async ( ) => {
154
+ const result = await readClipsFetcher ( readClipsKeyConstructor ( 1 ) ) ;
155
+ return [ result ] ;
156
+ } ,
157
+ {
158
+ optimisticData : ( prev ) => {
159
+ if ( prev === undefined ) return [ ] ;
160
+
161
+ const newResult : FetchArticleResult < ReadClipAdditionalProps > = {
162
+ articles : [ { ...article , clip } ] ,
163
+ finished : false ,
164
+ } ;
165
+
166
+ return [ newResult , ...prev ] ;
167
+ } ,
168
+ } ,
169
+ ) ;
170
+ } catch ( err ) {
171
+ console . error ( err ) ;
172
+ toast ( '記事の移動に失敗しました' , { type : 'error' } ) ;
173
+ }
174
+ } ,
175
+ [ mutators ] ,
122
176
) ;
177
+
178
+ const deleteItem = useCallback (
179
+ async ( article : Article & InboxItemAdditionalProps ) => {
180
+ try {
181
+ await fetch ( `/api/v1/users/me/inboxes/${ article . item . id } ` , {
182
+ method : 'DELETE' ,
183
+ } ) ;
184
+
185
+ void mutators . inboxItem ?.(
186
+ async ( ) => {
187
+ const result = await inboxFetcher ( inboxKeyConstructor ( 1 ) ) ;
188
+
189
+ return [ result ] ;
190
+ } ,
191
+ {
192
+ optimisticData : ( prev ) => {
193
+ if ( prev === undefined ) return [ ] ;
194
+
195
+ const result = prev . map ( ( r ) => ( {
196
+ ...r ,
197
+ articles : r . articles . filter ( ( a ) => a . id !== article . id ) ,
198
+ } ) ) ;
199
+
200
+ return result ;
201
+ } ,
202
+ } ,
203
+ ) ;
204
+ } catch ( err ) {
205
+ console . error ( err ) ;
206
+ toast ( '記事の削除に失敗しました' , { type : 'error' } ) ;
207
+ }
208
+ } ,
209
+ [ mutators ] ,
210
+ ) ;
211
+
212
+ return { moveToClip, archive, deleteItem } ;
123
213
} ;
124
214
125
215
export const InboxItemList : FC = ( ) => {
216
+ const { moveToClip, archive, deleteItem } = useReducers ( ) ;
126
217
return (
127
218
< ArticleListLayout label = "受信箱" >
128
219
< ArticleList
129
220
fetcher = { inboxFetcher }
130
221
keyConstructor = { inboxKeyConstructor }
131
222
noContentComponent = { NoContentComponent }
223
+ onArchive = { ( article ) => archive ( article ) }
224
+ onDelete = { ( article ) => deleteItem ( article ) }
132
225
renderActions = { ( article ) => (
133
- < ActionSection item = { { ...article . item , article } } />
226
+ < Button
227
+ fullWidth
228
+ onClick = { ( ) => moveToClip ( article ) }
229
+ rightIcon = { < IconChevronRight /> }
230
+ type = "button"
231
+ variant = "light"
232
+ >
233
+ スタックに積む
234
+ </ Button >
134
235
) }
135
236
stateKey = "inboxItem"
136
237
/>
0 commit comments