@umituz/react-native-subscription 2.27.144 → 2.27.145

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.27.144",
3
+ "version": "2.27.145",
4
4
  "description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,30 @@
1
+ import { StyleSheet } from "react-native";
2
+
3
+ export const transactionItemStyles = StyleSheet.create({
4
+ container: {
5
+ flexDirection: "row",
6
+ alignItems: "center",
7
+ padding: 12,
8
+ borderRadius: 12,
9
+ marginBottom: 8,
10
+ },
11
+ iconContainer: {
12
+ width: 40,
13
+ height: 40,
14
+ borderRadius: 20,
15
+ justifyContent: "center",
16
+ alignItems: "center",
17
+ marginRight: 12,
18
+ },
19
+ content: {
20
+ flex: 1,
21
+ gap: 2,
22
+ },
23
+ reason: {
24
+ fontWeight: "600",
25
+ },
26
+ change: {
27
+ fontWeight: "700",
28
+ marginLeft: 12,
29
+ },
30
+ });
@@ -1,41 +1,12 @@
1
- /**
2
- * Transaction Item Component
3
- *
4
- * Displays a single credit transaction.
5
- * Props-driven for full customization.
6
- */
7
-
8
1
  import React, { useMemo } from "react";
9
- import { View, StyleSheet } from "react-native";
10
- import {
11
- useAppDesignTokens,
12
- AtomicText,
13
- AtomicIcon,
14
- } from "@umituz/react-native-design-system";
15
- import { timezoneService } from "@umituz/react-native-design-system";
16
- import type { CreditLog } from "../../domain/types/transaction.types";
2
+ import { View } from "react-native";
3
+ import { useAppDesignTokens, AtomicText, AtomicIcon } from "@umituz/react-native-design-system";
17
4
  import { getTransactionIcon } from "../../utils/transactionIconMap";
5
+ import { transactionItemStyles } from "./TransactionItem.styles";
6
+ import type { TransactionItemProps } from "./TransactionItem.types";
7
+ import { defaultDateFormatter, getReasonLabel, getChangePrefix } from "./transactionItemHelpers";
18
8
 
19
- export interface TransactionItemTranslations {
20
- purchase: string;
21
- usage: string;
22
- refund: string;
23
- bonus: string;
24
- subscription: string;
25
- admin: string;
26
- reward: string;
27
- expired: string;
28
- }
29
-
30
- export interface TransactionItemProps {
31
- transaction: CreditLog;
32
- translations: TransactionItemTranslations;
33
- dateFormatter?: (timestamp: number) => string;
34
- }
35
-
36
- const defaultDateFormatter = (timestamp: number): string => {
37
- return timezoneService.formatToDisplayDateTime(new Date(timestamp));
38
- };
9
+ export type { TransactionItemTranslations, TransactionItemProps } from "./TransactionItem.types";
39
10
 
40
11
  export const TransactionItem: React.FC<TransactionItemProps> = ({
41
12
  transaction,
@@ -44,89 +15,34 @@ export const TransactionItem: React.FC<TransactionItemProps> = ({
44
15
  }) => {
45
16
  const tokens = useAppDesignTokens();
46
17
 
47
- const reasonLabel = useMemo(() => {
48
- return translations[transaction.reason] || transaction.reason;
49
- }, [transaction.reason, translations]);
18
+ const reasonLabel = useMemo(() => getReasonLabel(transaction.reason, translations), [transaction.reason, translations]);
50
19
 
51
20
  const isPositive = transaction.change > 0;
52
21
  const changeColor = isPositive ? tokens.colors.success : tokens.colors.error;
53
- const changePrefix = isPositive ? "+" : "";
22
+ const changePrefix = getChangePrefix(transaction.change);
54
23
  const iconName = getTransactionIcon(transaction.reason);
55
24
 
56
25
  return (
57
- <View
58
- style={[
59
- styles.container,
60
- { backgroundColor: tokens.colors.surfaceSecondary },
61
- ]}
62
- >
63
- <View
64
- style={[
65
- styles.iconContainer,
66
- { backgroundColor: tokens.colors.surface },
67
- ]}
68
- >
26
+ <View style={[transactionItemStyles.container, { backgroundColor: tokens.colors.surfaceSecondary }]}>
27
+ <View style={[transactionItemStyles.iconContainer, { backgroundColor: tokens.colors.surface }]}>
69
28
  <AtomicIcon name={iconName} size="md" color="secondary" />
70
29
  </View>
71
- <View style={styles.content}>
72
- <AtomicText
73
- type="bodyMedium"
74
- style={[styles.reason, { color: tokens.colors.textPrimary }]}
75
- >
30
+ <View style={transactionItemStyles.content}>
31
+ <AtomicText type="bodyMedium" style={[transactionItemStyles.reason, { color: tokens.colors.textPrimary }]}>
76
32
  {reasonLabel}
77
33
  </AtomicText>
78
34
  {transaction.description && (
79
- <AtomicText
80
- type="bodySmall"
81
- style={{ color: tokens.colors.textSecondary }}
82
- numberOfLines={1}
83
- >
35
+ <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary }} numberOfLines={1}>
84
36
  {transaction.description}
85
37
  </AtomicText>
86
38
  )}
87
- <AtomicText
88
- type="bodySmall"
89
- style={{ color: tokens.colors.textSecondary }}
90
- >
39
+ <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary }}>
91
40
  {dateFormatter(transaction.createdAt)}
