@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
@@ -3,188 +3,73 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = void 0;
6
+ exports.default = ViewerItem;
7
7
 
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
 
10
10
  var _reactNative = require("react-native");
11
11
 
12
- var R = _interopRequireWildcard(require("ramda"));
13
-
14
12
  var _core = require("@fountain-ui/core");
15
13
 
16
- var _icons = require("@fountain-ui/icons");
14
+ var _ReloadButton = _interopRequireDefault(require("./ReloadButton"));
17
15
 
18
- var _ComicViewerProps = require("./ComicViewerProps");
16
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
17
 
20
18
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
21
19
 
22
20
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
23
21
 
24
- const useStyles = function () {
25
- const theme = (0, _core.useTheme)();
26
- return {
27
- root: {
28
- display: 'flex',
29
- flexDirection: 'row',
30
- justifyContent: 'center'
31
- },
32
- init: {
33
- backgroundColor: theme.palette.paper.grey
34
- },
35
- failed: {
36
- backgroundColor: theme.palette.paper.grey
37
- },
38
- reload: {
39
- backgroundColor: theme.palette.paper.grey,
40
- display: 'flex',
41
- alignItems: 'center'
42
- }
43
- };
44
- };
45
-
46
- function ViewerItem(_ref) {
47
- var _itemState$error;
48
-
49
- let {
50
- props
51
- } = _ref;
22
+ function ViewerItem(props) {
52
23
  const {
53
- expiresAt,
54
- errorRetryCount = 3,
55
24
  height,
56
- itemState,
57
- isViewable,
58
- sortKey,
59
25
  url,
60
26
  width,
61
- getNextPage,
62
27
  onError,
63
- onLoaded,
64
- onItemPress
28
+ onLoad,
29
+ onPress,
30
+ onReloadPress,
31
+ reloadButtonVisible = false
65
32
  } = props;
66
- const [isLoaded, setIsLoaded] = (0, _react.useState)(false);
67
- const styles = useStyles();
68
- const errorCount = (0, _react.useRef)(R.defaultTo(0)(itemState === null || itemState === void 0 ? void 0 : (_itemState$error = itemState.error) === null || _itemState$error === void 0 ? void 0 : _itemState$error.count));
69
- const onLoad = (0, _react.useCallback)(() => {
70
- errorCount.current = 0;
71
- setIsLoaded(true);
72
- onLoaded && onLoaded(sortKey);
73
- }, [sortKey]);
74
- const handleError = (0, _react.useCallback)(() => {
75
- errorCount.current = errorCount.current + 1;
76
- const now = new Date();
77
- const utcNow = now.getTime() + now.getTimezoneOffset() * 60 * 1000;
78
- const expired = new Date(expiresAt).getTime() <= utcNow;
79
- onError && onError({
80
- sortKey,
81
- count: errorCount.current,
82
- expired
83
- });
84
- }, [errorCount.current]);
85
- const onReloadPress = (0, _react.useCallback)(() => {
86
- errorCount.current = 1;
87
- onError && onError({
88
- sortKey,
89
- count: errorCount.current,
90
- expired: false
91
- });
92
- }, [sortKey]);
93
- const viewStyle = {
94
- width: '100%',
95
- height,
96
- ..._reactNative.Platform.select({
97
- web: {
98
- 'cursor': 'default'
99
- }
100
- })
101
- };
102
- const imageStyle = {
103
- width,
104
- height
105
- };
106
- const Placeholder = (0, _react.useCallback)(props => {
107
- const {
108
- children,
109
- failed
110
- } = props;
111
-
112
- if (!(isViewable || isLoaded) || failed || (itemState === null || itemState === void 0 ? void 0 : itemState.state) === _ComicViewerProps.STATE.INIT) {
113
- return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
114
- style: [viewStyle, styles.init]
115
- });
116
- }
117
-
118
- if (errorCount.current >= errorRetryCount) {
119
- return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
120
- style: [viewStyle, styles.reload]
121
- }, /*#__PURE__*/_react.default.createElement(_core.Spacer, {
122
- size: 20
123
- }), /*#__PURE__*/_react.default.createElement(_core.IconButton, {
124
- children: /*#__PURE__*/_react.default.createElement(_icons.Restart, {
125
- fill: '#ffffff'
126
- }),
127
- style: {
128
- width: 48,
129
- height: 48,
130
- borderRadius: 24,
131
- color: '#ffffff',
132
- backgroundColor: '#767676'
133
- },
134
- onPress: onReloadPress
135
- }));
136
- }
137
-
138
- return children;
139
- }, [isViewable, isLoaded, errorCount.current, url, onItemPress]);
140
- (0, _react.useEffect)(() => {
141
- if ((itemState === null || itemState === void 0 ? void 0 : itemState.state) === _ComicViewerProps.STATE.INIT) {
142
- getNextPage === null || getNextPage === void 0 ? void 0 : getNextPage(sortKey);
33
+ const styles = {
34
+ view: {
35
+ height,
36
+ width: '100%'
37
+ },
38
+ image: {
39
+ height,
40
+ width
143
41
  }
144
- }, []);
145
- return /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, {
146
- activeOpacity: 1,
147
- onPress: onItemPress
42
+ };
43
+ const error = reloadButtonVisible ? /*#__PURE__*/_react.default.createElement(_ReloadButton.default, {
44
+ onPress: onReloadPress
45
+ }) : null;
46
+ const placeholder = (0, _react.useMemo)(() => /*#__PURE__*/_react.default.createElement(_reactNative.Image, {
47
+ source: require('./checkered-loading.jpg'),
48
+ resizeMode: "repeat",
49
+ style: styles.image
50
+ }), [width]);
51
+ return /*#__PURE__*/_react.default.createElement(_reactNative.TouchableWithoutFeedback, {
52
+ onPress: onPress
148
53
  }, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
149
- style: [styles.root, viewStyle]
54
+ style: styles.view
150
55
  }, /*#__PURE__*/_react.default.createElement(_core.Image, {
151
- disableOutline: true,
152
- key: sortKey,
153
- disableLongClick: true,
154
56
  disableDrag: true,
57
+ disableLongClick: true,
58
+ disableOutline: true,
59
+ error: error,
60
+ onError: onError,
155
61
  onLoad: onLoad,
156
- onError: handleError,
157
62
  loading: 'eager',
63
+ placeholder: placeholder,
158
64
  source: {
159
65
  uri: url
160
66
  },
161
- style: imageStyle,
162
67
  square: true,
163
- Placeholder: Placeholder
68
+ style: (0, _core.css)([{
69
+ alignSelf: 'center'
70
+ }, styles.image])
164
71
  })));
