@fountain-ui/lab 2.0.0-beta.34 → 2.0.0-beta.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/build/commonjs/ComicViewer/ComicViewer.js +197 -143
  2. package/build/commonjs/ComicViewer/ComicViewer.js.map +1 -1
  3. package/build/commonjs/ComicViewer/ComicViewerProps.js +0 -12
  4. package/build/commonjs/ComicViewer/ComicViewerProps.js.map +1 -1
  5. package/build/commonjs/ComicViewer/ReloadButton.js +43 -0
  6. package/build/commonjs/ComicViewer/ReloadButton.js.map +1 -0
  7. package/build/commonjs/ComicViewer/ViewerItem.js +37 -171
  8. package/build/commonjs/ComicViewer/ViewerItem.js.map +1 -1
  9. package/build/commonjs/ComicViewer/checkered-loading.jpg +0 -0
  10. package/build/commonjs/ComicViewer/index.js.map +1 -1
  11. package/build/module/ComicViewer/ComicViewer.js +196 -143
  12. package/build/module/ComicViewer/ComicViewer.js.map +1 -1
  13. package/build/module/ComicViewer/ComicViewerProps.js +1 -6
  14. package/build/module/ComicViewer/ComicViewerProps.js.map +1 -1
  15. package/build/module/ComicViewer/ReloadButton.js +29 -0
  16. package/build/module/ComicViewer/ReloadButton.js.map +1 -0
  17. package/build/module/ComicViewer/ViewerItem.js +39 -173
  18. package/build/module/ComicViewer/ViewerItem.js.map +1 -1
  19. package/build/module/ComicViewer/checkered-loading.jpg +0 -0
  20. package/build/module/ComicViewer/index.js.map +1 -1
  21. package/build/typescript/ComicViewer/ComicViewer.d.ts +1 -1
  22. package/build/typescript/ComicViewer/ComicViewerProps.d.ts +14 -89
  23. package/build/typescript/ComicViewer/ReloadButton.d.ts +6 -0
  24. package/build/typescript/ComicViewer/ViewerItem.d.ts +37 -7
  25. package/build/typescript/ComicViewer/index.d.ts +2 -2
  26. package/package.json +2 -2
  27. package/src/ComicViewer/ComicViewer.tsx +212 -157
  28. package/src/ComicViewer/ComicViewerProps.ts +14 -106
  29. package/src/ComicViewer/ReloadButton.tsx +33 -0
  30. package/src/ComicViewer/ViewerItem.tsx +81 -184
  31. package/src/ComicViewer/checkered-loading.jpg +0 -0
  32. package/src/ComicViewer/index.ts +2 -2
  33. package/build/commonjs/ComicViewer/ComicViewerItemProps.js +0 -6
  34. package/build/commonjs/ComicViewer/ComicViewerItemProps.js.map +0 -1
  35. package/build/module/ComicViewer/ComicViewerItemProps.js +0 -2
  36. package/build/module/ComicViewer/ComicViewerItemProps.js.map +0 -1
  37. package/build/typescript/ComicViewer/ComicViewerItemProps.d.ts +0 -34
  38. package/src/ComicViewer/ComicViewerItemProps.ts +0 -42
@@ -1,237 +1,292 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { FlatList, ListRenderItem, ViewToken } from 'react-native';
3
3
  import * as R from 'ramda';
4
- import {
5
- ComicViewerItemData,
6
- ComicViewerItemState,
7
- default as ComicViewerProps,
8
- ErrorInfo,
9
- STATE,
10
- } from './ComicViewerProps';
11
- import type ComicViewerItemProps from './ComicViewerItemProps';
4
+ import { useDebounce } from '@fountain-ui/core';
5
+ import { default as ComicViewerProps, Dimension } from './ComicViewerProps';
12
6
  import ViewerItem from './ViewerItem';
13
7
 
14
- const getItemHeights = <T, >(items: ComicViewerItemProps<T>[]): number[] => R.map((content: ComicViewerItemProps<T>) => content.height)(items);
15
8
  const appender = (left: number, right: number): [number, number] => [left + right, left + right];
