Skip to content

Commit d76e559

Browse files
committed
Resolve merge conflicts
2 parents bc79c7f + 1e18003 commit d76e559

7 files changed

Lines changed: 241 additions & 6 deletions

File tree

src/app/tabs/ProjectList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Project } from '../../types/db/types';
1111
import { getProjects } from '../../db/queries';
1212
import { useNavigation } from '@react-navigation/native';
1313
import FluentLogo from '../../assets/icons/fluent-logo.svg';
14-
import { RootStackParamList } from '../../types/navigationTypes';
14+
import { RootStackParamList } from '../../types/navigation/types';
1515
import { StackNavigationProp } from '@react-navigation/stack';
1616
import { Ionicons } from '@react-native-vector-icons/ionicons';
1717
import { appStyles as styles } from '../appStyles';

src/app/tabs/ViewChapter.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { logger } from '../../utils/logger';
1313
import { Ionicons } from '@react-native-vector-icons/ionicons';
1414
import { appStyles as styles } from '../appStyles';
15-
import { RootStackParamList } from '../../types/navigationTypes';
15+
import { RootStackParamList } from '../../types/navigation/types';
1616
import { ChapterAssignmentData, VerseData } from '../../types/db/types';
1717
import { getChapterAssignmentById, getBibleTexts } from '../../db/queries';
1818
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
@@ -180,7 +180,6 @@ export default function VerseDetailScreen() {
180180
)}
181181
</View>
182182