165
72
  }
166
73
 
167
- var _default = /*#__PURE__*/_react.default.memo(ViewerItem, (prevProps, nextProps) => {
168
- var _prevProps$props$item, _nextProps$props$item;
169
-
170
- if (prevProps.props.isViewable !== nextProps.props.isViewable) {
171
- return false;
172
- }
173
-
174
- if (prevProps.props.url !== nextProps.props.url) {
175
- return false;
176
- }
177
-
178
- if (prevProps.props.width !== nextProps.props.width) {
179
- return false;
180
- }
181
-
182
- if (((_prevProps$props$item = prevProps.props.itemState) === null || _prevProps$props$item === void 0 ? void 0 : _prevProps$props$item.state) !== ((_nextProps$props$item = nextProps.props.itemState) === null || _nextProps$props$item === void 0 ? void 0 : _nextProps$props$item.state)) {
183
- return false;
184
- }
185
-
186
- return true;
187
- });
188
-
189
- exports.default = _default;
74
+ ;
190
75
  //# sourceMappingURL=ViewerItem.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["useStyles","theme","useTheme","root","display","flexDirection","justifyContent","init","backgroundColor","palette","paper","grey","failed","reload","alignItems","ViewerItem","props","expiresAt","errorRetryCount","height","itemState","isViewable","sortKey","url","width","getNextPage","onError","onLoaded","onItemPress","isLoaded","setIsLoaded","useState","styles","errorCount","useRef","R","defaultTo","error","count","onLoad","useCallback","current","handleError","now","Date","utcNow","getTime","getTimezoneOffset","expired","onReloadPress","viewStyle","Platform","select","web","imageStyle","Placeholder","children","state","STATE","INIT","borderRadius","color","useEffect","uri","React","memo","prevProps","nextProps"],"sources":["ViewerItem.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { Platform, TouchableOpacity, View } from 'react-native';\nimport * as R from 'ramda';\nimport type { PlaceholderProps } from '@fountain-ui/core';\nimport { IconButton, Image, Spacer, useTheme } from '@fountain-ui/core';\nimport { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';\nimport { Restart } from '@fountain-ui/icons';\nimport ComicViewerItemProps from './ComicViewerItemProps';\nimport { STATE } from './ComicViewerProps';\n\ntype PlaceholderStyles = NamedStylesStringUnion<'init' | 'failed' | 'reload' | 'root'>;\n\nconst useStyles: UseStyles<PlaceholderStyles> = function (): PlaceholderStyles {\n const theme = useTheme();\n\n return {\n root: {\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'center',\n },\n init: {\n backgroundColor: theme.palette.paper.grey,\n },\n failed: {\n backgroundColor: theme.palette.paper.grey,\n },\n reload: {\n backgroundColor: theme.palette.paper.grey,\n display: 'flex',\n alignItems: 'center',\n },\n };\n};\n\nfunction ViewerItem<T>({ props }: { props: ComicViewerItemProps<T> }) {\n const {\n expiresAt,\n errorRetryCount = 3,\n height,\n itemState,\n isViewable,\n sortKey,\n url,\n width,\n getNextPage,\n onError,\n onLoaded,\n onItemPress,\n } = props;\n\n const [isLoaded, setIsLoaded] = useState(false);\n\n const styles = useStyles();\n\n const errorCount = useRef<number>(R.defaultTo(0)(itemState?.error?.count));\n\n const onLoad = useCallback(() => {\n errorCount.current = 0;\n\n setIsLoaded(true);\n\n onLoaded && onLoaded(sortKey);\n }, [sortKey]);\n\n const handleError = useCallback(() => {\n errorCount.current = errorCount.current + 1;\n\n const now = new Date();\n const utcNow = now.getTime() + (now.getTimezoneOffset() * 60 * 1000);\n const expired = new Date(expiresAt).getTime() <= utcNow;\n\n onError && onError({\n sortKey,\n count: errorCount.current,\n expired,\n });\n }, [errorCount.current]);\n\n const onReloadPress = useCallback(() => {\n errorCount.current = 1;\n\n onError && onError({\n sortKey,\n count: errorCount.current,\n expired: false,\n });\n }, [sortKey]);\n\n const viewStyle = {\n width: '100%',\n height,\n ...Platform.select({\n web: { 'cursor': 'default' },\n }),\n };\n\n const imageStyle = { width, height };\n\n const Placeholder = useCallback((props: PlaceholderProps) => {\n const { children, failed } = props;\n\n if (!(isViewable || isLoaded)\n || failed\n || itemState?.state === STATE.INIT\n ) {\n return <View style={[\n viewStyle,\n styles.init,\n ]}/>;\n }\n\n if (errorCount.current >= errorRetryCount) {\n return <View style={[\n viewStyle,\n styles.reload,\n ]}>\n <Spacer size={20}/>\n\n <IconButton\n children={<Restart fill={'#ffffff'}/>}\n style={{\n width: 48,\n height: 48,\n borderRadius: 24,\n color: '#ffffff',\n backgroundColor: '#767676',\n }}\n onPress={onReloadPress}\n />\n </View>;\n }\n\n return children;\n }, [isViewable, isLoaded, errorCount.current, url, onItemPress]);\n\n useEffect(() => {\n if (itemState?.state === STATE.INIT) {\n getNextPage?.(sortKey);\n }\n }, []);\n\n return (\n <TouchableOpacity\n activeOpacity={1}\n onPress={onItemPress}\n >\n <View\n style={[\n styles.root,\n viewStyle,\n ]}\n >\n <Image\n disableOutline={true}\n key={sortKey}\n disableLongClick={true}\n disableDrag={true}\n onLoad={onLoad}\n onError={handleError}\n loading={'eager'}\n source={{ uri: url }}\n style={imageStyle}\n square={true}\n Placeholder={Placeholder}\n />\n </View>\n </TouchableOpacity>\n );\n}\n\nexport default React.memo(ViewerItem, (prevProps, nextProps) => {\n if (prevProps.props.isViewable !== nextProps.props.isViewable) {\n return false;\n }\n\n if (prevProps.props.url !== nextProps.props.url) {\n return false;\n }\n\n if (prevProps.props.width !== nextProps.props.width) {\n return false;\n }\n\n if (prevProps.props.itemState?.state !== nextProps.props.itemState?.state) {\n return false;\n }\n\n return true;\n});\n"],"mappings":"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"}
