@fountain-ui/lab 2.0.0-beta.33 → 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 -142
  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 -152
  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 -142
  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 -154
  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 +15 -82
  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 +210 -155
  28. package/src/ComicViewer/ComicViewerProps.ts +16 -98
  29. package/src/ComicViewer/ReloadButton.tsx +33 -0
  30. package/src/ComicViewer/ViewerItem.tsx +81 -169
  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,236 +1,291 @@
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,
55
+ debounceMillis = 100,
56
+ autoHandleErrorCount = 3,
57
+ getUrlByIndex,
25
58
  initialNumToRender = 1,
26
59
  initialScrollPercentage = 0,
27
60
  itemVisiblePercentThreshold = 0,
28
- onError,
29
- onScroll,
61
+ intrinsicDimensions,
30
62
  onItemPress,
31
- getNextPage,
32
- viewerWidth,
63
+ viewportWidth,
33
64
  windowSize = 3,
34
- pageUnit,
35
- ListFooterComponent,
36
65
  ...otherProps
37
66
  } = props;
38
67
 
39
68
  const flatListRef = useRef<FlatList>(null);
40
69
 
41
- 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]);
42
73
 
43
- const debounceTimeOut = useRef<NodeJS.Timeout | null>(null);
74
+ const actualImageWidth = Math.min(viewportWidth, MAXIMUM_WIDTH);
44
75
 
45
- const resourceString = R.toString(R.map((itemData: ComicViewerItemData) => itemData.url)(data));
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
+ };
84
+
85
+ const [itemStates, setItemStates] = useState<Array<ItemState>>(() => {
86
+ return mapImageStatesToItemStates(imageStatesRef.current);
87
+ });
46
88
 
47
- const imageWidth = Math.min(viewerWidth, 720);
48
- const initialItems = R.map((itemData: ComicViewerItemData<T>) => ({
49
- ...itemData,
50
- isViewable: false,
51
- width: imageWidth,
52
- height: (itemData.height * imageWidth) / itemData.width,
53
- }))(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]);
54
95
 
55
- 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);
56
100
 
57
- const initialItemState: ComicViewerItemState[] = R.map((itemData: ComicViewerItemData<T>) => ({
58
- sortKey: itemData.sortKey,
59
- state: R.isEmpty(itemData.expiresAt) ? STATE.INIT : STATE.URL_LOADED,
60
- }))(data);
101
+ const getItemLayout = (data: any, index: number) => ({
102
+ index,
103
+ length: itemHeights[index],
104
+ offset: itemOffsets[index],
105
+ });
61
106
 
62
- const itemStates = useRef<Array<ComicViewerItemState>>(initialItemState);
107
+ return {
108
+ totalHeight,
109
+ getItemLayout,
110
+ };
111
+ }, [renderedDimensions]);
63
112
 
64
- const itemHeights = [...getItemHeights(items)];
65
- const itemHeightAccum = getHeightAccum(itemHeights);
113
+ const { totalHeight, getItemLayout } = layoutFromDimensions();
66
114
 
67
115
  const viewabilityConfig = useMemo(() => ({
68
116
  itemVisiblePercentThreshold,
69
117
  }), [itemVisiblePercentThreshold]);
70
118
 
