@moneylion/react-native-offer-carousel 1.10.2 → 1.11.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.
Files changed (25) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/commonjs/capabilities/ui/elements/src/components/MarkdownText/components.js +114 -122
  3. package/lib/commonjs/capabilities/ui/elements/src/components/MarkdownText/components.js.map +1 -1
  4. package/lib/commonjs/capabilities/ui/elements/src/components/MarkdownText/index.js +8 -30
  5. package/lib/commonjs/capabilities/ui/elements/src/components/MarkdownText/index.js.map +1 -1
  6. package/lib/commonjs/components/DynamicOffers/Render/DynamicOffersRender.js +1 -1
  7. package/lib/commonjs/components/DynamicOffers/Render/DynamicOffersRender.js.map +1 -1
  8. package/lib/commonjs/version.js +1 -1
  9. package/lib/module/capabilities/ui/elements/src/components/MarkdownText/components.js +114 -121
  10. package/lib/module/capabilities/ui/elements/src/components/MarkdownText/components.js.map +1 -1
  11. package/lib/module/capabilities/ui/elements/src/components/MarkdownText/index.js +9 -31
  12. package/lib/module/capabilities/ui/elements/src/components/MarkdownText/index.js.map +1 -1
  13. package/lib/module/components/DynamicOffers/Render/DynamicOffersRender.js +2 -2
  14. package/lib/module/components/DynamicOffers/Render/DynamicOffersRender.js.map +1 -1
  15. package/lib/module/version.js +1 -1
  16. package/lib/typescript/capabilities/ui/elements/src/components/MarkdownText/components.d.ts +12 -3
  17. package/lib/typescript/capabilities/ui/elements/src/components/MarkdownText/components.d.ts.map +1 -1
  18. package/lib/typescript/capabilities/ui/elements/src/components/MarkdownText/index.d.ts.map +1 -1
  19. package/lib/typescript/components/DynamicOffers/Render/DynamicOffersRender.d.ts.map +1 -1
  20. package/lib/typescript/version.d.ts +1 -1
  21. package/package.json +3 -3
  22. package/src/capabilities/ui/elements/src/components/MarkdownText/components.tsx +148 -154
  23. package/src/capabilities/ui/elements/src/components/MarkdownText/index.tsx +11 -33
  24. package/src/components/DynamicOffers/Render/DynamicOffersRender.tsx +1 -2
  25. package/src/version.ts +1 -1
@@ -1,11 +1,7 @@
1
1
  import React from "react";
2
- import { StyleSheet } from "react-native";
2
+ import { Linking, StyleSheet } from "react-native";
3
3
  import { Abbreviation } from "./Abbreviation";
