@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.
- package/build/commonjs/ComicViewer/ComicViewer.js +197 -143
- package/build/commonjs/ComicViewer/ComicViewer.js.map +1 -1
- package/build/commonjs/ComicViewer/ComicViewerProps.js +0 -12
- package/build/commonjs/ComicViewer/ComicViewerProps.js.map +1 -1
- package/build/commonjs/ComicViewer/ReloadButton.js +43 -0
- package/build/commonjs/ComicViewer/ReloadButton.js.map +1 -0
- package/build/commonjs/ComicViewer/ViewerItem.js +37 -171
- package/build/commonjs/ComicViewer/ViewerItem.js.map +1 -1
- package/build/commonjs/ComicViewer/checkered-loading.jpg +0 -0
- package/build/commonjs/ComicViewer/index.js.map +1 -1
- package/build/module/ComicViewer/ComicViewer.js +196 -143
- package/build/module/ComicViewer/ComicViewer.js.map +1 -1
- package/build/module/ComicViewer/ComicViewerProps.js +1 -6
- package/build/module/ComicViewer/ComicViewerProps.js.map +1 -1
- package/build/module/ComicViewer/ReloadButton.js +29 -0
- package/build/module/ComicViewer/ReloadButton.js.map +1 -0
- package/build/module/ComicViewer/ViewerItem.js +39 -173
- package/build/module/ComicViewer/ViewerItem.js.map +1 -1
- package/build/module/ComicViewer/checkered-loading.jpg +0 -0
- package/build/module/ComicViewer/index.js.map +1 -1
- package/build/typescript/ComicViewer/ComicViewer.d.ts +1 -1
- package/build/typescript/ComicViewer/ComicViewerProps.d.ts +14 -89
- package/build/typescript/ComicViewer/ReloadButton.d.ts +6 -0
- package/build/typescript/ComicViewer/ViewerItem.d.ts +37 -7
- package/build/typescript/ComicViewer/index.d.ts +2 -2
- package/package.json +2 -2
- package/src/ComicViewer/ComicViewer.tsx +212 -157
- package/src/ComicViewer/ComicViewerProps.ts +14 -106
- package/src/ComicViewer/ReloadButton.tsx +33 -0
- package/src/ComicViewer/ViewerItem.tsx +81 -184
- package/src/ComicViewer/checkered-loading.jpg +0 -0
- package/src/ComicViewer/index.ts +2 -2
- package/build/commonjs/ComicViewer/ComicViewerItemProps.js +0 -6
- package/build/commonjs/ComicViewer/ComicViewerItemProps.js.map +0 -1
- package/build/module/ComicViewer/ComicViewerItemProps.js +0 -2
- package/build/module/ComicViewer/ComicViewerItemProps.js.map +0 -1
- package/build/typescript/ComicViewer/ComicViewerItemProps.d.ts +0 -34
- 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
|
-
|
|
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 = (
|
|
17
|
-
|
|
18
|
-
const keyExtractor = <T, >(item:
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
responseTimestamp,
|
|
55
|
+
debounceMillis = 100,
|
|
56
|
+
autoHandleErrorCount = 3,
|
|
57
|
+
getUrlByIndex,
|
|
26
58
|
initialNumToRender = 1,
|
|
27
59
|
initialScrollPercentage = 0,
|
|
28
60
|
itemVisiblePercentThreshold = 0,
|
|
29
|
-
|
|
30
|
-
onScroll,
|
|
61
|
+
intrinsicDimensions,
|
|
31
62
|
onItemPress,
|
|
32
|
-
|
|
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
|
|
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
|
|
85
|
+
const [itemStates, setItemStates] = useState<Array<ItemState>>(() => {
|
|
86
|
+
return mapImageStatesToItemStates(imageStatesRef.current);
|
|
87
|
+
});
|
|
45
88
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
101
|
+
const getItemLayout = (data: any, index: number) => ({
|
|
102
|
+
index,
|
|
103
|
+
length: itemHeights[index],
|
|
104
|
+
offset: itemOffsets[index],
|
|
105
|
+
});
|
|
60
106
|
|
|
61
|
-
|
|
107
|
+
return {
|
|
108
|
+
totalHeight,
|
|
109
|
+
getItemLayout,
|
|
110
|
+
};
|
|
111
|
+
}, [renderedDimensions]);
|
|
62
112
|
|
|
63
|
-
const
|
|
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
|
|
71
|
-
const
|
|
119
|
+
const updateImageState = (updateFunction: (prev: ImageState, index: number) => ImageState) => {
|
|
120
|
+
const prevImageStates = imageStatesRef.current;
|
|
121
|
+
const newImageStates = prevImageStates.map(updateFunction);
|
|
72
122
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
140
|
+
updateImageState((imageState, i) => {
|
|
141
|
+
return R.includes(i, filteredIndexes)
|
|
142
|
+
? { ...imageState, isNewUrlIncoming: true }
|
|
143
|
+
: imageState;
|
|
108
144
|
});
|
|
109
|
-
});
|
|
110
145
|
|
|
111
|
-
|
|
112
|
-
|
|
146
|
+
try {
|
|
147
|
+
const urls = await getUrlByIndex(filteredIndexes);
|
|
113
148
|
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
174
|
+
const loadMaybeLoadableItems = async () => {
|
|
175
|
+
const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
|
|
176
|
+
const affectedIndexes = R.range(startIndex, endIndex);
|
|
121
177
|
|
|
122
|
-
|
|
123
|
-
|
|
178
|
+
await loadUrlByIndex(affectedIndexes);
|
|
179
|
+
};
|
|
124
180
|
|
|
125
|
-
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
181
|
+
const loadItemsDebounce = useDebounce(loadMaybeLoadableItems, debounceMillis);
|
|
128
182
|
|
|
129
|
-
|
|
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
|
|
186
|
+
const firstViewableIndex = R.head(orderedViewableItems)?.index;
|
|
187
|
+
const lastViewableItemIndex = R.last(orderedViewableItems)?.index;
|
|
132
188
|
|
|
133
|
-
if (R.isNil(
|
|
189
|
+
if (R.isNil(firstViewableIndex) || R.isNil(lastViewableItemIndex)) {
|
|
134
190
|
return;
|
|
135
191
|
}
|
|
136
192
|
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
};
|
|
198
|
+
loadItemsDebounce();
|
|
199
|
+
});
|
|
147
200
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
201
|
+
const renderItem: ListRenderItem<ItemState> = useCallback(({ item, index }) => {
|
|
202
|
+
const onError = () => {
|
|
203
|
+
updateImageState((imageState, i) => {
|
|
204
|
+
const urlState = imageState.urlState;
|
|
151
205
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
174
|
-
|
|
217
|
+
return imageState;
|
|
218
|
+
});
|
|
175
219
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
230
|
+
const onReloadPress = () => {
|
|
231
|
+
const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
|
|
232
|
+
if (index >= startIndex || index < endIndex) {
|
|
233
|
+
loadUrlByIndex([index]);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
186
236
|
|
|
187
|
-
|
|
237
|
+
const onLoad = () => {
|
|
238
|
+
updateImageState((imageState, i) => {
|
|
239
|
+
const urlState = imageState.urlState;
|
|
188
240
|
|
|
241
|
+
if (i === index && urlState !== undefined) {
|
|
189
242
|
return {
|
|
190
|
-
...
|
|
191
|
-
|
|
192
|
-
|
|
243
|
+
...imageState,
|
|
244
|
+
urlState: {
|
|
245
|
+
...urlState,
|
|
246
|
+
validity: 'valid',
|
|
247
|
+
},
|
|
193
248
|
};
|
|
194
249
|
}
|
|
195
250
|
|
|
196
|
-
return
|
|
197
|
-
})
|
|
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
|
-
|
|
210
|
-
|
|
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
|
-
}, [
|
|
275
|
+
}, []);
|
|
220
276
|
|
|
221
277
|
return (
|
|
222
278
|
<FlatList
|
|
223
|
-
data={
|
|
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
|
|
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
|
|
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
|
-
|
|
15
|
+
debounceMillis?: number;
|
|
96
16
|
|
|
97
17
|
/**
|
|
98
|
-
* How many times
|
|
18
|
+
* How many times handle onError directly when item error occur
|
|
99
19
|
* @default 3
|
|
100
20
|
*/
|
|
101
|
-
|
|
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
|
-
*
|
|
42
|
+
/**
|
|
43
|
+
* Dimensions of each Image considering viewport.
|
|
124
44
|
*/
|
|
125
|
-
|
|
45
|
+
intrinsicDimensions: Array<Dimension>;
|
|
126
46
|
|
|
127
47
|
/**
|
|
128
|
-
*
|
|
48
|
+
* TBD
|
|
129
49
|
*/
|
|
130
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
+
};
|