@quintype/native-components 2.20.5 → 2.20.7

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/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [2.20.7](https://github.com/quintype/native-components/compare/v2.20.6...v2.20.7) (2023-06-16)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **images:** Fixes crop issues and adds rich-text support for attribution ([#201](https://github.com/quintype/native-components/issues/201)) ([c0a5ca0](https://github.com/quintype/native-components/commit/c0a5ca0f593f1ce3459ae0fcc2aba3f06f21e053))
11
+
12
+ ### [2.20.6](https://github.com/quintype/native-components/compare/v2.20.5...v2.20.6) (2023-06-15)
13
+
5
14
  ### [2.20.5](https://github.com/quintype/native-components/compare/v2.20.4...v2.20.5) (2023-03-21)
6
15
 
7
16
  ### [2.20.4](https://github.com/quintype/native-components/compare/v2.20.3...v2.20.4) (2023-02-01)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quintype/native-components",
3
- "version": "2.20.5",
3
+ "version": "2.20.7",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -17,10 +17,30 @@ const replaceDefaultProtocol = (content) => {
17
17
  }
18
18
  };
19
19
 
20
+ const removeWidthnHeight = (htmlContent) => {
21
+ const temp = htmlContent.split(' ');
22
+ let finalHtml = '';
23
+ if (temp[0] === '<iframe') {
24
+ for (let i = 0; i < temp.length; i++) {
25
+ if (
26
+ (temp[i].includes('width') || temp[i].includes('height'))
27
+ && !temp[i].includes('/')
28
+ ) {
29
+ continue;
30
+ }
31
+ finalHtml = `${finalHtml + temp[i]} `;
32
+ }
33
+
34
+ return finalHtml;
35
+ }
36
+ return htmlContent;
37
+ };
38
+
20
39
  const getHTMLContent = (embedJs) => {
40
+ const { width } = Dimensions.get('window');
21
41
  const decodedContent = getDecodedContent(embedJs);
22
- const htmlContent = replaceDefaultProtocol(decodedContent);
23
-
42
+ let htmlContent = replaceDefaultProtocol(decodedContent);
43
+ htmlContent = removeWidthnHeight(htmlContent);
24
44
  const webViewScript = `
25
45
  <script type="application/javascript">
26
46
  var interValId;
@@ -41,6 +61,12 @@ const getHTMLContent = (embedJs) => {
41
61
  <html>
42
62
  <head>
43
63
  <meta name="viewport" content="width=device-width, initial-scale=1">
64
+ <style>
65
+ iframe{
66
+ width:${width - 20}px;
67
+ height:${width - 20}px;
68
+ }
69
+ </style>
44
70
  </head>
45
71
  <body>
46
72
  ${htmlContent}
@@ -52,7 +78,11 @@ const getHTMLContent = (embedJs) => {
52
78
  export const JSEmbedElement = (props) => {
53
79
  const [height, setHeight] = useState(300);
54
80
  const webViewRef = useRef(null);
55
- const width = get(props, ['currentLayout', 'width'], Dimensions.get('window').width);
81
+ const width = get(
82
+ props,
83
+ ['currentLayout', 'width'],
84
+ Dimensions.get('window').width,
85
+ );
56
86
 
57
87
  useEffect(() => {
58
88
  webViewRef?.current?.reload();
@@ -71,10 +101,10 @@ export const JSEmbedElement = (props) => {
71
101
 
72
102
  const baseUrl = urlMapper[props.element.subtype] || 'https://twitter.com';
73
103
 
74
- return ({
104
+ return {
75
105
  html: getHTMLContent(props.element['embed-js']),
76
106
  baseUrl,
77
- });
107
+ };
78
108
  };
79
109
 
80
110
  return (
@@ -82,7 +112,10 @@ export const JSEmbedElement = (props) => {
82
112
  <WebView
83
113
  ref={webViewRef}
84
114
  style={{
85
- width, height, flex: 0, opacity: 0.99,
115
+ width,
116
+ height,
117
+ flex: 0,
118
+ opacity: 0.99,
86
119
  }}
87
120
  automaticallyAdjustContentInsets={false}
88
121
  scrollEnabled={false}
@@ -6,15 +6,23 @@ import { LightBox } from '../LightBox';
6
6
  import { getImageHeight } from '../../utils';
7
7
 
8
8
  export const LightBoxImage = ({
9
- data, hero, currentLayout, additionalStyles,
9
+ data, hero, currentLayout, additionalStyles, elementType, onClickHandler = () => {}, index,
10
10
  }) => {
11
- const { cdn, slug, metaData } = data;
11
+ const {
12
+ cdn, slug, metaData, imageWidth: previewWidth,
13
+ } = data;
12
14
  const { width, height } = metaData || {};
13
15
  const imageHeight = getImageHeight(width, height);
14
16
  const imageUri = `${data?.cdn}/${data?.slug}`;
15
17
  const [showModal, setShowModal] = useState(false);
16
18
 
17
- const toggleModal = () => setShowModal(!showModal);
19
+ const toggleModal = () => {
20
+ if (elementType === 'gallery') {
21
+ onClickHandler(index);
22
+ } else {
23
+ setShowModal(!showModal);
24
+ }
25
+ };
18
26
 
19
27
  const styles = {
20
28
  image: {
@@ -34,6 +42,8 @@ export const LightBoxImage = ({
34
42
  metaData={metaData}
35
43
  hero={hero || false}
36
44
  styles={cumulativeStyles}
45
+ imageWidth={previewWidth}
46
+ elementType={elementType || null}
37
47
  />
38
48
  </TouchableOpacity>
39
49
  </>
@@ -41,9 +51,11 @@ export const LightBoxImage = ({
41
51
  };
42
52
 
43
53
  LightBoxImage.propTypes = {
44
- children: PropTypes.element,
45
54
  data: PropTypes.object,
46
55
  hero: PropTypes.bool,
47
56
  currentLayout: PropTypes.object,
48
57
  additionalStyles: PropTypes.object,
58
+ elementType: PropTypes.string,
59
+ onClickHandler: PropTypes.func,
60
+ index: PropTypes.number,
49
61
  };
@@ -5,18 +5,18 @@ import React, {
5
5
  import { StyleSheet, View } from 'react-native';
6
6
  import FastImage from 'react-native-fast-image';
7
7
  import Icon from 'react-native-vector-icons/FontAwesome';
8
- import { isTablet } from 'react-native-device-info';
9
8
  import { FallbackIcon } from '../../Icons/FallBackIcon';
10
9
  import { AppTheme } from '../../utils';
11
- import { getImageURL } from '../../utils/imageUtils';
10
+ import {
11
+ getImageURL,
12
+ getCustomResolutionImageURL,
13
+ } from '../../utils/imageUtils';
12
14
 
13
15
  const ResponsiveImageBase = (props) => {
14
16
  const [placeholder, setPlaceholder] = useState(true);
15
17
  const { theme } = useContext(AppTheme);
16
18
  const { COLORS, CustomFallBackIcon, CustomFallBackBackground } = theme;
17
19
 
18
- const HERO_IMAGE_HEIGHT = isTablet() ? 300 : 232;
19
-
20
20
  const flattenedImageStyle = StyleSheet.flatten([
21
21
  styles.defaultImage,
22
22
  props.styles,
@@ -24,10 +24,8 @@ const ResponsiveImageBase = (props) => {
24
24
  width: props.imageWidth,
25
25
  height: (props.imageWidth * 9) / 16,
26
26
  },
27
- props.hero && {
28
- width: '100%',
29
- height: HERO_IMAGE_HEIGHT,
30
- alignSelf: 'stretch',
27
+ props?.elementType === 'gallery' && {
28
+ backgroundColor: COLORS.BRAND_BLACK,
31
29
  },
32
30
  ]);
33
31
 
@@ -43,7 +41,14 @@ const ResponsiveImageBase = (props) => {
43
41
  ) : (
44
42
  <FallbackIcon />
45
43
  );
46
- const imageUrl = getImageURL(props);
44
+
45
+ let imageUrl = '';
46
+
47
+ if (props?.elementType === 'gallery') {
48
+ imageUrl = getCustomResolutionImageURL(props, [1, 1]);
49
+ } else {
50
+ imageUrl = getImageURL(props);
51
+ }
47
52
 
48
53
  const userFallback = () => <Icon name="user" size={20} />;
49
54
 
@@ -71,7 +76,7 @@ const ResponsiveImageBase = (props) => {
71
76
  style={flattenedImageStyle}
72
77
  source={sourceURI}
73
78
  onLoad={onLoadHandler}
74
- resizeMode={FastImage.resizeMode.cover}
79
+ resizeMode={props?.elementType === 'gallery' && !(props.hero) ? FastImage.resizeMode.contain : FastImage.resizeMode.cover}
75
80
  {...props}
76
81
  />
77
82
  {placeholder && (
@@ -94,6 +99,7 @@ ResponsiveImageBase.propTypes = {
94
99
  testID: PropTypes.string,
95
100
  containerTestID: PropTypes.string,
96
101
  imageWidth: PropTypes.number,
102
+ elementType: PropTypes.string,
97
103
  };
98
104
 
99
105
  ResponsiveImageBase.defaultProps = {
@@ -1,6 +1,7 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { useContext } from 'react';
3
- import { Text, View } from 'react-native';
3
+ import { View, Linking } from 'react-native';
4
+ import HTML from 'react-native-render-html';
4
5
  import { AppTheme, getScreenPercentageWidth } from '../../utils/index';
5
6
  import { LightBoxImage } from '../LightBoxImage';
6
7
  import { slideshowStoryCardStyles } from './styles';
@@ -9,26 +10,37 @@ export const SlideshowStoryCard = ({ card, cdn }) => {
9
10
  const attribution = card['image-attribution'];
10
11
  const { title } = card;
11
12
  const { theme } = useContext(AppTheme);
12
- const displayDivider = attribution && title ? ' | ' : '';
13
- const styles = slideshowStoryCardStyles(theme)
13
+ const { CAN_COPY_TEXT } = theme;
14
+ const styles = slideshowStoryCardStyles(theme);
14
15
  const imgData = {
15
16
  cdn,
16
17
  slug: card['image-s3-key'],
17
- imageWidth: getScreenPercentageWidth(60),
18
+ imageWidth: getScreenPercentageWidth(100),
18
19
  metaData: card['image-metadata'],
19
20
  };
20
21
 
21
22
  return (
22
23
  <View style={styles.container}>
23
- <View style={styles.image}>
24
- <LightBoxImage data={imgData} hero />
25
- </View>
26
-
24
+ <LightBoxImage data={imgData} />
27
25
  <View style={styles.textContainer}>
28
- <Text style={styles.attributionText} lato>
29
- {`${attribution}${displayDivider}`}
30
- <Text style={styles.cardTitleText}>{`${title}`}</Text>
31
- </Text>
26
+ <HTML
27
+ html={title}
28
+ textSelectable={CAN_COPY_TEXT}
29
+ key={Math.random()}
30
+ baseFontStyle={styles.cardTitleText}
31
+ onLinkPress={(e, href) => {
32
+ Linking.openURL(href);
33
+ }}
34
+ />
35
+ <HTML
36
+ html={attribution}
37
+ textSelectable={CAN_COPY_TEXT}
38
+ key={Math.random()}
39
+ baseFontStyle={styles.attributionText}
40
+ onLinkPress={(e, href) => {
41
+ Linking.openURL(href);
42
+ }}
43
+ />
32
44
  </View>
33
45
  </View>
34
46
  );
@@ -37,5 +49,4 @@ export const SlideshowStoryCard = ({ card, cdn }) => {
37
49
  SlideshowStoryCard.propTypes = {
38
50
  cdn: PropTypes.string.isRequired,
39
51
  card: PropTypes.object,
40
- imageSize: PropTypes.object,
41
52
  };
@@ -1,28 +1,26 @@
1
1
  import { StyleSheet, Dimensions } from 'react-native';
2
- import { COMP_GENERAL_CONSTANTS } from '../../constants/component-constants/general-constants/constants'
2
+
3
3
  const { width } = Dimensions.get('window');
4
4
 
5
- export const slideshowStoryCardStyles = ({ COLORS }) => {
6
- return StyleSheet.create({
7
- image: {
8
- marginRight: 10,
9
- width,
10
- },
11
- textContainer: {
12
- marginTop: 10,
13
- opacity: 0.8,
14
- width,
15
- },
16
- cardTitleText: {
17
- opacity: 0.7,
18
- color: COLORS.BRAND_BLACK
19
- },
20
- container: {
21
- flexDirection: 'column',
22
- },
23
- attributionText: {
24
- color: COLORS.BRAND_BLACK,
25
- textAlign : COMP_GENERAL_CONSTANTS.textAlignment
26
- }
27
- })
28
- }
5
+ export const slideshowStoryCardStyles = ({ COLORS, FONT_FAMILY }) => StyleSheet.create({
6
+ textContainer: {
7
+ paddingHorizontal: 10,
8
+ flexDirection: 'column',
9
+ flexWrap: 'wrap',
10
+ marginTop: 10,
11
+ },
12
+ cardTitleText: {
13
+ opacity: 0.7,
14
+ color: COLORS.BRAND_BLACK,
15
+ fontFamily: FONT_FAMILY.secondary,
16
+ },
17
+ container: {
18
+ flexDirection: 'column',
19
+ width,
20
+ },
21
+ attributionText: {
22
+ color: COLORS.BRAND_BLACK,
23
+ fontFamily: FONT_FAMILY.secondary,
24
+ opacity: 0.8,
25
+ },
26
+ });
@@ -1,39 +1,245 @@
1
1
  import PropTypes from 'prop-types';
2
- import React, { useContext } from 'react';
3
- import { View } from 'react-native';
2
+ import React, { useContext, useState, useRef } from 'react';
3
+ import {
4
+ View,
5
+ Modal,
6
+ Linking,
7
+ SafeAreaView,
8
+ FlatList,
9
+ TouchableOpacity,
10
+ } from 'react-native';
11
+ import Icon from 'react-native-vector-icons/FontAwesome';
12
+ import FastImage from 'react-native-fast-image';
13
+ import HTML from 'react-native-render-html';
4
14
  import { styles } from './styles';
5
15
  import { Text } from '../index';
6
- import { getScreenPercentageWidth, AppTheme } from '../../utils/index';
16
+ import { AppTheme } from '../../utils/index';
7
17
  import { LightBoxImage } from '../LightBoxImage';
8
18
 
9
19
  export const StoryGallery = ({ cdn, card }) => {
10
20
  const { theme } = useContext(AppTheme);
21
+ const { COLORS, DARK_MODE, CAN_COPY_TEXT } = theme;
11
22
  const galleryStyles = styles(theme);
12
23
  const storyElements = card['story-elements'];
24
+ const [showModal, setShowModal] = useState(false);
25
+
26
+ const [currentIndex, setCurrentTabIndex] = useState(0);
27
+
28
+ const flatlistRef = useRef();
29
+ const [index, setIndex] = useState(0);
30
+ const totalSlides = card['story-elements']?.length;
31
+ const lastSlide = currentIndex + 1 === totalSlides;
32
+ const firstSlide = currentIndex === 0;
33
+ const [galleryLastImgUrl, setGalleryLastImageUrl] = useState('');
34
+
35
+ const onPressHandler = (index = 5) => {
36
+ setIndex(index);
37
+ setShowModal(true);
38
+ };
39
+
40
+ const moveNext = () => {
41
+ if (!lastSlide) {
42
+ flatlistRef.current.scrollToIndex({
43
+ index: Math.floor(Math.min(currentIndex + 1, totalSlides - 1)),
44
+ });
45
+ setCurrentTabIndex(currentIndex + 1);
46
+ }
47
+ };
48
+
49
+ const movePrev = () => {
50
+ if (!firstSlide) {
51
+ flatlistRef.current.scrollToIndex({
52
+ index: Math.ceil(Math.max(currentIndex - 1, 0)),
53
+ });
54
+ setCurrentTabIndex(Math.max(currentIndex - 1, 0));
55
+ }
56
+ };
57
+
58
+ const showArrow = (direction) => {
59
+ const arrowStyles = direction === 'left' ? galleryStyles.leftArrow : galleryStyles.rightArrow;
60
+ const onPressHandler = direction === 'left' ? movePrev : moveNext;
61
+ const iconName = direction === 'left' ? 'chevron-left' : 'chevron-right';
62
+ return (
63
+ <View style={arrowStyles}>
64
+ <TouchableOpacity onPress={onPressHandler}>
65
+ <Icon name={iconName} size={22} color={COLORS?.MONO7} />
66
+ </TouchableOpacity>
67
+ </View>
68
+ );
69
+ };
70
+
71
+ const handleScroll = (event) => {
72
+ const xOffset = event.nativeEvent?.contentOffset?.x;
73
+ const contentWidth = event.nativeEvent?.contentSize?.width;
74
+ const layoutWidth = event.nativeEvent?.layoutMeasurement?.width;
75
+ const value = xOffset / contentWidth;
76
+ const leftThreshold = contentWidth / (2 * totalSlides);
77
+ const rightThreshold = contentWidth - 1.5 * layoutWidth;
78
+
79
+ if (
80
+ leftThreshold <= xOffset
81
+ && (layoutWidth > xOffset || rightThreshold > xOffset)
82
+ ) {
83
+ setCurrentTabIndex(value * totalSlides);
84
+ } else if (xOffset < leftThreshold) {
85
+ setCurrentTabIndex(0);
86
+ } else if (xOffset >= rightThreshold) {
87
+ setCurrentTabIndex(totalSlides - 1);
88
+ }
89
+ };
90
+
91
+ const closeModalHandler = () => {
92
+ setShowModal(false);
93
+ setCurrentTabIndex(0);
94
+ };
95
+
96
+ const renderItem = (item) => {
97
+ const data = {
98
+ cdn,
99
+ slug: item.item['image-s3-key'],
100
+ metaData: item.item['image-metadata'],
101
+ };
102
+ const { title } = item.item;
103
+ const attribution = item.item['image-attribution'];
104
+ const imageUri = `${data?.cdn}/${data?.slug}`;
105
+
106
+ return (
107
+ <View style={galleryStyles.carouselContainer}>
108
+ <FastImage
109
+ source={{ uri: imageUri }}
110
+ resizeMode={FastImage.resizeMode.contain}
111
+ style={galleryStyles.carouselImage}
112
+ />
113
+ <HTML
114
+ html={title}
115
+ textSelectable={CAN_COPY_TEXT}
116
+ key={Math.random()}
117
+ baseFontStyle={galleryStyles.titleText}
118
+ onLinkPress={(e, href) => {
119
+ Linking.openURL(href);
120
+ }}
121
+ />
122
+ <HTML
123
+ html={attribution}
124
+ textSelectable={CAN_COPY_TEXT}
125
+ key={Math.random()}
126
+ baseFontStyle={galleryStyles.attributionText}
127
+ onLinkPress={(e, href) => {
128
+ Linking.openURL(href);
129
+ }}
130
+ />
131
+ </View>
132
+ );
133
+ };
13
134
 
14
135
  return (
15
- <View style={galleryStyles.container}>
16
- <Text style={galleryStyles.titleText}>{card.title}</Text>
17
-
18
- <View style={galleryStyles.imgContainer}>
19
- {storyElements.map((element, index) => {
20
- const data = {
21
- cdn,
22
- slug: element['image-s3-key'],
23
- metaData: element['image-metadata'],
24
- imageWidth: getScreenPercentageWidth(100),
25
- };
26
- const indexTestForFullWidthStyles = storyElements.length % 3 == 1 ? index % 3 : (index + 1) % 3;
27
- const style = indexTestForFullWidthStyles
28
- ? galleryStyles.halfWidth
29
- : galleryStyles.fullWidth;
30
- return <LightBoxImage key={index} data={data} additionalStyles={style} />;
31
- })}
136
+ <>
137
+ <View style={galleryStyles.container}>
138
+ <Text style={galleryStyles.titleText}>{card.title}</Text>
139
+
140
+ <View style={galleryStyles.imgContainer}>
141
+ {storyElements.map((element, index) => {
142
+ const data = {
143
+ cdn,
144
+ slug: element['image-s3-key'],
145
+ metaData: element['image-metadata'],
146
+ };
147
+
148
+ if (storyElements.length > 6 && index >= 5) {
149
+ const imageUri = `${data?.cdn}/${data?.slug}`;
150
+ if (galleryLastImgUrl === '') {
151
+ setGalleryLastImageUrl(imageUri);
152
+ }
153
+ return null;
154
+ }
155
+
156
+ const style = galleryStyles.fullWidth;
157
+ return (
158
+ <TouchableOpacity>
159
+ <LightBoxImage
160
+ onClickHandler={onPressHandler}
161
+ hero={!!data.metaData['focus-point']}
162
+ key={Math.random()}
163
+ data={data}
164
+ additionalStyles={style}
165
+ elementType={card?.metadata?.type}
166
+ index={index}
167
+ />
168
+ </TouchableOpacity>
169
+ );
170
+ })}
171
+ {storyElements && storyElements.length > 6 && (
172
+ <TouchableOpacity
173
+ onPress={() => onPressHandler(5)}
174
+ style={[
175
+ galleryStyles.fullWidth,
176
+ galleryStyles.showMoreImageContainer,
177
+ ]}
178
+ >
179
+ <FastImage
180
+ source={{ uri: galleryLastImgUrl }}
181
+ resizeMode={FastImage.resizeMode.cover}
182
+ style={galleryStyles.showMoreImage}
183
+ />
184
+ <Text style={galleryStyles.showMoreText}>
185
+ +
186
+ {storyElements.length - 5}
187
+ </Text>
188
+ </TouchableOpacity>
189
+ )}
190
+ </View>
191
+ <Text style={galleryStyles.descText} lato>
192
+ {card.description}
193
+ </Text>
32
194
  </View>
33
- <Text style={galleryStyles.descText} lato>
34
- {card.description}
35
- </Text>
36
- </View>
195
+ <Modal
196
+ visible={showModal}
197
+ animationType="slide"
198
+ transparent={false}
199
+ supportedOrientations={['portrait', 'landscape']}
200
+ onRequestClose={closeModalHandler}
201
+ onBackdropPress={closeModalHandler}
202
+ >
203
+ <SafeAreaView style={galleryStyles.safeAreaView}>
204
+ <View style={galleryStyles.modalContainer}>
205
+ <TouchableOpacity onPress={closeModalHandler}>
206
+ <View style={galleryStyles.close}>
207
+ <Icon
208
+ name="times"
209
+ size={20}
210
+ color={DARK_MODE ? COLORS.BRAND_BLACK : COLORS.BRAND_WHITE}
211
+ />
212
+ </View>
213
+ </TouchableOpacity>
214
+ <View style={galleryStyles.wrapper}>
215
+ <FlatList
216
+ ref={flatlistRef}
217
+ data={card['story-elements']}
218
+ renderItem={renderItem}
219
+ keyExtractor={(item) => item.id}
220
+ horizontal
221
+ extraData={currentIndex}
222
+ onScroll={handleScroll}
223
+ initialScrollIndex={index}
224
+ onScrollToIndexFailed={(info) => {
225
+ const wait = new Promise((resolve) => setTimeout(resolve, 500));
226
+ wait.then(() => {
227
+ flatlistRef.current?.scrollToIndex({
228
+ index: info.index,
229
+ animated: true,
230
+ });
231
+ });
232
+ }}
233
+ />
234
+
235
+ {!firstSlide && showArrow('left')}
236
+
237
+ {!lastSlide && showArrow('right')}
238
+ </View>
239
+ </View>
240
+ </SafeAreaView>
241
+ </Modal>
242
+ </>
37
243
  );
38
244
  };
39
245
 
@@ -1,35 +1,134 @@
1
1
  import { StyleSheet, Dimensions } from 'react-native';
2
+ import { useContext } from 'react';
3
+ import { AppTheme } from '../../utils';
2
4
 
3
- const { width: deviceWidth } = Dimensions.get('window');
5
+ export const styles = ({ FONT_SIZE }) => {
6
+ const { width: deviceWidth } = Dimensions.get('window');
7
+ const { theme } = useContext(AppTheme);
8
+ const { COLORS, DARK_MODE, FONT_FAMILY } = theme;
4
9
 
5
- export const styles = ({ FONT_SIZE }) => StyleSheet.create({
6
- container: {
7
- width: '100%',
8
- paddingHorizontal: 10,
9
- },
10
- titleText: {
11
- fontSize: FONT_SIZE.h1,
12
- marginBottom: 10,
13
- opacity: 0.8,
14
- },
15
- imgContainer: {
16
- width: '100%',
17
- flexDirection: 'row',
18
- flexWrap: 'wrap',
19
- justifyContent: 'space-between',
20
- },
21
- halfWidth: {
22
- width: (deviceWidth - 30) / 2,
23
- height: 124,
24
- },
25
- fullWidth: {
26
- width: deviceWidth - 20,
27
- height: 195,
28
- marginVertical: 10,
29
- },
30
- descText: {
31
- fontSize: FONT_SIZE.h2,
32
- opacity: 0.8,
33
- paddingTop: 10,
34
- },
35
- });
10
+ return StyleSheet.create({
11
+ container: {
12
+ width: '100%',
13
+ paddingHorizontal: 10,
14
+ },
15
+ imgContainer: {
16
+ width: '100%',
17
+ flexDirection: 'row',
18
+ flexWrap: 'wrap',
19
+ justifyContent: 'space-between',
20
+ },
21
+ halfWidth: {
22
+ width: (deviceWidth - 30) / 2,
23
+ height: 124,
24
+ },
25
+ fullWidth: {
26
+ width: (deviceWidth - 30) / 3,
27
+ height: (deviceWidth - 30) / 3,
28
+ marginVertical: 2.5,
29
+ },
30
+ descText: {
31
+ fontSize: FONT_SIZE.h2,
32
+ opacity: 0.8,
33
+ paddingTop: 10,
34
+ },
35
+ modalContainer: {
36
+ backgroundColor: DARK_MODE ? COLORS.MONO6 : COLORS.BRAND_BLACK,
37
+ flex: 1,
38
+ justifyContent: 'center',
39
+ },
40
+ safeAreaView: {
41
+ flex: 1,
42
+ backgroundColor: 'transparent',
43
+ },
44
+ close: {
45
+ justifyContent: 'flex-start',
46
+ padding: 10,
47
+ height: 40,
48
+ zIndex: 1,
49
+ marginTop: 25,
50
+ },
51
+ imageContainer: {
52
+ justifyContent: 'center',
53
+ alignItems: 'center',
54
+ width: '100%',
55
+ height: '100%',
56
+ },
57
+ portrait: {
58
+ width: '100%',
59
+ height: '80%',
60
+ },
61
+ wrapper: {
62
+ paddingHorizontal: 10,
63
+ flex: 1,
64
+ backgroundColor: 'black',
65
+ width: '100%',
66
+ },
67
+ titleText: {
68
+ fontWeight: 'normal',
69
+ fontSize: FONT_SIZE.h3,
70
+ opacity: 0.8,
71
+ color: 'white',
72
+ marginTop: 10,
73
+ marginHorizontal: 10,
74
+ fontFamily: FONT_FAMILY.secondary,
75
+ },
76
+ attributionText: {
77
+ fontWeight: 'normal',
78
+ marginBottom: 10,
79
+ fontSize: FONT_SIZE.h3,
80
+ opacity: 0.8,
81
+ color: 'white',
82
+ marginHorizontal: 10,
83
+ fontFamily: FONT_FAMILY.secondary,
84
+ },
85
+ leftArrow: {
86
+ position: 'absolute',
87
+ top: '46%',
88
+ right: '95%',
89
+ left: 20,
90
+ zIndex: 999,
91
+ width: 30,
92
+ height: 40,
93
+ backgroundColor: COLORS.TRANSPARENT_BLACK,
94
+ display: 'flex',
95
+ alignItems: 'center',
96
+ justifyContent: 'center',
97
+ },
98
+ rightArrow: {
99
+ position: 'absolute',
100
+ top: '46%',
101
+ left: '93%',
102
+ right: 20,
103
+ zIndex: 999,
104
+ width: 30,
105
+ height: 40,
106
+ backgroundColor: COLORS.TRANSPARENT_BLACK,
107
+ display: 'flex',
108
+ alignItems: 'center',
109
+ justifyContent: 'center',
110
+ },
111
+ carouselImage: {
112
+ width: '100%',
113
+ height: '80%',
114
+ },
115
+ carouselContainer: {
116
+ width: deviceWidth - 10,
117
+ margin: 10,
118
+ },
119
+ showMoreImageContainer: {
120
+ justifyContent: 'center',
121
+ alignItems: 'center',
122
+ },
123
+ showMoreImage: {
124
+ width: '100%',
125
+ height: '100%',
126
+ opacity: 0.5,
127
+ },
128
+ showMoreText: {
129
+ fontSize: 30,
130
+ position: 'absolute',
131
+ color: COLORS.BRAND_WHITE,
132
+ },
133
+ });
134
+ };
@@ -33,7 +33,6 @@ const getHeroImage = (cdn, story) => {
33
33
  metaData={getImageMetadata(story)}
34
34
  slug={imageSlug}
35
35
  imageWidth={getScreenPercentageWidth(100)}
36
- hero
37
36
  />
38
37
  );
39
38
  };
@@ -1,12 +1,12 @@
1
- import PropTypes from "prop-types";
2
- import React, { useContext, useRef, useState } from "react";
3
- import { TouchableOpacity, View } from "react-native";
4
- import { FlatList } from "react-native-gesture-handler";
5
- import Icon from "react-native-vector-icons/AntDesign";
6
- import { AppTheme } from "../../utils";
7
- import { Text } from "../index";
8
- import { SlideshowStoryCard } from "../SlideshowStoryCard";
9
- import { storySlideshowStyles } from "./styles";
1
+ import PropTypes from 'prop-types';
2
+ import React, { useContext, useRef, useState } from 'react';
3
+ import { TouchableOpacity, View } from 'react-native';
4
+ import { FlatList } from 'react-native-gesture-handler';
5
+ import Icon from 'react-native-vector-icons/AntDesign';
6
+ import { AppTheme } from '../../utils';
7
+ import { Text } from '../index';
8
+ import { SlideshowStoryCard } from '../SlideshowStoryCard';
9
+ import { storySlideshowStyles } from './styles';
10
10
 
11
11
  export const StorySlideshow = ({ card, cdn }) => {
12
12
  const { theme } = useContext(AppTheme);
@@ -16,13 +16,13 @@ export const StorySlideshow = ({ card, cdn }) => {
16
16
 
17
17
  const flatlistRef = useRef();
18
18
 
19
- const totalSlides = card["story-elements"]?.length;
19
+ const totalSlides = card['story-elements']?.length;
20
20
  const lastSlide = currentIndex + 1 === totalSlides;
21
21
  const firstSlide = currentIndex === 0;
22
22
 
23
23
  const moveNext = () => {
24
24
  if (!lastSlide) {
25
- flatlistRef.current.scrollToIndex({ index: Math.floor(Math.min(currentIndex + 1, totalSlides - 1))});
25
+ flatlistRef.current.scrollToIndex({ index: Math.floor(Math.min(currentIndex + 1, totalSlides - 1)) });
26
26
  setCurrentTabIndex(currentIndex + 1);
27
27
  }
28
28
  };
@@ -35,9 +35,8 @@ export const StorySlideshow = ({ card, cdn }) => {
35
35
  };
36
36
 
37
37
  const showArrow = (direction) => {
38
- const arrowStyles =
39
- direction === "left" ? styles.leftArrow : styles.rightArrow;
40
- const onPressHandler = direction === "left" ? movePrev : moveNext;
38
+ const arrowStyles = direction === 'left' ? styles.leftArrow : styles.rightArrow;
39
+ const onPressHandler = direction === 'left' ? movePrev : moveNext;
41
40
 
42
41
  return (
43
42
  <View style={arrowStyles}>
@@ -53,27 +52,27 @@ export const StorySlideshow = ({ card, cdn }) => {
53
52
  const contentWidth = event.nativeEvent?.contentSize?.width;
54
53
  const layoutWidth = event.nativeEvent?.layoutMeasurement?.width;
55
54
  const value = (xOffset / contentWidth);
56
- const leftThreshold = contentWidth / ( 2 * totalSlides );
57
- const rightThreshold = contentWidth - ( 1.5 ) * layoutWidth;
55
+ const leftThreshold = contentWidth / (2 * totalSlides);
56
+ const rightThreshold = contentWidth - (1.5) * layoutWidth;
58
57
 
59
- if((leftThreshold <= xOffset && (layoutWidth > xOffset || rightThreshold > xOffset ))){
58
+ if ((leftThreshold <= xOffset && (layoutWidth > xOffset || rightThreshold > xOffset))) {
60
59
  setCurrentTabIndex(value * totalSlides);
61
- } else if(xOffset < leftThreshold){
60
+ } else if (xOffset < leftThreshold) {
62
61
  setCurrentTabIndex(0);
63
- } else if(xOffset >= rightThreshold){
64
- setCurrentTabIndex(totalSlides-1);
62
+ } else if (xOffset >= rightThreshold) {
63
+ setCurrentTabIndex(totalSlides - 1);
65
64
  }
66
- }
65
+ };
67
66
 
68
67
  return (
69
- <View style={styles.wrapper}>
68
+ <View>
70
69
  <Text primary style={styles.titleText}>
71
70
  {card.title}
72
71
  </Text>
73
72
 
74
73
  <FlatList
75
74
  ref={flatlistRef}
76
- data={card["story-elements"]}
75
+ data={card['story-elements']}
77
76
  renderItem={({ item }) => <SlideshowStoryCard card={item} cdn={cdn} />}
78
77
  keyExtractor={(item) => item.id}
79
78
  horizontal
@@ -81,9 +80,14 @@ export const StorySlideshow = ({ card, cdn }) => {
81
80
  onScroll={handleScroll}
82
81
  />
83
82
 
84
- {!firstSlide && showArrow("left")}
83
+ {!firstSlide && showArrow('left')}
85
84
 
86
- {!lastSlide && showArrow("right")}
85
+ {!lastSlide && showArrow('right')}
87
86
  </View>
88
87
  );
89
88
  };
89
+
90
+ StorySlideshow.propTypes = {
91
+ cdn: PropTypes.string,
92
+ card: PropTypes.object,
93
+ };
@@ -1,40 +1,36 @@
1
- import { StyleSheet } from "react-native";
1
+ import { StyleSheet } from 'react-native';
2
2
 
3
- export const storySlideshowStyles = ({ FONT_SIZE, COLORS }) =>
4
- StyleSheet.create({
5
- wrapper: {
6
- paddingHorizontal: 10,
7
- },
8
- titleText: {
9
- fontWeight: "normal",
10
- marginBottom: 10,
11
- fontSize: FONT_SIZE.h1,
12
- opacity: 0.8,
13
- },
14
- leftArrow: {
15
- position: "absolute",
16
- top: "46%",
17
- right: "95%",
18
- left: 20,
19
- zIndex: 999,
20
- width: 30,
21
- height: 40,
22
- backgroundColor: COLORS.TRANSPARENT_BLACK,
23
- display: "flex",
24
- alignItems: "center",
25
- justifyContent: "center",
26
- },
27
- rightArrow: {
28
- position: "absolute",
29
- top: "46%",
30
- left: "93%",
31
- right: 20,
32
- zIndex: 999,
33
- width: 30,
34
- height: 40,
35
- backgroundColor: COLORS.TRANSPARENT_BLACK,
36
- display: "flex",
37
- alignItems: "center",
38
- justifyContent: "center",
39
- },
40
- });
3
+ export const storySlideshowStyles = ({ FONT_SIZE, COLORS }) => StyleSheet.create({
4
+ titleText: {
5
+ fontWeight: 'normal',
6
+ marginBottom: 10,
7
+ fontSize: FONT_SIZE.h1,
8
+ opacity: 0.8,
9
+ },
10
+ leftArrow: {
11
+ position: 'absolute',
12
+ top: '46%',
13
+ right: '95%',
14
+ left: 20,
15
+ zIndex: 999,
16
+ width: 30,
17
+ height: 40,
18
+ backgroundColor: COLORS.TRANSPARENT_BLACK,
19
+ display: 'flex',
20
+ alignItems: 'center',
21
+ justifyContent: 'center',
22
+ },
23
+ rightArrow: {
24
+ position: 'absolute',
25
+ top: '46%',
26
+ left: '93%',
27
+ right: 20,
28
+ zIndex: 999,
29
+ width: 30,
30
+ height: 40,
31
+ backgroundColor: COLORS.TRANSPARENT_BLACK,
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ },
36
+ });
@@ -29,9 +29,11 @@ export function getImageQuality() {
29
29
  if (NETWORK_TYPE) {
30
30
  if (NETWORK_TYPE === 'wifi') {
31
31
  return 100;
32
- } if (NETWORK_TYPE === '4g') {
32
+ }
33
+ if (NETWORK_TYPE === '4g') {
33
34
  return 85;
34
- } if (NETWORK_TYPE === '3g') {
35
+ }
36
+ if (NETWORK_TYPE === '3g') {
35
37
  return 50;
36
38
  }
37
39
  }
@@ -118,3 +120,19 @@ export const getImageURL = (props) => {
118
120
  q: IMAGE_QUALITY,
119
121
  })}`;
120
122
  };
123
+
124
+ export const getCustomResolutionImageURL = (props, customAspectRatio) => {
125
+ const { theme } = useContext(AppTheme);
126
+ const { IMAGE_QUALITY } = theme;
127
+
128
+ const {
129
+ metaData, slug, imageWidth, cdn,
130
+ } = props;
131
+ const image = new FocusedImage(slug, metaData);
132
+
133
+ const imageCdn = cdn || 'https://www.quintype.com';
134
+ return `${imageCdn}/${image.path(customAspectRatio, {
135
+ w: getPixelRatioForDevice(imageWidth) || Math.round(deviceWidth),
136
+ q: IMAGE_QUALITY,
137
+ })}`;
138
+ };