@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.
- package/build/commonjs/ComicViewer/ComicViewer.js +197 -142
- 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 -152
- 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 -142
- 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 -154
- 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 +15 -82
- 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 +210 -155
- package/src/ComicViewer/ComicViewerProps.ts +16 -98
- package/src/ComicViewer/ReloadButton.tsx +33 -0
- package/src/ComicViewer/ViewerItem.tsx +81 -169
- 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,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
|
-
|
|
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
|
-
|
|
55
|
+
debounceMillis = 100,
|
|
56
|
+
autoHandleErrorCount = 3,
|
|
57
|
+
getUrlByIndex,
|
|
25
58
|
initialNumToRender = 1,
|
|
26
59
|
initialScrollPercentage = 0,
|
|
27
60
|
itemVisiblePercentThreshold = 0,
|
|
28
|
-
|
|
29
|
-
onScroll,
|
|
61
|
+
intrinsicDimensions,
|
|
30
62
|
onItemPress,
|
|
31
|
-
|
|
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
|
|
70
|
+
const debounceTimeout = useRef<NodeJS.Timeout | null>(null);
|
|
71
|
+
|
|
72
|
+
const maybeLoadableItemsIndexRange = useRef<[number, number]>([-1, 0]);
|
|
42
73
|
|
|
43
|
-
const
|
|
74
|
+
const actualImageWidth = Math.min(viewportWidth, MAXIMUM_WIDTH);
|
|
44
75
|
|
|
45
|
-
const
|
|
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
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
101
|
+
const getItemLayout = (data: any, index: number) => ({
|
|
102
|
+
index,
|
|
103
|
+
length: itemHeights[index],
|
|
104
|
+
offset: itemOffsets[index],
|
|
105
|
+
});
|
|
61
106
|
|
|
62
|
-
|
|
107
|
+
return {
|
|
108
|
+
totalHeight,
|
|
109
|
+
getItemLayout,
|
|
110
|
+
};
|
|
111
|
+
}, [renderedDimensions]);
|
|
63
112
|
|
|
64
|
-
const
|
|
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
|
|
72
|
-
const
|
|
119
|
+
const updateImageState = (updateFunction: (prev: ImageState, index: number) => ImageState) => {
|
|
120
|
+
const prevImageStates = imageStatesRef.current;
|
|
121
|
+
const newImageStates = prevImageStates.map(updateFunction);
|
|
73
122
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
100
|
-
|
|
132
|
+
const loadUrlByIndex = async (indexes: number[]) => {
|
|
133
|
+
const filteredIndexes = R.filter(index => {
|
|
134
|
+
const state = imageStatesRef.current[index];
|
|
101
135
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
140
|
+
updateImageState((imageState, i) => {
|
|
141
|
+
return R.includes(i, filteredIndexes)
|
|
142
|
+
? { ...imageState, isNewUrlIncoming: true }
|
|
143
|
+
: imageState;
|
|
109
144
|
});
|
|
110
|
-
});
|
|
111
145
|
|
|
112
|
-
|
|
113
|
-
|
|
146
|
+
try {
|
|
147
|
+
const urls = await getUrlByIndex(filteredIndexes);
|
|
114
148
|
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
174
|
+
const loadMaybeLoadableItems = async () => {
|
|
175
|
+
const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
|
|
176
|
+
const affectedIndexes = R.range(startIndex, endIndex);
|
|
122
177
|
|
|
123
|
-
|
|
124
|
-
|
|
178
|
+
await loadUrlByIndex(affectedIndexes);
|
|
179
|
+
};
|
|
125
180
|
|
|
126
|
-
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
181
|
+
const loadItemsDebounce = useDebounce(loadMaybeLoadableItems, debounceMillis);
|
|
129
182
|
|
|
130
|
-
|
|
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
|
|
186
|
+
const firstViewableIndex = R.head(orderedViewableItems)?.index;
|
|
187
|
+
const lastViewableItemIndex = R.last(orderedViewableItems)?.index;
|
|
133
188
|
|
|
134
|
-
if (R.isNil(
|
|
189
|
+
if (R.isNil(firstViewableIndex) || R.isNil(lastViewableItemIndex)) {
|
|
135
190
|
return;
|
|
136
191
|
}
|
|
137
192
|
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
};
|
|
198
|
+
loadItemsDebounce();
|
|
199
|
+
});
|
|
148
200
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
201
|
+
const renderItem: ListRenderItem<ItemState> = useCallback(({ item, index }) => {
|
|
202
|
+
const onError = () => {
|
|
203
|
+
updateImageState((imageState, i) => {
|
|
204
|
+
const urlState = imageState.urlState;
|
|
152
205
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
174
|
-
|
|
217
|
+
return imageState;
|
|
218
|
+
});
|
|
175
219
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
224
|
+
const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
|
|
225
|
+
if (index >= startIndex || index < endIndex) {
|
|
226
|
+
loadItemsDebounce();
|
|
227
|
+
}
|
|
228
|
+
};
|
|
186
229
|
|
|
187
|
-
|
|
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
|
-
...
|
|
191
|
-
|
|
192
|
-
|
|
243
|
+
...imageState,
|
|
244
|
+
urlState: {
|
|
245
|
+
...urlState,
|
|
246
|
+
validity: 'valid',
|
|
247
|
+
},
|
|
193
248
|
};
|
|
194
249
|
}
|
|
195
250
|
|
|
196
|
-
return
|
|
197
|
-
})
|
|
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
|
-
|
|
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
|
);
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
15
|
+
debounceMillis?: number;
|
|
91
16
|
|
|
92
17
|
/**
|
|
93
|
-
* How many times
|
|
18
|
+
* How many times handle onError directly when item error occur
|
|
94
19
|
* @default 3
|
|
95
20
|
*/
|
|
96
|
-
|
|
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
|
-
*
|
|
43
|
+
* Dimensions of each Image considering viewport.
|
|
119
44
|
*/
|
|
120
|
-
|
|
45
|
+
intrinsicDimensions: Array<Dimension>;
|
|
121
46
|
|
|
122
47
|
/**
|
|
123
|
-
*
|
|
124
|
-
* @default 3
|
|
48
|
+
* TBD
|
|
125
49
|
*/
|
|
126
|
-
|
|
50
|
+
viewportWidth: number;
|
|
127
51
|
|
|
128
52
|
/**
|
|
129
|
-
*
|
|
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
|
-
|
|
56
|
+
windowSize?: number;
|
|
138
57
|
|
|
139
58
|
/**
|
|
140
|
-
*
|
|
141
|
-
* @param errors Array of ViewerItems errorInfo.
|
|
59
|
+
* TBD
|
|
142
60
|
*/
|
|
143
|
-
|
|
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
|
+
};
|