1
+ {"version":3,"names":["ViewerItem","props","height","url","width","onError","onLoad","onPress","onReloadPress","reloadButtonVisible","styles","view","image","error","placeholder","useMemo","require","uri","css","alignSelf"],"sources":["ViewerItem.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\nimport { Image, TouchableWithoutFeedback, View } from 'react-native';\nimport { css, Image as FuiImage, ImageProps } from '@fountain-ui/core';\nimport ReloadButton from './ReloadButton';\n\nexport interface ViewerItemProps {\n /**\n * Image width.\n */\n width: number;\n\n /**\n * Image height.\n */\n height: number;\n\n /**\n * Image sourceUrl for displaying.\n */\n url?: string;\n\n /**\n * Error handler.\n */\n onError?: ImageProps['onError'];\n\n /**\n * Load handler.\n */\n onLoad?: ImageProps['onLoad'];\n\n /**\n * Handle Reload button press event.\n */\n onReloadPress?: ImageProps['onError'];\n\n /**\n * Handle item press event.\n */\n onPress?: () => void;\n\n /**\n * If true, reload button visible.\n * @default false\n */\n reloadButtonVisible?: boolean;\n}\n\nexport default function ViewerItem(props: ViewerItemProps) {\n const {\n height,\n url,\n width,\n onError,\n onLoad,\n onPress,\n onReloadPress,\n reloadButtonVisible = false,\n } = props;\n\n const styles = {\n view: {\n height,\n width: '100%',\n },\n image: {\n height,\n width,\n },\n };\n\n const error = reloadButtonVisible ? <ReloadButton onPress={onReloadPress}/> : null;\n\n const placeholder = useMemo(() => <Image\n source={require('./checkered-loading.jpg')}\n resizeMode=\"repeat\"\n style={styles.image}\n />, [width]);\n\n return (\n <TouchableWithoutFeedback onPress={onPress}>\n <View style={styles.view}>\n <FuiImage\n disableDrag={true}\n disableLongClick={true}\n disableOutline={true}\n error={error}\n onError={onError}\n onLoad={onLoad}\n loading={'eager'}\n placeholder={placeholder}\n source={{ uri: url }}\n square={true}\n style={css([\n { alignSelf: 'center' },\n styles.image,\n ])}\n />\n </View>\n </TouchableWithoutFeedback>\n );\n};\n"],"mappings":";;;;;;;AAAA;;AACA;;AACA;;AACA;;;;;;;;AA6Ce,SAASA,UAAT,CAAoBC,KAApB,EAA4C;EACvD,MAAM;IACFC,MADE;IAEFC,GAFE;IAGFC,KAHE;IAIFC,OAJE;IAKFC,MALE;IAMFC,OANE;IAOFC,aAPE;IAQFC,mBAAmB,GAAG;EARpB,IASFR,KATJ;EAWA,MAAMS,MAAM,GAAG;IACXC,IAAI,EAAE;MACFT,MADE;MAEFE,KAAK,EAAE;IAFL,CADK;IAKXQ,KAAK,EAAE;MACHV,MADG;MAEHE;IAFG;EALI,CAAf;EAWA,MAAMS,KAAK,GAAGJ,mBAAmB,gBAAG,6BAAC,qBAAD;IAAc,OAAO,EAAED;EAAvB,EAAH,GAA6C,IAA9E;EAEA,MAAMM,WAAW,GAAG,IAAAC,cAAA,EAAQ,mBAAM,6BAAC,kBAAD;IAC9B,MAAM,EAAEC,OAAO,CAAC,yBAAD,CADe;IAE9B,UAAU,EAAC,QAFmB;IAG9B,KAAK,EAAEN,MAAM,CAACE;EAHgB,EAAd,EAIhB,CAACR,KAAD,CAJgB,CAApB;EAMA,oBACI,6BAAC,qCAAD;IAA0B,OAAO,EAAEG;EAAnC,gBACI,6BAAC,iBAAD;IAAM,KAAK,EAAEG,MAAM,CAACC;EAApB,gBACI,6BAAC,WAAD;IACI,WAAW,EAAE,IADjB;IAEI,gBAAgB,EAAE,IAFtB;IAGI,cAAc,EAAE,IAHpB;IAII,KAAK,EAAEE,KAJX;IAKI,OAAO,EAAER,OALb;IAMI,MAAM,EAAEC,MANZ;IAOI,OAAO,EAAE,OAPb;IAQI,WAAW,EAAEQ,WARjB;IASI,MAAM,EAAE;MAAEG,GAAG,EAAEd;IAAP,CATZ;IAUI,MAAM,EAAE,IAVZ;IAWI,KAAK,EAAE,IAAAe,SAAA,EAAI,CACP;MAAEC,SAAS,EAAE;IAAb,CADO,EAEPT,MAAM,CAACE,KAFA,CAAJ;EAXX,EADJ,CADJ,CADJ;AAsBH;;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export { default } from './ComicViewer';\nexport type { ComicViewerItemData, default as ComicViewerProps, ErrorInfo } from './ComicViewerProps';\nexport type { default as ComicViewerItemProps } from './ComicViewerItemProps';"],"mappings":";;;;;;;;;;;;AAAA"}
1
+ {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export { default } from './ComicViewer';\nexport type { Dimension, default as ComicViewerProps } from './ComicViewerProps';\nexport type { ViewerItemProps } from './ViewerItem';\n"],"mappings":";;;;;;;;;;;;AAAA"}
@@ -3,183 +3,238 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
3
3
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { FlatList } from 'react-native';
5
5
  import * as R from 'ramda';
6
- import { STATE } from './ComicViewerProps';
6
+ import { useDebounce } from '@fountain-ui/core';
7
7
  import ViewerItem from './ViewerItem';
8
8
 
9
- const getItemHeights = items => R.map(content => content.height)(items);
10
-
11
9
  const appender = (left, right) => [left + right, left + right];
12
10
 
13
- const getHeightAccum = itemHeights => R.mapAccum(appender, 0, itemHeights);
11
+ const getHeightAccum = heights => R.mapAccum(appender, 0, heights);
12
+
13
+ const keyExtractor = item => String(item.index);
14
+
15
+ const createInitialImageState = dimension => ({
16
+ isNewUrlIncoming: false,
17
+ totalErrorCount: 0,
18
+ dimension
19
+ });
14
20
 
15
- const keyExtractor = item => `${item.sortKey}`;
21
+ const mapImageStateToItemState = (index, imageState, autoHandleErrorCount) => {
22
+ var _imageState$urlState, _imageState$urlState2;
16
23
 
24
+ return {
25
+ index,
26
+ url: (_imageState$urlState = imageState.urlState) === null || _imageState$urlState === void 0 ? void 0 : _imageState$urlState.url,
27
+ reloadButtonVisible: ((_imageState$urlState2 = imageState.urlState) === null || _imageState$urlState2 === void 0 ? void 0 : _imageState$urlState2.validity) !== 'valid' && imageState.totalErrorCount >= autoHandleErrorCount,
28
+ dimension: imageState.dimension
29
+ };
30
+ };
31
+
32
+ const MAXIMUM_WIDTH = 720;
33
+ const NUMBER_OF_ADJACENT_ITEM = 5;
17
34
  export default function ComicViewer(props) {
18
35
  const {
19
- data,
20
- errorDebounceMillis = 500,
21
- errorRetryCount = 3,
36
+ debounceMillis = 100,
37
+ autoHandleErrorCount = 3,
38
+ getUrlByIndex,
22
39
  initialNumToRender = 1,
23
40
  initialScrollPercentage = 0,
24
41
  itemVisiblePercentThreshold = 0,
25
- onError,
26
- onScroll,
42
+ intrinsicDimensions,
27
43
  onItemPress,
28
- getNextPage,
29
- viewerWidth,
44
+ viewportWidth,
30
45
  windowSize = 3,
31
- pageUnit,
32
- ListFooterComponent,
33
46
  ...otherProps
34
47
  } = props;
35
48
  const flatListRef = useRef(null);
36
- const errors = useRef(new Map());
37
- const debounceTimeOut = useRef(null);
38
- const resourceString = R.toString(R.map(itemData => itemData.url)(data));
39
- const imageWidth = Math.min(viewerWidth, 720);
40
- const initialItems = R.map(itemData => ({ ...itemData,
41
- isViewable: false,
42
- width: imageWidth,
43
- height: itemData.height * imageWidth / itemData.width
44
- }))(data);
45
- const [items, setItems] = useState(initialItems);
46
- const initialItemState = R.map(itemData => ({
47
- sortKey: itemData.sortKey,
48
- state: R.isEmpty(itemData.expiresAt) ? STATE.INIT : STATE.URL_LOADED
49
- }))(data);
50
- const itemStates = useRef(initialItemState);
51
- const itemHeights = [...getItemHeights(items)];
52
- const itemHeightAccum = getHeightAccum(itemHeights);
49
+ const debounceTimeout = useRef(null);
50
+ const maybeLoadableItemsIndexRange = useRef([-1, 0]);
51
+ const actualImageWidth = Math.min(viewportWidth, MAXIMUM_WIDTH);
52
+ const initialImageStates = useMemo(() => R.map(createInitialImageState, intrinsicDimensions), []);
53
+ const imageStatesRef = useRef(initialImageStates);
54
+
55
+ const mapImageStatesToItemStates = imageStates => {
56
+ return imageStates.map((image, index) => mapImageStateToItemState(index, image, autoHandleErrorCount));
57
+ };
58
+
59
+ const [itemStates, setItemStates] = useState(() => {
60
+ return mapImageStatesToItemStates(imageStatesRef.current);
61
+ });
62
+ const renderedDimensions = useMemo(() => {
63
+ return R.map(intrinsicDimension => ({
64
+ width: actualImageWidth,
65
+ height: intrinsicDimension.height * actualImageWidth / intrinsicDimension.width
66
+ }), intrinsicDimensions);
67
+ }, [actualImageWidth]);
68
+ const layoutFromDimensions = useCallback(() => {
69
+ const itemHeights = R.map(dimension => dimension.height, renderedDimensions);
70
+ const [totalHeight, heightAccum] = getHeightAccum(itemHeights);
71
+ const itemOffsets = R.prepend(0, heightAccum);
72
+
73
+ const getItemLayout = (data, index) => ({
74
+ index,
75
+ length: itemHeights[index],
76
+ offset: itemOffsets[index]
77
+ });
78
+
79
+ return {
80
+ totalHeight,
81
+ getItemLayout
82
+ };
83
+ }, [renderedDimensions]);
84
+ const {
85
+ totalHeight,
86
+ getItemLayout
87
+ } = layoutFromDimensions();
53
88
  const viewabilityConfig = useMemo(() => ({
54
89
  itemVisiblePercentThreshold
55
90
  }), [itemVisiblePercentThreshold]);
56
- const getItemLayout = useCallback((data, index) => {
57
- const offsets = R.prepend(0, itemHeightAccum[1]);
58
- return {
59
- length: itemHeights[index],
60
- offset: offsets[index],
61
- index
62
- };
63
- }, [itemHeights]);
91
+
92
+ const updateImageState = updateFunction => {
93
+ const prevImageStates = imageStatesRef.current;
94
+ const newImageStates = prevImageStates.map(updateFunction);
95
+ imageStatesRef.current = newImageStates;
96
+ setItemStates(prevItemStates => {
97
+ const newItemStates = mapImageStatesToItemStates(newImageStates);
98
+ return R.equals(prevItemStates, newItemStates) ? prevItemStates : newItemStates;
99
+ });
100
+ };
101
+
102
+ const loadUrlByIndex = async indexes => {
103
+ const filteredIndexes = R.filter(index => {
104
+ var _state$urlState;
105
+
106
+ const state = imageStatesRef.current[index];
107
+ return R.isNil(state.urlState) || ((_state$urlState = state.urlState) === null || _state$urlState === void 0 ? void 0 : _state$urlState.validity) === 'invalid' && !state.isNewUrlIncoming;
108
+ }, indexes);
109
+ updateImageState((imageState, i) => {
110
+ return R.includes(i, filteredIndexes) ? { ...imageState,
111
+ isNewUrlIncoming: true
112
+ } : imageState;
113
+ });
114
+
115
+ try {
116
+ const urls = await getUrlByIndex(filteredIndexes);
117
+ updateImageState((imageState, i) => {
118
+ const newUrl = urls === null || urls === void 0 ? void 0 : urls.get(i);
119
+ const urlState = imageState.urlState;
120
+
121
+ if (newUrl !== undefined && (urlState === null || urlState === void 0 ? void 0 : urlState.validity) !== 'valid') {
122
+ return { ...imageState,
123
+ urlState: {
124
+ url: newUrl,
125
+ validity: 'unknown'
126
+ }
127
+ };
128
+ }
129
+
130
+ return imageState;
131
+ });
132
+ } finally {
133
+ updateImageState((imageState, i) => {
134
+ return R.includes(i, filteredIndexes) ? { ...imageState,
135
+ isNewUrlIncoming: false
136
+ } : imageState;
137
+ });
138
+ }
139
+ };
140
+
141
+ const loadMaybeLoadableItems = async () => {
142
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
143
+ const affectedIndexes = R.range(startIndex, endIndex);
144
+ await loadUrlByIndex(affectedIndexes);
145
+ };
146
+
147
+ const loadItemsDebounce = useDebounce(loadMaybeLoadableItems, debounceMillis);
64
148
  const onViewableItemsChanged = useRef(_ref => {
149
+ var _R$head, _R$last;
150
+
65
151
  let {
66
152
  viewableItems
67
153
  } = _ref;
68
- setItems(prev => {
69
- const viewableItemSortKeys = R.map(viewableItem => viewableItem.item.sortKey)(viewableItems);
70
- const firstViewableSortKey = R.head(viewableItemSortKeys);
71
- const lastViewableItemSortKey = R.last(viewableItemSortKeys);
72
- const firstItem = R.head(prev);
73
- const lastItem = R.last(prev);
74
-
75
- if (R.isNil(firstViewableSortKey) || R.isNil(lastViewableItemSortKey) || R.isNil(firstItem) || R.isNil(lastItem)) {
76
- return prev;
77
- }
154
+ const orderedViewableItems = R.sort((a, b) => (a.index || 0) - (b.index || 0), viewableItems);
155
+ const firstViewableIndex = (_R$head = R.head(orderedViewableItems)) === null || _R$head === void 0 ? void 0 : _R$head.index;
156
+ const lastViewableItemIndex = (_R$last = R.last(orderedViewableItems)) === null || _R$last === void 0 ? void 0 : _R$last.index;
78
157
 
79
- const frontBoundary = R.max(firstItem.sortKey, firstViewableSortKey - 1);
80
- const backBoundary = R.min(lastItem.sortKey, lastViewableItemSortKey + 1);
81
- const viewableItemBoundary = R.range(frontBoundary, backBoundary + 1);
82
- const newItems = R.map(prevItem => ({ ...prevItem,
83
- isViewable: R.includes(prevItem.sortKey, viewableItemBoundary)
84
- }))([...prev]);
85
- return newItems;
86
- });
87
- });
88
- const itemLoadedHandler = useCallback(sortKey => {
89
- const itemState = R.find(state => state.sortKey === sortKey)(itemStates.current);
90
-
91
- if (R.isNil(itemState)) {
158
+ if (R.isNil(firstViewableIndex) || R.isNil(lastViewableItemIndex)) {
92
159
  return;
93
160
  }
94
161
 
95
- itemState.state = STATE.LOADED;
96
- itemState.error = undefined;
97
- }, [itemStates]);
98
- const itemErrorHandler = useCallback(errorInfo => {
99
- const {
100
- sortKey,
101
- count
102
- } = errorInfo;
162
+ const startIndex = R.max(firstViewableIndex - NUMBER_OF_ADJACENT_ITEM, 0);
163
+ const endIndex = R.min(lastViewableItemIndex + NUMBER_OF_ADJACENT_ITEM, itemStates.length - 1);
164
+ maybeLoadableItemsIndexRange.current = [startIndex, endIndex + 1];
165
+ loadItemsDebounce();
166
+ });
167
+ const renderItem = useCallback(_ref2 => {
168
+ var _renderedDimensions$i, _renderedDimensions$i2;
103
169
 
104
- if (count >= errorRetryCount) {
105
- return;
106
- }
170
+ let {
171
+ item,
172
+ index
173
+ } = _ref2;
107
174
 
108
- errors.current.set(sortKey, errorInfo);
109
- const itemState = R.find(state => state.sortKey === sortKey)(itemStates.current);
175
+ const onError = () => {
176
+ updateImageState((imageState, i) => {
177
+ const urlState = imageState.urlState;
110
178
 
111
- if (R.isNil(itemState)) {
112
- return;
113
- }
179
+ if (i === index && urlState !== undefined) {
180
+ return { ...imageState,
181
+ totalErrorCount: imageState.totalErrorCount + 1,
182
+ urlState: { ...urlState,
183
+ validity: 'invalid'
184
+ }
185
+ };
186
+ }
187
+
188
+ return imageState;
189
+ });
190
+
191
+ if (item.reloadButtonVisible) {
192
+ return;
193
+ }
114
194
 
115
- itemState.state = STATE.FAIL;
116
- itemState.error = errorInfo;
117
-
118
- const handleError = () => {
119
- const errorsArray = Array.from(errors.current.entries());
120
- const errorsInfo = R.map(_ref2 => {
121
- let [key, value] = _ref2;
122
- return value;
123
- })(errorsArray);
124
- onError && onError([...errorsInfo]);
125
- errors.current.clear();
195
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
196
+
197
+ if (index >= startIndex || index < endIndex) {
198
+ loadItemsDebounce();
199
+ }
126
200
  };
127
201
 
128
- if (debounceTimeOut.current) {
129
- clearTimeout(debounceTimeOut.current);
130
- }
202
+ const onReloadPress = () => {
203
+ const [startIndex, endIndex] = maybeLoadableItemsIndexRange.current;
131
204
 
132
- if (errors.current.size === pageUnit) {
133
- handleError();
134
- } else {
135
- debounceTimeOut.current = setTimeout(handleError, errorDebounceMillis);
136
- }
137
- }, [errors.current, itemStates]);
138
- const renderItem = useCallback(_ref3 => {
139
- let {
140
- item
141
- } = _ref3;
142
- const itemState = R.find(state => state.sortKey === item.sortKey)(itemStates.current);
143
- const props = { ...item,
144
- itemState,
145
- errorRetryCount,
146
- onError: itemErrorHandler,
147
- onLoaded: itemLoadedHandler,
148
- onItemPress,
149
- getNextPage
205
+ if (index >= startIndex || index < endIndex) {
206
+ loadUrlByIndex([index]);
207
+ }
150
208
  };
151
- return /*#__PURE__*/React.createElement(ViewerItem, {
152
- props: props
153
- });
154
- }, [resourceString, itemErrorHandler, itemLoadedHandler, onItemPress]);
155
- useEffect(() => {
156
- setItems(prev => {
157
- return R.map(prevItem => {
158
- const currentData = R.find(currentItemData => prevItem.sortKey === currentItemData.sortKey)(data);
159
- const itemState = R.find(state => state.sortKey === (currentData === null || currentData === void 0 ? void 0 : currentData.sortKey))(itemStates.current);
160
-
161
- if (currentData && itemState && itemState.state !== STATE.LOADED && currentData.url !== prevItem.url) {
162
- itemState.state = STATE.URL_LOADED;
163
- return { ...prevItem,
164
- url: currentData.url,
165
- expiresAt: currentData.expiresAt
209
+
210
+ const onLoad = () => {
211
+ updateImageState((imageState, i) => {
212
+ const urlState = imageState.urlState;
213
+
214
+ if (i === index && urlState !== undefined) {
215
+ return { ...imageState,
216
+ urlState: { ...urlState,
217
+ validity: 'valid'
218
+ }
166
219
  };
167
220
  }
168
221
 
169
- return prevItem;
170
- })([...prev]);
171
- ;
222
+ return imageState;
223
+ });
224
+ };
225
+
226
+ return /*#__PURE__*/React.createElement(ViewerItem, {
227
+ onError: onError,
228
+ onLoad: onLoad,
229
+ onPress: onItemPress,
230
+ onReloadPress: onReloadPress,
231
+ url: item.url,
232
+ width: ((_renderedDimensions$i = renderedDimensions[index]) === null || _renderedDimensions$i === void 0 ? void 0 : _renderedDimensions$i.width) ?? 0,
233
+ height: ((_renderedDimensions$i2 = renderedDimensions[index]) === null || _renderedDimensions$i2 === void 0 ? void 0 : _renderedDimensions$i2.height) ?? 0,
234
+ reloadButtonVisible: item.reloadButtonVisible
172
235
  });
173
- }, [resourceString]);
174
- useEffect(() => {
175
- const newItems = R.map(item => ({ ...item,
176
- width: imageWidth,
177
- height: item.height * imageWidth / item.width
178
- }))(items);
179
- setItems(newItems);
180
- }, [imageWidth]);
236
+ }, [onItemPress, renderedDimensions]);
181
237
  useEffect(() => {
182
- const totalHeight = itemHeightAccum[0];
183
238
  const offset = Math.floor(initialScrollPercentage / 100 * totalHeight);
184
239
 
185
240
  if (flatListRef.current) {
@@ -188,19 +243,18 @@ export default function ComicViewer(props) {
188
243
  animated: false
189
244
  });
190
245
  }
191
- }, [flatListRef.current]);
246
+ }, []);
192
247
  return /*#__PURE__*/React.createElement(FlatList, _extends({
193
- data: items,
248
+ data: itemStates,
194
249
  getItemLayout: getItemLayout,
195
250
  initialNumToRender: initialNumToRender,
196
251
  keyExtractor: keyExtractor,
197
252
  onViewableItemsChanged: onViewableItemsChanged.current,
198
- onScroll: onScroll,
199
253
  ref: flatListRef,
254
+ removeClippedSubviews: false,
200
255
  renderItem: renderItem,
201
256
  viewabilityConfig: viewabilityConfig,
202
- windowSize: windowSize,
203
- ListFooterComponent: ListFooterComponent
257
+ windowSize: windowSize
204
258
  }, otherProps));
205
259
  }
206
260
  ;