react-native-elearn-ui 0.1.8 → 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/components/Icon.js +27 -0
- package/lib/module/components/Icon.js.map +1 -1
- 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 +151 -0
- package/lib/module/screens/PearlsScreen.js.map +1 -0
- package/lib/module/screens/QbankScreen.js +9 -2
- package/lib/module/screens/QbankScreen.js.map +1 -1
- package/lib/module/screens/QuestionScreen.js +22 -10
- package/lib/module/screens/QuestionScreen.js.map +1 -1
- package/lib/module/screens/QuizStartScreen.js +55 -7
- package/lib/module/screens/QuizStartScreen.js.map +1 -1
- package/lib/module/screens/ResultScreen.js +10 -6
- package/lib/module/screens/ResultScreen.js.map +1 -1
- package/lib/module/screens/TopicListScreen.js +53 -6
- package/lib/module/screens/TopicListScreen.js.map +1 -1
- package/lib/module/screens/index.js +3 -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/components/Icon.d.ts +1 -1
- package/lib/typescript/src/components/Icon.d.ts.map +1 -1
- 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 +10 -0
- package/lib/typescript/src/screens/PearlsScreen.d.ts.map +1 -0
- package/lib/typescript/src/screens/QbankScreen.d.ts +2 -0
- package/lib/typescript/src/screens/QbankScreen.d.ts.map +1 -1
- package/lib/typescript/src/screens/QuestionScreen.d.ts +6 -4
- package/lib/typescript/src/screens/QuestionScreen.d.ts.map +1 -1
- package/lib/typescript/src/screens/QuizStartScreen.d.ts +6 -3
- package/lib/typescript/src/screens/QuizStartScreen.d.ts.map +1 -1
- package/lib/typescript/src/screens/ResultScreen.d.ts +2 -1
- package/lib/typescript/src/screens/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/screens/TopicListScreen.d.ts +4 -2
- package/lib/typescript/src/screens/TopicListScreen.d.ts.map +1 -1
- package/lib/typescript/src/screens/index.d.ts +3 -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/components/Icon.tsx +35 -1
- package/src/index.tsx +3 -0
- package/src/screens/PearlChaptersScreen.tsx +328 -0
- package/src/screens/PearlReaderScreen.tsx +304 -0
- package/src/screens/PearlsScreen.tsx +145 -0
- package/src/screens/QbankScreen.tsx +14 -2
- package/src/screens/QuestionScreen.tsx +28 -14
- package/src/screens/QuizStartScreen.tsx +45 -10
- package/src/screens/ResultScreen.tsx +13 -8
- package/src/screens/TopicListScreen.tsx +40 -7
- package/src/screens/index.ts +3 -0
- package/src/screens/pearlsMockData.ts +70 -0
- package/src/types/index.ts +17 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
ScrollView,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
SafeAreaView,
|
|
8
|
+
StatusBar,
|
|
9
|
+
Modal,
|
|
10
|
+
Dimensions,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import { useTheme } from '../context/ThemeContext';
|
|
13
|
+
import { Typography, Card, Icon } from '../components';
|
|
14
|
+
import { MOCK_PEARLS_TOPICS } from './pearlsMockData';
|
|
15
|
+
import type { PearlTopic, PearlChapter } from '../types';
|
|
16
|
+
|
|
17
|
+
const { height: SCREEN_HEIGHT } = Dimensions.get('window');
|
|
18
|
+
|
|
19
|
+
interface PearlChaptersScreenProps {
|
|
20
|
+
topic?: PearlTopic;
|
|
21
|
+
topicId?: string;
|
|
22
|
+
onBack: () => void;
|
|
23
|
+
hideHeader?: boolean;
|
|
24
|
+
onSelectChapter?: (topic: PearlTopic, chapter: PearlChapter) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const PearlChaptersScreen: React.FC<PearlChaptersScreenProps> = ({
|
|
28
|
+
topic,
|
|
29
|
+
topicId,
|
|
30
|
+
onBack,
|
|
31
|
+
hideHeader = false,
|
|
32
|
+
onSelectChapter,
|
|
33
|
+
}) => {
|
|
34
|
+
const theme = useTheme();
|
|
35
|
+
const [bottomSheetVisible, setBottomSheetVisible] = useState(false);
|
|
36
|
+
|
|
37
|
+
const activeTopic = topic || MOCK_PEARLS_TOPICS.find((t) => t.id === topicId);
|
|
38
|
+
|
|
39
|
+
if (!activeTopic) {
|
|
40
|
+
return (
|
|
41
|
+
<SafeAreaView style={[styles.safeArea, { backgroundColor: theme.background }]}>
|
|
42
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
|
|
43
|
+
<Typography variant="h2" bold style={{ marginBottom: 12 }}>Topic not found</Typography>
|
|
44
|
+
<TouchableOpacity
|
|
45
|
+
onPress={onBack}
|
|
46
|
+
style={{
|
|
47
|
+
paddingHorizontal: 20,
|
|
48
|
+
paddingVertical: 10,
|
|
49
|
+
borderRadius: 8,
|
|
50
|
+
backgroundColor: theme.primary,
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<Typography variant="body" bold color="#FFFFFF">Go Back</Typography>
|
|
54
|
+
</TouchableOpacity>
|
|
55
|
+
</View>
|
|
56
|
+
</SafeAreaView>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const sections = Object.keys(activeTopic.sections || {});
|
|
61
|
+
|
|
62
|
+
// --- Render bottom sheet overlay for topics filter ---
|
|
63
|
+
const renderBottomSheet = () => {
|
|
64
|
+
const list = [
|
|
65
|
+
{ name: 'Vertebral Curves', count: 25 },
|
|
66
|
+
{ name: 'Introduction', count: 15 },
|
|
67
|
+
{ name: 'Definition and scope', count: 15 },
|
|
68
|
+
{ name: 'Branches of anatomy', count: 5 },
|
|
69
|
+
{ name: 'Anatomical terminology', count: 23 },
|
|
70
|
+
{ name: 'Anatomical positions', count: 25 },
|
|
71
|
+
{ name: 'CSF', count: 15 },
|
|
72
|
+
{ name: 'Levels of structural organization', count: 15 },
|
|
73
|
+
{ name: 'Systems of the human body overview', count: 5 },
|
|
74
|
+
{ name: 'Tissue types', count: 23 },
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Modal
|
|
79
|
+
visible={bottomSheetVisible}
|
|
80
|
+
transparent
|
|
81
|
+
animationType="slide"
|
|
82
|
+
onRequestClose={() => setBottomSheetVisible(false)}
|
|
83
|
+
>
|
|
84
|
+
<View style={styles.sheetOverlay}>
|
|
85
|
+
{/* Backdrop click closer */}
|
|
86
|
+
<TouchableOpacity
|
|
87
|
+
activeOpacity={1}
|
|
88
|
+
style={styles.sheetBackdrop}
|
|
89
|
+
onPress={() => setBottomSheetVisible(false)}
|
|
90
|
+
/>
|
|
91
|
+
|
|
92
|
+
<View style={styles.sheetContent}>
|
|
93
|
+
{/* Grabber indicator bar */}
|
|
94
|
+
<View style={styles.sheetGrabber} />
|
|
95
|
+
|
|
96
|
+
<View style={styles.sheetHeader}>
|
|
97
|
+
<Typography variant="h2" bold color={theme.textPrimary}>
|
|
98
|
+
{activeTopic.title}
|
|
99
|
+
</Typography>
|
|
100
|
+
</View>
|
|
101
|
+
|
|
102
|
+
<ScrollView contentContainerStyle={styles.sheetScroll}>
|
|
103
|
+
{list.map((item, idx) => (
|
|
104
|
+
<TouchableOpacity
|
|
105
|
+
key={idx}
|
|
106
|
+
style={styles.sheetItemRow}
|
|
107
|
+
onPress={() => {
|
|
108
|
+
setBottomSheetVisible(false);
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<Typography variant="body" color={theme.textPrimary}>
|
|
112
|
+
{item.name}
|
|
113
|
+
</Typography>
|
|
114
|
+
<Typography variant="caption" color={theme.textSecondary}>
|
|
115
|
+
{item.count} Modules
|
|
116
|
+
</Typography>
|
|
117
|
+
</TouchableOpacity>
|
|
118
|
+
))}
|
|
119
|
+
</ScrollView>
|
|
120
|
+
</View>
|
|
121
|
+
</View>
|
|
122
|
+
</Modal>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<SafeAreaView style={[styles.safeArea, { backgroundColor: theme.background }]}>
|
|
128
|
+
<StatusBar barStyle="dark-content" />
|
|
129
|
+
|
|
130
|
+
{/* Header */}
|
|
131
|
+
{!hideHeader && (
|
|
132
|
+
<View style={styles.header}>
|
|
133
|
+
<TouchableOpacity onPress={onBack} style={styles.backButton}>
|
|
134
|
+
<Icon name="arrow-left" size={20} color={theme.textPrimary} />
|
|
135
|
+
</TouchableOpacity>
|
|
136
|
+
|
|
137
|
+
<Typography variant="h2" bold style={styles.headerTitle}>
|
|
138
|
+
Pearls
|
|
139
|
+
</Typography>
|
|
140
|
+
|
|
141
|
+
<TouchableOpacity onPress={() => setBottomSheetVisible(true)} style={styles.headerRightBtn}>
|
|
142
|
+
<Icon name="list" size={20} color={theme.textPrimary} />
|
|
143
|
+
</TouchableOpacity>
|
|
144
|
+
</View>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{/* Chapters list view */}
|
|
148
|
+
<ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
|
149
|
+
{sections.map((sectionName) => {
|
|
150
|
+
const list = activeTopic.sections?.[sectionName] || [];
|
|
151
|
+
return (
|
|
152
|
+
<View key={sectionName} style={styles.sectionBlock}>
|
|
153
|
+
<Typography variant="h2" bold color={theme.textPrimary} style={styles.sectionHeading}>
|
|
154
|
+
{sectionName}
|
|
155
|
+
</Typography>
|
|
156
|
+
|
|
157
|
+
<View style={styles.chaptersList}>
|
|
158
|
+
{list.map((chapter) => (
|
|
159
|
+
<TouchableOpacity
|
|
160
|
+
key={chapter.id}
|
|
161
|
+
activeOpacity={0.8}
|
|
162
|
+
onPress={() => onSelectChapter?.(activeTopic, chapter)}
|
|
163
|
+
disabled={chapter.isLocked}
|
|
164
|
+
>
|
|
165
|
+
<Card bordered style={styles.chapterCard}>
|
|
166
|
+
<View style={styles.chapterRow}>
|
|
167
|
+
{/* Left sequential index */}
|
|
168
|
+
<Typography variant="body" bold color={theme.textSecondary} style={styles.chapterNumber}>
|
|
169
|
+
{chapter.number}
|
|
170
|
+
</Typography>
|
|
171
|
+
|
|
172
|
+
{/* Middle Text */}
|
|
173
|
+
<Typography variant="body" color={theme.textPrimary} style={styles.chapterTitle}>
|
|
174
|
+
{chapter.title}
|
|
175
|
+
</Typography>
|
|
176
|
+
|
|
177
|
+
{/* Right locks/free badges */}
|
|
178
|
+
<View style={styles.chapterBadgeCol}>
|
|
179
|
+
{chapter.isFree ? (
|
|
180
|
+
<View style={[styles.freeBadge, { borderColor: theme.primary }]}>
|
|
181
|
+
<Typography variant="caption" bold color={theme.primary} style={styles.freeText}>
|
|
182
|
+
FREE
|
|
183
|
+
</Typography>
|
|
184
|
+
</View>
|
|
185
|
+
) : chapter.isLocked ? (
|
|
186
|
+
<View style={styles.lockCircle}>
|
|
187
|
+
<Icon name="lock" size={12} color={theme.textSecondary} />
|
|
188
|
+
</View>
|
|
189
|
+
) : null}
|
|
190
|
+
</View>
|
|
191
|
+
</View>
|
|
192
|
+
</Card>
|
|
193
|
+
</TouchableOpacity>
|
|
194
|
+
))}
|
|
195
|
+
</View>
|
|
196
|
+
</View>
|
|
197
|
+
);
|
|
198
|
+
})}
|
|
199
|
+
</ScrollView>
|
|
200
|
+
|
|
201
|
+
{/* Bottom Filter Sheet */}
|
|
202
|
+
{renderBottomSheet()}
|
|
203
|
+
</SafeAreaView>
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const styles = StyleSheet.create({
|
|
208
|
+
safeArea: {
|
|
209
|
+
flex: 1,
|
|
210
|
+
},
|
|
211
|
+
header: {
|
|
212
|
+
flexDirection: 'row',
|
|
213
|
+
justifyContent: 'space-between',
|
|
214
|
+
alignItems: 'center',
|
|
215
|
+
paddingHorizontal: 16,
|
|
216
|
+
paddingVertical: 12,
|
|
217
|
+
},
|
|
218
|
+
backButton: {
|
|
219
|
+
padding: 6,
|
|
220
|
+
},
|
|
221
|
+
headerTitle: {
|
|
222
|
+
fontSize: 18,
|
|
223
|
+
fontWeight: '700',
|
|
224
|
+
},
|
|
225
|
+
headerRightBtn: {
|
|
226
|
+
padding: 6,
|
|
227
|
+
},
|
|
228
|
+
scrollContent: {
|
|
229
|
+
paddingHorizontal: 16,
|
|
230
|
+
paddingTop: 8,
|
|
231
|
+
paddingBottom: 24,
|
|
232
|
+
},
|
|
233
|
+
sectionBlock: {
|
|
234
|
+
marginBottom: 20,
|
|
235
|
+
},
|
|
236
|
+
sectionHeading: {
|
|
237
|
+
fontSize: 16,
|
|
238
|
+
marginBottom: 12,
|
|
239
|
+
},
|
|
240
|
+
chaptersList: {
|
|
241
|
+
marginTop: 2,
|
|
242
|
+
},
|
|
243
|
+
chapterCard: {
|
|
244
|
+
padding: 12,
|
|
245
|
+
marginBottom: 10,
|
|
246
|
+
borderRadius: 12,
|
|
247
|
+
},
|
|
248
|
+
chapterRow: {
|
|
249
|
+
flexDirection: 'row',
|
|
250
|
+
alignItems: 'center',
|
|
251
|
+
},
|
|
252
|
+
chapterNumber: {
|
|
253
|
+
fontSize: 14,
|
|
254
|
+
width: 24,
|
|
255
|
+
textAlign: 'center',
|
|
256
|
+
marginRight: 8,
|
|
257
|
+
},
|
|
258
|
+
chapterTitle: {
|
|
259
|
+
flex: 1,
|
|
260
|
+
fontSize: 14,
|
|
261
|
+
lineHeight: 18,
|
|
262
|
+
},
|
|
263
|
+
chapterBadgeCol: {
|
|
264
|
+
justifyContent: 'center',
|
|
265
|
+
alignItems: 'flex-end',
|
|
266
|
+
marginLeft: 8,
|
|
267
|
+
},
|
|
268
|
+
freeBadge: {
|
|
269
|
+
borderWidth: 1.5,
|
|
270
|
+
borderRadius: 6,
|
|
271
|
+
paddingHorizontal: 8,
|
|
272
|
+
paddingVertical: 3,
|
|
273
|
+
},
|
|
274
|
+
freeText: {
|
|
275
|
+
fontSize: 9,
|
|
276
|
+
},
|
|
277
|
+
lockCircle: {
|
|
278
|
+
width: 20,
|
|
279
|
+
height: 20,
|
|
280
|
+
borderRadius: 10,
|
|
281
|
+
backgroundColor: '#F1F5F9',
|
|
282
|
+
justifyContent: 'center',
|
|
283
|
+
alignItems: 'center',
|
|
284
|
+
},
|
|
285
|
+
// Bottom Sheet styling
|
|
286
|
+
sheetOverlay: {
|
|
287
|
+
flex: 1,
|
|
288
|
+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
289
|
+
justifyContent: 'flex-end',
|
|
290
|
+
},
|
|
291
|
+
sheetBackdrop: {
|
|
292
|
+
...StyleSheet.absoluteFillObject,
|
|
293
|
+
},
|
|
294
|
+
sheetContent: {
|
|
295
|
+
backgroundColor: '#FFFFFF',
|
|
296
|
+
borderTopLeftRadius: 20,
|
|
297
|
+
borderTopRightRadius: 20,
|
|
298
|
+
maxHeight: SCREEN_HEIGHT * 0.7,
|
|
299
|
+
paddingBottom: 20,
|
|
300
|
+
},
|
|
301
|
+
sheetGrabber: {
|
|
302
|
+
width: 40,
|
|
303
|
+
height: 4,
|
|
304
|
+
backgroundColor: '#E2E8F0',
|
|
305
|
+
borderRadius: 2,
|
|
306
|
+
alignSelf: 'center',
|
|
307
|
+
marginTop: 8,
|
|
308
|
+
marginBottom: 16,
|
|
309
|
+
},
|
|
310
|
+
sheetHeader: {
|
|
311
|
+
paddingHorizontal: 20,
|
|
312
|
+
paddingBottom: 12,
|
|
313
|
+
borderBottomWidth: 1,
|
|
314
|
+
borderBottomColor: '#F1F5F9',
|
|
315
|
+
},
|
|
316
|
+
sheetScroll: {
|
|
317
|
+
paddingHorizontal: 20,
|
|
318
|
+
paddingVertical: 8,
|
|
319
|
+
},
|
|
320
|
+
sheetItemRow: {
|
|
321
|
+
flexDirection: 'row',
|
|
322
|
+
justifyContent: 'space-between',
|
|
323
|
+
alignItems: 'center',
|
|
324
|
+
paddingVertical: 14,
|
|
325
|
+
borderBottomWidth: 1,
|
|
326
|
+
borderBottomColor: '#F8FAFC',
|
|
327
|
+
},
|
|
328
|
+
});
|
|
@@ -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
|
+
});
|