92
41
  </AtomicText>
93
42
  </View>
94
- <AtomicText
95
- type="titleMedium"
96
- style={[styles.change, { color: changeColor }]}
97
- >
98
- {changePrefix}
99
- {transaction.change}
43
+ <AtomicText type="titleMedium" style={[transactionItemStyles.change, { color: changeColor }]}>
44
+ {changePrefix}{transaction.change}
100
45
  </AtomicText>
101
46
  </View>
102
47
  );
103
48
  };
104
-
105
- const styles = StyleSheet.create({
106
- container: {
107
- flexDirection: "row",
108
- alignItems: "center",
109
- padding: 12,
110
- borderRadius: 12,
111
- marginBottom: 8,
112
- },
113
- iconContainer: {
114
- width: 40,
115
- height: 40,
116
- borderRadius: 20,
117
- justifyContent: "center",
118
- alignItems: "center",
119
- marginRight: 12,
120
- },
121
- content: {
122
- flex: 1,
123
- gap: 2,
124
- },
125
- reason: {
126
- fontWeight: "600",
127
- },
128
- change: {
129
- fontWeight: "700",
130
- marginLeft: 12,
131
- },
132
- });
@@ -0,0 +1,18 @@
1
+ import type { CreditLog } from "../../domain/types/transaction.types";
2
+
3
+ export interface TransactionItemTranslations {
4
+ purchase: string;
5
+ usage: string;
6
+ refund: string;
7
+ bonus: string;
8
+ subscription: string;
9
+ admin: string;
10
+ reward: string;
11
+ expired: string;
12
+ }
13
+
14
+ export interface TransactionItemProps {
15
+ transaction: CreditLog;
16
+ translations: TransactionItemTranslations;
17
+ dateFormatter?: (timestamp: number) => string;
18
+ }
@@ -0,0 +1 @@
1
+ export const DEFAULT_TRANSACTION_LIST_MAX_HEIGHT = 400;
@@ -0,0 +1,34 @@
1
+ import { StyleSheet } from "react-native";
2
+
3
+ export const transactionListStyles = StyleSheet.create({
4
+ container: {
5
+ marginTop: 24,
6
+ marginBottom: 24,
7
+ },
8
+ header: {
9
+ flexDirection: "row",
10
+ justifyContent: "space-between",
11
+ alignItems: "center",
12
+ marginHorizontal: 16,
13
+ marginBottom: 16,
14
+ },
15
+ title: {
16
+ fontSize: 20,
17
+ fontWeight: "700",
18
+ },
19
+ scrollView: {
20
+ paddingHorizontal: 16,
21
+ },
22
+ scrollContent: {
23
+ paddingBottom: 8,
24
+ },
25
+ stateContainer: {
26
+ padding: 40,
27
+ alignItems: "center",
28
+ gap: 12,
29
+ },
30
+ stateText: {
31
+ fontSize: 14,
32
+ fontWeight: "500",
33
+ },
34
+ });
@@ -1,23 +1,11 @@
1
- /**
2
- * Transaction List Component
3
- *
4
- * Displays a list of credit transactions.
5
- * Props-driven for full customization.
6
- */
7
-
8
1
  import React from "react";
9
- import { View, StyleSheet, ScrollView } from "react-native";
10
- import {
11
- useAppDesignTokens,
12
- AtomicText,
13
- AtomicIcon,
14
- AtomicSpinner,
15
- } from "@umituz/react-native-design-system";
2
+ import { View, ScrollView } from "react-native";
3
+ import { useAppDesignTokens, AtomicText, AtomicIcon } from "@umituz/react-native-design-system";
16
4
  import type { CreditLog } from "../../domain/types/transaction.types";
17
- import {
18
- TransactionItem,
19
- type TransactionItemTranslations,
20
- } from "./TransactionItem";
5
+ import { TransactionItem, type TransactionItemTranslations } from "./TransactionItem";
6
+ import { transactionListStyles } from "./TransactionList.styles";
7
+ import { LoadingState, EmptyState } from "./TransactionListStates";
8
+ import { DEFAULT_TRANSACTION_LIST_MAX_HEIGHT } from "./TransactionList.constants";
21
9
 
