@umituz/react-native-settings 4.19.5 → 4.20.0

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 (37) hide show
  1. package/package.json +24 -22
  2. package/src/domains/about/presentation/components/AboutContent.tsx +2 -2
  3. package/src/domains/about/presentation/components/AboutHeader.tsx +2 -2
  4. package/src/domains/about/presentation/components/AboutSettingItem.tsx +2 -2
  5. package/src/domains/about/presentation/screens/AboutScreen.tsx +2 -2
  6. package/src/domains/appearance/presentation/components/ColorPicker.tsx +3 -3
  7. package/src/domains/appearance/presentation/components/ThemeOption.tsx +3 -3
  8. package/src/domains/appearance/presentation/screens/AppearanceScreen.tsx +3 -3
  9. package/src/domains/faqs/domain/entities/FAQEntity.ts +7 -0
  10. package/src/domains/faqs/domain/services/FAQSearchService.ts +33 -10
  11. package/src/domains/faqs/presentation/components/FAQCategory.tsx +17 -8
  12. package/src/domains/faqs/presentation/components/FAQEmptyState.tsx +2 -2
  13. package/src/domains/faqs/presentation/components/FAQItem.tsx +36 -21
  14. package/src/domains/faqs/presentation/components/FAQSearchBar.tsx +12 -11
  15. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +37 -27
  16. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +2 -2
  17. package/src/domains/feedback/presentation/components/FeedbackModal.tsx +2 -2
  18. package/src/domains/legal/presentation/screens/LegalScreen.tsx +2 -2
  19. package/src/domains/legal/presentation/screens/PrivacyPolicyScreen.tsx +2 -2
  20. package/src/domains/legal/presentation/screens/TermsOfServiceScreen.tsx +2 -2
  21. package/src/domains/video-tutorials/index.ts +8 -0
  22. package/src/domains/video-tutorials/infrastructure/services/video-tutorial.service.ts +117 -0
  23. package/src/domains/video-tutorials/presentation/components/VideoTutorialCard.tsx +191 -0
  24. package/src/domains/video-tutorials/presentation/hooks/index.ts +36 -0
  25. package/src/domains/video-tutorials/presentation/screens/VideoTutorialsScreen.tsx +198 -0
  26. package/src/domains/video-tutorials/types/index.ts +36 -0
  27. package/src/index.ts +4 -1
  28. package/src/presentation/components/DevSettingsSection.tsx +2 -2
  29. package/src/presentation/components/SettingItem.tsx +2 -2
  30. package/src/presentation/components/SettingsErrorBoundary.tsx +2 -2
  31. package/src/presentation/components/SettingsFooter.tsx +2 -2
  32. package/src/presentation/components/SettingsItemCard.tsx +2 -2
  33. package/src/presentation/components/SettingsSection.tsx +2 -2
  34. package/src/presentation/navigation/SettingsStackNavigator.tsx +2 -2
  35. package/src/presentation/screens/SettingsScreen.tsx +2 -2
  36. package/src/presentation/screens/components/SettingsContent.tsx +2 -2
  37. package/src/presentation/screens/components/SettingsHeader.tsx +2 -2
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Video Tutorials Screen
3
+ * Single Responsibility: Display video tutorials list
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Text, FlatList, StyleSheet } from "react-native";
8
+ import {
9
+ useAppDesignTokens,
10
+ ScreenLayout,
11
+ AtomicSpinner,
12
+ AtomicText,
13
+ } from "@umituz/react-native-design-system";
14
+ import type { VideoTutorial } from "../../types";
15
+ import { VideoTutorialCard } from "../components/VideoTutorialCard";
16
+ import { useVideoTutorials, useFeaturedTutorials } from "../hooks";
17
+
18
+ export interface VideoTutorialsScreenProps {
19
+ /**
20
+ * Title of the screen
21
+ */
22
+ title?: string;
23
+ /**
24
+ * Title for the featured tutorials section
25
+ */
26
+ featuredTitle?: string;
27
+ /**
28
+ * Title for all tutorials section
29
+ */
30
+ allTutorialsTitle?: string;
31
+ /**
32
+ * Error message to show when tutorials fail to load
33
+ */
34
+ errorLoadingMessage?: string;
35
+ /**
36
+ * Maximum number of featured tutorials to show
37
+ */
38
+ maxFeaturedCount?: number;
39
+ /**
40
+ * Callback when a tutorial is pressed
41
+ */
42
+ onTutorialPress?: (tutorialId: string) => void;
43
+ /**
44
+ * Optional manual override for loading state
45
+ */
46
+ customIsLoading?: boolean;
47
+ /**
48
+ * Optional manual override for all tutorials data
49
+ */
50
+ customAllTutorials?: VideoTutorial[];
51
+ /**
52
+ * Optional manual override for featured tutorials data
53
+ */
54
+ customFeaturedTutorials?: VideoTutorial[];
55
+ }
56
+
57
+ export const VideoTutorialsScreen: React.FC<VideoTutorialsScreenProps> =
58
+ React.memo(
59
+ ({
60
+ title = "Video Tutorials",
61
+ featuredTitle = "Featured",
62
+ allTutorialsTitle = "All Tutorials",
63
+ errorLoadingMessage = "Failed to load tutorials.",
64
+ maxFeaturedCount = 3,
65
+ onTutorialPress,
66
+ customIsLoading,
67
+ customAllTutorials,
68
+ customFeaturedTutorials,
69
+ }) => {
70
+ const tokens = useAppDesignTokens();
71
+
72
+ const featuredQuery = useFeaturedTutorials(maxFeaturedCount);
73
+ const allQuery = useVideoTutorials();
74
+
75
+ const isLoading =
76
+ customIsLoading !== undefined
77
+ ? customIsLoading
78
+ : featuredQuery.isLoading || allQuery.isLoading;
79
+ const error = featuredQuery.error || allQuery.error;
80
+
81
+ const featuredTutorials =
82
+ customFeaturedTutorials || featuredQuery.data || [];
83
+ const allTutorials = customAllTutorials || allQuery.data || [];
84
+
85
+ const handleTutorialPress = React.useCallback(
86
+ (tutorialId: string) => {
87
+ if (onTutorialPress) {
88
+ onTutorialPress(tutorialId);
89
+ } else if (__DEV__) {
90
+ // eslint-disable-next-line no-console
91
+ console.log("VideoTutorialsScreen: No onTutorialPress handler", {
92
+ tutorialId,
93
+ });
94
+ }
95
+ },
96
+ [onTutorialPress],
97
+ );
98
+
99
+ const renderTutorialItem = React.useCallback(
100
+ ({ item }: { item: VideoTutorial }) => (
101
+ <VideoTutorialCard
102
+ tutorial={item}
103
+ onPress={() => handleTutorialPress(item.id)}
104
+ />
105
+ ),
106
+ [handleTutorialPress],
107
+ );
108
+
109
+ if (isLoading) {
110
+ return <AtomicSpinner size="lg" fullContainer />;
111
+ }
112
+
113
+ if (error) {
114
+ return (
115
+ <View style={styles.errorContainer}>
116
+ <AtomicText color="error" type="bodyLarge">
117
+ {errorLoadingMessage}
118
+ </AtomicText>
119
+ </View>
120
+ );
121
+ }
122
+
123
+ return (
124
+ <ScreenLayout scrollable={false} edges={["top", "bottom"]}>
125
+ <Text style={[styles.title, { color: tokens.colors.textPrimary }]}>
126
+ {title}
127
+ </Text>
128
+
129
+ {featuredTutorials && featuredTutorials.length > 0 && (
130
+ <View style={styles.section}>
131
+ <Text
132
+ style={[
133
+ styles.sectionTitle,
134
+ { color: tokens.colors.textSecondary },
135
+ ]}
136
+ >
137
+ {featuredTitle}
138
+ </Text>
139
+ <FlatList
140
+ data={featuredTutorials}
141
+ renderItem={renderTutorialItem}
142
+ keyExtractor={(item: VideoTutorial) => item.id}
143
+ horizontal
144
+ showsHorizontalScrollIndicator={false}
145
+ contentContainerStyle={styles.horizontalList}
146
+ />
147
+ </View>
148
+ )}
149
+
150
+ <View style={styles.section}>
151
+ <Text
152
+ style={[
153
+ styles.sectionTitle,
154
+ { color: tokens.colors.textSecondary },
155
+ ]}
156
+ >
157
+ {allTutorialsTitle}
158
+ </Text>
159
+ <FlatList
160
+ data={allTutorials}
161
+ renderItem={renderTutorialItem}
162
+ keyExtractor={(item: VideoTutorial) => item.id}
163
+ showsVerticalScrollIndicator={false}
164
+ contentContainerStyle={styles.verticalList}
165
+ />
166
+ </View>
167
+ </ScreenLayout>
168
+ );
169
+ },
170
+ );
171
+
172
+ const styles = StyleSheet.create({
173
+ title: {
174
+ fontSize: 24,
175
+ fontWeight: "600",
176
+ marginBottom: 24,
177
+ },
178
+ section: {
179
+ marginBottom: 24,
180
+ },
181
+ sectionTitle: {
182
+ fontSize: 18,
183
+ fontWeight: "500",
184
+ marginBottom: 12,
185
+ },
186
+ horizontalList: {
187
+ paddingRight: 16,
188
+ },
189
+ verticalList: {
190
+ paddingBottom: 16,
191
+ },
192
+ errorContainer: {
193
+ flex: 1,
194
+ justifyContent: "center",
195
+ alignItems: "center",
196
+ padding: 20,
197
+ },
198
+ });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Video Tutorial Types
3
+ * Single Responsibility: Define type definitions for video tutorials
4
+ */
5
+
6
+ export interface VideoTutorial {
7
+ readonly id: string;
8
+ readonly title: string;
9
+ readonly description: string;
10
+ readonly videoUrl: string;
11
+ readonly thumbnailUrl: string;
12
+ readonly duration: number; // in seconds
13
+ readonly category: VideoTutorialCategory;
14
+ readonly difficulty: "beginner" | "intermediate" | "advanced";
15
+ readonly featured: boolean;
16
+ readonly tags: readonly string[];
17
+ readonly createdAt: Date;
18
+ readonly updatedAt: Date;
19
+ readonly viewCount: number;
20
+ }
21
+
22
+ export type VideoTutorialCategory =
23
+ | "getting-started"
24
+ | "ai-generation"
25
+ | "editing"
26
+ | "effects"
27
+ | "exporting"
28
+ | "tips-tricks"
29
+ | "troubleshooting";
30
+
31
+ export interface VideoTutorialFilters {
32
+ readonly category?: VideoTutorialCategory;
33
+ readonly difficulty?: VideoTutorial["difficulty"];
34
+ readonly featured?: boolean;
35
+ readonly tags?: readonly string[];
36
+ }
package/src/index.ts CHANGED
@@ -115,7 +115,10 @@ export * from './domains/feedback';
115
115
  export * from './domains/faqs';