71
- const getItemLayout = useCallback((data: any, index: number) => {
72
- 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);
73
122
 
74
- return {
75
- length: itemHeights[index],
76
- offset: offsets[index],
77
- index,
78
- };
79
- }, [itemHeights]);
80
-
81
- const onViewableItemsChanged = useRef(({ viewableItems }: {
82
- viewableItems: Array<ViewToken>,
83
- }) => {
84
- setItems((prev: ComicViewerItemProps<T>[]) => {
85
- const viewableItemSortKeys: number[] = R.map((viewableItem: ViewToken) => viewableItem.item.sortKey)(viewableItems);
86
- const firstViewableSortKey = R.head(viewableItemSortKeys);
87
- const lastViewableItemSortKey = R.last(viewableItemSortKeys);
88
- const firstItem = R.head(prev);
89
- const lastItem = R.last(prev);
90
-
91
- if (R.isNil(firstViewableSortKey)
92
- || R.isNil(lastViewableItemSortKey)
93
- || R.isNil(firstItem)
94
- || R.isNil(lastItem)
95
- ) {
96
- return prev;
97
- }
123
+ imageStatesRef.current = newImageStates;
124
+
125
+ setItemStates(prevItemStates => {
126
+ const newItemStates = mapImageStatesToItemStates(newImageStates);
127
+
128
+ return R.equals(prevItemStates, newItemStates) ? prevItemStates : newItemStates;
129
+ });
130
+ };
98
131
 
99
- const frontBoundary = R.max(firstItem.sortKey, firstViewableSortKey - 1);
100
- const backBoundary = R.min(lastItem.sortKey, lastViewableItemSortKey + 1);
132
+ const loadUrlByIndex = async (indexes: number[]) => {
133
+ const filteredIndexes = R.filter(index => {
134
+ const state = imageStatesRef.current[index];
101
135
 
102
- const viewableItemBoundary = R.range(frontBoundary, backBoundary + 1);
103
- const newItems = R.map((prevItem: ComicViewerItemProps<T>) => ({
104
- ...prevItem,
105
- isViewable: R.includes(prevItem.sortKey, viewableItemBoundary),
106
- }))([...prev]);
136
+ return R.isNil(state.urlState)
137
+ || (state.urlState?.validity === 'invalid' && !state.isNewUrlIncoming);
138
+ }, indexes);
107
139
 
108
- return newItems;
140
+ updateImageState((imageState, i) => {
141
+ return R.includes(i, filteredIndexes)
142
+ ? { ...imageState, isNewUrlIncoming: true }
143
+ : imageState;
109
144
  });
110
- });
111
145
 
112
- const itemLoadedHandler = useCallback((sortKey: number) => {
113
- const itemState: ComicViewerItemState | undefined = R.find((state: ComicViewerItemState) => state.sortKey === sortKey)(itemStates.current);
146
+ try {
147
+ const urls = await getUrlByIndex(filteredIndexes);
114
148
 
115
- if (R.isNil(itemState)) {
116
- 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
+ });
117
171
  }
172
+ };
118
173
 
119
- itemState.state = STATE.LOADED;
120
- itemState.error = undefined;
121
- }, [itemStates]);
174
+ const loadMaybeLoadableItems = async () => {
175
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
176
+ const affectedIndexes = R.range(startIndex, endIndex);
122
177
 
123
- const itemErrorHandler = useCallback((errorInfo: ErrorInfo) => {
124
- const { sortKey, count } = errorInfo;
178
+ await loadUrlByIndex(affectedIndexes);
179
+ };
125
180
 
126
- if (count >= errorRetryCount) {
127
- return;
128
- }
181
+ const loadItemsDebounce = useDebounce(loadMaybeLoadableItems, debounceMillis);
129
182
 
130
- 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);
131
185
 
132
- 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;
133
188
 
134
- if (R.isNil(itemState)) {
189
+ if (R.isNil(firstViewableIndex) || R.isNil(lastViewableItemIndex)) {
135
190
  return;
136
191
  }
137
192
 
138
- itemState.state = STATE.FAIL;
139
- 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);
140
195
 
141
- const handleError = () => {
142
- const errorsArray = Array.from(errors.current.entries());
143
- const errorsInfo = R.map(([key, value]: [number, ErrorInfo]) => value)(errorsArray);
196
+ maybeLoadableItemsIndexRange.current = [startIndex, endIndex + 1];
144
197
 
145
- onError && onError([...errorsInfo]);
146
- errors.current.clear();
147
- };
198
+ loadItemsDebounce();
199
+ });
148
200
 