22
10
  export interface TransactionListTranslations extends TransactionItemTranslations {
23
11
  title: string;
@@ -37,89 +25,35 @@ export const TransactionList: React.FC<TransactionListProps> = ({
37
25
  transactions,
38
26
  loading,
39
27
  translations,
40
- maxHeight = 400,
28
+ maxHeight = DEFAULT_TRANSACTION_LIST_MAX_HEIGHT,
41
29
  dateFormatter,
42
30
  }) => {
43
31
  const tokens = useAppDesignTokens();
44
32
 
45
33
  return (
46
- <View style={styles.container}>
47
- <View style={styles.header}>
48
- <AtomicText
49
- type="titleLarge"
50
- style={[styles.title, { color: tokens.colors.textPrimary }]}
51
- >
34
+ <View style={transactionListStyles.container}>
35
+ <View style={transactionListStyles.header}>
36
+ <AtomicText type="titleLarge" style={[transactionListStyles.title, { color: tokens.colors.textPrimary }]}>
52
37
  {translations.title}
53
38
  </AtomicText>
54
39
  <AtomicIcon name="time-outline" size="md" color="secondary" />
55
40
  </View>
56
41
 
57
42
  {loading ? (
58
- <AtomicSpinner
59
- size="lg"
60
- color="primary"
61
- text={translations.loading}
62
- style={styles.stateContainer}
63
- />
43
+ <LoadingState message={translations.loading} />
64
44
  ) : transactions.length === 0 ? (
65
- <View style={styles.stateContainer}>
66
- <AtomicIcon name="file-tray-outline" size="xl" color="secondary" />
67
- <AtomicText
68
- type="bodyMedium"
69
- style={[styles.stateText, { color: tokens.colors.textSecondary }]}
70
- >
71
- {translations.empty}
72
- </AtomicText>
73
- </View>
45
+ <EmptyState message={translations.empty} />
74
46
  ) : (
75
47
  <ScrollView
76
- style={[styles.scrollView, { maxHeight }]}
77
- contentContainerStyle={styles.scrollContent}
48
+ style={[transactionListStyles.scrollView, { maxHeight }]}
49
+ contentContainerStyle={transactionListStyles.scrollContent}
78
50
  showsVerticalScrollIndicator={false}
79
51
  >
80
52
  {transactions.map((transaction) => (
81
- <TransactionItem
82
- key={transaction.id}
83
- transaction={transaction}
84
- translations={translations}
85
- dateFormatter={dateFormatter}
86
- />
53
+ <TransactionItem key={transaction.id} transaction={transaction} translations={translations} dateFormatter={dateFormatter} />
87
54
  ))}
88
55
  </ScrollView>
89
56
  )}
90
57
  </View>
91
58
  );
92
59
  };
93
-
94
- const styles = StyleSheet.create({
95
- container: {
96
- marginTop: 24,
97
- marginBottom: 24,
98
- },
99
- header: {
100
- flexDirection: "row",
101
- justifyContent: "space-between",
102
- alignItems: "center",
103
- marginHorizontal: 16,
104
- marginBottom: 16,
105
- },
106
- title: {
107
- fontSize: 20,
108
- fontWeight: "700",
109
- },
110
- scrollView: {
111
- paddingHorizontal: 16,
112
- },
113
- scrollContent: {
114
- paddingBottom: 8,
115
- },
116
- stateContainer: {
117
- padding: 40,
118
- alignItems: "center",
119
- gap: 12,
120
- },
121
- stateText: {
122
- fontSize: 14,
123
- fontWeight: "500",
124
- },
125
- });
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { useAppDesignTokens, AtomicText, AtomicIcon, AtomicSpinner } from "@umituz/react-native-design-system";
4
+ import { transactionListStyles } from "./TransactionList.styles";
5
+
6
+ interface LoadingStateProps {
7
+ message: string;
8
+ }
9
+
10
+ export const LoadingState: React.FC<LoadingStateProps> = ({ message }) => (
11
+ <AtomicSpinner size="lg" color="primary" text={message} style={transactionListStyles.stateContainer} />
12
+ );
13
+
14
+ interface EmptyStateProps {
15
+ message: string;
16
+ }
17
+
18
+ export const EmptyState: React.FC<EmptyStateProps> = ({ message }) => {
19
+ const tokens = useAppDesignTokens();
20
+ return (
21
+ <View style={transactionListStyles.stateContainer}>
22
+ <AtomicIcon name="file-tray-outline" size="xl" color="secondary" />
23
+ <AtomicText type="bodyMedium" style={[transactionListStyles.stateText, { color: tokens.colors.textSecondary }]}>
24
+ {message}
25
+ </AtomicText>
26
+ </View>
27
+ );
28
+ };
@@ -0,0 +1,14 @@
1
+ import { timezoneService } from "@umituz/react-native-design-system";
2
+ import type { TransactionItemTranslations } from "./TransactionItem.types";
3
+
4
+ export const defaultDateFormatter = (timestamp: number): string => {
5
+ return timezoneService.formatToDisplayDateTime(new Date(timestamp));
6
+ };
7
+
8
+ export const getReasonLabel = (reason: string, translations: TransactionItemTranslations): string => {
9
+ return translations[reason as keyof TransactionItemTranslations] || reason;
10
+ };
11
+
12
+ export const getChangePrefix = (change: number): string => {
13
+ return change > 0 ? "+" : "";
14
+ };