~repos /only-bible-app

#kotlin#android#ios

GIT_CONFIG_PARAMETERS="'http.version=HTTP/1.1'" git clone https://git.pyrossh.dev/only-bible-app.git
Discussions: https://groups.google.com/g/rust-embed-devs

The only bible app you will ever need. No ads. No in-app purchases. No distractions.


assets/bibles/Bengali.bin ADDED
Binary file
assets/bibles/English.bin ADDED
Binary file
assets/bibles/Gujarati.bin ADDED
Binary file
assets/bibles/Hindi.bin ADDED
Binary file
assets/bibles/Kannada.bin ADDED
Binary file
assets/bibles/Malayalam.bin ADDED
Binary file
assets/bibles/Nepali.bin ADDED
Binary file
assets/bibles/Oriya.bin ADDED
Binary file
assets/bibles/Punjabi.bin ADDED
Binary file
assets/bibles/Tamil.bin ADDED
Binary file
assets/bibles/Telugu.bin ADDED
Binary file
lib/bible_generated.dart CHANGED
@@ -6,8 +6,6 @@ library only_bible_app.bible;
6
6
  import 'dart:typed_data' show Uint8List;
7
7
  import 'package:flat_buffers/flat_buffers.dart' as fb;
8
8
 
9
-
10
-
11
9
  class Verse {
12
10
  Verse._(this._bc, this._bcOffset);
13
11
  factory Verse(List<int> bytes) {
@@ -36,8 +34,7 @@ class _VerseReader extends fb.TableReader<Verse> {
36
34
  const _VerseReader();
37
35
 
38
36
  @override
39
- Verse createObject(fb.BufferContext bc, int offset) =>
37
+ Verse createObject(fb.BufferContext bc, int offset) => Verse._(bc, offset);
40
- Verse._(bc, offset);
41
38
  }
42
39
 
43
40
  class VerseBuilder {
@@ -53,18 +50,22 @@ class VerseBuilder {
53
50
  fbBuilder.addInt32(0, index);
54
51
  return fbBuilder.offset;
55
52
  }
53
+
56
54
  int addBook(int? book) {
57
55
  fbBuilder.addInt32(1, book);
58
56
  return fbBuilder.offset;
59
57
  }
58
+
60
59
  int addChapter(int? chapter) {
61
60
  fbBuilder.addInt32(2, chapter);
62
61
  return fbBuilder.offset;
63
62
  }
63
+
64
64
  int addHeadingOffset(int? offset) {
65
65
  fbBuilder.addOffset(3, offset);
66
66
  return fbBuilder.offset;
67
67
  }
68
+
68
69
  int addTextOffset(int? offset) {
69
70
  fbBuilder.addOffset(4, offset);
70
71
  return fbBuilder.offset;
@@ -88,8 +89,7 @@ class VerseObjectBuilder extends fb.ObjectBuilder {
88
89
  int? chapter,
89
90
  String? heading,
90
91
  String? text,
91
- })
92
- : _index = index,
92
+ }) : _index = index,
93
93
  _book = book,
94
94
  _chapter = chapter,
95
95
  _heading = heading,
@@ -98,10 +98,8 @@ class VerseObjectBuilder extends fb.ObjectBuilder {
98
98
  /// Finish building, and store into the [fbBuilder].
99
99
  @override
100
100
  int finish(fb.Builder fbBuilder) {
101
- final int? headingOffset = _heading == null ? null
101
+ final int? headingOffset = _heading == null ? null : fbBuilder.writeString(_heading!);
102
- : fbBuilder.writeString(_heading!);
103
- final int? textOffset = _text == null ? null
102
+ final int? textOffset = _text == null ? null : fbBuilder.writeString(_text!);
104
- : fbBuilder.writeString(_text!);
105
103
  fbBuilder.startTable(5);
106
104
  fbBuilder.addInt32(0, _index);
107
105
  fbBuilder.addInt32(1, _book);
@@ -119,6 +117,7 @@ class VerseObjectBuilder extends fb.ObjectBuilder {
119
117
  return fbBuilder.buffer;
120
118
  }
121
119
  }
120
+
122
121
  class Chapter {
123
122
  Chapter._(this._bc, this._bcOffset);
124
123
  factory Chapter(List<int> bytes) {
@@ -145,8 +144,7 @@ class _ChapterReader extends fb.TableReader<Chapter> {
145
144
  const _ChapterReader();
146
145
 
147
146
  @override
148
- Chapter createObject(fb.BufferContext bc, int offset) =>
147
+ Chapter createObject(fb.BufferContext bc, int offset) => Chapter._(bc, offset);
149
- Chapter._(bc, offset);
150
148
  }
151
149
 
152
150
  class ChapterBuilder {
@@ -162,10 +160,12 @@ class ChapterBuilder {
162
160
  fbBuilder.addInt32(0, index);
163
161
  return fbBuilder.offset;
164
162
  }
163
+
165
164
  int addBook(int? book) {
166
165
  fbBuilder.addInt32(1, book);
167
166
  return fbBuilder.offset;
168
167
  }
168
+
169
169
  int addVersesOffset(int? offset) {
170
170
  fbBuilder.addOffset(2, offset);
171
171
  return fbBuilder.offset;
@@ -185,16 +185,15 @@ class ChapterObjectBuilder extends fb.ObjectBuilder {
185
185
  int? index,
186
186
  int? book,
187
187
  List<VerseObjectBuilder>? verses,
188
- })
189
- : _index = index,
188
+ }) : _index = index,
190
189
  _book = book,
191
190
  _verses = verses;
192
191
 
193
192
  /// Finish building, and store into the [fbBuilder].
194
193
  @override
195
194
  int finish(fb.Builder fbBuilder) {
196
- final int? versesOffset = _verses == null ? null
195
+ final int? versesOffset =
197
- : fbBuilder.writeList(_verses!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
196
+ _verses == null ? null : fbBuilder.writeList(_verses!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
198
197
  fbBuilder.startTable(3);
199
198
  fbBuilder.addInt32(0, _index);
200
199
  fbBuilder.addInt32(1, _book);
@@ -210,6 +209,7 @@ class ChapterObjectBuilder extends fb.ObjectBuilder {
210
209
  return fbBuilder.buffer;
211
210
  }
212
211
  }
212
+
213
213
  class Book {
214
214
  Book._(this._bc, this._bcOffset);
215
215
  factory Book(List<int> bytes) {
@@ -235,8 +235,7 @@ class _BookReader extends fb.TableReader<Book> {
235
235
  const _BookReader();
236
236
 
237
237
  @override
238
- Book createObject(fb.BufferContext bc, int offset) =>
238
+ Book createObject(fb.BufferContext bc, int offset) => Book._(bc, offset);
239
- Book._(bc, offset);
240
239
  }
241
240
 
242
241
  class BookBuilder {
@@ -252,6 +251,7 @@ class BookBuilder {
252
251
  fbBuilder.addInt32(0, index);
253
252
  return fbBuilder.offset;
254
253
  }
254
+
255
255
  int addChaptersOffset(int? offset) {
256
256
  fbBuilder.addOffset(1, offset);
257
257
  return fbBuilder.offset;
@@ -269,15 +269,14 @@ class BookObjectBuilder extends fb.ObjectBuilder {
269
269
  BookObjectBuilder({
270
270
  int? index,
271
271
  List<ChapterObjectBuilder>? chapters,
272
- })
273
- : _index = index,
272
+ }) : _index = index,
274
273
  _chapters = chapters;
275
274
 
276
275
  /// Finish building, and store into the [fbBuilder].
277
276
  @override
278
277
  int finish(fb.Builder fbBuilder) {
279
- final int? chaptersOffset = _chapters == null ? null
278
+ final int? chaptersOffset =
280
- : fbBuilder.writeList(_chapters!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
279
+ _chapters == null ? null : fbBuilder.writeList(_chapters!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
281
280
  fbBuilder.startTable(2);
282
281
  fbBuilder.addInt32(0, _index);
283
282
  fbBuilder.addOffset(1, chaptersOffset);
@@ -292,6 +291,7 @@ class BookObjectBuilder extends fb.ObjectBuilder {
292
291
  return fbBuilder.buffer;
293
292
  }
294
293
  }
294
+
295
295
  class Bible {
296
296
  Bible._(this._bc, this._bcOffset);
297
297
  factory Bible(List<int> bytes) {
@@ -317,8 +317,7 @@ class _BibleReader extends fb.TableReader<Bible> {
317
317
  const _BibleReader();
318
318
 
319
319
  @override
320
- Bible createObject(fb.BufferContext bc, int offset) =>
320
+ Bible createObject(fb.BufferContext bc, int offset) => Bible._(bc, offset);
321
- Bible._(bc, offset);
322
321
  }
323
322
 
324
323
  class BibleBuilder {
@@ -334,6 +333,7 @@ class BibleBuilder {
334
333
  fbBuilder.addOffset(0, offset);
335
334
  return fbBuilder.offset;
336
335
  }
336
+
337
337
  int addBooksOffset(int? offset) {
338
338
  fbBuilder.addOffset(1, offset);
339
339
  return fbBuilder.offset;
@@ -351,17 +351,15 @@ class BibleObjectBuilder extends fb.ObjectBuilder {
351
351
  BibleObjectBuilder({
352
352
  String? name,
353
353
  List<BookObjectBuilder>? books,
354
- })
355
- : _name = name,
354
+ }) : _name = name,
356
355
  _books = books;
357
356
 
358
357
  /// Finish building, and store into the [fbBuilder].
359
358
  @override
360
359
  int finish(fb.Builder fbBuilder) {
361
- final int? nameOffset = _name == null ? null
360
+ final int? nameOffset = _name == null ? null : fbBuilder.writeString(_name!);
362
- : fbBuilder.writeString(_name!);
363
- final int? booksOffset = _books == null ? null
361
+ final int? booksOffset =
364
- : fbBuilder.writeList(_books!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
362
+ _books == null ? null : fbBuilder.writeList(_books!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
365
363
  fbBuilder.startTable(2);
366
364
  fbBuilder.addOffset(0, nameOffset);
367
365
  fbBuilder.addOffset(1, booksOffset);
lib/main.dart CHANGED
@@ -7,12 +7,10 @@ import "package:flutter_native_splash/flutter_native_splash.dart";
7
7
  import "package:async_redux/async_redux.dart";
8
8
  import "package:only_bible_app/app.dart";
9
9
  import "package:only_bible_app/dialog.dart";
10
- import "package:only_bible_app/env.dart";
11
10
  import "package:only_bible_app/navigation.dart";
12
11
  import "package:only_bible_app/store/actions.dart";
13
12
  import "package:only_bible_app/store/app_persistor.dart";
14
13
  import "package:only_bible_app/store/app_state.dart";
15
- import "package:only_bible_app/store/actions.dart";
16
14
 
17
15
  void main() async {
18
16
  final globalNavigatorKey = GlobalKey<NavigatorState>();
@@ -41,9 +39,9 @@ void main() async {
41
39
  // });
42
40
  // return true;
43
41
  // };
44
- // FlutterNativeSplash.preserve(
42
+ FlutterNativeSplash.preserve(
45
- // widgetsBinding: WidgetsFlutterBinding.ensureInitialized(),
43
+ widgetsBinding: WidgetsFlutterBinding.ensureInitialized(),
46
- // );
44
+ );
47
45
  usePathUrlStrategy();
48
46
  WidgetsFlutterBinding.ensureInitialized();
49
47
  FlutterAzureTts.init(
@@ -54,5 +52,5 @@ void main() async {
54
52
  updateStatusBar(store.state.darkMode);
55
53
  await store.dispatchAndWait(LoadBibleAction());
56
54
  runApp(App(globalNavigatorKey: globalNavigatorKey, store: store));
57
- // FlutterNativeSplash.remove();
55
+ FlutterNativeSplash.remove();
58
56
  }
lib/models.dart DELETED
@@ -1,121 +0,0 @@
1
- class Bible {
2
- final String name;
3
- final List<Book> books;
4
-
5
- const Bible({
6
- required this.name,
7
- required this.books,
8
- });
9
-
10
- String shortName() {
11
- return name.substring(0, 3).toUpperCase();
12
- }
13
-
14
- List<Book> getOldBooks() {
15
- return books.where((it) => it.isOldTestament()).toList();
16
- }
17
-
18
- List<Book> getNewBooks() {
19
- return books.where((it) => it.isNewTestament()).toList();
20
- }
21
- }
22
-
23
- class Book {
24
- final int index;
25
- final List<Chapter> chapters;
26
-
27
- const Book({
28
- required this.index,
29
- required this.chapters,
30
- });
31
-
32
- bool isOldTestament() => index < 39;
33
-
34
- bool isNewTestament() => index >= 39;
35
-
36
- String shortName(String name) {
37
- if (name[0] == "1" || name[0] == "2" || name[0] == "3") {
38
- return "${name[0]}${name[2].toUpperCase()}${name.substring(3, 4).toLowerCase()}";
39
- }
40
- return "${name[0].toUpperCase()}${name.substring(1, 3).toLowerCase()}";
41
- }
42
- }
43
-
44
- class Chapter {
45
- final int index;
46
- final int book;
47
- final List<Verse> verses;
48
-
49
- const Chapter(
50
- {required this.index, required this.verses, required this.book,});
51
- }
52
-
53
- class Verse {
54
- final int index;
55
- final String bibleName;
56
- final int book;
57
- final int chapter;
58
- String heading;
59
- final String text;
60
-
61
- Verse({
62
- required this.index,
63
- required this.text,
64
- required this.bibleName,
65
- required this.chapter,
66
- required this.book,
67
- required this.heading,
68
- });
69
- }
70
-
71
- List<Book> parseBible(String bibleName, String text) {
72
- final List<Book> books = [];
73
- final lines = text.split("\n");
74
- for (var (index, line) in lines.indexed) {
75
- // ignore last empty line
76
- if (lines.length - 1 == index) {
77
- continue;
78
- }
79
- final arr = line.split("|");
80
- final book = int.parse(arr[0]);
81
- final chapter = int.parse(arr[1]);
82
- final verseNo = int.parse(arr[2]);
83
- final heading = arr[3];
84
- final verseText = arr.sublist(4, arr.length).join("|");
85
- if (books.length < book + 1) {
86
- books.add(
87
- Book(
88
- index: book,
89
- chapters: [],
90
- ),
91
- );
92
- }
93
- if (books[book].chapters.length < chapter + 1) {
94
- books[book].chapters.add(
95
- Chapter(
96
- index: chapter,
97
- book: book,
98
- verses: [],
99
- ),
100
- );
101
- }
102
- books[book].chapters[chapter].verses.add(
103
- Verse(
104
- index: verseNo,
105
- bibleName: bibleName,
106
- chapter: chapter,
107
- book: book,
108
- text: verseText,
109
- heading: heading,
110
- ),
111
- );
112
- }
113
- return books;
114
- }
115
-
116
- class HistoryFrame {
117
- final int book;
118
- final int chapter;
119
-
120
- const HistoryFrame({required this.book, required this.chapter});
121
- }
lib/navigation.dart CHANGED
@@ -2,7 +2,7 @@ import "package:flutter/material.dart";
2
2
  import "package:flutter/services.dart";
3
3
  import "package:app_review/app_review.dart";
4
4
  import "package:go_router/go_router.dart";
5
- import "package:only_bible_app/models.dart";
5
+ import "package:only_bible_app/bible_generated.dart";
6
6
  import "package:only_bible_app/sheets/actions_sheet.dart";
7
7
  import "package:only_bible_app/sheets/settings_sheet.dart";
8
8
  import "package:only_bible_app/store/actions.dart";
@@ -68,21 +68,21 @@ void replaceBookChapter(
68
68
  }
69
69
 
70
70
  void nextChapter(BuildContext context, Bible bible, int book, int chapter) {
71
- final selectedBook = bible.books[book];
71
+ final selectedBook = bible.books![book];
72
- if (selectedBook.chapters.length > chapter + 1) {
72
+ if (selectedBook.chapters!.length > chapter + 1) {
73
73
  pushBookChapter(
74
74
  context,
75
- bible.name,
75
+ bible.name!,
76
76
  selectedBook.index,
77
77
  chapter + 1,
78
78
  TextDirection.ltr,
79
79
  );
80
80
  } else {
81
- if (selectedBook.index + 1 < bible.books.length) {
81
+ if (selectedBook.index + 1 < bible.books!.length) {
82
- final nextBook = bible.books[selectedBook.index + 1];
82
+ final nextBook = bible.books![selectedBook.index + 1];
83
83
  pushBookChapter(
84
84
  context,
85
- bible.name,
85
+ bible.name!,
86
86
  nextBook.index,
87
87
  0,
88
88
  TextDirection.ltr,
@@ -92,27 +92,23 @@ void nextChapter(BuildContext context, Bible bible, int book, int chapter) {
92
92
  }
93
93
 
94
94
  void previousChapter(BuildContext context, Bible bible, int book, int chapter) {
95
- final selectedBook = bible.books[book];
95
+ final selectedBook = bible.books![book];
96
96
  if (chapter - 1 >= 0) {
97
- // if (Navigator.of(context).canPop()) {
98
- // Navigator.of(context).pop();
99
- // } else {
100
97
  pushBookChapter(
101
98
  context,
102
- bible.name,
99
+ bible.name!,
103
100
  selectedBook.index,
104
101
  chapter - 1,
105
102
  TextDirection.rtl,
106
103
  );
107
- // }
108
104
  } else {
109
105
  if (selectedBook.index - 1 >= 0) {
110
- final prevBook = bible.books[selectedBook.index - 1];
106
+ final prevBook = bible.books![selectedBook.index - 1];
111
107
  pushBookChapter(
112
108
  context,
113
- bible.name,
109
+ bible.name!,
114
110
  prevBook.index,
115
- prevBook.chapters.length - 1,
111
+ prevBook.chapters!.length - 1,
116
112
  TextDirection.rtl,
117
113
  );
118
114
  }
@@ -140,11 +136,11 @@ void changeBibleFromHeader(BuildContext context) {
140
136
  }
141
137
 
142
138
  void changeBook(BuildContext context, Bible bible) {
143
- context.push("/books/${Uri.encodeComponent(bible.name)}");
139
+ context.push("/books/${Uri.encodeComponent(bible.name!)}");
144
140
  }
145
141
 
146
142
  void changeChapter(BuildContext context, Bible bible, Book book, int index) {
147
- context.push("/chapters/${Uri.encodeComponent(bible.name)}/${book.index}");
143
+ context.push("/chapters/${Uri.encodeComponent(bible.name!)}/${book.index}");
148
144
  }
149
145
 
150
146
  Future<void> updateCurrentBible(
@@ -185,7 +181,7 @@ Future<void> shareVerses(BuildContext context, Bible bible, List<Verse> verses)
185
181
  final versesThrough = items.length >= 3 ? "${items.first}-${items.last}" : items.join(",");
186
182
  final version = context.currentLang.languageCode == "en" ? "KJV" : "";
187
183
  final title = "$name $chapter:$versesThrough $version";
188
- final text = verses.map((e) => e.text).join("\n");
184
+ final text = verses.map((e) => e.text ?? "").join("\n");
189
185
  await SharePlus.instance.share(
190
186
  ShareParams(
191
187
  title: title,
lib/screens/book_select_screen.dart CHANGED
@@ -5,7 +5,7 @@ import "package:only_bible_app/utils.dart";
5
5
  import "package:only_bible_app/widgets/scaffold_menu.dart";
6
6
  import "package:only_bible_app/widgets/sliver_heading.dart";
7
7
  import "package:only_bible_app/widgets/sliver_tile_grid.dart";
8
- import "package:only_bible_app/models.dart";
8
+ import "package:only_bible_app/bible_generated.dart";
9
9
 
10
10
  class BookSelectScreen extends StatelessWidget {
11
11
  final String bibleName;
@@ -13,11 +13,11 @@ class BookSelectScreen extends StatelessWidget {
13
13
  const BookSelectScreen({super.key, required this.bibleName});
14
14
 
15
15
  dynamic onBookSelected(BuildContext context, Bible bible, int index) {
16
- final book = bible.books[index];
16
+ final book = bible.books![index];
17
- if (book.chapters.length == 1) {
17
+ if (book.chapters!.length == 1) {
18
- return replaceBookChapter(context, bible.name, index, 0);
18
+ return replaceBookChapter(context, bible.name!, index, 0);
19
19
  }
20
- context.go("/chapters/${Uri.encodeComponent(bible.name)}/${book.index}");
20
+ context.go("/chapters/${Uri.encodeComponent(bible.name!)}/${book.index}");
21
21
  }
22
22
 
23
23
  @override
lib/screens/chapter_select_screen.dart CHANGED
@@ -20,17 +20,17 @@ class ChapterSelectScreen extends StatelessWidget {
20
20
  child: const Center(child: CircularProgressIndicator()),
21
21
  );
22
22
  }
23
- final book = bible.books[bookIndex];
23
+ final book = bible.books![bookIndex];
24
24
  return ScaffoldMenu(
25
25
  child: CustomScrollView(
26
26
  physics: const BouncingScrollPhysics(),
27
27
  slivers: [
28
28
  SliverHeading(title: context.bookNames[book.index], showClose: true),
29
29
  SliverTileGrid(
30
- children: List.generate(book.chapters.length, (index) {
30
+ children: List.generate(book.chapters!.length, (index) {
31
31
  return TextButton(
32
32
  child: Text("${index + 1}"),
33
- onPressed: () => replaceBookChapter(context, bible.name, bookIndex, index),
33
+ onPressed: () => replaceBookChapter(context, bible.name!, bookIndex, index),
34
34
  );
35
35
  }),
36
36
  ),
lib/screens/chapter_view_screen.dart CHANGED
@@ -24,8 +24,8 @@ class ChapterViewScreen extends StatelessWidget {
24
24
  child: const Center(child: CircularProgressIndicator()),
25
25
  );
26
26
  }
27
- final book = bible.books[bookIndex];
27
+ final book = bible.books![bookIndex];
28
- final chapter = book.chapters[chapterIndex];
28
+ final chapter = book.chapters![chapterIndex];
29
29
  return Scaffold(
30
30
  appBar: ChapterAppBar(bible: bible, book: book, chapter: chapter),
31
31
  backgroundColor: Theme.of(context).colorScheme.surface,
lib/screens/webview_screen.dart CHANGED
@@ -15,7 +15,7 @@ class WebViewScreen extends StatelessWidget {
15
15
  backgroundColor: Theme.of(context).colorScheme.surface,
16
16
  appBar: AppBar(),
17
17
  body: SafeArea(
18
- child: WebViewWidget(controller: controller),
18
+ child: WebViewWidget(controller: controller),
19
19
  ),
20
20
  );
21
21
  }
lib/sheets/actions_sheet.dart CHANGED
@@ -1,5 +1,5 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/models.dart";
2
+ import "package:only_bible_app/bible_generated.dart";
3
3
  import "package:only_bible_app/navigation.dart";
4
4
  import "package:only_bible_app/store/actions.dart";
5
5
  import "package:only_bible_app/theme.dart";
lib/sheets/highlight_sheet.dart ADDED
@@ -0,0 +1,67 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/app.dart";
3
+ import "package:only_bible_app/bible_generated.dart";
4
+ import "package:only_bible_app/navigation.dart";
5
+ import "package:only_bible_app/store/actions.dart";
6
+ import "package:only_bible_app/theme.dart";
7
+ import "package:only_bible_app/utils.dart";
8
+ import "package:only_bible_app/widgets/highlight_button.dart";
9
+
10
+ class HighlightSheet extends StatelessWidget {
11
+ const HighlightSheet({super.key});
12
+
13
+ @override
14
+ Widget build(BuildContext context) {
15
+ final iconColor = store.state.darkMode ? Colors.white.withOpacity(0.9) : Colors.black.withOpacity(0.9);
16
+ void onHighlight(int index) {
17
+ store.dispatch(SetHighlightAction(
18
+ List<Verse>.from(store.state.selectedVerses),
19
+ index,
20
+ ));
21
+ hideActions(context);
22
+ }
23
+
24
+ return Container(
25
+ height: 50,
26
+ color: context.theme.colorScheme.surface,
27
+ padding: const EdgeInsets.only(left: 20, right: 20),
28
+ child: Row(
29
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
30
+ children: [
31
+ IconButton(
32
+ padding: EdgeInsets.zero,
33
+ onPressed: () {
34
+ store.dispatch(
35
+ RemoveHighlightAction(
36
+ List<Verse>.from(store.state.selectedVerses),
37
+ ),
38
+ );
39
+ hideActions(context);
40
+ },
41
+ icon: Icon(Icons.cancel_outlined, size: 28, color: iconColor),
42
+ ),
43
+ HighlightButton(
44
+ index: 0,
45
+ color: store.state.darkMode ? darkHighlights[0] : lightHighlights[0],
46
+ onHighlightSelected: onHighlight,
47
+ ),
48
+ HighlightButton(
49
+ index: 1,
50
+ color: store.state.darkMode ? darkHighlights[1] : lightHighlights[1],
51
+ onHighlightSelected: onHighlight,
52
+ ),
53
+ HighlightButton(
54
+ index: 2,
55
+ color: store.state.darkMode ? darkHighlights[2] : lightHighlights[2],
56
+ onHighlightSelected: onHighlight,
57
+ ),
58
+ HighlightButton(
59
+ index: 3,
60
+ color: store.state.darkMode ? darkHighlights[3] : lightHighlights[3],
61
+ onHighlightSelected: onHighlight,
62
+ ),
63
+ ],
64
+ ),
65
+ );
66
+ }
67
+ }
lib/sheets/settings_sheet.dart CHANGED
@@ -1,5 +1,5 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/models.dart";
2
+ import "package:only_bible_app/bible_generated.dart";
3
3
  import "package:only_bible_app/navigation.dart";
4
4
  import "package:only_bible_app/store/actions.dart";
5
5
  import "package:only_bible_app/utils.dart";
@@ -32,7 +32,7 @@ class SettingsSheet extends StatelessWidget {
32
32
  SettingsTile.navigation(
33
33
  leading: const Icon(Icons.book_outlined, color: Colors.blueAccent),
34
34
  title: Text(context.l.bibleTitle),
35
- value: Text(bible.name),
35
+ value: Text(bible.name!),
36
36
  onPressed: changeBible,
37
37
  ),
38
38
  SettingsTile.navigation(
lib/store/actions.dart CHANGED
@@ -1,5 +1,5 @@
1
1
  import "package:async_redux/async_redux.dart";
2
- import "package:only_bible_app/models.dart";
2
+ import "package:only_bible_app/bible_generated.dart";
3
3
  import "package:only_bible_app/store/app_state.dart";
4
4
  import "package:only_bible_app/utils.dart";
5
5
 
lib/store/app_state.dart CHANGED
@@ -2,7 +2,7 @@ import "dart:developer";
2
2
  import "package:flutter/material.dart";
3
3
  import "package:just_audio/just_audio.dart";
4
4
  import "package:only_bible_app/dialog.dart";
5
- import "package:only_bible_app/models.dart";
5
+ import "package:only_bible_app/bible_generated.dart";
6
6
  import "package:only_bible_app/store/actions.dart";
7
7
  import "package:only_bible_app/store/buffer_audio_source.dart";
8
8
  import "package:only_bible_app/theme.dart";
@@ -143,9 +143,9 @@ class AppState {
143
143
  } else {
144
144
  context.dispatch(SetPlayingAction(true));
145
145
  for (final v in versesToPlay) {
146
- final pathname = "${bible.name}_${v.book}_${v.chapter}_${v.index}";
146
+ final pathname = "${bible.name!}_${v.book}_${v.chapter}_${v.index}";
147
147
  try {
148
- final data = await convertText(context.currentLang.audioVoice, v.text);
148
+ final data = await convertText(context.currentLang.audioVoice, v.text ?? "");
149
149
  await player.setAudioSource(BufferAudioSource(data));
150
150
  await player.play();
151
151
  await player.stop();
lib/utils.dart CHANGED
@@ -1,4 +1,5 @@
1
1
  import "dart:convert";
2
+ import "dart:io";
2
3
  import "package:async_redux/async_redux.dart";
3
4
  import "package:http/http.dart" as http;
4
5
  import "package:flutter/services.dart";
@@ -10,9 +11,28 @@ import "package:only_bible_app/l10n/app_localizations.dart";
10
11
  import "package:flutter_azure_tts/flutter_azure_tts.dart";
11
12
  import "package:only_bible_app/dialog.dart";
12
13
  import "package:only_bible_app/env.dart";
13
- import "package:only_bible_app/models.dart";
14
+ import "package:only_bible_app/bible_generated.dart";
14
15
  import "package:only_bible_app/store/app_state.dart";
15
16
 
17
+ extension BibleExt on Bible {
18
+ List<Book> getOldBooks() {
19
+ return books!.where((it) => it.index < 39).toList();
20
+ }
21
+
22
+ List<Book> getNewBooks() {
23
+ return books!.where((it) => it.index >= 39).toList();
24
+ }
25
+ }
26
+
27
+ extension BookExt on Book {
28
+ String shortName(String name) {
29
+ if (name[0] == "1" || name[0] == "2" || name[0] == "3") {
30
+ return "${name[0]}${name[2].toUpperCase()}${name.substring(3, 4).toLowerCase()}";
31
+ }
32
+ return "${name[0].toUpperCase()}${name.substring(1, 3).toLowerCase()}";
33
+ }
34
+ }
35
+
16
36
  extension IterableUtils<E> on Iterable<E> {
17
37
  Iterable<E> sortedBy(Comparable Function(E e) key) => toList()..sort((a, b) => key(a).compareTo(key(b)));
18
38
 
@@ -159,13 +179,9 @@ Future<Uint8List> convertText(String langCode, String text) async {
159
179
  }
160
180
 
161
181
  Future<Bible> loadBible(String name) async {
162
- final bytes = await rootBundle.load("assets/bibles/$name.txt");
182
+ final data = await rootBundle.load("assets/bibles/$name.bin");
163
- final books = parseBible(name, utf8.decode(bytes.buffer.asUint8List(), allowMalformed: false));
183
+ final decompressed = gzip.decode(data.buffer.asUint8List());
164
- // await Future.delayed(Duration(seconds: 2));
165
- return Bible(
184
+ return Bible(decompressed);
166
- name: name,
167
- books: books,
168
- );
169
185
  }
170
186
 
171
187
  Future<void> recordError(String message, StackTrace? stack) async {
lib/widgets/chapter_app_bar.dart CHANGED
@@ -1,5 +1,5 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/models.dart";
2
+ import "package:only_bible_app/bible_generated.dart";
3
3
  import "package:only_bible_app/navigation.dart";
4
4
  import "package:only_bible_app/utils.dart";
5
5
 
lib/widgets/verses_view.dart CHANGED
@@ -1,6 +1,6 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:flutter_swipe_detector/flutter_swipe_detector.dart";
3
- import "package:only_bible_app/models.dart";
3
+ import "package:only_bible_app/bible_generated.dart";
4
4
  import "package:only_bible_app/navigation.dart";
5
5
  import "package:only_bible_app/store/actions.dart";
6
6
  import "package:only_bible_app/utils.dart";
@@ -35,7 +35,7 @@ class VersesView extends StatelessWidget {
35
35
  child: Column(
36
36
  crossAxisAlignment: CrossAxisAlignment.start,
37
37
  children: [
38
- for (final v in chapter.verses)
38
+ for (final v in chapter.verses!)
39
39
  Padding(
40
40
  padding: const EdgeInsets.only(bottom: 4),
41
41
  child: GestureDetector(
@@ -45,14 +45,14 @@ class VersesView extends StatelessWidget {
45
45
  TextSpan(
46
46
  style: baseStyle,
47
47
  children: [
48
- if (v.heading != "")
48
+ if (v.heading != null && v.heading!.isNotEmpty)
49
49
  TextSpan(
50
- text: "${v.heading.replaceAll("<br>", "\n")}\n",
50
+ text: "${v.heading!.replaceAll("<br>", "\n")}\n",
51
51
  style: theme.labelLarge,
52
52
  ),
53
53
  TextSpan(text: "${v.index + 1} ", style: theme.labelMedium),
54
54
  TextSpan(
55
- text: v.text,
55
+ text: v.text ?? "",
56
56
  style: appState.getHighlightStyle(context, v, false),
57
57
  ),
58
58
  ],
{assets → scripts}/bibles/Bengali.txt RENAMED
File without changes
{assets → scripts}/bibles/English.txt RENAMED
File without changes
{assets → scripts}/bibles/Gujarati.txt RENAMED
File without changes
{assets → scripts}/bibles/Hindi.txt RENAMED
File without changes
{assets → scripts}/bibles/Kannada.txt RENAMED
File without changes
{assets → scripts}/bibles/Malayalam.txt RENAMED
File without changes
{assets → scripts}/bibles/Nepali.txt RENAMED
File without changes
{assets → scripts}/bibles/Oriya.txt RENAMED
File without changes
{assets → scripts}/bibles/Punjabi.txt RENAMED
File without changes
{assets → scripts}/bibles/Tamil.txt RENAMED
File without changes
{assets → scripts}/bibles/Telugu.txt RENAMED
File without changes
scripts/generate_flatbuffers.dart CHANGED
@@ -3,45 +3,22 @@ import "dart:io";
3
3
  import "dart:typed_data";
4
4
  import "package:flat_buffers/flat_buffers.dart" as fb;
5
5
 
6
- // Mirrors parseBible from models.dart but outputs FlatBuffer binary
6
+ // Converts bible .txt to FlatBuffer binary
7
+ // Format: bookName|bookIndex|chapterIndex|verseNo|heading|text...
7
8
  Uint8List convertBibleToFlatBuffer(String name, String text) {
8
- // First parse into intermediate structure
9
9
  final books = <int, Map<int, List<Map<String, dynamic>>>>{};
10
- final bookNameToIndex = <String, int>{};
11
10
 
12
11
  final lines = text.split("\n");
13
12
  for (var i = 0; i < lines.length; i++) {
14
13
  final line = lines[i];
15
14
  if (line.isEmpty) continue;
16
15
  final arr = line.split("|");
17
-
18
- // Detect format: English has numeric first field, others have book name
19
- int book;
20
- int chapter;
21
- int verseNo;
22
- String heading;
23
- String verseText;
24
-
25
- final firstFieldNumeric = int.tryParse(arr[0]) != null;
26
- if (firstFieldNumeric) {
27
- // English format: book|chapter|verse|heading|text
16
+ // bookName|bookIndex|chapterIndex|verseNo|heading|text...
28
- book = int.parse(arr[0]);
17
+ final book = int.parse(arr[1]);
29
- chapter = int.parse(arr[1]);
18
+ final chapter = int.parse(arr[2]);
30
- verseNo = int.parse(arr[2]);
19
+ final verseNo = int.parse(arr[3]);
31
- heading = arr[3];
20
+ final heading = arr[4];
32
- verseText = arr.sublist(4).join("|");
21
+ final verseText = arr.sublist(5).join("|");
33
- } else {
34
- // Non-English format: bookName|chapter|verse|heading|text|...
35
- final bookName = arr[0];
36
- if (!bookNameToIndex.containsKey(bookName)) {
37
- bookNameToIndex[bookName] = bookNameToIndex.length;
38
- }
39
- book = bookNameToIndex[bookName]!;
40
- chapter = int.parse(arr[1]);
41
- verseNo = int.parse(arr[2]);
42
- heading = arr[3];
43
- verseText = arr.sublist(4).join("|");
44
- }
45
22
 
46
23
  books.putIfAbsent(book, () => {});
47
24
  books[book]!.putIfAbsent(chapter, () => []);
@@ -109,17 +86,18 @@ Uint8List convertBibleToFlatBuffer(String name, String text) {
109
86
  }
110
87
 
111
88
  void main() async {
112
- final biblesDir = Directory("assets/bibles");
89
+ final biblesDir = Directory("scripts/bibles");
113
90
  final txtFiles = biblesDir.listSync().whereType<File>().where((f) => f.path.endsWith(".txt"));
114
91
 
115
92
  for (final file in txtFiles) {
116
93
  final name = file.uri.pathSegments.last.replaceAll(".txt", "");
117
94
  final text = await file.readAsString(encoding: utf8);
118
95
  final bytes = convertBibleToFlatBuffer(name, text);
96
+ final compressed = gzip.encode(bytes);
119
97
  final outPath = "assets/bibles/$name.bin";
120
- await File(outPath).writeAsBytes(bytes);
98
+ await File(outPath).writeAsBytes(compressed);
121
99
  final txtSize = (file.lengthSync() / 1024).toStringAsFixed(1);
122
- final binSize = (bytes.length / 1024).toStringAsFixed(1);
100
+ final binSize = (compressed.length / 1024).toStringAsFixed(1);
123
101
  print("$name: ${txtSize}KB txt -> ${binSize}KB bin");
124
102
  }
125
103
  print("Done! Generated ${txtFiles.length} .bin files.");