react-native-elearn-ui 0.1.9 → 0.1.10
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/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/PearlChaptersScreen.js +357 -0
- package/lib/module/screens/PearlChaptersScreen.js.map +1 -0
- package/lib/module/screens/PearlReaderScreen.js +311 -0
- package/lib/module/screens/PearlReaderScreen.js.map +1 -0
- package/lib/module/screens/PearlsScreen.js +41 -631
- package/lib/module/screens/PearlsScreen.js.map +1 -1
- package/lib/module/screens/index.js +2 -0
- package/lib/module/screens/index.js.map +1 -1
- package/lib/module/screens/pearlsMockData.js +77 -0
- package/lib/module/screens/pearlsMockData.js.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/PearlChaptersScreen.d.ts +12 -0
- package/lib/typescript/src/screens/PearlChaptersScreen.d.ts.map +1 -0
- package/lib/typescript/src/screens/PearlReaderScreen.d.ts +13 -0
- package/lib/typescript/src/screens/PearlReaderScreen.d.ts.map +1 -0
- package/lib/typescript/src/screens/PearlsScreen.d.ts +2 -0
- package/lib/typescript/src/screens/PearlsScreen.d.ts.map +1 -1
- package/lib/typescript/src/screens/index.d.ts +2 -0
- package/lib/typescript/src/screens/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/pearlsMockData.d.ts +3 -0
- package/lib/typescript/src/screens/pearlsMockData.d.ts.map +1 -0
- package/lib/typescript/src/types/index.d.ts +15 -0
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +2 -0
- package/src/screens/PearlChaptersScreen.tsx +328 -0
- package/src/screens/PearlReaderScreen.tsx +304 -0
- package/src/screens/PearlsScreen.tsx +27 -602
- package/src/screens/index.ts +2 -0
- package/src/screens/pearlsMockData.ts +70 -0
- package/src/types/index.ts +17 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
ScrollView,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
SafeAreaView,
|
|
8
|
+
StatusBar,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
import { useTheme } from '../context/ThemeContext';
|
|
11
|
+
import { Typography, Icon } from '../components';
|
|
12
|
+
import { MOCK_PEARLS_TOPICS } from './pearlsMockData';
|
|
13
|
+
import type { PearlTopic, PearlChapter } from '../types';
|
|
14
|
+
|
|
15
|
+
interface PearlReaderScreenProps {
|
|
16
|
+
topic?: PearlTopic;
|
|
17
|
+
topicId?: string;
|
|
18
|
+
chapter?: PearlChapter;
|
|
19
|
+
chapterId?: string;
|
|
20
|
+
onBack: () => void;
|
|
21
|
+
hideHeader?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const PearlReaderScreen: React.FC<PearlReaderScreenProps> = ({
|
|
25
|
+
topic,
|
|
26
|
+
topicId,
|
|
27
|
+
chapter,
|
|
28
|
+
chapterId,
|
|
29
|
+
onBack,
|
|
30
|
+
hideHeader = false,
|
|
31
|
+
}) => {
|
|
32
|
+
const theme = useTheme();
|
|
33
|
+
|
|
34
|
+
const activeTopic = topic || MOCK_PEARLS_TOPICS.find((t) => t.id === topicId);
|
|
35
|
+
|
|
36
|
+
// Flatten chapters for reader mode navigation
|
|
37
|
+
const flattenedChapters: PearlChapter[] = [];
|
|
38
|
+
if (activeTopic && activeTopic.sections) {
|
|
39
|
+
Object.keys(activeTopic.sections).forEach((sectionName) => {
|
|
40
|
+
const sectionChapters = activeTopic.sections?.[sectionName] || [];
|
|
41
|
+
flattenedChapters.push(...sectionChapters);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const initialIndex = chapterId
|
|
46
|
+
? flattenedChapters.findIndex((c) => c.id === chapterId)
|
|
47
|
+
: chapter
|
|
48
|
+
? flattenedChapters.findIndex((c) => c.id === chapter.id)
|
|
49
|
+
: 0;
|
|
50
|
+
|
|
51
|
+
const [activeChapterIndex, setActiveChapterIndex] = useState<number>(initialIndex >= 0 ? initialIndex : 0);
|
|
52
|
+
const [isCompleted, setIsCompleted] = useState(false);
|
|
53
|
+
|
|
54
|
+
const activeChapter = flattenedChapters[activeChapterIndex];
|
|
55
|
+
|
|
56
|
+
if (!activeChapter) {
|
|
57
|
+
return (
|
|
58
|
+
<SafeAreaView style={[styles.safeArea, { backgroundColor: theme.background }]}>
|
|
59
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
|
|
60
|
+
<Typography variant="h2" bold style={{ marginBottom: 12 }}>Chapter not found</Typography>
|
|
61
|
+
<TouchableOpacity
|
|
62
|
+
onPress={onBack}
|
|
63
|
+
style={{
|
|
64
|
+
paddingHorizontal: 20,
|
|
65
|
+
paddingVertical: 10,
|
|
66
|
+
borderRadius: 8,
|
|
67
|
+
backgroundColor: theme.primary,
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
<Typography variant="body" bold color="#FFFFFF">Go Back</Typography>
|
|
71
|
+
</TouchableOpacity>
|
|
72
|
+
</View>
|
|
73
|
+
</SafeAreaView>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const handlePrevChapter = () => {
|
|
78
|
+
if (activeChapterIndex > 0) {
|
|
79
|
+
setActiveChapterIndex(activeChapterIndex - 1);
|
|
80
|
+
setIsCompleted(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleNextChapter = () => {
|
|
85
|
+
if (activeChapterIndex < flattenedChapters.length - 1) {
|
|
86
|
+
setActiveChapterIndex(activeChapterIndex + 1);
|
|
87
|
+
setIsCompleted(false);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<SafeAreaView style={[styles.safeArea, { backgroundColor: theme.background }]}>
|
|
93
|
+
<StatusBar barStyle="dark-content" />
|
|
94
|
+
|
|
95
|
+
{/* Header */}
|
|
96
|
+
{!hideHeader && (
|
|
97
|
+
<View style={styles.header}>
|
|
98
|
+
<TouchableOpacity onPress={onBack} style={styles.backButton}>
|
|
99
|
+
<Icon name="arrow-left" size={20} color={theme.textPrimary} />
|
|
100
|
+
</TouchableOpacity>
|
|
101
|
+
|
|
102
|
+
<Typography variant="h2" bold style={styles.headerTitle}>
|
|
103
|
+
Pearls
|
|
104
|
+
</Typography>
|
|
105
|
+
|
|
106
|
+
<View style={styles.headerRightPlaceholder} />
|
|
107
|
+
</View>
|
|
108
|
+
)}
|
|
109
|
+
|
|
110
|
+
{/* Reader Container */}
|
|
111
|
+
<View style={styles.readerContainer}>
|
|
112
|
+
{/* Subheader progress banner */}
|
|
113
|
+
<View style={styles.readerSubHeader}>
|
|
114
|
+
<Typography variant="body" bold color={theme.textPrimary} style={styles.readerSubTitle}>
|
|
115
|
+
{activeChapter.title.split('.')[0] || activeChapter.title}
|
|
116
|
+
</Typography>
|
|
117
|
+
<Typography variant="body" bold color={theme.textSecondary}>
|
|
118
|
+
{activeChapterIndex + 1}/{flattenedChapters.length}
|
|
119
|
+
</Typography>
|
|
120
|
+
</View>
|
|
121
|
+
|
|
122
|
+
<ScrollView contentContainerStyle={styles.readerScroll} showsVerticalScrollIndicator={false}>
|
|
123
|
+
{activeChapter.bullets.map((bullet, idx) => {
|
|
124
|
+
const parts = bullet.split(':');
|
|
125
|
+
const boldPart = parts[0];
|
|
126
|
+
const rest = parts.slice(1).join(':');
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<View key={idx} style={styles.bulletRow}>
|
|
130
|
+
<Typography variant="body" color={theme.textPrimary} style={styles.bulletPointDot}>
|
|
131
|
+
•
|
|
132
|
+
</Typography>
|
|
133
|
+
<Typography variant="body" color={theme.textPrimary} style={styles.bulletText}>
|
|
134
|
+
{rest ? (
|
|
135
|
+
<>
|
|
136
|
+
<Typography variant="body" bold>{boldPart}:</Typography>
|
|
137
|
+
{rest}
|
|
138
|
+
</>
|
|
139
|
+
) : (
|
|
140
|
+
bullet
|
|
141
|
+
)}
|
|
142
|
+
</Typography>
|
|
143
|
+
</View>
|
|
144
|
+
);
|
|
145
|
+
})}
|
|
146
|
+
</ScrollView>
|
|
147
|
+
|
|
148
|
+
{/* Checkbox status bar */}
|
|
149
|
+
<View style={styles.checkboxBar}>
|
|
150
|
+
<TouchableOpacity
|
|
151
|
+
activeOpacity={0.8}
|
|
152
|
+
onPress={() => setIsCompleted(!isCompleted)}
|
|
153
|
+
style={styles.checkboxRow}
|
|
154
|
+
>
|
|
155
|
+
<View style={[
|
|
156
|
+
styles.checkboxBox,
|
|
157
|
+
{ borderColor: theme.border },
|
|
158
|
+
isCompleted && { backgroundColor: theme.primary, borderColor: theme.primary }
|
|
159
|
+
]}>
|
|
160
|
+
{isCompleted && <View style={styles.checkboxInner} />}
|
|
161
|
+
</View>
|
|
162
|
+
<Typography variant="body" bold color={theme.textPrimary}>
|
|
163
|
+
Mark as Complete
|
|
164
|
+
</Typography>
|
|
165
|
+
</TouchableOpacity>
|
|
166
|
+
</View>
|
|
167
|
+
|
|
168
|
+
{/* Navigation bottom buttons */}
|
|
169
|
+
<View style={styles.footerRow}>
|
|
170
|
+
<TouchableOpacity
|
|
171
|
+
onPress={handlePrevChapter}
|
|
172
|
+
disabled={activeChapterIndex === 0}
|
|
173
|
+
style={[styles.footerBtn, activeChapterIndex === 0 && { opacity: 0.4 }]}
|
|
174
|
+
>
|
|
175
|
+
<Typography variant="body" bold color={theme.textPrimary}>
|
|
176
|
+
Prev
|
|
177
|
+
</Typography>
|
|
178
|
+
</TouchableOpacity>
|
|
179
|
+
|
|
180
|
+
<TouchableOpacity
|
|
181
|
+
onPress={handleNextChapter}
|
|
182
|
+
disabled={activeChapterIndex === flattenedChapters.length - 1}
|
|
183
|
+
style={[
|
|
184
|
+
styles.nextActionBtn,
|
|
185
|
+
{ backgroundColor: theme.primary },
|
|
186
|
+
activeChapterIndex === flattenedChapters.length - 1 && { opacity: 0.4 }
|
|
187
|
+
]}
|
|
188
|
+
>
|
|
189
|
+
<Typography variant="body" bold color="#FFFFFF">
|
|
190
|
+
Next
|
|
191
|
+
</Typography>
|
|
192
|
+
</TouchableOpacity>
|
|
193
|
+
</View>
|
|
194
|
+
</View>
|
|
195
|
+
</SafeAreaView>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const styles = StyleSheet.create({
|
|
200
|
+
safeArea: {
|
|
201
|
+
flex: 1,
|
|
202
|
+
},
|
|
203
|
+
header: {
|
|
204
|
+
flexDirection: 'row',
|
|
205
|
+
justifyContent: 'space-between',
|
|
206
|
+
alignItems: 'center',
|
|
207
|
+
paddingHorizontal: 16,
|
|
208
|
+
paddingVertical: 12,
|
|
209
|
+
},
|
|
210
|
+
backButton: {
|
|
211
|
+
padding: 6,
|
|
212
|
+
},
|
|
213
|
+
headerTitle: {
|
|
214
|
+
fontSize: 18,
|
|
215
|
+
fontWeight: '700',
|
|
216
|
+
},
|
|
217
|
+
headerRightPlaceholder: {
|
|
218
|
+
width: 32,
|
|
219
|
+
},
|
|
220
|
+
readerContainer: {
|
|
221
|
+
flex: 1,
|
|
222
|
+
},
|
|
223
|
+
readerSubHeader: {
|
|
224
|
+
flexDirection: 'row',
|
|
225
|
+
justifyContent: 'space-between',
|
|
226
|
+
alignItems: 'center',
|
|
227
|
+
paddingHorizontal: 16,
|
|
228
|
+
paddingVertical: 12,
|
|
229
|
+
borderBottomWidth: 1,
|
|
230
|
+
borderBottomColor: '#E2E8F0',
|
|
231
|
+
},
|
|
232
|
+
readerSubTitle: {
|
|
233
|
+
flex: 1,
|
|
234
|
+
fontSize: 14,
|
|
235
|
+
marginRight: 16,
|
|
236
|
+
},
|
|
237
|
+
readerScroll: {
|
|
238
|
+
paddingHorizontal: 16,
|
|
239
|
+
paddingVertical: 16,
|
|
240
|
+
},
|
|
241
|
+
bulletRow: {
|
|
242
|
+
flexDirection: 'row',
|
|
243
|
+
alignItems: 'flex-start',
|
|
244
|
+
marginBottom: 16,
|
|
245
|
+
},
|
|
246
|
+
bulletPointDot: {
|
|
247
|
+
fontSize: 18,
|
|
248
|
+
marginRight: 10,
|
|
249
|
+
lineHeight: 20,
|
|
250
|
+
},
|
|
251
|
+
bulletText: {
|
|
252
|
+
flex: 1,
|
|
253
|
+
fontSize: 14,
|
|
254
|
+
lineHeight: 22,
|
|
255
|
+
},
|
|
256
|
+
checkboxBar: {
|
|
257
|
+
paddingHorizontal: 16,
|
|
258
|
+
paddingVertical: 14,
|
|
259
|
+
borderTopWidth: 1,
|
|
260
|
+
borderTopColor: '#E2E8F0',
|
|
261
|
+
backgroundColor: '#F8FAFC',
|
|
262
|
+
},
|
|
263
|
+
checkboxRow: {
|
|
264
|
+
flexDirection: 'row',
|
|
265
|
+
alignItems: 'center',
|
|
266
|
+
},
|
|
267
|
+
checkboxBox: {
|
|
268
|
+
width: 20,
|
|
269
|
+
height: 20,
|
|
270
|
+
borderWidth: 2,
|
|
271
|
+
borderRadius: 4,
|
|
272
|
+
justifyContent: 'center',
|
|
273
|
+
alignItems: 'center',
|
|
274
|
+
marginRight: 10,
|
|
275
|
+
},
|
|
276
|
+
checkboxInner: {
|
|
277
|
+
width: 10,
|
|
278
|
+
height: 10,
|
|
279
|
+
borderRadius: 2,
|
|
280
|
+
backgroundColor: '#FFFFFF',
|
|
281
|
+
},
|
|
282
|
+
footerRow: {
|
|
283
|
+
flexDirection: 'row',
|
|
284
|
+
alignItems: 'center',
|
|
285
|
+
paddingHorizontal: 16,
|
|
286
|
+
paddingVertical: 12,
|
|
287
|
+
backgroundColor: '#FFFFFF',
|
|
288
|
+
borderTopWidth: 1,
|
|
289
|
+
borderTopColor: '#E2E8F0',
|
|
290
|
+
justifyContent: 'space-between',
|
|
291
|
+
},
|
|
292
|
+
footerBtn: {
|
|
293
|
+
paddingVertical: 8,
|
|
294
|
+
paddingHorizontal: 12,
|
|
295
|
+
},
|
|
296
|
+
nextActionBtn: {
|
|
297
|
+
paddingHorizontal: 24,
|
|
298
|
+
paddingVertical: 10,
|
|
299
|
+
borderRadius: 8,
|
|
300
|
+
minWidth: 80,
|
|
301
|
+
justifyContent: 'center',
|
|
302
|
+
alignItems: 'center',
|
|
303
|
+
},
|
|
304
|
+
});
|