4
- import {
5
- openUrl,
6
- type ASTNode,
7
- type RenderRules,
8
- } from "react-native-markdown-display";
4
+ import { lexer } from "marked";
9
5
  import Text, {
10
6
  type TextProps,
11
7
  type TextWeight,
@@ -39,152 +35,161 @@ const BoldText = ({
39
35
  return <Text testID={testID}>{processChildren(children)}</Text>;
40
36
  };
41
37
 
42
- // Helper to create text variants
43
- const createTextVariant =
44
- (props: TextProps) => (node: ASTNode, children: React.ReactNode) => {
45
- return (
46
- <Text
47
- key={node.key || `${node.type}-${node.index}`}
48
- {...props}
49
- testID={`${node.type}-${node.index}`}
50
- >
51
- {children}
52
- </Text>
53
- );
54
- };
55
-
56
- export const createMarkdownComponents = ({
57
- numberOfLines,
58
- variant,
59
- weight,
60
- color,
61
- }: {
38
+ export type RenderProps = {
62
39
  numberOfLines?: number;
63
40
  variant?: Variant;
64
41
  weight?: TextWeight;
65
42
  color?: keyof ThemeColors;
66
- }): RenderRules => {
67
- // Keep track of the first list item
68
- let isFirstListItem = true;
69
-
70
- return {
71
- // Headings
72
- heading1: createTextVariant({ variant: "title-1" }),
73
- heading2: createTextVariant({ variant: "title-2" }),
74
- heading3: createTextVariant({ variant: "title-3" }),
75
- heading4: createTextVariant({ variant: "title-4" }),
76
- heading5: createTextVariant({ variant: "title-5" }),
77
-
78
- // Lists
79
- bullet_list: (node: ASTNode, children: React.ReactNode) => {
80
- // Reset the flag when a new list starts
81
- isFirstListItem = true;
82
- return (
83
- <Text
84
- key={node.key || `${node.type}-${node.index}`}
85
- variant={variant}
86
- weight={weight}
87
- color={color}
88
- numberOfLines={numberOfLines}
89
- style={styles.bulletList}
90
- testID={`${node.type}-${node.index}`}
91
- >
92
- {children}
93
- </Text>
94
- );
95
- },
96
-
97
- // Links - Add detection for abbreviations in the text content
98
- link: (node: ASTNode, children: React.ReactNode) => {
99
- // Check if this link has a title attribute - if so, treat it as an abbreviation
100
- if (node.attributes && node.attributes.title) {
43
+ };
44
+
45
+ // infer the type based on our entry point for rendering tokens
46
+ type AssumedTokenType = ReturnType<typeof lexer>[number];
47
+
48
+ /**
49
+ * Parses `content` and renders the marked token tree to React Native
50
+ * components.
51
+ *
52
+ * Recursively renders a marked token tree to React Native components. Other
53
+ * methods from marked are not suitable, as we need to build a component node
54
+ * and thread all the children into the parent element.
55
+ */
56
+ export function renderMarkdown(
57
+ content: string,
58
+ { numberOfLines, ...props }: RenderProps
59
+ ): React.ReactNode {
60
+ const tokens = lexer(content, { gfm: false });
61
+ let nodeIdx = 0;
62
+
63
+ function renderTokens(
64
+ tokenList: AssumedTokenType[] | undefined
65
+ ): React.ReactNode {
66
+ if (!tokenList || tokenList.length === 0) {
67
+ return null;
68
+ }
69
+ return tokenList.map((token) => renderToken(token));
70
+ }
71
+
72
+ function renderToken(token: AssumedTokenType): React.ReactNode {
73
+ const id = nodeIdx++;
74
+ const key = `${token.type}-${id}`;
75
+
76
+ // general reference to child tokens for DRY-ness
77
+ const childTokens =
78
+ "tokens" in token && token.tokens ? token.tokens : undefined;
79
+
80
+ switch (token.type) {
81
+ case "heading": {
82
+ const variant = `title-${token.depth}` as Variant;
83
+
101
84
  return (
102
- <Abbreviation
103
- key={node.key || `${node.type}-${node.index}`}
104
- title={node.attributes.title}
85
+ <Text
86
+ key={key}
87
+ variant={variant}
88
+ testID={`heading${token.depth}-${id}`}
105
89
  >
106
- {children}
107
- </Abbreviation>
90
+ {renderTokens(childTokens)}
91
+ </Text>
108
92
  );
109
93
  }
110
94
 
111
- return (
112
- <Text
113
- key={node.key || `${node.type}-${node.index}`}
114
- variant={variant}
115
- weight={weight}
116
- color={color}
117
- style={styles.linkText}
118
- onPress={() => openUrl(node.attributes.href || "")}
119
- testID={`${node.type}-${node.index}`}
120
- >
121
- {children}
122
- </Text>
123
- );
124
- },
125
-
126
- // Custom parser for text to find potential abbreviation patterns
127
- text: (node: ASTNode) => {
128
- // If this implementation doesn't catch abbreviations, we'll need to
129
- // pre-process the markdown before passing it to the renderer
130
- return (
131
- <Text
132
- key={node.key || `${node.type}-${node.index}`}
133
- variant={variant}
134
- weight={weight}
135
- color={color}
136
- >
137
- {node.content}
138
- </Text>
139
- );
140
- },
141
-
142
- // List items
143
- list_item: (node: ASTNode, children: React.ReactNode) => {
144
- // Only add \n if this is not the first item
145
- const prefix = isFirstListItem ? "" : "\n";
146
- // Set flag to false after the first item
147
- isFirstListItem = false;
148
-
149
- return (
150
- <Text
151
- key={node.key || `${node.type}-${node.index}`}
152
- variant={variant || "body-3"}
153
- weight={weight}
154
- color={color}
155
- testID={`${node.type}-${node.index}`}
156
- >
157
- <Text variant={"body-1"}>{prefix}• </Text>
158
- {children}
159
- </Text>
160
- );
161
- },
162
-
163
- // Paragraphs
164
- paragraph: (node: ASTNode, children: React.ReactNode) => {
165
- return (
166
- <Text
167
- key={node.key || `${node.type}-${node.index}`}
168
- variant={variant}
169
- weight={weight}
170
- color={color}
171
- testID={`${node.type}-${node.index}`}
172
- >
173
- {children}
174
- </Text>
175
- );
176
- },
177
-
178
- strong: (node, children) => (
179
- <BoldText
180
- key={node.key || `${node.type}-${node.index}`}
181
- testID={`${node.type}-${node.index}`}
182
- >
183
- {children}
184
- </BoldText>
185
- ),
186
- };
187
- };
95
+ case "paragraph":
96
+ return (
97
+ <Text key={key} {...props} testID={`paragraph-${id}`}>
98
+ {renderTokens(childTokens)}
99
+ </Text>
100
+ );
101
+
102
+ case "list": {
103
+ // attempt to assert child items as an array, since it is typed as any
104
+ const tokenItems: AssumedTokenType[] =
105
+ token.items && Array.isArray(token.items) ? token.items : [];
106
+
107
+ const renderedItems = tokenItems.map((item, i) => {
108
+ const itemId = nodeIdx++;
109
+ const prefix = i === 0 ? "" : "\n";
110
+
111
+ return (
112
+ <Text
113
+ key={`list_item-${itemId}`}
114
+ {...props}
115
+ variant={props.variant || "body-3"}
116
+ testID={`list_item-${itemId}`}
117
+ >
118
+ <Text variant="body-1">{prefix}• </Text>
119
+ {"tokens" in item ? renderTokens(item.tokens) : null}
120
+ </Text>
121
+ );
122
+ });
123
+
124
+ return (
125
+ <Text
126
+ key={key}
127
+ {...props}
128
+ numberOfLines={numberOfLines}
129
+ style={styles.bulletList}
130
+ testID={`bullet_list-${id}`}
131
+ >
132
+ {renderedItems}
133
+ </Text>
134
+ );
135
+ }
136
+
137
+ case "strong":
138
+ return (
139
+ <BoldText key={key} testID={`strong-${id}`}>
140
+ {renderTokens(childTokens)}
141
+ </BoldText>
142
+ );
143
+
144
+ case "text": {
145
+ const inlineChildren = renderTokens(childTokens);
146
+ if (inlineChildren) {
147
+ return <React.Fragment key={key}>{inlineChildren}</React.Fragment>;
148
+ }
149
+
150
+ return (
151
+ <Text key={key} {...props}>
152
+ {token.text}
153
+ </Text>
154
+ );
155
+ }
156
+
157
+ case "escape":
158
+ return (
159
+ <Text key={key} {...props}>
160
+ {token.text}
161
+ </Text>
162
+ );
163
+
164
+ case "link": {
165
+ if (token.title) {
166
+ return (
167
+ <Abbreviation key={key} title={token.title}>
168
+ {renderTokens(childTokens)}
169
+ </Abbreviation>
170
+ );
171
+ }
172
+
173
+ return (
174
+ <Text
175
+ key={key}
176
+ {...props}
177
+ style={styles.linkText}
178
+ onPress={() => Linking.openURL(token.href)}
179
+ testID={`link-${id}`}
180
+ >
181
+ {renderTokens(childTokens)}
182
+ </Text>
183
+ );
184
+ }
185
+
186
+ default:
187
+ return null;
188
+ }
189
+ }
190
+
191
+ return renderTokens(tokens);
192
+ }
188
193
 
189
194
  const styles = StyleSheet.create({
190
195
  bulletList: {
@@ -192,17 +197,6 @@ const styles = StyleSheet.create({
192
197
  flexDirection: "column",
193
198
  alignItems: "flex-start",
194
199
  },
195
- bulletPoint: {
196
- width: 6,
197
- height: 6,
198
- borderRadius: 3,
199
- backgroundColor: "black",
200
- marginTop: 8,
201
- marginRight: 8,
202
- },
203
- listItemText: {
204
- marginBottom: 4,
205
- },
206
200
  linkText: {
207
201
  textDecorationLine: "underline",
208
202
  },
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
- import { createMarkdownComponents } from "./components";
2
+ import { View } from "react-native";
3
+ import { renderMarkdown } from "./components";
3
4
  import { preprocessMarkdown } from "./markdownPreprocessor";
4
- import Markdown from "react-native-markdown-display";
5
5
  import type { TextProps } from "../../../../../../components/Text";
6
6
 
7
7
  export interface Props extends TextProps {
@@ -15,38 +15,16 @@ export const MarkdownText = ({
15
15
  weight,
16
16
  variant,
17
17
  }: Props) => {
18
- // Create style object for text properties
19
- const textStyle = {
20
- color: color,
21
- };
18
+ const preparedContent = preprocessMarkdown(content);
22
19
 
23
- // Prepare styles for markdown
24
- const markdownStyles = {
25
- body: {
26
- ...textStyle,
27
- flexShrink: 1,
28
- },
29
- paragraph: {
30
- flexShrink: 1,
31
- },
32
- // Include styles for all markdown elements that need to respect text properties
33
- text: {
34
- ...textStyle,
35
- },
36
- };
37
-
38
- // Use the factory function to create components with numberOfLines support
39
- const customRules = createMarkdownComponents({
40
- numberOfLines,
41
- variant,
42
- weight,
43
- color,
44
- });
45
- // Preprocess the markdown to handle abbreviations
46
- const processedContent = preprocessMarkdown(content);
47
20
  return (
48
- <Markdown rules={customRules} style={markdownStyles}>
49
- {processedContent}
50
- </Markdown>
21
+ <View style={{ flexShrink: 1 }}>
22
+ {renderMarkdown(preparedContent, {
23
+ numberOfLines,
24
+ color,
25
+ weight,
26
+ variant,
27
+ })}
28
+ </View>
51
29
  );
52
30
  };
@@ -8,7 +8,6 @@ import React, {
8
8
  import { productTypeBuilder as builder } from "../../../builder/builder";
9
9
  import { Offer } from "./Offer";
10
10
  import {
11
- InteractionManager,
12
11
  ScrollView,
13
12
  StyleSheet,
14
13
  View,
@@ -243,7 +242,7 @@ export const DynamicOffersRender = ({
243
242
  setScrollViewWidth(width);
244
243
 
245
244
  // Do an initial visibility check after layout
246
- InteractionManager.runAfterInteractions(() => {
245
+ requestAnimationFrame(() => {
247
246
  checkVisibleOffers();
248
247
  initialCheckDone.current = true;
249
248
  });
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Version is kept in sync with package.json via the sync-version script
2
- export const VERSION = "1.10.2";
2
+ export const VERSION = "1.11.1";