@quintype/native-components 2.22.0-beta.0 → 2.22.0-beta.1

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.
@@ -0,0 +1,6 @@
1
+ {
2
+ "ignore_dirs": [
3
+ ".git",
4
+ "node_modules"
5
+ ]
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quintype/native-components",
3
- "version": "2.22.0-beta.0",
3
+ "version": "2.22.0-beta.1",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -25,7 +25,7 @@ export const AlsoRead = (props) => {
25
25
  const linkedImage = get(linkedStories, [linkedStoryId, 'hero-image-s3-key']);
26
26
 
27
27
  const { theme } = useContext(AppTheme);
28
- const { CustomFallBackIcon, translate } = theme;
28
+ const { CustomFallBackIcon, translate, showAlsoRead } = theme;
29
29
  const styles = alsoReadStyles(theme);
30
30
 
31
31
  const fallBackIcon = CustomFallBackIcon ? <CustomFallBackIcon /> : <FallbackIcon />;
@@ -36,7 +36,7 @@ export const AlsoRead = (props) => {
36
36
  <ResponsiveImage
37
37
  metaData={linkedStoryMetadata}
38
38
  cdn={cdn}
39
- imageWidth={120}
39
+ imageWidth={150}
40
40
  slug={linkedImage}
41
41
  />
42
42
  );
@@ -44,13 +44,13 @@ export const AlsoRead = (props) => {
44
44
 
45
45
  return (
46
46
  <View style={styles.wrapper}>
47
+ {showAlsoRead && <Text style={styles.alsoReadText}>{translate("Also Read")}</Text>}
47
48
  <TouchableOpacity
48
49
  onPress={() => handleAlsoReadTap(slug, navigation, screenList)}
49
50
  >
50
51
  <View style={styles.container}>
51
52
  {getImageComponent()}
52
53
  <View style={styles.alsoReadContentWrapper}>
53
- <Text style={styles.alsoReadText}>{translate("Also Read")}</Text>
54
54
  <Text primary numberOfLines={2} style={styles.alsoReadTitle}>
55
55
  {text}
56
56
  </Text>
@@ -5,9 +5,15 @@ export const alsoReadStyles = (appThemeContext) => {
5
5
  return StyleSheet.create({
6
6
  wrapper: {
7
7
  padding: 10,
8
+ margin:15
8
9
  },
9
10
  container: {
10
11
  flexDirection: "row",
12
+ padding:10,
13
+ borderWidth:0.5,
14
+ borderRadius:2,
15
+ marginTop:8,
16
+ borderColor:COLORS.MONO5
11
17
  },
12
18
  alsoReadContentWrapper: {
13
19
  paddingLeft: 12,
@@ -15,12 +21,13 @@ export const alsoReadStyles = (appThemeContext) => {
15
21
  },
16
22
  alsoReadText: {
17
23
  fontSize: FONT_SIZE.h2,
18
- fontFamily: FONT_FAMILY.secondaryBold,
19
- color: DARK_MODE ? COLORS.BRAND_BLACK : COLORS.BRAND_1,
24
+ fontFamily: FONT_FAMILY.primaryBold,
25
+ color: COLORS.BRAND_BLACK,
20
26
  },
21
27
  alsoReadTitle: {
22
28
  fontSize: FONT_SIZE.h2,
23
- color: DARK_MODE ? COLORS.BRAND_BLACK : COLORS.BRAND_1,
29
+ color: COLORS.BRAND_1,
30
+ fontFamily: FONT_FAMILY.primary,
24
31
  },
25
32
  fallBackImageWrapper: {
26
33
  backgroundColor: COLORS.BRAND_WHITE,
@@ -55,7 +55,7 @@ const CollectionCardNewBase = ({
55
55
  cdn={cdn}
56
56
  story={secondaryStory}
57
57
  iconComponent={<ShareButton story={secondaryStory} />}
58
- imageWidth={getScreenPercentageWidth(25)}
58
+ imageWidth={getScreenPercentageWidth(29)}
59
59
  horizontalPadding={horizontalPadding}
60
60
  />
61
61
  );
@@ -1,7 +1,7 @@
1
1
  import { StyleSheet } from 'react-native';
2
2
 
3
3
  export const collectionTitleStyles = (appThemeContext) => {
4
- const { COLORS, FONT_SIZE } = appThemeContext;
4
+ const { COLORS, FONT_SIZE, FONT_FAMILY } = appThemeContext;
5
5
 
6
6
  return StyleSheet.create({
7
7
  container: {
@@ -13,6 +13,7 @@ export const collectionTitleStyles = (appThemeContext) => {
13
13
  },
14
14
  title: {
15
15
  fontSize: FONT_SIZE.h3,
16
+ fontFamily: FONT_FAMILY.secondaryBold,
16
17
  color: COLORS.BRAND_5,
17
18
  },
18
19
  });
@@ -7,10 +7,9 @@ import {
7
7
  AppTheme,
8
8
  getImageMetadata,
9
9
  getImageSlug,
10
- getTimeForStoryCards,
11
10
  } from '../../utils';
12
11
  import { getStoryHeadline } from '../../utils/story';
13
- import { ResponsiveImage, Text } from '../index';
12
+ import { ResponsiveImage, Text, StoryCardDetailsRow } from '../index';
14
13
  import { storyStyles } from './styles';
15
14
  import {
16
15
  COMP_GENERAL_CONSTANTS,
@@ -24,18 +23,13 @@ const PrimaryStoryCardNewBase = (props) => {
24
23
  const {
25
24
  COLORS,
26
25
  FONT_SIZE,
27
- locale,
28
- DARK_MODE,
29
- reverseTimeAdverbPosition,
30
- enableReadTimeOnStoryCards,
31
- DATE_TIME_FORMAT,
26
+ FONT_FAMILY,
32
27
  premiumIcon,
33
28
  lineHeightMultiplier,
29
+ storyCardOptions,
34
30
  } = theme || {};
35
31
 
36
- const translate = get(theme, ['translate'], (word) => word);
37
-
38
- const styles = storyStyles(COLORS, FONT_SIZE, lineHeightMultiplier);
32
+ const styles = storyStyles(theme);
39
33
  const containerStyle = StyleSheet.flatten([
40
34
  styles.container,
41
35
  { paddingHorizontal: props.horizontalPadding },
@@ -44,15 +38,7 @@ const PrimaryStoryCardNewBase = (props) => {
44
38
  styles.headline,
45
39
  props.headlineStyle,
46
40
  ]);
47
- const timestampStyle = StyleSheet.flatten([
48
- styles.timestamp,
49
- props.timestampStyle,
50
- ]);
51
41
 
52
- const DATE_FORMAT = DATE_TIME_FORMAT.dateFormat;
53
- const readTime = enableReadTimeOnStoryCards && story['read-time']
54
- ? `${story['read-time']} ${translate('min read')} · `
55
- : '';
56
42
  const isPremiumStory = story['access'] === 'subscription';
57
43
 
58
44
  const throttledOnpress = throttle(props.onPress, 1000);
@@ -85,6 +71,7 @@ const PrimaryStoryCardNewBase = (props) => {
85
71
  };
86
72
 
87
73
  return (
74
+ <>
88
75
  <TouchableOpacity
89
76
  testID={COMP_GENERAL_CONSTANTS.primaryStoryCard}
90
77
  onPress={throttledOnpress}
@@ -101,12 +88,11 @@ const PrimaryStoryCardNewBase = (props) => {
101
88
  </ResponsiveImage>
102
89
  <View style={styles.contentContainer}>
103
90
  <View style={styles.headlineAndTimestampContainer}>
91
+ {storyCardOptions.enableSectionName && <Text numberOfLines={1} ellipsizeMode="tail" style={styles.sectionName}>{get(story, ['sections', 0, 'display-name'], '')}</Text>}
104
92
  <View style={{display:'flex', flexDirection:'row'}}>
105
-
106
-
107
93
  <Text
108
94
  primary
109
- numberOfLines={3}
95
+ numberOfLines={storyCardOptions.numberOfLinesForTitle}
110
96
  ellipsizeMode="tail"
111
97
  style={headlineStyle}
112
98
  testID={COMP_GENERAL_CONSTANTS.primaryStoryHeadline}
@@ -116,24 +102,17 @@ const PrimaryStoryCardNewBase = (props) => {
116
102
  {getStoryHeadline(story)}
117
103
  </Text>
118
104
  </View>
119
- <Text
120
- style={timestampStyle}
121
- numberOfLines={2}
122
- testID={COMP_CONTENT_CONSTANTS.publishedDate}
123
- >
124
- {readTime
125
- + getTimeForStoryCards(
126
- story['published-at'],
127
- DATE_FORMAT,
128
- locale,
129
- translate,
130
- reverseTimeAdverbPosition,
131
- )}
132
- </Text>
105
+ <StoryCardDetailsRow
106
+ authorName={get(story.authors, [0, 'name'])}
107
+ publishedAt={story["published-at"]}
108
+ readTime={story['read-time']}
109
+ />
133
110
  </View>
134
111
  <View style={styles.icon}>{props.iconComponent}</View>
135
112
  </View>
136
113
  </TouchableOpacity>
114
+ <View style={[styles.separatorLine, {marginHorizontal: props.horizontalPadding}]}/>
115
+ </>
137
116
  );
138
117
  };
139
118
 
@@ -141,7 +120,6 @@ PrimaryStoryCardNewBase.propTypes = {
141
120
  cdn: PropTypes.string,
142
121
  imageWidth: PropTypes.number,
143
122
  headlineStyle: PropTypes.func,
144
- timestampStyle: PropTypes.func,
145
123
  story: PropTypes.object.isRequired,
146
124
  onPress: PropTypes.func,
147
125
  horizontalPadding: PropTypes.number,
@@ -1,32 +1,34 @@
1
- export const storyStyles = (COLORS = {}, FONT_SIZE = {}, lineHeightMultiplier) => ({
1
+ export const storyStyles = ({COLORS, FONT_SIZE, FONT_FAMILY, lineHeightMultiplier, storyCardOptions}) => ({
2
2
  container: {
3
3
  backgroundColor: COLORS.BRAND_WHITE,
4
- padding: 8,
4
+ paddingVertical: 10,
5
+ gap: 10,
5
6
  },
6
7
  contentContainer: {
7
8
  flexDirection: 'row',
8
9
  justifyContent: 'space-between',
9
- alignItems: 'center',
10
+ alignItems: 'flex-start',
11
+ gap: 10,
10
12
  },
11
13
  headlineAndTimestampContainer: {
12
14
  flexShrink: 1,
13
- flex: 1,
15
+ gap: 4,
14
16
  },
15
- timestamp: {
16
- color: COLORS?.BRAND_BLACK,
17
- fontSize: FONT_SIZE.h5,
18
- lineHeight: FONT_SIZE.h5 * lineHeightMultiplier,
19
- opacity: 0.6,
20
- marginTop: 4,
17
+ icon: {
18
+ marginBottom: 4,
19
+ marginTop: storyCardOptions.enableSectionName ? FONT_SIZE.h4 * lineHeightMultiplier + 8 : 4,
20
+ },
21
+ sectionName: {
22
+ color: COLORS.BRAND_1,
23
+ fontSize: FONT_SIZE.h4,
24
+ lineHeight: FONT_SIZE.h4 * lineHeightMultiplier,
21
25
  },
22
26
  headline: {
27
+ fontFamily: FONT_FAMILY.primaryBold,
23
28
  fontSize: FONT_SIZE.title,
24
29
  lineHeight: FONT_SIZE.title * lineHeightMultiplier,
25
30
  color: COLORS.BRAND_BLACK,
26
- marginTop: 4,
27
- },
28
- icon: {
29
- marginLeft: 8,
31
+ marginBottom: 4,
30
32
  },
31
33
  storyTypeContainer: {
32
34
  position: 'absolute',
@@ -60,4 +62,9 @@ export const storyStyles = (COLORS = {}, FONT_SIZE = {}, lineHeightMultiplier) =
60
62
  marginTop: FONT_SIZE.h3 * 0.92,
61
63
  marginRight: 5,
62
64
  },
65
+ separatorLine: {
66
+ height: 0.8,
67
+ opacity: 0.1,
68
+ backgroundColor: COLORS.BRAND_BLACK,
69
+ },
63
70
  });
@@ -41,6 +41,7 @@ const ResponsiveImageBase = (props) => {
41
41
 
42
42
  const placeholderStyle = {
43
43
  ...StyleSheet.absoluteFillObject,
44
+ ...props.styles,
44
45
  backgroundColor:
45
46
  (CustomFallBackIcon && CustomFallBackBackground) ?? COLORS.MONO6,
46
47
  justifyContent: "center",
@@ -1,6 +1,6 @@
1
1
  import { get, isNull, throttle } from 'lodash';
2
2
  import PropTypes from 'prop-types';
3
- import React, { useContext, memo, useState, useEffect } from 'react';
3
+ import React, { useContext, memo } from 'react';
4
4
  import {
5
5
  StyleSheet,
6
6
  TouchableOpacity,
@@ -13,10 +13,9 @@ import {
13
13
  AppTheme,
14
14
  getImageMetadata,
15
15
  getImageSlug,
16
- getTimeForStoryCards,
17
16
  } from '../../utils';
18
17
  import { getStoryHeadline } from '../../utils/story';
19
- import { ResponsiveImage, Text } from '../index';
18
+ import { ResponsiveImage, Text, StoryCardDetailsRow } from '../index';
20
19
  import { storyStyles } from './styles';
21
20
  import {
22
21
  COMP_GENERAL_CONSTANTS,
@@ -28,18 +27,13 @@ const SecondaryStoryCardNewBase = (props) => {
28
27
  const { story = {} } = props;
29
28
  const { theme } = useContext(AppTheme);
30
29
  const {
31
- locale,
32
- DARK_MODE,
33
30
  COLORS,
34
- reverseTimeAdverbPosition,
35
- enableReadTimeOnStoryCards,
36
- DATE_TIME_FORMAT,
37
31
  premiumIcon,
38
- FONT_SIZE
32
+ FONT_SIZE,
33
+ storyCardOptions,
39
34
  } = theme;
40
35
 
41
- const translate = get(theme, ['translate'], (word) => word);
42
- const styles = storyStyles(theme, DARK_MODE);
36
+ const styles = storyStyles(theme);
43
37
  const containerStyle = StyleSheet.flatten([
44
38
  styles.container,
45
39
  { paddingHorizontal: props.horizontalPadding },
@@ -48,15 +42,7 @@ const SecondaryStoryCardNewBase = (props) => {
48
42
  styles.headline,
49
43
  props.headlineStyle,
50
44
  ]);
51
- const timestampStyle = StyleSheet.flatten([
52
- styles.timestamp,
53
- props.timestampStyle,
54
- ]);
55
45
 
56
- const DATE_FORMAT = DATE_TIME_FORMAT.dateFormat;
57
- const readTime = enableReadTimeOnStoryCards && story['read-time']
58
- ? `${story['read-time']} ${translate('min read')} · `
59
- : '';
60
46
  const isPremiumStory = story['access'] === 'subscription';
61
47
 
62
48
  const throttledOnpress = throttle(props.onPress, 1000);
@@ -90,25 +76,30 @@ const SecondaryStoryCardNewBase = (props) => {
90
76
  };
91
77
 
92
78
  return (
79
+ <>
93
80
  <TouchableOpacity
94
81
  testID={COMP_GENERAL_CONSTANTS.secondaryStoryCard}
95
82
  onPress={throttledOnpress}
96
83
  activeOpacity={0.8}
97
84
  style={containerStyle}
98
85
  >
99
- <ResponsiveImage
86
+ <View style={[styles.imageAndDetails, storyCardOptions.alignSmallCardImage === "right" && { flexDirection: 'row-reverse'}]}>
87
+ {storyCardOptions.enableSmallCardImage && (<ResponsiveImage
88
+ styles={styles.image}
100
89
  metaData={getImageMetadata(story)}
101
90
  slug={getImageSlug(story) || ''}
102
91
  cdn={props.cdn || ''}
103
92
  imageWidth={props.imageWidth}
104
93
  >
105
94
  <View style={styles.storyTypeContainer}>{showStoryType()}</View>
106
- </ResponsiveImage>
95
+ </ResponsiveImage>)
96
+ }
107
97
  <View style={styles.headlineAndTimestampBlockContainer}>
98
+ {storyCardOptions.enableSectionName && <Text numberOfLines={1} ellipsizeMode="tail" style={styles.sectionName}>{get(story, ['sections', 0, 'display-name'], '')}</Text>}
108
99
  <View style={{display:'flex', flexDirection:'row'}}>
109
100
  <Text
110
101
  primary
111
- numberOfLines={2}
102
+ numberOfLines={storyCardOptions.numberOfLinesForTitle}
112
103
  ellipsizeMode="tail"
113
104
  style={headlineStyle}
114
105
  testID={COMP_GENERAL_CONSTANTS.secondaryStoryHeadline}
@@ -118,24 +109,17 @@ const SecondaryStoryCardNewBase = (props) => {
118
109
  {getStoryHeadline(story)?.trim()}
119
110
  </Text>
120
111
  </View>
121
- <Text
122
- style={timestampStyle}
123
- numberOfLines={2}
124
- // TODO: Add corrected testID here
125
- testID={COMP_CONTENT_CONSTANTS.publishedDate}
126
- >
127
- {readTime
128
- + getTimeForStoryCards(
129
- story['published-at'],
130
- DATE_FORMAT,
131
- locale,
132
- translate,
133
- reverseTimeAdverbPosition,
134
- )}
135
- </Text>
112
+ <StoryCardDetailsRow
113
+ authorName={get(story.authors, [0, 'name'])}
114
+ publishedAt={story["published-at"]}
115
+ readTime={story['read-time']}
116
+ />
117
+ </View>
136
118
  </View>
137
119
  <View style={styles.icon}>{props.iconComponent}</View>
138
120
  </TouchableOpacity>
121
+ <View style={[styles.separatorLine, { marginHorizontal: props.horizontalPadding }]}/>
122
+ </>
139
123
  );
140
124
  };
141
125
 
@@ -143,7 +127,6 @@ SecondaryStoryCardNewBase.propTypes = TouchableOpacityProps && {
143
127
  cdn: PropTypes.string.isRequired,
144
128
  imageWidth: PropTypes.number,
145
129
  headlineStyle: TextStyle,
146
- timestampStyle: TextStyle,
147
130
  story: PropTypes.any.isRequired,
148
131
  iconComponent: PropTypes.element,
149
132
  horizontalPadding: PropTypes.number,
@@ -1,33 +1,46 @@
1
1
  import { StyleSheet } from 'react-native';
2
2
 
3
- export const storyStyles = ({ COLORS, FONT_SIZE, lineHeightMultiplier }) => StyleSheet.create({
3
+ export const storyStyles = ({ COLORS, FONT_SIZE, FONT_FAMILY, lineHeightMultiplier, storyCardOptions }) => StyleSheet.create({
4
4
  container: {
5
5
  flexDirection: 'row',
6
+ justifyContent: 'space-between',
6
7
  backgroundColor: COLORS.BRAND_WHITE,
7
- padding: 8,
8
- alignItems: 'center',
8
+ paddingVertical: 10,
9
+ alignItems: 'flex-start',
10
+ gap: 10,
11
+ },
12
+ imageAndDetails: {
13
+ flexDirection: 'row',
14
+ flexShrink: 1,
15
+ alignItems: 'flex-start',
16
+ gap: 10,
17
+ },
18
+ image: {
19
+ marginBottom: 4,
20
+ marginTop: storyCardOptions.enableSectionName ? FONT_SIZE.h4 * lineHeightMultiplier + 8 : 4,
21
+ },
22
+ icon: {
23
+ marginBottom: 4,
24
+ marginTop: storyCardOptions.enableSectionName ? FONT_SIZE.h4 * lineHeightMultiplier + 8 : 4,
9
25
  },
10
26
  headlineAndTimestampBlockContainer: {
11
27
  flexShrink: 1,
12
- flex: 1,
28
+ flexGrow: 1,
29
+ gap: 4,
13
30
  justifyContent: 'space-between',
14
- paddingLeft: 12,
15
31
  },
16
- timestamp: {
17
- color: COLORS.BRAND_BLACK,
18
- fontSize: FONT_SIZE.h5,
19
- lineHeight: FONT_SIZE.h5 * lineHeightMultiplier,
20
- opacity: 0.6,
21
- marginTop: 4,
32
+ sectionName: {
33
+ color: COLORS.BRAND_1,
34
+ fontSize: FONT_SIZE.h4,
35
+ lineHeight: FONT_SIZE.h4 * lineHeightMultiplier,
22
36
  },
23
37
  headline: {
24
38
  color: COLORS.BRAND_BLACK,
25
39
  flexWrap: 'wrap',
26
40
  fontSize: FONT_SIZE.h3,
41
+ fontFamily: FONT_FAMILY.primaryBold,
27
42
  lineHeight: FONT_SIZE.h3 * lineHeightMultiplier,
28
- },
29
- icon: {
30
- marginLeft: 8,
43
+ marginBottom: 4,
31
44
  },
32
45
  storyTypeContainer: {
33
46
  position: 'absolute',
@@ -61,4 +74,9 @@ export const storyStyles = ({ COLORS, FONT_SIZE, lineHeightMultiplier }) => Styl
61
74
  marginTop: FONT_SIZE.h3 * 0.45,
62
75
  marginRight: 3,
63
76
  },
77
+ separatorLine: {
78
+ height: 0.8,
79
+ opacity: 0.1,
80
+ backgroundColor: COLORS.BRAND_BLACK,
81
+ },
64
82
  });
@@ -2,6 +2,7 @@ import React, { useContext, memo } from 'react';
2
2
  import { TouchableOpacity } from 'react-native';
3
3
  import PropTypes from 'prop-types';
4
4
  import Icon from 'react-native-vector-icons/Ionicons';
5
+ import IconAlt from 'react-native-vector-icons/MaterialCommunityIcons';
5
6
  import Share from 'react-native-share';
6
7
  import { AppTheme } from '../../utils';
7
8
  import { shareButtonStyles } from './styles';
@@ -11,7 +12,7 @@ import { COMP_GENERAL_CONSTANTS } from '../../constants/component-constants';
11
12
  const ShareButtonBase = (props) => {
12
13
  const { theme } = useContext(AppTheme);
13
14
  const styles = shareButtonStyles(theme);
14
- const { enableShareOnStoryCards } = theme;
15
+ const { storyCardOptions } = theme;
15
16
  const { story, type = 'story', slug } = props;
16
17
 
17
18
  const getShareURL = () => {
@@ -48,12 +49,24 @@ const ShareButtonBase = (props) => {
48
49
  return null;
49
50
  }
50
51
 
51
- return enableShareOnStoryCards ? (
52
+ const renderIcon = () => {
53
+ switch (storyCardOptions.shareButtonIcon) {
54
+ case 'right-arrow':
55
+ return <IconAlt style={styles.iconStyle} name="share-outline" size={20} />;
56
+ case 'up-arrow':
57
+ return <Icon style={styles.iconStyle} name="share-outline" size={20} />;
58
+ case 'classic':
59
+ default:
60
+ return <Icon style={styles.iconStyle} name="share-social-outline" size={20} />;
61
+ }
62
+ };
63
+
64
+ return storyCardOptions.enableShareButton ? (
52
65
  <TouchableOpacity
53
66
  onPress={share}
54
67
  testID={COMP_GENERAL_CONSTANTS.shareButtonTouch}
55
68
  >
56
- <Icon style={styles.iconStyle} name="share-social-outline" size={20} />
69
+ {renderIcon()}
57
70
  </TouchableOpacity>
58
71
  ) : null;
59
72
  };
@@ -5,7 +5,7 @@ export const shareButtonStyles = ({ COLORS }) => StyleSheet.create({
5
5
  textAlign: 'center',
6
6
  textAlignVertical: 'center',
7
7
  color: COLORS.BRAND_BLACK,
8
- width: 32,
9
- height: 32,
8
+ width: 24,
9
+ height: 24,
10
10
  },
11
11
  });
@@ -0,0 +1,52 @@
1
+ import { get } from 'lodash';
2
+ import PropTypes from "prop-types";
3
+ import React, { useContext, memo } from "react";
4
+ import { View } from "react-native";
5
+ import { Text } from "../index";
6
+ import { storyCardDetailsRowStyles } from "./styles";
7
+ import { AppTheme, getTimeForStoryCards } from "../../utils";
8
+
9
+ const StoryCardDetailsRowBase = ({ authorName, publishedAt, readTime }) => {
10
+ const { theme } = useContext(AppTheme);
11
+ const styles = storyCardDetailsRowStyles(theme);
12
+ const {
13
+ locale,
14
+ reverseTimeAdverbPosition,
15
+ DATE_TIME_FORMAT,
16
+ storyCardOptions,
17
+ } = theme;
18
+ const translate = get(theme, ["translate"], (word) => word);
19
+
20
+ const authorNameText = authorName;
21
+ const publishedAtText = getTimeForStoryCards(
22
+ publishedAt,
23
+ DATE_TIME_FORMAT.dateFormat,
24
+ locale,
25
+ translate,
26
+ reverseTimeAdverbPosition
27
+ );
28
+ const readTimeText = readTime ? readTime + " " + translate("min read") : "";
29
+ const details = [
30
+ storyCardOptions.enableAuthorName && authorNameText,
31
+ storyCardOptions.enablePublishedAt && publishedAtText,
32
+ storyCardOptions.enableReadTime && readTimeText,
33
+ ].filter((detail) => detail && detail.trim());
34
+
35
+ return (
36
+ <View style={styles.container}>
37
+ {details.map((detail, index) => (
38
+ <Text key={index} style={styles.detailText}>
39
+ {detail}
40
+ {index < details.length - 1 && ' · '}
41
+ </Text>
42
+ ))}
43
+ </View>
44
+ );
45
+ };
46
+
47
+ StoryCardDetailsRowBase.propTypes = {
48
+ authorName: PropTypes.string,
49
+ readTime: PropTypes.string,
50
+ };
51
+
52
+ export const StoryCardDetailsRow = memo(StoryCardDetailsRowBase);
@@ -0,0 +1,14 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export const storyCardDetailsRowStyles = ({ COLORS, FONT_SIZE, lineHeightMultiplier }) => StyleSheet.create({
4
+ container: {
5
+ flexDirection: 'row',
6
+ flexWrap: 'wrap',
7
+ },
8
+ detailText: {
9
+ color: COLORS.BRAND_BLACK,
10
+ fontSize: FONT_SIZE.h5,
11
+ lineHeight: FONT_SIZE.h5 * lineHeightMultiplier,
12
+ opacity: 0.6,
13
+ },
14
+ });
@@ -23,6 +23,7 @@ export const StoryContent = ({
23
23
  const textProps = {
24
24
  text: storyElement.text,
25
25
  attribution: storyElement?.metadata?.attribution,
26
+ navigation
26
27
  };
27
28
 
28
29
  switch (storyElement.type) {
@@ -39,15 +40,18 @@ export const StoryContent = ({
39
40
  return React.createElement(AllComponents.TextBlockQuote, textProps);
40
41
  case STORY_ELEMENT_SUBTYPES.BIGFACT:
41
42
  return React.createElement(AllComponents.TextBigFact, textProps);
42
- case STORY_ELEMENT_SUBTYPES.QUESTION:
43
- return React.createElement(AllComponents.StoryText, {
44
- ...textProps,
45
- question: true,
46
- });
47
43
  case STORY_ELEMENT_SUBTYPES.Q_A:
48
44
  return React.createElement(AllComponents.TextQandA, {
49
- question: stripHTML(storyElement.metadata.question),
50
- answer: stripHTML(storyElement.metadata.answer),
45
+ question: storyElement.metadata.question,
46
+ answer: storyElement.metadata.answer,
47
+ });
48
+ case STORY_ELEMENT_SUBTYPES.QUESTION:
49
+ return React.createElement(AllComponents.TextQ, {
50
+ question: storyElement.text,
51
+ });
52
+ case STORY_ELEMENT_SUBTYPES.ANSWER:
53
+ return React.createElement(AllComponents.TextA, {
54
+ answer: storyElement.text,
51
55
  });
52
56
 
53
57
  case STORY_ELEMENT_SUBTYPES.ALSO_READ: {
@@ -10,7 +10,8 @@ import { customHTMLStyles } from '../../constants/renderHTML';
10
10
  import { storyTextStyles } from './styles';
11
11
 
12
12
  export const StoryText = (props) => {
13
- const { theme } = useContext(AppTheme);
13
+ const {navigation} = props;
14
+ const { theme, useDeeplinkHandler } = useContext(AppTheme);
14
15
  const {
15
16
  COLORS,
16
17
  FONT_FAMILY,
@@ -39,7 +40,7 @@ export const StoryText = (props) => {
39
40
  key={Math.random()}
40
41
  baseFontStyle={style}
41
42
  onLinkPress={(evt, href) => {
42
- Linking.openURL(href);
43
+ useDeeplinkHandler(href, navigation)
43
44
  }}
44
45
  listsPrefixesRenderers={{
45
46
  ul: () => <View style={customTagsStyles.ul} />,
@@ -0,0 +1,55 @@
1
+ import PropTypes from 'prop-types';
2
+ import React, { useContext } from 'react';
3
+ import { View, StyleSheet, Linking,I18nManager } from 'react-native';
4
+ import { StoryText, Text } from '@quintype/native-components/src/components';
5
+ import { textAnswerStyles } from '@quintype/native-components/src/components/TextA/styles';
6
+ import { AppTheme } from '@quintype/native-components/src/utils/context';
7
+ import HTML from 'react-native-render-html';
8
+ import { customHTMLStyles } from '../../constants/renderHTML';
9
+
10
+ export const TextA = ({ answer }) => {
11
+ const { theme } = useContext(AppTheme);
12
+ const { COLORS, FONT_SIZE, lineHeightMultiplier, FONT_FAMILY} = theme;
13
+ const styles = textAnswerStyles(COLORS, FONT_SIZE, lineHeightMultiplier);
14
+ const customTagsStyles = customHTMLStyles();
15
+ const style = StyleSheet.flatten([
16
+ styles.answerText,
17
+ {
18
+ fontFamily: FONT_FAMILY.secondary,
19
+ writingDirection: I18nManager.isRTL ? "rtl" : "ltr",
20
+ },
21
+ ]);
22
+ return (
23
+ <View testID="text-answer" style={styles.container}>
24
+ <View style={styles.iconContainer}>
25
+ <Text style={styles.iconText}>A</Text>
26
+ </View>
27
+ <HTML
28
+ html={answer}
29
+ key={Math.random()}
30
+ baseFontStyle={style}
31
+ onLinkPress={(evt, href) => {
32
+ Linking.openURL(href);
33
+ }}
34
+ tagsStyles={{
35
+ del: customTagsStyles.del,
36
+ ins: customTagsStyles.ins,
37
+ p: customTagsStyles.p,
38
+ li: customTagsStyles.li,
39
+ a: customTagsStyles.a,
40
+ h2: customTagsStyles.h,
41
+ h3: customTagsStyles.h,
42
+ h4: customTagsStyles.h,
43
+ h5: customTagsStyles.h,
44
+ h6: customTagsStyles.h,
45
+ }}
46
+ containerStyle={styles.htmlContainer}
47
+ />
48
+ </View>
49
+ );
50
+ };
51
+
52
+ TextA.propTypes = {
53
+ question: PropTypes.string.isRequired,
54
+ answer: PropTypes.string.isRequired,
55
+ };
@@ -0,0 +1,46 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { isRTLEnabled } from '@quintype/native-components/src/utils';
3
+ export const textAnswerStyles = (COLORS, FONT_SIZE, lineHeightMultiplier) => StyleSheet.create({
4
+ container: {
5
+ margin: 10,
6
+ display:'flex',
7
+ flexDirection:'row',
8
+ alignItems:'center',
9
+ marginBottom:20,
10
+ marginTop:5
11
+ },
12
+ questionText: {
13
+ fontSize: FONT_SIZE.h2,
14
+ fontWeight: '600',
15
+ lineHeight: FONT_SIZE.h2 * lineHeightMultiplier,
16
+ color: COLORS.BRAND_BLACK,
17
+ opacity: 0.8,
18
+ textAlign: isRTLEnabled() ? 'left' : 'auto',
19
+ },
20
+ answerText: {
21
+ fontSize: FONT_SIZE.h2,
22
+ lineHeight: FONT_SIZE.h2 * lineHeightMultiplier,
23
+ color: COLORS.BRAND_BLACK,
24
+ opacity: 0.8,
25
+ },
26
+ iconContainer:{
27
+ borderRadius:5,
28
+ height:10+FONT_SIZE.h1,
29
+ width:10+FONT_SIZE.h1,
30
+ backgroundColor:COLORS.BRAND_1,
31
+ display:'flex',
32
+ justifyContent:'center',
33
+ alignItems:'center',
34
+ marginRight:5,
35
+ alignSelf:'flex-start'
36
+ },
37
+ iconText:{
38
+ fontSize:FONT_SIZE.h1,
39
+ color:COLORS.DYNAMIC_PRIMARY_TEXT_COLOR
40
+ },
41
+ htmlContainer:{
42
+ alignSelf:'flex-start',
43
+ marginTop:-5,
44
+ width:'90%'
45
+ }
46
+ });
@@ -9,7 +9,7 @@ import { customHTMLStyles } from '../../constants/renderHTML';
9
9
  import { alterQuoteData } from '../../utils';
10
10
 
11
11
  export const TextBigFact = ({ text, attribution, id }) => {
12
- const { theme } = useContext(AppTheme);
12
+ const { theme, useDeeplinkHandler } = useContext(AppTheme);
13
13
  const {
14
14
  FONT_FAMILY,
15
15
  COLORS,
@@ -40,7 +40,7 @@ export const TextBigFact = ({ text, attribution, id }) => {
40
40
  key={Math.random()}
41
41
  baseFontStyle={style}
42
42
  onLinkPress={(evt, href) => {
43
- Linking.openURL(href);
43
+ useDeeplinkHandler(href, props?.navigation);
44
44
  }}
45
45
  tagsStyles={{
46
46
  del: customTagsStyles.del,
@@ -10,7 +10,7 @@ import { AppTheme } from '../../utils/context';
10
10
  import { alterQuoteData } from '../../utils';
11
11
 
12
12
  export const TextBlockQuote = ({ text, attribution }) => {
13
- const { theme } = useContext(AppTheme);
13
+ const { theme, useDeeplinkHandler } = useContext(AppTheme);
14
14
  const {
15
15
  FONT_FAMILY, FONT_SIZE, COLORS, CAN_COPY_TEXT, DARK_MODE, lineHeightMultiplier
16
16
  } = theme;
@@ -36,7 +36,7 @@ export const TextBlockQuote = ({ text, attribution }) => {
36
36
  key={Math.random()}
37
37
  baseFontStyle={style}
38
38
  onLinkPress={(evt, href) => {
39
- Linking.openURL(href);
39
+ useDeeplinkHandler(href, props?.navigation);
40
40
  }}
41
41
  tagsStyles={{
42
42
  del: customTagsStyles.del,
@@ -7,7 +7,7 @@ import { AppTheme } from '../../utils/context';
7
7
  import { customHTMLStyles } from '../../constants/renderHTML';
8
8
 
9
9
  export const TextBlurb = ({ text }) => {
10
- const { theme } = useContext(AppTheme);
10
+ const { theme, useDeeplinkHandler } = useContext(AppTheme);
11
11
  const {
12
12
  FONT_FAMILY, COLORS, FONT_SIZE, CAN_COPY_TEXT, lineHeightMultiplier,
13
13
  } = theme;
@@ -35,7 +35,7 @@ export const TextBlurb = ({ text }) => {
35
35
  key={Math.random()}
36
36
  baseFontStyle={style}
37
37
  onLinkPress={(evt, href) => {
38
- Linking.openURL(href);
38
+ useDeeplinkHandler(href, props?.navigation);
39
39
  }}
40
40
  tagsStyles={{
41
41
  del: customTagsStyles.del,
@@ -0,0 +1,54 @@
1
+ import PropTypes from 'prop-types';
2
+ import React, { useContext } from 'react';
3
+ import { View, StyleSheet, Linking, I18nManager } from 'react-native';
4
+ import { Text } from '@quintype/native-components/src/components';
5
+ import { textQuestionStyles } from '@quintype/native-components/src/components/TextQ/styles';
6
+ import { AppTheme } from '@quintype/native-components/src/utils/context';
7
+ import HTML from 'react-native-render-html';
8
+ import { customHTMLStyles } from '../../constants/renderHTML';
9
+ export const TextQ = ({ question }) => {
10
+ const { theme } = useContext(AppTheme);
11
+ const { COLORS, FONT_SIZE, lineHeightMultiplier, FONT_FAMILY } = theme;
12
+ const styles = textQuestionStyles(COLORS, FONT_SIZE, lineHeightMultiplier);
13
+ const customTagsStyles = customHTMLStyles();
14
+ const style = StyleSheet.flatten([
15
+ styles.questionText,
16
+ {
17
+ fontFamily: FONT_FAMILY.primaryBold,
18
+ writingDirection: I18nManager.isRTL ? "rtl" : "ltr",
19
+ },
20
+ ]);
21
+ return (
22
+ <View testID="text-question" style={styles.container}>
23
+ <View style={styles.iconContainer}>
24
+ <Text style={styles.iconText}>Q</Text>
25
+ </View>
26
+ <HTML
27
+ html={question}
28
+ key={Math.random()}
29
+ baseFontStyle={style}
30
+ onLinkPress={(evt, href) => {
31
+ Linking.openURL(href);
32
+ }}
33
+ tagsStyles={{
34
+ del: customTagsStyles.del,
35
+ ins: customTagsStyles.ins,
36
+ p: customTagsStyles.p,
37
+ li: customTagsStyles.li,
38
+ a: customTagsStyles.a,
39
+ h2: customTagsStyles.h,
40
+ h3: customTagsStyles.h,
41
+ h4: customTagsStyles.h,
42
+ h5: customTagsStyles.h,
43
+ h6: customTagsStyles.h,
44
+ }}
45
+ containerStyle={styles.htmlContainer}
46
+ />
47
+ </View>
48
+ );
49
+ };
50
+
51
+ TextQ.propTypes = {
52
+ question: PropTypes.string.isRequired,
53
+ answer: PropTypes.string.isRequired,
54
+ };
@@ -0,0 +1,46 @@
1
+ import { isRTLEnabled } from '@quintype/native-components/src/utils';
2
+ import { StyleSheet } from 'react-native';
3
+
4
+ export const textQuestionStyles = (COLORS, FONT_SIZE, lineHeightMultiplier) => StyleSheet.create({
5
+ container: {
6
+ margin: 10,
7
+ display:'flex',
8
+ flexDirection:'row',
9
+ alignItems:'center',
10
+ marginBottom:0,
11
+ marginTop:0
12
+ },
13
+ questionText: {
14
+ fontSize: FONT_SIZE.h2,
15
+ lineHeight: FONT_SIZE.h2 * lineHeightMultiplier,
16
+ color: COLORS.BRAND_BLACK,
17
+ opacity: 0.8,
18
+ textAlign: isRTLEnabled() ? 'left' : 'auto',
19
+ },
20
+ answerText: {
21
+ fontSize: FONT_SIZE.h2,
22
+ lineHeight: FONT_SIZE.h2 * lineHeightMultiplier,
23
+ color: COLORS.BRAND_BLACK,
24
+ opacity: 0.8,
25
+ },
26
+ iconContainer:{
27
+ borderRadius:5,
28
+ height:10+FONT_SIZE.h1,
29
+ width:10+FONT_SIZE.h1,
30
+ backgroundColor:COLORS.BRAND_1,
31
+ display:'flex',
32
+ justifyContent:'center',
33
+ alignItems:'center',
34
+ marginRight:5,
35
+ alignSelf:'flex-start'
36
+ },
37
+ iconText:{
38
+ fontSize:FONT_SIZE.h1,
39
+ color:COLORS.DYNAMIC_PRIMARY_TEXT_COLOR
40
+ },
41
+ htmlContainer:{
42
+ alignSelf:'flex-start',
43
+ marginTop:-10,
44
+ width:'90%'
45
+ }
46
+ });
@@ -4,6 +4,8 @@ import { View } from 'react-native';
4
4
  import { Text } from '../index';
5
5
  import { textQandAStyles } from './styles';
6
6
  import { AppTheme } from '../../utils/context';
7
+ import { TextQ } from '../TextQ/index';
8
+ import { TextA } from '../TextA/index';
7
9
 
8
10
  export const TextQandA = ({ question, answer }) => {
9
11
  const { theme } = useContext(AppTheme);
@@ -11,8 +13,8 @@ export const TextQandA = ({ question, answer }) => {
11
13
  const styles = textQandAStyles(COLORS, FONT_SIZE, lineHeightMultiplier);
12
14
  return (
13
15
  <View testID="text-q-and-a" style={styles.container}>
14
- <Text style={styles.questionText}>{question}</Text>
15
- <Text style={styles.answerText}>{answer}</Text>
16
+ {question && <TextQ question={question}/>}
17
+ {answer && <TextA answer={answer}/>}
16
18
  </View>
17
19
  );
18
20
  };
@@ -2,19 +2,6 @@ import { StyleSheet } from 'react-native';
2
2
 
3
3
  export const textQandAStyles = (COLORS, FONT_SIZE, lineHeightMultiplier) => StyleSheet.create({
4
4
  container: {
5
- margin: 10,
6
- },
7
- questionText: {
8
- fontSize: FONT_SIZE.h2,
9
- fontWeight: '600',
10
- lineHeight: FONT_SIZE.h2 * lineHeightMultiplier,
11
- color: COLORS.BRAND_BLACK,
12
- opacity: 0.8,
13
- },
14
- answerText: {
15
- fontSize: FONT_SIZE.h2,
16
- lineHeight: FONT_SIZE.h2 * lineHeightMultiplier,
17
- color: COLORS.BRAND_BLACK,
18
- opacity: 0.8,
19
- },
5
+ margin: 0
6
+ }
20
7
  });
@@ -44,3 +44,6 @@ export { IconText } from './IconText';
44
44
  export { CustomSwitch } from './CustomSwitch';
45
45
  export { RelatedStoriesCard } from './RelatedStoriesCard';
46
46
  export { References } from './References';
47
+ export { StoryCardDetailsRow } from './StoryCardDetailsRow';
48
+ export { TextA } from './TextA'
49
+ export { TextQ } from './TextQ'