Skip to content

Commit e0b2789

Browse files
committed
Error Handling
1 parent 8acf52e commit e0b2789

10 files changed

Lines changed: 330 additions & 212 deletions

File tree

src/app/tabs/ProjectList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import { StackNavigationProp } from '@react-navigation/stack';
1717
import { Ionicons } from '@react-native-vector-icons/ionicons';
1818
import { RootStackParamList } from '../../types/navigation/types';
1919

20-
const log = logger.create('ProjectListScreen');
20+
const log = logger.create('ProjectList');
2121
type Nav = StackNavigationProp<RootStackParamList, 'Projects'>;
2222

23-
export default function ProjectsScreen() {
23+
export default function ProjectList() {
2424
const navigation = useNavigation<Nav>();
2525
const [projects, setProjects] = useState<Project[]>([]);
2626
const [loading, setLoading] = useState(true);

src/app/tabs/ViewChapter.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { ChapterAssignmentData, VerseData } from '../../types/db/types';
1717
import { getChapterAssignmentById, getBibleTexts } from '../../db/queries';
1818
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
1919

20-
const log = logger.create('ViewChapterScreen');
20+
const log = logger.create('ViewChapter');
2121

2222
if (Platform.OS === 'android') {
2323
UIManager.setLayoutAnimationEnabledExperimental?.(true);
@@ -26,7 +26,7 @@ if (Platform.OS === 'android') {
2626
type Route = RouteProp<RootStackParamList, 'VerseDetail'>;
2727
type VerseState = 'idle' | 'recording' | 'recorded';
2828

29-
export default function VerseDetailScreen() {
29+
export default function ViewChapter() {
3030
const navigation = useNavigation();
3131
const { chapterId, chapterName, language, projectName } =
3232
useRoute<Route>().params;

src/app/tabs/ViewProject.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import { appStyles as styles } from '../appStyles';
1818
import { RootStackParamList } from '../../types/navigation/types';
1919
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
2020

21-
const log = logger.create('ChaptersScreen');
21+
const log = logger.create('ViewProject');
2222

2323
type Nav = StackNavigationProp<RootStackParamList, 'Chapters'>;
2424
type Route = RouteProp<RootStackParamList, 'Chapters'>;
2525

26-
export default function ChaptersScreen() {
26+
export default function ViewProject() {
2727
const navigation = useNavigation<Nav>();
2828
const { projectId, projectName, language } = useRoute<Route>().params;
2929

src/components/ui/SyncButton.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export function SyncButton({
5555
};
5656

5757
const getFailedStep = (): string | null => {
58+
const userError = getSyncError(KV_KEYS.SYNC_ERROR_USER);
59+
if (userError) return 'user';
60+
61+
const masterDataError = getSyncError(KV_KEYS.SYNC_ERROR_MASTER_DATA);
62+
if (masterDataError) return 'master data';
63+
5864
const projectsError = getSyncError(KV_KEYS.SYNC_ERROR_PROJECTS);
5965
if (projectsError) return 'projects';
6066

src/db/repository.ts

Lines changed: 141 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,101 @@ import { Transaction } from '@op-engineering/op-sqlite';
55

66
const log = logger.create('DBRepository');
77

8+
async function insertLanguageTx(tx: Transaction, lang: DBTypes.Language) {
9+
await tx.execute(
10+
`INSERT OR REPLACE INTO languages
11+
(id, lang_name, lang_name_localized, lang_code_iso_639_3, script_direction)
12+
VALUES (?, ?, ?, ?, ?)`,
13+
[
14+
lang.id,
15+
lang.langName,
16+
lang.langNameLocalized ?? null,
17+
lang.langCode ?? null,
18+
lang.scriptDirection ?? 'ltr',
19+
],
20+
);
21+
}
22+
23+
async function insertBookTx(tx: Transaction, book: DBTypes.Book) {
24+
if (!book.eng_display_name) return;
25+
26+
await tx.execute(
27+
`INSERT OR REPLACE INTO books
28+
(id, code, eng_display_name)
29+
VALUES (?, ?, ?)`,
30+
[book.id, book.code, book.eng_display_name],
31+
);
32+
}
33+
34+
async function insertBibleTx(tx: Transaction, bible: DBTypes.Bible) {
35+
if (!bible.name || !bible.abbreviation) return;
36+
37+
await tx.execute(
38+
`INSERT OR REPLACE INTO bibles
39+
(id, language_id, name, abbreviation)
40+
VALUES (?, ?, ?, ?)`,
41+
[bible.id, bible.languageId, bible.name, bible.abbreviation],
42+
);
43+
}
44+
45+
async function insertProjectUnitTx(
46+
tx: Transaction,
47+
unit: { id: number; projectId: number },
48+
) {
49+
await tx.execute(
50+
`INSERT OR REPLACE INTO project_units
51+
(id, project_id, status)
52+
VALUES (?, ?, ?)`,
53+
[unit.id, unit.projectId, 'not_started'],
54+
);
55+
}
56+
57+
async function insertChapterAssignmentTx(
58+
tx: Transaction,
59+
assignment: DBTypes.ChapterAssignment,
60+
) {
61+
if (!assignment?.chapterAssignmentId) return;
62+
63+
await tx.execute(
64+
`INSERT OR REPLACE INTO chapter_assignments
65+
(id, project_unit_id, bible_id, book_id, chapter_number,
66+
assigned_user_id, status, submitted_time, updated_at)
67+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
68+
[
69+
assignment.chapterAssignmentId,
70+
assignment.projectUnitId,
71+
assignment.bibleId,
72+
assignment.bookId,
73+
assignment.chapterNumber,
74+
assignment.assignedUserId ?? null,
75+
assignment.chapterStatus ?? 'not_started',
76+
assignment.submittedTime ?? null,
77+
assignment.updatedAt ?? new Date().toISOString(),
78+
],
79+
);
80+
}
81+
82+
function getUniqueProjectUnits(assignments: DBTypes.ChapterAssignment[]) {
83+
const unitsMap = new Map<number, { id: number; projectId: number }>();
84+
85+
for (const assignment of assignments) {
86+
if (!assignment.projectUnitId || !assignment.projectId) continue;
87+
if (unitsMap.has(assignment.projectUnitId)) continue;
88+
89+
unitsMap.set(assignment.projectUnitId, {
90+
id: assignment.projectUnitId,
91+
projectId: assignment.projectId,
92+
});
93+
}
94+
95+
return unitsMap;
96+
}
97+
898
export async function insertUser(user: DBTypes.User) {
999
const db = getDatabase();
10100

11-
try {
12-
await db.execute(
101+
await db.transaction(async (tx: Transaction) => {
102+
await tx.execute(
13103
`INSERT OR REPLACE INTO users
14104
(id, username, email, first_name, last_name)
15105
VALUES (?, ?, ?, ?, ?)`,
@@ -21,28 +111,15 @@ export async function insertUser(user: DBTypes.User) {
21111
user.lastName ?? null,
22112
],
23113
);
24-
} catch (error) {
25-
log.error('Error inserting user', { error });
26-
}
114+
});
27115
}
28116

29117
export async function insertLanguages(data: DBTypes.Language[]) {
30118
const db = getDatabase();
31119

32120
await db.transaction(async (tx: Transaction) => {
33121
for (const lang of data) {
34-
await tx.execute(
35-
`INSERT OR REPLACE INTO languages
36-
(id, lang_name, lang_name_localized, lang_code_iso_639_3, script_direction)
37-
VALUES (?, ?, ?, ?, ?)`,
38-
[
39-
lang.id,
40-
lang.langName,
41-
lang.langNameLocalized ?? null,
42-
lang.langCode ?? null,
43-
lang.scriptDirection ?? 'ltr',
44-
],
45-
);
122+
await insertLanguageTx(tx, lang);
46123
}
47124
});
48125
}
@@ -52,14 +129,7 @@ export async function insertBooks(data: DBTypes.Book[]) {
52129

53130
await db.transaction(async (tx: Transaction) => {
54131
for (const book of data) {
55-
if (!book.eng_display_name) continue;
56-
57-
await tx.execute(
58-
`INSERT OR REPLACE INTO books
59-
(id, code, eng_display_name)
60-
VALUES (?, ?, ?)`,
61-
[book.id, book.code, book.eng_display_name],
62-
);
132+
await insertBookTx(tx, book);
63133
}
64134
});
65135
}
@@ -69,14 +139,29 @@ export async function insertBibles(data: DBTypes.Bible[]) {
69139

70140
await db.transaction(async (tx: Transaction) => {
71141
for (const bible of data) {
72-
if (!bible.name || !bible.abbreviation) continue;
142+
await insertBibleTx(tx, bible);
143+
}
144+
});
145+
}
73146

74-
await tx.execute(
75-
`INSERT OR REPLACE INTO bibles
76-
(id, language_id, name, abbreviation)
77-
VALUES (?, ?, ?, ?)`,
78-
[bible.id, bible.languageId, bible.name, bible.abbreviation],
79-
);
147+
export async function insertMasterData(
148+
languages: DBTypes.Language[],
149+
books: DBTypes.Book[],
150+
bibles: DBTypes.Bible[],
151+
) {
152+
const db = getDatabase();
153+
154+
await db.transaction(async (tx: Transaction) => {
155+
for (const lang of languages) {
156+
await insertLanguageTx(tx, lang);
157+
}
158+
159+
for (const book of books) {
160+
await insertBookTx(tx, book);
161+
}
162+
163+
for (const bible of bibles) {
164+
await insertBibleTx(tx, bible);
80165
}
81166
});
82167
}
@@ -88,8 +173,10 @@ export async function insertProjects(data: DBTypes.Project[]) {
88173
for (const project of data) {
89174
if (!project?.id || !project?.name) continue;
90175

91-
const sourceLangId = project.sourceLanguageId ?? project.sourceLanguageId;
92-
const targetLangId = project.targetLanguageId ?? project.targetLanguageId;
176+
const sourceLangId =
177+
project.sourceLanguageId ?? project.source_language_id;
178+
const targetLangId =
179+
project.targetLanguageId ?? project.target_language_id;
93180

94181
if (!sourceLangId || !targetLangId) continue;
95182

@@ -118,25 +205,7 @@ export async function insertChapterAssignments(
118205

119206
await db.transaction(async (tx: Transaction) => {
120207
for (const assignment of data) {
121-
if (!assignment?.chapterAssignmentId) continue;
122-
123-
await tx.execute(
124-
`INSERT OR REPLACE INTO chapter_assignments
125-
(id, project_unit_id, bible_id, book_id, chapter_number,
126-
assigned_user_id, status, submitted_time, updated_at)
127-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
128-
[
129-
assignment.chapterAssignmentId,
130-
assignment.projectUnitId,
131-
assignment.bibleId,
132-
assignment.bookId,
133-
assignment.chapterNumber,
134-
assignment.assignedUserId ?? null,
135-
assignment.chapterStatus ?? 'not_started',
136-
assignment.submittedTime ?? null,
137-
assignment.updatedAt ?? new Date().toISOString(),
138-
],
139-
);
208+
await insertChapterAssignmentTx(tx, assignment);
140209
}
141210
});
142211
}
@@ -146,32 +215,34 @@ export async function insertProjectUnits(
146215
) {
147216
const db = getDatabase();
148217

149-
const unitsMap = new Map<number, { id: number; projectId: number }>();
150-
151-
for (const assignment of assignments) {
152-
if (!assignment.projectUnitId || !assignment.projectId) continue;
153-
if (unitsMap.has(assignment.projectUnitId)) continue;
154-
155-
unitsMap.set(assignment.projectUnitId, {
156-
id: assignment.projectUnitId,
157-
projectId: assignment.projectId,
158-
});
159-
}
218+
const unitsMap = getUniqueProjectUnits(assignments);
160219

161220
if (unitsMap.size > 0) {
162221
await db.transaction(async (tx: Transaction) => {
163222
for (const unit of unitsMap.values()) {
164-
await tx.execute(
165-
`INSERT OR REPLACE INTO project_units
166-
(id, project_id, status)
167-
VALUES (?, ?, ?)`,
168-
[unit.id, unit.projectId, 'not_started'],
169-
);
223+
await insertProjectUnitTx(tx, unit);
170224
}
171225
});
172226
}
173227
}
174228

229+
export async function insertChapterAssignmentSyncData(
230+
assignments: DBTypes.ChapterAssignment[],
231+
) {
232+
const db = getDatabase();
233+
const unitsMap = getUniqueProjectUnits(assignments);
234+
235+
await db.transaction(async (tx: Transaction) => {
236+
for (const unit of unitsMap.values()) {
237+
await insertProjectUnitTx(tx, unit);
238+
}
239+
240+
for (const assignment of assignments) {
241+
await insertChapterAssignmentTx(tx, assignment);
242+
}
243+
});
244+
}
245+
175246
export async function getChaptersToSync() {
176247
const db = getDatabase();
177248

src/db/schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const createTableQueries: string[] = [
4040

4141
`CREATE TABLE IF NOT EXISTS project_units (
4242
id INTEGER PRIMARY KEY,
43-
project_id INTEGER NOT NULL,
43+
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
4444
status TEXT NOT NULL DEFAULT 'not_started',
4545
updated_at TEXT
4646
);`,
@@ -49,7 +49,7 @@ export const createTableQueries: string[] = [
4949

5050
` CREATE TABLE IF NOT EXISTS chapter_assignments (
5151
id INTEGER PRIMARY KEY,
52-
project_unit_id INTEGER NOT NULL REFERENCES project_units(id),
52+
project_unit_id INTEGER NOT NULL REFERENCES project_units(id) ON DELETE CASCADE,
5353
bible_id INTEGER NOT NULL REFERENCES bibles(id),
5454
book_id INTEGER NOT NULL REFERENCES books(id),
5555
chapter_number INTEGER NOT NULL,
@@ -78,7 +78,7 @@ export const createTableQueries: string[] = [
7878
`CREATE TABLE IF NOT EXISTS recordings (
7979
id TEXT PRIMARY KEY,
8080
bible_text_id INTEGER NOT NULL REFERENCES bible_texts(id),
81-
chapter_assignment_id INTEGER NOT NULL REFERENCES chapter_assignments(id),
81+
chapter_assignment_id INTEGER NOT NULL REFERENCES chapter_assignments(id) ON DELETE CASCADE,
8282
assigned_user_id INTEGER NOT NULL REFERENCES users(id),
8383
local_file_path TEXT NOT NULL,
8484
blob_key TEXT,

src/navigation/AppNavigator.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createStackNavigator } from '@react-navigation/stack';
33
import { RootStackParamList } from '../types/navigation/types';
44
import ProjectList from '../app/tabs/ProjectList';
55
import ViewProject from '../app/tabs/ViewProject';
6-
import VerseDetailScreen from '../app/tabs/ViewChapter';
6+
import ViewChapter from '../app/tabs/ViewChapter';
77

88
const Stack = createStackNavigator<RootStackParamList>();
99

@@ -12,7 +12,7 @@ export default function AppNavigator() {
1212
<Stack.Navigator screenOptions={{ headerShown: false }}>
1313
<Stack.Screen name="Projects" component={ProjectList} />
1414
<Stack.Screen name="Chapters" component={ViewProject} />
15-
<Stack.Screen name="VerseDetail" component={VerseDetailScreen} />
15+
<Stack.Screen name="VerseDetail" component={ViewChapter} />
1616
</Stack.Navigator>
1717
);
1818
}

0 commit comments

Comments
 (0)