183-
{/* Target Language Card */}
184183
<View style={styles.cardColumn}>
185184
<Text style={styles.cardTitle}>
186185
{language} - Verse {selectedVerse}
@@ -228,7 +227,6 @@ export default function VerseDetailScreen() {
228227
</View>
229228
</ScrollView>
230229

231-
{/* Verse chips */}
232230
<ScrollView
233231
horizontal
234232
showsHorizontalScrollIndicator={false}

src/app/tabs/ViewProject.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { ChapterListItem } from '../../types/db/types';
1515
import { StackNavigationProp } from '@react-navigation/stack';
1616
import { Ionicons } from '@react-native-vector-icons/ionicons';
1717
import { appStyles as styles } from '../appStyles';
18-
import { RootStackParamList } from '../../types/navigationTypes';
18+
import { RootStackParamList } from '../../types/navigation/types';
1919
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
2020

2121
const log = logger.create('ChaptersScreen');

src/navigation/AppNavigator.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { createStackNavigator } from '@react-navigation/stack';
3-
import { RootStackParamList } from '../types/navigationTypes';
3+
import { RootStackParamList } from '../types/navigation/types';
44
import ProjectList from '../app/tabs/ProjectList';
55
import ViewProject from '../app/tabs/ViewProject';
66
import VerseDetailScreen from '../app/tabs/ViewChapter';

src/services/fluentApi.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { API_BASE_URL } from '@env';
2+
3+
async function request(endpoint: string, options?: RequestInit) {
4+
const res = await fetch(`${API_BASE_URL}${endpoint}`, {
5+
...options,
6+
headers: {
7+
'Content-Type': 'application/json',
8+
...options?.headers,
9+
},
10+
});
11+
12+
if (!res.ok) {
13+
throw new Error(`API failed: ${res.status}`);
14+
}
15+
16+
return res.json();
17+
}
18+
19+
function getHeaders(email?: string) {
20+
const headers: Record<string, string> = {
21+
'Content-Type': 'application/json',
22+
};
23+
if (email) {
24+
headers['x-user-email'] = email;
25+
}
26+
return headers;
27+
}
28+
29+
export const FluentAPI = {
30+
getLanguages: () => request('/languages'),
31+
getBooks: () => request('/books'),
32+
getBibles: () => request('/bibles'),
33+
34+
getUserByEmail: (email: string) =>
35+
request(`/users/email/${encodeURIComponent(email)}`, {
36+
method: 'GET',
37+
headers: getHeaders(email),
38+
}),
39+
40+
getUserProjects: (userId: number, email: string) =>
41+
request(`/users/${userId}/projects`, {
42+
method: 'GET',
43+
headers: getHeaders(email),
44+
}),
45+
46+
getChapterAssignments: (userId: number, email: string) =>
47+
request(`/users/${userId}/chapter-assignments`, {
48+
method: 'GET',
49+
headers: getHeaders(email),
50+
}),
51+
52+
getBibleTexts: (
53+
bibleId: number,
54+
chapters: Array<{ bookId: number; chapterNumber: number }>,
55+
email: string,
56+
) =>
57+
request(`/bibles/${bibleId}/bulk-texts`, {
58+
method: 'POST',
59+
headers: getHeaders(email),
60+
body: JSON.stringify({ chapters }),
61+
}),
62+
};

src/services/syncServices.ts

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import { FluentAPI } from './fluentApi';
2+
import {
3+
insertUser,
4+
insertLanguages,
5+
insertBooks,
6+
insertBibles,
7+
insertProjects,
8+
insertChapterAssignments,
9+
insertProjectUnits,
10+
insertBibleTexts,
11+
getChaptersToSync,
12+
} from '../db/repository';
13+
import { logger } from '../utils/logger';
14+
import { ApiBook, ApiVerse } from '../types/apiTypes';
15+
16+
const log = logger.create('SyncService');
17+
18+
export async function syncUser(email: string) {
19+
try {
20+
log.info('Syncing user...');
21+
22+
const user = await FluentAPI.getUserByEmail(email);
23+
24+
if (!user?.id) {
25+
throw new Error('Invalid user response');
26+
}
27+
28+
await insertUser(user);
29+
30+
log.info('User synced', { email: user.email });
31+
32+
return user;
33+
} catch (error) {
34+
log.error('User sync failed', { error });
35+
throw error;
36+
}
37+
}
38+
39+
export async function syncMasterData() {
40+
try {
41+
log.info('Sync started');
42+
43+
const [languages, books, bibles] = await Promise.all([
44+
FluentAPI.getLanguages(),
45+
FluentAPI.getBooks(),
46+
FluentAPI.getBibles(),
47+
]);
48+
49+
await insertLanguages(languages);
50+
await insertBooks(books);
51+
await insertBibles(bibles);
52+
53+
log.info('Sync completed');
54+
} catch (error) {
55+
log.error('Sync failed', { error });
56+
}
57+
}
58+
59+
export async function syncProjects(userId: number, email: string) {
60+
try {
61+
log.info('Syncing projects...');
62+
63+
const projects = await FluentAPI.getUserProjects(userId, email);
64+
65+
await insertProjects(projects);
66+
67+
log.info('Projects synced', { count: projects.length });
68+
} catch (error) {
69+
log.error('Project sync failed', { error });
70+
}
71+
}
72+
73+
export async function syncChapterAssignments(userId: number, email: string) {
74+
try {
75+
log.info('Syncing chapter assignments...');
76+
77+
const response = await FluentAPI.getChapterAssignments(userId, email);
78+
79+
const allAssignments = [
80+
...(response?.assignedChapters || []),
81+
...(response?.peerCheckChapters || []),
82+
];
83+
84+
if (allAssignments.length > 0) {
85+
await insertProjectUnits(allAssignments);
86+
87+
await insertChapterAssignments(allAssignments);
88+
log.info('Chapter assignments synced', { count: allAssignments.length });
89+
}
90+
} catch (error) {
91+
log.error('Chapter assignment sync failed', {
92+
message: error instanceof Error ? error.message : String(error),
93+
stack: error instanceof Error ? error.stack : undefined,
94+
raw: error,
95+
});
96+
}
97+
}
98+
99+
export async function syncBibleTexts(email: string) {
100+
try {
101+
log.info('Syncing bible texts...');
102+
103+
const bibleGroups = await getChaptersToSync();
104+
105+
if (bibleGroups.size === 0) {
106+
log.info('No chapters to sync');
107+
return;
108+
}
109+
110+
let totalTextsInserted = 0;
111+
112+
for (const [bibleId, chapters] of bibleGroups) {
113+
try {
114+
log.info('Syncing chapters for bible', {
115+
bibleId,
116+
chapterCount: chapters.length,
117+
});
118+
119+
const response: ApiBook[] = await FluentAPI.getBibleTexts(
120+
bibleId,
121+
chapters,
122+
email,
123+
);
124+
125+
if (response && Array.isArray(response)) {
126+
const textsWithBibleId = response.map((book: ApiBook) => ({
127+
bookId: book.bookId,
128+
chapterNumber: book.chapterNumber,
129+
verses: book.verses.map((verse: ApiVerse) => ({
130+
bible_id: bibleId,
131+
book_id: book.bookId,
132+
chapter_number: book.chapterNumber,
133+
verse_number: verse.verseNumber,
134+
text: verse.text,
135+
})),
136+
}));
137+
138+
await insertBibleTexts(textsWithBibleId);
139+
totalTextsInserted += response.length;
140+
log.info('Synced books for bible', {
141+
bibleId,
142+
count: response.length,
143+
});
144+
}
145+
} catch (error) {
146+
log.error(`Failed to sync texts for bible ${bibleId}:`, { error });
147+
continue;
148+
}
149+
}
150+
151+
log.info('Bible texts sync completed', { count: totalTextsInserted });
152+
} catch (error) {
153+
log.error('Bible texts sync failed', { error });
154+
}
155+
}
156+
157+
export async function syncAllData(email: string) {
158+
log.info('Starting full sync...');
159+
160+
const user = await syncUser(email);
161+
162+
await syncMasterData();
163+
164+
await syncProjects(user.id, email);
165+
166+
await syncChapterAssignments(user.id, email);
167+
168+
try {
169+
await syncBibleTexts(email);
170+
} catch (e) {
171+
log.warn('Bible text sync failed, continuing...', { error: e });
172+
}
173+
174+
log.info('Full sync completed successfully!');
175+
}

0 commit comments

Comments
 (0)