16
- const getHeightAccum = (itemHeights: number[]): [number, number[]] => R.mapAccum(appender, 0, itemHeights);
17
-
18
- const keyExtractor = <T, >(item: ComicViewerItemProps<T>) => `${item.sortKey}`;
19
-
20
- export default function ComicViewer<T>(props: ComicViewerProps<T>) {
9
+ const getHeightAccum = (heights: number[]): [number, number[]] => R.mapAccum(appender, 0, heights);
10
+
11
+ const keyExtractor = <T, >(item: ItemState) => String(item.index);
12
+
13
+ interface UrlState {
14
+ url: string;
15
+ validity: 'valid' | 'invalid' | 'unknown';
16
+ }
17
+
18
+ interface ImageState {
19
+ urlState?: UrlState;
20
+ isNewUrlIncoming: boolean;
21
+ totalErrorCount: number;
22
+ dimension: Dimension;
23
+ }
24
+
25
+ interface ItemState {
26
+ index: number;
27
+ url?: string;
28
+ reloadButtonVisible: boolean;
29
+ dimension: Dimension;
30
+ }
31
+
32
+ const createInitialImageState = (dimension: Dimension): ImageState => ({
33
+ isNewUrlIncoming: false,
34
+ totalErrorCount: 0,
35
+ dimension,
36
+ });
37
+
38
+ const mapImageStateToItemState = (
39
+ index: number,
40
+ imageState: ImageState,
41
+ autoHandleErrorCount: number,
42
+ ): ItemState => ({
43
+ index,
44
+ url: imageState.urlState?.url,
45
+ reloadButtonVisible: (imageState.urlState?.validity !== 'valid') && imageState.totalErrorCount >= autoHandleErrorCount,
46
+ dimension: imageState.dimension,
47
+ });
48
+
49
+ const MAXIMUM_WIDTH = 720;
50
+
51
+ const NUMBER_OF_ADJACENT_ITEM = 5;
52
+
53
+ export default function ComicViewer(props: ComicViewerProps) {
21
54
  const {
22
- data,
23
- errorDebounceMillis = 500,
24
- errorRetryCount = 3,
25
- responseTimestamp,
55
+ debounceMillis = 100,
56
+ autoHandleErrorCount = 3,
57
+ getUrlByIndex,
26
58
  initialNumToRender = 1,
27
59
  initialScrollPercentage = 0,
28
60
  itemVisiblePercentThreshold = 0,
29
- onError,
30
- onScroll,
61
+ intrinsicDimensions,
31
62
  onItemPress,
32
- getNextPage,
33
- viewerWidth,
63
+ viewportWidth,
34
64
  windowSize = 3,
35
- pageUnit,
36
- ListFooterComponent,
37
65
  ...otherProps
38
66
  } = props;
39
67
 
40
68
  const flatListRef = useRef<FlatList>(null);
41
69
 
42
- const errors = useRef<Map<number, ErrorInfo>>(new Map());
70
+ const debounceTimeout = useRef<NodeJS.Timeout | null>(null);
71
+
72
+ const maybeLoadableItemsIndexRange = useRef<[number, number]>([-1, 0]);
73
+
74
+ const actualImageWidth = Math.min(viewportWidth, MAXIMUM_WIDTH);
75
+
76
+ const initialImageStates = useMemo<Array<ImageState>>(() => R.map(createInitialImageState, intrinsicDimensions), []);
77
+ const imageStatesRef = useRef<Array<ImageState>>(initialImageStates);
78
+
79
+ const mapImageStatesToItemStates = (imageStates: Array<ImageState>): Array<ItemState> => {
80
+ return imageStates.map((image, index) => mapImageStateToItemState(
81
+ index, image, autoHandleErrorCount,
82
+ ));
83
+ };
43
84
 
44
- const debounceTimeOut = useRef<NodeJS.Timeout | null>(null);
85
+ const [itemStates, setItemStates] = useState<Array<ItemState>>(() => {
86
+ return mapImageStatesToItemStates(imageStatesRef.current);
87
+ });
45
88
 
46
- const imageWidth = Math.min(viewerWidth, 720);
47
- const initialItems = R.map((itemData: ComicViewerItemData<T>) => ({
48
- ...itemData,
49
- isViewable: false,
50
- width: imageWidth,
51
- height: (itemData.height * imageWidth) / itemData.width,
52
- }))(data);
89
+ const renderedDimensions = useMemo<Array<Dimension>>(() => {
90
+ return R.map(intrinsicDimension => ({
91
+ width: actualImageWidth,
92
+ height: (intrinsicDimension.height * actualImageWidth) / intrinsicDimension.width,
93
+ }), intrinsicDimensions);
94
+ }, [actualImageWidth]);
53
95
 
54
- const [items, setItems] = useState<ComicViewerItemProps<T>[]>(initialItems);
96
+ const layoutFromDimensions = useCallback(() => {
97
+ const itemHeights = R.map(dimension => dimension.height, renderedDimensions);
98
+ const [totalHeight, heightAccum] = getHeightAccum(itemHeights);
99
+ const itemOffsets = R.prepend(0, heightAccum);
55
100
 
56
- const initialItemState: ComicViewerItemState[] = R.map((itemData: ComicViewerItemData<T>) => ({
57
- sortKey: itemData.sortKey,
58
- state: R.isNil(itemData.id) ? STATE.INIT : STATE.URL_LOADED,
59
- }))(data);
101
+ const getItemLayout = (data: any, index: number) => ({
102
+ index,
103
+ length: itemHeights[index],
104
+ offset: itemOffsets[index],
105
+ });
60
106
 
61
- const itemStates = useRef<Array<ComicViewerItemState>>(initialItemState);
107
+ return {
108
+ totalHeight,
109
+ getItemLayout,
110
+ };
111
+ }, [renderedDimensions]);
62
112
 
63
- const itemHeights = [...getItemHeights(items)];
64
- const itemHeightAccum = getHeightAccum(itemHeights);
113
+ const { totalHeight, getItemLayout } = layoutFromDimensions();
65
114
 
66
115
  const viewabilityConfig = useMemo(() => ({
67
116
  itemVisiblePercentThreshold,
68
117
  }), [itemVisiblePercentThreshold]);
69
118
 
70
- const getItemLayout = useCallback((data: any, index: number) => {
71
- const offsets = R.prepend(0, itemHeightAccum[1]);
119
+ const updateImageState = (updateFunction: (prev: ImageState, index: number) => ImageState) => {
120
+ const prevImageStates = imageStatesRef.current;
121
+ const newImageStates = prevImageStates.map(updateFunction);
72
122
 
73
- return {
74
- length: itemHeights[index],
75
- offset: offsets[index],
76
- index,
77
- };
78
- }, [itemHeights]);
79
-
80
- const onViewableItemsChanged = useRef(({ viewableItems }: {
81
- viewableItems: Array<ViewToken>,
82
- }) => {
83
- setItems((prev: ComicViewerItemProps<T>[]) => {
84
- const viewableItemSortKeys: number[] = R.map((viewableItem: ViewToken) => viewableItem.item.sortKey)(viewableItems);
85
- const firstViewableSortKey = R.head(viewableItemSortKeys);
86
- const lastViewableItemSortKey = R.last(viewableItemSortKeys);
87
- const firstItem = R.head(prev);
88
- const lastItem = R.last(prev);
89
-
90
- if (R.isNil(firstViewableSortKey)
91
- || R.isNil(lastViewableItemSortKey)
92
- || R.isNil(firstItem)
93
- || R.isNil(lastItem)
94
- ) {
95
- return prev;
96
- }
123
+ imageStatesRef.current = newImageStates;
124
+
125
+ setItemStates(prevItemStates => {
126
+ const newItemStates = mapImageStatesToItemStates(newImageStates);
97
127
 
98
- const frontBoundary = R.max(firstItem.sortKey, firstViewableSortKey - 1);
99
- const backBoundary = R.min(lastItem.sortKey, lastViewableItemSortKey + 1);
128
+ return R.equals(prevItemStates, newItemStates) ? prevItemStates : newItemStates;
129
+ });
130
+ };
131
+
132
+ const loadUrlByIndex = async (indexes: number[]) => {
133
+ const filteredIndexes = R.filter(index => {
134
+ const state = imageStatesRef.current[index];
100
135
 
101
- const viewableItemBoundary = R.range(frontBoundary, backBoundary + 1);
102
- const newItems = R.map((prevItem: ComicViewerItemProps<T>) => ({
103
- ...prevItem,
104
- isViewable: R.includes(prevItem.sortKey, viewableItemBoundary),
105
- }))([...prev]);
136
+ return R.isNil(state.urlState)
137
+ || (state.urlState?.validity === 'invalid' && !state.isNewUrlIncoming);
138
+ }, indexes);
106
139
 
107
- return newItems;
140
+ updateImageState((imageState, i) => {
141
+ return R.includes(i, filteredIndexes)
142
+ ? { ...imageState, isNewUrlIncoming: true }
143
+ : imageState;
108
144
  });
109
- });
110
145
 
111
- const itemLoadedHandler = useCallback((sortKey: number) => {
112
- const itemState: ComicViewerItemState | undefined = R.find((state: ComicViewerItemState) => state.sortKey === sortKey)(itemStates.current);
146
+ try {
147
+ const urls = await getUrlByIndex(filteredIndexes);
113
148
 
114
- if (R.isNil(itemState)) {
115
- return;
149
+ updateImageState((imageState, i) => {
150
+ const newUrl = urls?.get(i);
151
+ const urlState = imageState.urlState;
152
+
153
+ if (newUrl !== undefined && urlState?.validity !== 'valid') {
154
+ return {
155
+ ...imageState,
156
+ urlState: {
157
+ url: newUrl,
158
+ validity: 'unknown',
159
+ },
160
+ };
161
+ }
162
+
163
+ return imageState;
164
+ });
165
+ } finally {
166
+ updateImageState((imageState, i) => {
167
+ return R.includes(i, filteredIndexes)
168
+ ? { ...imageState, isNewUrlIncoming: false }
169
+ : imageState;
170
+ });
116
171
  }
172
+ };
117
173
 
118
- itemState.state = STATE.LOADED;
119
- itemState.error = undefined;
120
- }, [itemStates]);
174
+ const loadMaybeLoadableItems = async () => {
175
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
176
+ const affectedIndexes = R.range(startIndex, endIndex);
121
177
 
122
- const itemErrorHandler = useCallback((errorInfo: ErrorInfo) => {
123
- const { sortKey, count } = errorInfo;
178
+ await loadUrlByIndex(affectedIndexes);
179
+ };
124
180
 
125
- if (count >= errorRetryCount) {
126
- return;
127
- }
181
+ const loadItemsDebounce = useDebounce(loadMaybeLoadableItems, debounceMillis);
128
182
 
129
- errors.current.set(sortKey, errorInfo);
183
+ const onViewableItemsChanged = useRef(({ viewableItems }: { viewableItems: Array<ViewToken> }) => {
184
+ const orderedViewableItems = R.sort((a, b) => (a.index || 0) - (b.index || 0), viewableItems);
130
185
 
131
- const itemState: ComicViewerItemState | undefined = R.find((state: ComicViewerItemState) => state.sortKey === sortKey)(itemStates.current);
186
+ const firstViewableIndex = R.head(orderedViewableItems)?.index;
187
+ const lastViewableItemIndex = R.last(orderedViewableItems)?.index;
132
188
 
133
- if (R.isNil(itemState)) {
189
+ if (R.isNil(firstViewableIndex) || R.isNil(lastViewableItemIndex)) {
134
190
  return;
135
191
  }
136
192
 
137
- itemState.state = STATE.FAIL;
138
- itemState.error = errorInfo;
193
+ const startIndex = R.max(firstViewableIndex - NUMBER_OF_ADJACENT_ITEM, 0);
194
+ const endIndex = R.min(lastViewableItemIndex + NUMBER_OF_ADJACENT_ITEM, itemStates.length - 1);
139
195
 
140
- const handleError = () => {
141
- const errorsArray = Array.from(errors.current.entries());
142
- const errorsInfo = R.map(([key, value]: [number, ErrorInfo]) => value)(errorsArray);
196
+ maybeLoadableItemsIndexRange.current = [startIndex, endIndex + 1];
143
197
 
144
- onError && onError([...errorsInfo]);
145
- errors.current.clear();
146
- };
198
+ loadItemsDebounce();
199
+ });
147
200
 
148
- if (debounceTimeOut.current) {
149
- clearTimeout(debounceTimeOut.current);
150
- }
201
+ const renderItem: ListRenderItem<ItemState> = useCallback(({ item, index }) => {
202
+ const onError = () => {
203
+ updateImageState((imageState, i) => {
204
+ const urlState = imageState.urlState;
151
205
 
152
- if (errors.current.size === pageUnit) {
153
- handleError();
154
- } else {
155
- debounceTimeOut.current = setTimeout(handleError, errorDebounceMillis);
156
- }
157
- }, [errors.current, itemStates]);
158
-
159
- const renderItem: ListRenderItem<ComicViewerItemProps<T>> = useCallback(({ item }) => {
160
- const itemState: ComicViewerItemState | undefined = R.find((state: ComicViewerItemState) => state.sortKey === item.sortKey)(itemStates.current);
161
-
162
- const props = {
163
- ...item,
164
- itemState,
165
- responseTimestamp,
166
- errorRetryCount,
167
- onError: itemErrorHandler,
168
- onLoaded: itemLoadedHandler,
169
- onItemPress,
170
- getNextPage,
171
- };
206
+ if (i === index && urlState !== undefined) {
207
+ return {
208
+ ...imageState,
209
+ totalErrorCount: imageState.totalErrorCount + 1,
210
+ urlState: {
211
+ ...urlState,
212
+ validity: 'invalid',
213
+ },
214
+ };
215
+ }
172
216
 
173
- return <ViewerItem props={props}/>;
174
- }, [responseTimestamp, itemErrorHandler, itemLoadedHandler, onItemPress]);
217
+ return imageState;
218
+ });
175
219
 
176
- useEffect(() => {
177
- setItems((prev: ComicViewerItemProps<T>[]) => {
178
- return R.map((prevItem: ComicViewerItemProps<T>) => {
179
- const currentData: ComicViewerItemData | undefined = R.find((currentItemData: ComicViewerItemData<T>) => prevItem.sortKey === currentItemData.sortKey)(data);
180
- const itemState: ComicViewerItemState | undefined = R.find((state: ComicViewerItemState) => state.sortKey === currentData?.sortKey)(itemStates.current);
220
+ if (item.reloadButtonVisible) {
221
+ return;
222
+ }
223
+
224
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
225
+ if (index >= startIndex || index < endIndex) {
226
+ loadItemsDebounce();
227
+ }
228
+ };
181
229
 
182
- if (currentData
183
- && currentData.id
184
- && itemState
185
- && itemState.state !== STATE.LOADED) {
230
+ const onReloadPress = () => {
231
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
232
+ if (index >= startIndex || index < endIndex) {
233
+ loadUrlByIndex([index]);
234
+ }
235
+ };
186
236
 
187
- itemState.state = STATE.URL_LOADED;
237
+ const onLoad = () => {
238
+ updateImageState((imageState, i) => {
239
+ const urlState = imageState.urlState;
188
240
 
241
+ if (i === index && urlState !== undefined) {
189
242
  return {
190
- ...prevItem,
191
- url: currentData.url,
192
- expiresAt: currentData.expiresAt,
243
+ ...imageState,
244
+ urlState: {
245
+ ...urlState,
246
+ validity: 'valid',
247
+ },
193
248
  };
194
249
  }
195
250
 
196
- return prevItem;
197
- })([...prev]);
198
- ;
199
- });
200
- }, [responseTimestamp]);
201
-
202
- useEffect(() => {
203
- const newItems = R.map((item: ComicViewerItemProps<T>) => ({
204
- ...item,
205
- width: imageWidth,
206
- height: (item.height * imageWidth) / item.width,
207
- }))(items);
251
+ return imageState;
252
+ });
253
+ };
208
254
 
209
- setItems(newItems);
210
- }, [imageWidth]);
255
+ return (
256
+ <ViewerItem
257
+ onError={onError}
258
+ onLoad={onLoad}
259
+ onPress={onItemPress}
260
+ onReloadPress={onReloadPress}
261
+ url={item.url}
262
+ width={renderedDimensions[index]?.width ?? 0}
263
+ height={renderedDimensions[index]?.height ?? 0}
264
+ reloadButtonVisible={item.reloadButtonVisible}
265
+ />
266
+ );
267
+ }, [onItemPress, renderedDimensions]);
211
268
 
212
269
  useEffect(() => {
213
- const totalHeight = itemHeightAccum[0];
214
270
  const offset = Math.floor((initialScrollPercentage / 100) * totalHeight);
215
271
 
216
272
  if (flatListRef.current) {
217
273
  flatListRef.current.scrollToOffset({ offset, animated: false });
218
274
  }
219
- }, [flatListRef.current]);
275
+ }, []);
220
276
 
221
277
  return (
222
278
  <FlatList
223
- data={items}
279
+ data={itemStates}
224
280
  getItemLayout={getItemLayout}
225
281
  initialNumToRender={initialNumToRender}
226
282
  keyExtractor={keyExtractor}
227
283
  onViewableItemsChanged={onViewableItemsChanged.current}
228
- onScroll={onScroll}
229
284
  ref={flatListRef}
285
+ removeClippedSubviews={false}
230
286
  renderItem={renderItem}
231
287
  viewabilityConfig={viewabilityConfig}
232
288
  windowSize={windowSize}
233
- ListFooterComponent={ListFooterComponent}
234
289
  {...otherProps}
235
290
  />
236
291
  );
237
- };
292
+ };
@@ -2,103 +2,23 @@ import React from 'react';
2
2
  import { ComponentProps } from '@fountain-ui/core';
3
3
  import { NativeScrollEvent, NativeSyntheticEvent } from 'react-native';
4
4
 
5
- export const STATE = {
6
- INIT: 'init',
7
- URL_LOADED: 'url_loaded',
8
- LOADED: 'loaded',
9
- FAIL: 'fail',
10
- } as const;
11
-
12
- export type LoadingState = typeof STATE[keyof typeof STATE];
13
-
14
- export interface ComicViewerItemState{
15
- /**
16
- * Comic viewer item sortKey.
17
- */
18
- sortKey: number;
19
-
20
- /**
21
- * Content's loading state.
22
- */
23
- state: LoadingState;
24
-
25
- /***
26
- * Content's error Info.
27
- */
28
- error?: ErrorInfo;
29
- }
30
-
31
- export interface ErrorInfo {
32
- /**
33
- * ComicViewerItemData.sortKey.
34
- */
35
- sortKey: number;
36
-
37
- /**
38
- * Number of times an error occurred.
39
- */
40
- count: number;
41
-
42
- /**
43
- * Content is Expired: true
44
- */
45
- expired: boolean;
46
- }
47
-
48
- export type ComicViewerItemData<T = {}> = T & {
49
- /**
50
- * Image height.
51
- */
52
- height: number;
53
-
54
- /**
55
- * Unique value for identifying.
56
- */
57
- id: number | undefined;
58
-
59
- /**
60
- * Image sourceUrl for displaying.
61
- */
62
- url: string;
63
-
64
- /**
65
- * Image width.
66
- */
5
+ export interface Dimension {
67
6
  width: number;
68
-
69
- /**
70
- * SortKey
71
- */
72
- sortKey: number;
73
-
74
- /**
75
- * Image expire date.
76
- */
77
- expiresAt: string;
78
-
79
- /***
80
- * Timestamp when get content response
81
- */
82
- responseTimestamp: number;
7
+ height: number;
83
8
  }
84
9
 
85
- export default interface ComicViewerProps<T> extends ComponentProps <{
86
- /**
87
- * Data for render.
88
- */
89
- data: ComicViewerItemData<T>[];
90
-
10
+ export default interface ComicViewerProps extends ComponentProps <{
91
11
  /**
92
12
  * Delay Time to call the error handler.
93
13
  * @default 500
94
14
  */
95
- errorDebounceMillis?: number;
15
+ debounceMillis?: number;
96
16
 
97
17
  /**
98
- * How many times retry onError when same item error occur
18
+ * How many times handle onError directly when item error occur
99
19
  * @default 3
100
20
  */
101
- errorRetryCount?: number;
21
+ autoHandleErrorCount?: number;
102
22
 
103
23
  /**
104
24
  * How many items to render in the initial batch.
@@ -119,15 +39,15 @@ export default interface ComicViewerProps<T> extends ComponentProps <{
119
39
  */
120
40
  itemVisiblePercentThreshold?: number;
121
41
 
122
- /***
123
- * Timestamp when get content response
42
+ /**
43
+ * Dimensions of each Image considering viewport.
124
44
  */
125
- responseTimestamp: number;
45
+ intrinsicDimensions: Array<Dimension>;
126
46
 
127
47
  /**
128
- * Comic viewer width.
48
+ * TBD
129
49
  */
130
- viewerWidth: number;
50
+ viewportWidth: number;
131
51
 
132
52
  /**
133
53
  * The value for FlatList windowSize.
@@ -136,21 +56,9 @@ export default interface ComicViewerProps<T> extends ComponentProps <{
136
56
  windowSize?: number;
137
57
 
138
58
  /**
139
- * How many images in one page.
140
- */
141
- pageUnit: number;
142
-
143
- /**
144
- * Method for getting next page contents.
145
- * @param sortKey
146
- */
147
- getNextPage?: (sortKey: number) => void;
148
-
149
- /**
150
- * Handling all viewerItem errors at once.
151
- * @param errors Array of ViewerItems errorInfo.
59
+ * TBD
152
60
  */
153
- onError?: (errors: ErrorInfo[]) => void;
61
+ getUrlByIndex: (indexes: Array<number>) => Promise<Map<number, string> | undefined> ;
154
62
 
155
63
  /**
156
64
  * Handle scroll event.
@@ -167,4 +75,4 @@ export default interface ComicViewerProps<T> extends ComponentProps <{
167
75
  * Component for comic viewer footer.
168
76
  */
169
77
  ListFooterComponent?: React.ReactElement;
170
- }> {}
78
+ }> {}
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { IconButton, IconButtonProps } from '@fountain-ui/core';
4
+ import { Restart } from '@fountain-ui/icons';
5
+
6
+ interface ReloadButtonProps {
7
+ onPress: IconButtonProps['onPress'];
8
+ }
9
+
10
+ export default function ReloadButton(props: ReloadButtonProps) {
11
+ return (
12
+ <View
13
+ style={{
14
+ width: '100%',
15
+ height: '100%',
16
+ alignItems: 'center',
17
+ paddingTop: 80,
18
+ }}
19
+ >
20
+ <IconButton
21
+ {...props}
22
+ children={<Restart fill={'#ffffff'}/>}
23
+ style={{
24
+ width: 48,
25
+ height: 48,
26
+ borderRadius: 24,
27
+ color: '#ffffff',
28
+ backgroundColor: '#767676',
29
+ }}
30
+ />
31
+ </View>
32
+ );
33
+ };