149
- if (debounceTimeOut.current) {
150
- clearTimeout(debounceTimeOut.current);
151
- }
201
+ const renderItem: ListRenderItem<ItemState> = useCallback(({ item, index }) => {
202
+ const onError = () => {
203
+ updateImageState((imageState, i) => {
204
+ const urlState = imageState.urlState;
152
205
 
153
- if (errors.current.size === pageUnit) {
154
- handleError();
155
- } else {
156
- debounceTimeOut.current = setTimeout(handleError, errorDebounceMillis);
157
- }
158
- }, [errors.current, itemStates]);
159
-
160
- const renderItem: ListRenderItem<ComicViewerItemProps<T>> = useCallback(({ item }) => {
161
- const itemState: ComicViewerItemState | undefined = R.find((state: ComicViewerItemState) => state.sortKey === item.sortKey)(itemStates.current);
162
-
163
- const props = {
164
- ...item,
165
- itemState,
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
- }, [resourceString, 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
+ }
181
223
 
182
- if (currentData
183
- && itemState
184
- && itemState.state !== STATE.LOADED
185
- && (currentData.url !== prevItem.url)) {
224
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
225
+ if (index >= startIndex || index < endIndex) {
226
+ loadItemsDebounce();
227
+ }
228
+ };
186
229
 
187
- itemState.state = STATE.URL_LOADED;
230
+ const onReloadPress = () => {
231
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
232
+ if (index >= startIndex || index < endIndex) {
233
+ loadUrlByIndex([index]);
234
+ }
235
+ };
188
236
 
237
+ const onLoad = () => {
238
+ updateImageState((imageState, i) => {
239
+ const urlState = imageState.urlState;
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
- }, [resourceString]);
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
  );
@@ -2,98 +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;
7
+ height: number;
78
8
  }
79
9
 
80
- export default interface ComicViewerProps<T> extends ComponentProps <{
81
- /**
82
- * Data for render.
83
- */
84
- data: ComicViewerItemData<T>[];
85
-
10
+ export default interface ComicViewerProps extends ComponentProps <{
86
11
  /**
87
12
  * Delay Time to call the error handler.
88
13
  * @default 500
89
14
  */
90
- errorDebounceMillis?: number;
15
+ debounceMillis?: number;
91
16
 
92
17
  /**
93
- * How many times retry onError when same item error occur
18
+ * How many times handle onError directly when item error occur
94
19
  * @default 3
95
20
  */
96
- errorRetryCount?: number;
21
+ autoHandleErrorCount?: number;
97
22
 
98
23
  /**
99
24
  * How many items to render in the initial batch.
@@ -115,32 +40,25 @@ export default interface ComicViewerProps<T> extends ComponentProps <{
115
40
  itemVisiblePercentThreshold?: number;
116
41
 
117
42
  /**
118
- * Comic viewer width.
43
+ * Dimensions of each Image considering viewport.
119
44
  */
120
- viewerWidth: number;
45
+ intrinsicDimensions: Array<Dimension>;
121
46
 
122
47
  /**
123
- * The value for FlatList windowSize.
124
- * @default 3
48
+ * TBD
125
49
  */
126
- windowSize?: number;
50
+ viewportWidth: number;
127
51
 
128
52
  /**
129
- * How many images in one page.
130
- */
131
- pageUnit: number;
132
-
133
- /**
134
- * Method for getting next page contents.
135
- * @param sortKey
53
+ * The value for FlatList windowSize.
54
+ * @default 3
136
55
  */
137
- getNextPage?: (sortKey: number) => void;
56
+ windowSize?: number;
138
57
 
139
58
  /**
140
- * Handling all viewerItem errors at once.
141
- * @param errors Array of ViewerItems errorInfo.
59
+ * TBD
142
60
  */
143
- onError?: (errors: ErrorInfo[]) => void;
61
+ getUrlByIndex: (indexes: Array<number>) => Promise<Map<number, string> | undefined> ;
144
62
 
145
63
  /**
146
64
  * Handle scroll event.
@@ -157,4 +75,4 @@ export default interface ComicViewerProps<T> extends ComponentProps <{
157
75
  * Component for comic viewer footer.
158
76
  */
159
77
  ListFooterComponent?: React.ReactElement;
160
- }> {}
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
+ };