116
116
 
117
117
  // Rating Domain - Star ratings, reviews, statistics
118
- export * from './domains/rating';
118
+ export * from "./domains/rating";
119
+
120
+ // Video Tutorials Domain - Learning resources, tutorials
121
+ export * from "./domains/video-tutorials";
119
122
 
120
123
  // =============================================================================
121
124
  // PRESENTATION LAYER - Re-exports from Dependencies
@@ -9,7 +9,7 @@
9
9
 
10
10
  import React from "react";
11
11
  import { Alert } from "react-native";
12
- import { useResponsiveDesignTokens } from "@umituz/react-native-design-system";
12
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
13
13
  import { storageRepository } from "@umituz/react-native-storage";
14
14
  import { SettingsSection } from "./SettingsSection";
15
15
  import { SettingItem } from "./SettingItem";
@@ -46,7 +46,7 @@ export const DevSettingsSection: React.FC<DevSettingsProps> = ({
46
46
  texts = {},
47
47
  customDevComponents = [],
48
48
  }) => {
49
- const tokens = useResponsiveDesignTokens();
49
+ const tokens = useAppDesignTokens();
50
50
 
51
51
  // Merge custom texts with defaults
52
52
  const t = { ...DEFAULT_TEXTS, ...texts };
@@ -3,7 +3,7 @@ import { View, StyleSheet, Switch } from "react-native";
3
3
  import {
4
4
  AtomicIcon,
5
5
  AtomicText,
6
- useResponsiveDesignTokens,
6
+ useAppDesignTokens,
7
7
  } from "@umituz/react-native-design-system";
8
8
  import { SettingsItemCard } from "./SettingsItemCard";
9
9
 
@@ -40,7 +40,7 @@ export const SettingItem: React.FC<SettingItemProps> = ({
40
40
  testID,
41
41
  disabled = false,
42
42
  }) => {
43
- const tokens = useResponsiveDesignTokens();
43
+ const tokens = useAppDesignTokens();
44
44
 
45
45
  // For switch items, we need custom rendering
46
46
  if (showSwitch) {
@@ -5,7 +5,7 @@
5
5
 
6
6
  import React, { Component, ReactNode } from 'react';
7
7
  import { View, StyleSheet } from 'react-native';
8
- import { useResponsiveDesignTokens } from '@umituz/react-native-design-system';
8
+ import { useAppDesignTokens } from '@umituz/react-native-design-system';
9
9
  import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
10
10
  import { useLocalization } from '@umituz/react-native-localization';
11
11
 
@@ -67,7 +67,7 @@ const ErrorBoundaryFallback: React.FC<ErrorBoundaryFallbackProps> = ({
67
67
  fallbackTitle,
68
68
  fallbackMessage
69
69
  }) => {
70
- const tokens = useResponsiveDesignTokens();
70
+ const tokens = useAppDesignTokens();
71
71
  const { t } = useLocalization();
72
72
 
73
73
  const title = __DEV__ && error?.message
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { View, StyleSheet } from "react-native";
3
- import { useResponsiveDesignTokens, AtomicText } from "@umituz/react-native-design-system";
3
+ import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
4
4
 
5
5
  export interface SettingsFooterProps {
6
6
  versionText?: string;
@@ -13,7 +13,7 @@ export const SettingsFooter: React.FC<SettingsFooterProps> = ({
13
13
  appVersion,
14
14
  versionLabel,
15
15
  }) => {
16
- const tokens = useResponsiveDesignTokens();
16
+ const tokens = useAppDesignTokens();
17
17
  const colors = tokens.colors;
18
18
 
19
19
  const displayText = versionText || (appVersion && versionLabel
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { View, Pressable, StyleSheet, ViewStyle } from "react-native";
3
3
  import {
4
- useResponsiveDesignTokens,
4
+ useAppDesignTokens,
5
5
  AtomicIcon,
6
6
  AtomicText,
7
7
  type IconName,
@@ -45,7 +45,7 @@ export const SettingsItemCard: React.FC<SettingsItemCardProps> = ({
45
45
  iconBgColor,
46
46
  iconColor,
47
47
  }) => {
48
- const tokens = useResponsiveDesignTokens();
48
+ const tokens = useAppDesignTokens();
49
49
  const colors = tokens.colors;
50
50
 
51
51
  const defaultIconBg = iconBgColor || `${colors.primary}15`;
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { View, StyleSheet } from "react-native";
3
- import { useResponsiveDesignTokens, AtomicText } from "@umituz/react-native-design-system";
3
+ import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
4
4
 
5
5
  export interface SettingsSectionProps {
6
6
  title: string;
@@ -11,7 +11,7 @@ export const SettingsSection: React.FC<SettingsSectionProps> = ({
11
11
  title,
12
12
  children,
13
13
  }) => {
14
- const tokens = useResponsiveDesignTokens();
14
+ const tokens = useAppDesignTokens();
15
15
  const colors = tokens.colors;
16
16
 
17
17
  return (
@@ -7,7 +7,7 @@
7
7
 
8
8
  import React from "react";
9
9
  import { createStackNavigator } from "@react-navigation/stack";
10
- import { useResponsiveDesignTokens } from "@umituz/react-native-design-system";
10
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
11
11
  import { SettingsScreen } from "../screens/SettingsScreen";
12
12
  import { AppearanceScreen } from "../screens/AppearanceScreen";
13
13
  import type { SettingsConfig, CustomSettingsSection } from "../screens/types";
@@ -83,7 +83,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
83
83
  devSettings,
84
84
  customSections = [],
85
85
  }) => {
86
- const tokens = useResponsiveDesignTokens();
86
+ const tokens = useAppDesignTokens();
87
87
 
88
88
  const screenOptions = {
89
89
  headerStyle: {
@@ -8,7 +8,7 @@ import { View, StatusBar, StyleSheet } from "react-native";
8
8
  import { useNavigation } from "@react-navigation/native";
9
9
  import {
10
10
  useDesignSystemTheme,
11
- useResponsiveDesignTokens,
11
+ useAppDesignTokens,
12
12
  } from "@umituz/react-native-design-system";
13
13
  import { SettingsHeader } from "./components/SettingsHeader";
14
14
  import { SettingsContent } from "./components/SettingsContent";
@@ -68,7 +68,7 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
68
68
  }) => {
69
69
  const navigation = useNavigation();
70
70
  const { themeMode } = useDesignSystemTheme();
71
- const tokens = useResponsiveDesignTokens();
71
+ const tokens = useAppDesignTokens();
72
72
 
73
73
  const isDark = themeMode === "dark";
74
74
  const colors = tokens.colors;
@@ -1,7 +1,7 @@
1
1
  import React, { useMemo } from "react";
2
2
  import { View, ScrollView, StyleSheet } from "react-native";
3
3
  import { useSafeAreaInsets } from "react-native-safe-area-context";
4
- import { useResponsiveDesignTokens } from "@umituz/react-native-design-system";
4
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
5
5
  import { useLocalization } from "@umituz/react-native-localization";
6
6
  import { SettingsFooter } from "../../components/SettingsFooter";
7
7
  import { SettingsSection } from "../../components/SettingsSection";
@@ -53,7 +53,7 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
53
53
  showCloseButton = false,
54
54
  devSettings,
55
55
  }) => {
56
- const tokens = useResponsiveDesignTokens();
56
+ const tokens = useAppDesignTokens();
57
57
  const insets = useSafeAreaInsets();
58
58
  const { t } = useLocalization();
59
59
 
@@ -7,7 +7,7 @@ import React from "react";
7
7
  import { View, TouchableOpacity, StyleSheet } from "react-native";
8
8
  import { useSafeAreaInsets } from "react-native-safe-area-context";
9
9
  import { useNavigation } from "@react-navigation/native";
10
- import { useResponsiveDesignTokens } from "@umituz/react-native-design-system";
10
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
11
11
  import { AtomicIcon } from "@umituz/react-native-design-system";
12
12
 
13
13
  interface SettingsHeaderProps {
@@ -20,7 +20,7 @@ export const SettingsHeader: React.FC<SettingsHeaderProps> = ({
20
20
  onClose,
21
21
  }) => {
22
22
  const navigation = useNavigation();
23
- const tokens = useResponsiveDesignTokens();
23
+ const tokens = useAppDesignTokens();
24
24
  const insets = useSafeAreaInsets();
25
25
 
26
26
  const handleClose = () => {