~repos /only-bible-app

#kotlin#android#ios

git clone https://pyrossh.dev/repos/only-bible-app.git

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


89a99c6d pyrossh

2 years ago
Fix various issues
lib/l10n/app_en.arb CHANGED
@@ -1,4 +1,5 @@
1
1
  {
2
+ "hasAudio": "false",
2
3
  "languageTitle": "English",
3
4
  "localeLanguageTitle": "English",
4
5
  "oldTestamentTitle": "Old Testament",
lib/l10n/app_kn.arb CHANGED
@@ -1,4 +1,5 @@
1
1
  {
2
+ "hasAudio": "true",
2
3
  "languageTitle": "Kannada",
3
4
  "localeLanguageTitle": "ಕನ್ನಡ",
4
5
  "oldTestamentTitle": "ಹಳೆಯ ಒಡಂಬಡಿಕೆ",
lib/models.dart CHANGED
@@ -3,17 +3,14 @@ import "package:only_bible_app/providers/app_model.dart";
3
3
 
4
4
  class Bible {
5
5
  final String name;
6
- final bool hasAudio;
7
6
  List<Book> books = [];
8
7
 
9
8
  Bible({
10
9
  required this.name,
11
- required this.hasAudio,
12
10
  });
13
11
 
14
12
  Bible.withBooks({
15
13
  required this.name,
16
- required this.hasAudio,
17
14
  required this.books,
18
15
  });
19
16
 
@@ -50,7 +47,7 @@ class Book {
50
47
  String shortName(BuildContext context) {
51
48
  final name = this.name(context);
52
49
  if (name[0] == "1" || name[0] == "2" || name[0] == "3") {
53
- return "${name[0]}${name[2].toUpperCase()}${name.substring(3, 5).toLowerCase()}";
50
+ return "${name[0]}${name[2].toUpperCase()}${name.substring(3, 4).toLowerCase()}";
54
51
  }
55
52
  return "${name[0].toUpperCase()}${name.substring(1, 3).toLowerCase()}";
56
53
  }
lib/providers/app_model.dart CHANGED
@@ -1,18 +1,24 @@
1
1
  // import "package:firebase_performance/firebase_performance.dart";
2
+ import "dart:developer";
3
+ import "package:firebase_crashlytics/firebase_crashlytics.dart";
4
+ import "package:firebase_storage/firebase_storage.dart";
5
+ import "package:just_audio/just_audio.dart";
6
+ import "package:only_bible_app/dialog.dart";
7
+ import "package:only_bible_app/screens/chapter_view_screen.dart";
8
+ import "package:share_plus/share_plus.dart";
2
9
  import "package:flutter_gen/gen_l10n/app_localizations.dart";
3
- import "package:flutter/services.dart";
4
10
  import "package:flutter/material.dart";
11
+ import "package:flutter/services.dart";
5
12
  import "package:only_bible_app/screens/bible_select_screen.dart";
6
13
  import "package:only_bible_app/screens/book_select_screen.dart";
7
14
  import "package:only_bible_app/models.dart";
8
15
  import "package:only_bible_app/widgets/actions_sheet.dart";
9
- import "package:only_bible_app/widgets/highlight_button.dart";
16
+ import "package:only_bible_app/widgets/highlight_sheet.dart";
10
17
  import "package:only_bible_app/widgets/scaffold_markdown.dart";
11
18
  import "package:only_bible_app/widgets/note_sheet.dart";
12
19
  import "package:only_bible_app/widgets/settings_sheet.dart";
13
20
  import "package:package_info_plus/package_info_plus.dart";
14
21
  import "package:provider/provider.dart";
15
- import "package:share_plus/share_plus.dart";
16
22
  import "package:shared_preferences/shared_preferences.dart";
17
23
  import "package:get_storage/get_storage.dart";
18
24
  import "package:only_bible_app/utils.dart";
@@ -34,7 +40,10 @@ class AppModel extends ChangeNotifier {
34
40
  bool fontBold = false;
35
41
  double textScaleFactor = 0;
36
42
  bool actionsShown = false;
37
- bool highlightMenuShown = false;
43
+ bool highlightsShown = false;
44
+ final player = AudioPlayer();
45
+ bool isPlaying = false;
46
+ final List<Verse> selectedVerses = [];
38
47
  final TextEditingController noteTextController = TextEditingController();
39
48
  List<HistoryFrame> history = [];
40
49
  final box = GetStorage("only-bible-app-backup");
@@ -87,7 +96,6 @@ class AppModel extends ChangeNotifier {
87
96
  // }
88
97
  return Bible.withBooks(
89
98
  name: name,
90
- hasAudio: true,
91
99
  books: books,
92
100
  );
93
101
  }
@@ -164,6 +172,10 @@ class AppModel extends ChangeNotifier {
164
172
  ];
165
173
  }
166
174
 
175
+ hasAudio(BuildContext context) {
176
+ return context.l10n.hasAudio == "true";
177
+ }
178
+
167
179
  changeBible(BuildContext context) {
168
180
  Navigator.of(context).pushReplacement(
169
181
  createNoTransitionPageRoute(
@@ -197,6 +209,36 @@ class AppModel extends ChangeNotifier {
197
209
  );
198
210
  }
199
211
 
212
+ clearEvents(
213
+ BuildContext context,
214
+ ) {
215
+ if (isPlaying) {
216
+ pause();
217
+ }
218
+ clearSelections();
219
+ hideActions(context);
220
+ }
221
+
222
+ pushBookChapter(BuildContext context, int book, int chapter, TextDirection? dir) {
223
+ clearEvents(context);
224
+ Navigator.of(context).push(
225
+ createSlideRoute(
226
+ context: context,
227
+ slideDir: dir,
228
+ page: ChapterViewScreen(book: book, chapter: chapter),
229
+ ),
230
+ );
231
+ }
232
+
233
+ replaceBookChapter(BuildContext context, int book, int chapter) {
234
+ clearEvents(context);
235
+ Navigator.of(context).pushReplacement(
236
+ createNoTransitionPageRoute(
237
+ ChapterViewScreen(book: book, chapter: chapter),
238
+ ),
239
+ );
240
+ }
241
+
200
242
  toggleDarkMode() {
201
243
  darkMode = !darkMode;
202
244
  updateStatusBar();
@@ -286,6 +328,24 @@ class AppModel extends ChangeNotifier {
286
328
  }
287
329
  }
288
330
 
331
+ showHighlights(BuildContext context) {
332
+ highlightsShown = true;
333
+ Scaffold.of(context).showBottomSheet(
334
+ enableDrag: false,
335
+ clipBehavior: Clip.antiAliasWithSaveLayer,
336
+ (context) => const HighlightSheet(),
337
+ );
338
+ notifyListeners();
339
+ }
340
+
341
+ hideHighlights(BuildContext context) {
342
+ if (highlightsShown) {
343
+ highlightsShown = false;
344
+ Navigator.of(context).pop();
345
+ notifyListeners();
346
+ }
347
+ }
348
+
289
349
  bool hasNote(Verse v) {
290
350
  return box.hasData("${v.book}:${v.chapter}:${v.index}:note");
291
351
  }
@@ -308,11 +368,10 @@ class AppModel extends ChangeNotifier {
308
368
  final note = noteTextController.text;
309
369
  box.write("${v.book}:${v.chapter}:${v.index}:note", note);
310
370
  box.save();
311
- // Close the bottom sheet
312
- // if (!mounted) return;
313
- // Navigator.of(context).pop();
314
- notifyListeners();
315
371
  hideNoteField(context);
372
+ clearSelections();
373
+ hideActions(context);
374
+ notifyListeners();
316
375
  }
317
376
 
318
377
  deleteNote(BuildContext context, Verse v) {
@@ -359,55 +418,6 @@ class AppModel extends ChangeNotifier {
359
418
  box.save();
360
419
  }
361
420
 
362
- void showHighlightMenu(BuildContext context, List<Verse> verses, Offset position) {
363
- hideHighlightMenu(context);
364
- highlightMenuShown = true;
365
- final overlay = Overlay.of(context).context.findRenderObject();
366
- onTap(c) => setHighlight(context, verses, c);
367
-
368
- showMenu(
369
- context: context,
370
- position: RelativeRect.fromRect(
371
- Rect.fromLTWH(position.dx, position.dy + 30, 100, 100),
372
- Rect.fromLTWH(0, 0, overlay!.paintBounds.size.width, overlay.paintBounds.size.height),
373
- ),
374
- items: [
375
- PopupMenuItem(
376
- child: Row(
377
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
378
- children: [
379
- HighlightButton(
380
- color: const Color(0xFFDAEFFE),
381
- onColorSelected: onTap,
382
- ),
383
- HighlightButton(
384
- color: const Color(0xFFFFFBB1),
385
- onColorSelected: onTap,
386
- ),
387
- HighlightButton(
388
- color: const Color(0xFFFFDEF3),
389
- onColorSelected: onTap,
390
- ),
391
- HighlightButton(
392
- color: const Color(0xFFE6FCC3),
393
- onColorSelected: onTap,
394
- ),
395
- HighlightButton(
396
- color: const Color(0xFFEADDFF),
397
- onColorSelected: onTap,
398
- ),
399
- ],
400
- ),
401
- ),
402
- ]);
403
- }
404
-
405
- void hideHighlightMenu(BuildContext context) {
406
- if (highlightMenuShown) {
407
- Navigator.of(context).pop();
408
- }
409
- }
410
-
411
421
  void shareAppLink(BuildContext context) {
412
422
  if (isAndroid()) {
413
423
  Share.share(
@@ -450,4 +460,94 @@ class AppModel extends ChangeNotifier {
450
460
  ),
451
461
  );
452
462
  }
463
+
464
+ bool hasSelectedVerses() {
465
+ return selectedVerses.isNotEmpty;
466
+ }
467
+
468
+ void clearSelections() {
469
+ selectedVerses.clear();
470
+ notifyListeners();
471
+ }
472
+
473
+ void removeSelectedHighlights(BuildContext context) {
474
+ AppModel.ofEvent(context).removeHighlight(context, selectedVerses);
475
+ selectedVerses.clear();
476
+ AppModel.ofEvent(context).hideActions(context);
477
+ notifyListeners();
478
+ }
479
+
480
+ void closeActions(BuildContext context) {
481
+ selectedVerses.clear();
482
+ AppModel.ofEvent(context).hideActions(context);
483
+ notifyListeners();
484
+ }
485
+
486
+ bool isVerseSelected(Verse v) {
487
+ return selectedVerses.any((el) => el.index == v.index);
488
+ }
489
+
490
+ bool isVerseHighlighted(BuildContext context) {
491
+ // box.read("${book}:${chapter}:${verse}", "color");
492
+ return false;
493
+ }
494
+
495
+ void onVerseSelected(BuildContext context, Verse v) {
496
+ if (selectedVerses.isEmpty) {
497
+ AppModel.ofEvent(context).showActions(context);
498
+ }
499
+ if (isVerseSelected(v)) {
500
+ selectedVerses.removeWhere((it) => it.index == v.index);
501
+ } else {
502
+ selectedVerses.add(v);
503
+ }
504
+ if (selectedVerses.isEmpty) {
505
+ AppModel.ofEvent(context).hideActions(context);
506
+ }
507
+ notifyListeners();
508
+ }
509
+
510
+ void shareVerses(BuildContext context) {
511
+ final name = bible.books[selectedVerses.first.book].name;
512
+ final chapter = selectedVerses.first.chapter + 1;
513
+ final title = "$name $chapter: ${selectedVerses.map((e) => e.index + 1).join(", ")}";
514
+ final text = selectedVerses.map((e) => e.text).join("\n");
515
+ Share.share("$title\n$text", subject: title);
516
+ }
517
+
518
+ pause() async {
519
+ await player.pause();
520
+ isPlaying = false;
521
+ notifyListeners();
522
+ }
523
+
524
+ onPlay(BuildContext context) async {
525
+ if (isPlaying) {
526
+ pause();
527
+ } else {
528
+ isPlaying = true;
529
+ notifyListeners();
530
+ // add locks todo
531
+ for (final v in selectedVerses) {
532
+ final bibleName = bible.name;
533
+ final book = (v.book + 1).toString().padLeft(2, "0");
534
+ final chapter = (v.chapter + 1).toString().padLeft(3, "0");
535
+ final verseNo = (v.index + 1).toString().padLeft(3, "0");
536
+ final pathname = "$bibleName/$book-$chapter-$verseNo.mp3";
537
+ try {
538
+ final url = await FirebaseStorage.instance.ref(pathname).getDownloadURL();
539
+ await player.setUrl(url);
540
+ await player.play();
541
+ await player.stop();
542
+ } catch (err) {
543
+ log("Could not play audio", name: "play", error: (err.toString(), pathname));
544
+ FirebaseCrashlytics.instance.recordFlutterError(FlutterErrorDetails(exception: (err.toString(), pathname)));
545
+ showError(context, "Could not play audio");
546
+ return;
547
+ } finally {
548
+ pause();
549
+ }
550
+ }
551
+ }
552
+ }
453
553
  }
lib/providers/chapter_view_model.dart CHANGED
@@ -1,14 +1,7 @@
1
- import "dart:developer";
2
- import "package:firebase_crashlytics/firebase_crashlytics.dart";
3
- import "package:firebase_storage/firebase_storage.dart";
4
- import "package:flutter/services.dart";
5
1
  import "package:flutter/material.dart";
6
- import "package:just_audio/just_audio.dart";
7
2
  import "package:only_bible_app/screens/chapter_view_screen.dart";
8
- import "package:only_bible_app/dialog.dart";
9
3
  import "package:only_bible_app/models.dart";
10
4
  import "package:provider/provider.dart";
11
- import "package:share_plus/share_plus.dart";
12
5
  import "package:shared_preferences/shared_preferences.dart";
13
6
  import "package:only_bible_app/utils.dart";
14
7
  import "package:only_bible_app/providers/app_model.dart";
@@ -16,9 +9,6 @@ import "package:only_bible_app/providers/app_model.dart";
16
9
  class ChapterViewModel extends ChangeNotifier {
17
10
  final int book;
18
11
  final int chapter;
19
- final List<Verse> selectedVerses;
20
- final player = AudioPlayer();
21
- bool isPlaying = false;
22
12
 
23
13
  static ChapterViewModel of(BuildContext context) {
24
14
  return Provider.of(context, listen: true);
@@ -38,7 +28,7 @@ class ChapterViewModel extends ChangeNotifier {
38
28
  return AppModel.of(context).bible.books[model.book].chapters[model.chapter];
39
29
  }
40
30
 
41
- ChapterViewModel({required this.book, required this.chapter, required this.selectedVerses}) {
31
+ ChapterViewModel({required this.book, required this.chapter}) {
42
32
  save(book, chapter);
43
33
  }
44
34
 
@@ -49,17 +39,7 @@ class ChapterViewModel extends ChangeNotifier {
49
39
  }
50
40
 
51
41
  navigateBookChapter(BuildContext context, int book, int chapter, TextDirection? dir) {
52
- if (isPlaying) {
53
- pause();
54
- }
55
- AppModel.ofEvent(context).hideActions(context);
56
- Navigator.of(context).push(
57
- createSlideRoute(
58
- context: context,
59
- slideDir: dir,
60
- page: ChapterViewScreen(book: book, chapter: chapter),
42
+ context.appEvent.pushBookChapter(context, book, chapter, dir);
61
- ),
62
- );
63
43
  }
64
44
 
65
45
  onNext(BuildContext context, int book, int chapter) {
@@ -91,104 +71,4 @@ class ChapterViewModel extends ChangeNotifier {
91
71
  }
92
72
  }
93
73
  }
94
-
95
- bool hasSelectedVerses() {
96
- return selectedVerses.isNotEmpty;
97
- }
98
-
99
- void clearSelections(BuildContext context) {
100
- AppModel.ofEvent(context).removeHighlight(context, selectedVerses);
101
- selectedVerses.clear();
102
- AppModel.ofEvent(context).hideActions(context);
103
- notifyListeners();
104
- }
105
-
106
- void closeActions(BuildContext context) {
107
- selectedVerses.clear();
108
- AppModel.ofEvent(context).hideActions(context);
109
- notifyListeners();
110
- }
111
-
112
- bool isVerseSelected(Verse v) {
113
- return selectedVerses.any((el) => el.index == v.index);
114
- }
115
-
116
- bool isVerseHighlighted(BuildContext context) {
117
- // box.read("${book}:${chapter}:${verse}", "color");
118
- return false;
119
- }
120
-
121
- void onVerseSelected(BuildContext context, Verse v) {
122
- if (selectedVerses.isEmpty) {
123
- AppModel.ofEvent(context).showActions(context);
124
- }
125
- if (isVerseSelected(v)) {
126
- selectedVerses.removeWhere((it) => it.index == v.index);
127
- } else {
128
- selectedVerses.add(v);
129
- }
130
- if (selectedVerses.isEmpty) {
131
- AppModel.ofEvent(context).hideActions(context);
132
- }
133
- notifyListeners();
134
- }
135
-
136
- void copyVerses() {
137
- final text = selectedVerses.map((e) => e.text).join("\n");
138
- Clipboard.setData(ClipboardData(text: text));
139
- // maybe close the action menu or show a snackbar on iOS (android already does this)
140
- // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Copied to clipboard")));
141
- }
142
-
143
- void shareVerses(BuildContext context) {
144
- final bible = AppModel.ofEvent(context).bible;
145
- final name = bible.books[selectedVerses.first.book].name;
146
- final chapter = selectedVerses.first.chapter + 1;
147
- final title = "$name $chapter: ${selectedVerses.map((e) => e.index + 1).join(", ")}";
148
- final text = selectedVerses.map((e) => e.text).join("\n");
149
- Share.share("$title\n$text", subject: title);
150
- }
151
-
152
- pause() async {
153
- await player.pause();
154
- isPlaying = false;
155
- notifyListeners();
156
- }
157
-
158
- onPlay(BuildContext context) async {
159
- final bible = AppModel.ofEvent(context).bible;
160
- if (!bible.hasAudio) {
161
- showError(
162
- context,
163
- "This Bible doesn't support audio. Currently audio is only available for the Kannada Bible.",
164
- );
165
- return;
166
- }
167
- if (isPlaying) {
168
- pause();
169
- } else {
170
- isPlaying = true;
171
- notifyListeners();
172
- for (final v in selectedVerses) {
173
- final bibleName = bible.name;
174
- final book = (v.book + 1).toString().padLeft(2, "0");
175
- final chapter = (v.chapter + 1).toString().padLeft(3, "0");
176
- final verseNo = (v.index + 1).toString().padLeft(3, "0");
177
- final pathname = "$bibleName/$book-$chapter-$verseNo.mp3";
178
- try {
179
- final url = await FirebaseStorage.instance.ref(pathname).getDownloadURL();
180
- await player.setUrl(url);
181
- await player.play();
182
- await player.stop();
183
- } catch (err) {
184
- log("Could not play audio", name: "play", error: (err.toString(), pathname));
185
- FirebaseCrashlytics.instance.recordFlutterError(FlutterErrorDetails(exception: (err.toString(), pathname)));
186
- showError(context, "Could not play audio");
187
- return;
188
- } finally {
189
- pause();
190
- }
191
- }
192
- }
193
- }
194
74
  }
lib/screens/chapter_select_screen.dart CHANGED
@@ -4,7 +4,6 @@ import "package:only_bible_app/utils.dart";
4
4
  import "package:only_bible_app/widgets/scaffold_menu.dart";
5
5
  import "package:only_bible_app/widgets/sliver_tile_grid.dart";
6
6
  import "package:only_bible_app/widgets/sliver_heading.dart";
7
- import "package:only_bible_app/screens/chapter_view_screen.dart";
8
7
 
9
8
  class ChapterSelectScreen extends StatelessWidget {
10
9
  final Book book;
@@ -12,15 +11,6 @@ class ChapterSelectScreen extends StatelessWidget {
12
11
 
13
12
  const ChapterSelectScreen({super.key, required this.selectedBookIndex, required this.book});
14
13
 
15
- // TODO: move this to app and allow to pause
16
- onChapterSelected(BuildContext context, int index) {
17
- Navigator.of(context).pushReplacement(
18
- createNoTransitionPageRoute(
19
- ChapterViewScreen(book: selectedBookIndex, chapter: index),
20
- ),
21
- );
22
- }
23
-
24
14
  @override
25
15
  Widget build(BuildContext context) {
26
16
  return ScaffoldMenu(
@@ -32,7 +22,7 @@ class ChapterSelectScreen extends StatelessWidget {
32
22
  children: List.generate(book.chapters.length, (index) {
33
23
  return TextButton(
34
24
  child: Text("${index + 1}"),
35
- onPressed: () => onChapterSelected(context, index),
25
+ onPressed: () => context.appEvent.replaceBookChapter(context, selectedBookIndex, index),
36
26
  );
37
27
  }),
38
28
  ),
lib/screens/chapter_view_screen.dart CHANGED
@@ -36,7 +36,6 @@ class ChapterViewScreen extends StatelessWidget {
36
36
  create: (_) => ChapterViewModel(
37
37
  book: book,
38
38
  chapter: chapter,
39
- selectedVerses: [],
40
39
  ),
41
40
  child: Scaffold(
42
41
  appBar: isDesktop ? null : const ChapterAppBar(),
lib/theme.dart CHANGED
@@ -42,8 +42,8 @@ final lightTheme = ThemeData(
42
42
  // selectionColor: const Color(0xAAF8D0DC),
43
43
  // ),
44
44
  inputDecorationTheme: const InputDecorationTheme(
45
- focusColor: Colors.black,
45
+ focusColor: Colors.transparent,
46
- hoverColor: Colors.black,
46
+ hoverColor: Colors.transparent,
47
47
  activeIndicatorBorder: BorderSide(
48
48
  color: Colors.black,
49
49
  ),
lib/utils.dart CHANGED
@@ -1,16 +1,30 @@
1
1
  import "dart:convert";
2
2
  import "package:only_bible_app/dialog.dart";
3
+ import "package:only_bible_app/providers/app_model.dart";
4
+ import "package:only_bible_app/providers/chapter_view_model.dart";
3
5
  import "package:url_launcher/url_launcher.dart";
4
6
  import "package:flutter/foundation.dart" show defaultTargetPlatform, TargetPlatform;
5
7
  import "package:flutter/material.dart";
6
8
  import "package:flutter/services.dart";
7
9
  import "package:only_bible_app/models.dart";
10
+ import "package:flutter_gen/gen_l10n/app_localizations.dart";
11
+ import "package:provider/provider.dart";
8
12
 
9
13
  extension MyIterable<E> on Iterable<E> {
10
14
  Iterable<E> sortedBy(Comparable Function(E e) key) =>
11
15
  toList()..sort((a, b) => key(a).compareTo(key(b)));
12
16
  }
13
17
 
18
+ extension AppContext on BuildContext {
19
+ ThemeData get theme => Theme.of(this);
20
+ AppLocalizations get l10n => app.engTitles ? lookupAppLocalizations(const Locale("en")) : AppLocalizations.of(this)!;
21
+ AppModel get app => Provider.of(this, listen: true);
22
+ AppModel get appEvent => Provider.of(this, listen: false);
23
+ ChapterViewModel get chapter => Provider.of(this, listen: true);
24
+ ChapterViewModel get chapterEvent => Provider.of(this, listen: false);
25
+ }
26
+
27
+
14
28
  bool isDesktop() {
15
29
  return defaultTargetPlatform == TargetPlatform.macOS ||
16
30
  defaultTargetPlatform == TargetPlatform.windows ||
lib/widgets/actions_sheet.dart CHANGED
@@ -1,8 +1,7 @@
1
1
  import "package:flutter/material.dart";
2
+ import "package:only_bible_app/dialog.dart";
2
3
  import "package:only_bible_app/providers/app_model.dart";
3
- import "package:only_bible_app/providers/chapter_view_model.dart";
4
4
  import "package:only_bible_app/utils.dart";
5
- import "package:only_bible_app/widgets/highlight_button.dart";
6
5
  import "package:only_bible_app/widgets/icon_button_text.dart";
7
6
 
8
7
  class ActionsSheet extends StatelessWidget {
@@ -12,101 +11,73 @@ class ActionsSheet extends StatelessWidget {
12
11
  Widget build(BuildContext context) {
13
12
  final app = AppModel.of(context);
14
13
  final isDesktop = isWide(context);
15
- final iconSize = isDesktop ? 10.0 : 0.0;
14
+ final height = isDesktop || isIOS() ? 92.0 : 70.0;
16
15
  final iconColor = app.darkMode ? Colors.white.withOpacity(0.9) : Colors.black.withOpacity(0.9);
17
16
  final bodySmall = Theme.of(context).textTheme.bodySmall;
18
- final model = ChapterViewModel.of(context);
19
- final audioIcon = model.isPlaying ? Icons.pause_circle_outline : Icons.play_circle_outline;
17
+ final audioIcon = app.isPlaying ? Icons.pause_circle_outline : Icons.play_circle_outline;
20
- final audioText = model.isPlaying ? "Pause" : "Play";
18
+ final audioText = app.isPlaying ? "Pause" : "Play";
21
- final highlightRowEnabled = !isDesktop;
19
+ final audioEnabled = app.hasAudio(context);
22
- onHighlight(Color c) {
23
- final verses = ChapterViewModel.ofEvent(context).selectedVerses;
24
- app.setHighlight(context, verses, c);
25
- model.closeActions(context);
26
- }
27
-
28
- ;
29
20
  return Container(
30
- height: highlightRowEnabled
21
+ height: height,
31
- ? 150
32
- : isDesktop
33
- ? 95
34
- : isIOS()
35
- ? 100
36
- : 70,
37
22
  color: Theme.of(context).colorScheme.background,
38
23
  padding: EdgeInsets.only(left: 20, right: 20, top: isDesktop ? 10 : 10, bottom: 20),
39
24
  child: Column(
40
- mainAxisAlignment: highlightRowEnabled ? MainAxisAlignment.spaceAround : MainAxisAlignment.start,
25
+ mainAxisAlignment: MainAxisAlignment.start,
41
26
  children: [
42
- if (highlightRowEnabled)
43
- Row(
44
- mainAxisAlignment: MainAxisAlignment.end,
45
- children: [
46
- HighlightButton(
47
- color: const Color(0xFFDAEFFE),
48
- onColorSelected: onHighlight,
49
- ),
50
- HighlightButton(
51
- color: const Color(0xFFFFFBB1),
52
- onColorSelected: onHighlight,
53
- ),
54
- HighlightButton(
55
- color: const Color(0xFFFFDEF3),
56
- onColorSelected: onHighlight,
57
- ),
58
- HighlightButton(
59
- color: const Color(0xFFE6FCC3),
60
- onColorSelected: onHighlight,
61
- ),
62
- ],
63
- ),
64
- // const Padding(padding: EdgeInsets.only(top: 10)),
65
27
  Row(
66
28
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
67
29
  children: [
68
30
  IconButtonText(
69
31
  leading: IconButton(
70
32
  padding: EdgeInsets.zero,
71
- onPressed: () => model.clearSelections(context),
33
+ onPressed: () => context.appEvent.removeSelectedHighlights(context),
72
- icon: Icon(Icons.cancel_outlined, size: 24 + iconSize, color: iconColor),
34
+ icon: Icon(Icons.cancel_outlined, size: 28, color: iconColor),
73
35
  ),
74
36
  trailing: Text("Clear", style: bodySmall),
75
37
  ),
76
38
  IconButtonText(
77
39
  leading: IconButton(
78
40
  padding: EdgeInsets.zero,
79
- onPressed: model.copyVerses,
41
+ onPressed: () => context.appEvent.showHighlights(context),
80
- icon: Icon(Icons.copy, size: 24 + iconSize, color: iconColor),
42
+ icon: Icon(Icons.border_color_outlined, size: 28, color: iconColor),
81
43
  ),
82
- trailing: Text("Copy", style: bodySmall),
44
+ trailing: Text("Highlight", style: bodySmall),
83
45
  ),
84
46
  IconButtonText(
85
47
  leading: IconButton(
86
48
  padding: EdgeInsets.zero,
49
+ onPressed: () {
50
+ if (audioEnabled) {
87
- onPressed: () => model.onPlay(context),
51
+ context.appEvent.onPlay(context);
52
+ } else {
53
+ showError(
54
+ context,
55
+ "This Bible doesn't support audio. Currently audio is only available for the Kannada Bible.",
56
+ );
57
+ }
58
+ },
88
- icon: Icon(audioIcon, size: 34 + iconSize, color: app.bible.hasAudio ? iconColor : Colors.grey),
59
+ icon: Icon(audioIcon, size: 34, color: audioEnabled ? iconColor : Colors.grey),
89
60
  ),
90
61
  trailing: Text(
91
62
  audioText,
92
63
  style: bodySmall!.copyWith(
93
- color: app.bible.hasAudio ? bodySmall!.color : Colors.grey,
64
+ color: audioEnabled ? bodySmall.color : Colors.grey,
94
65
  ),
95
66
  ),
96
67
  ),
97
68
  IconButtonText(
98
69
  leading: IconButton(
99
70
  padding: EdgeInsets.zero,
100
- onPressed: () => app.showNoteField(context, model.selectedVerses.first),
71
+ onPressed: () => context.appEvent.showNoteField(context, context.appEvent.selectedVerses.first),
101
- icon: Icon(Icons.post_add_outlined, size: 32 + iconSize, color: iconColor),
72
+ icon: Icon(Icons.post_add_outlined, size: 34, color: iconColor),
102
73
  ),
103
74
  trailing: Text("Note", style: bodySmall),
104
75
  ),
105
76
  IconButtonText(
106
77
  leading: IconButton(
107
78
  padding: EdgeInsets.zero,
108
- onPressed: () => model.shareVerses(context),
79
+ onPressed: () => context.appEvent.shareVerses(context),
109
- icon: Icon(Icons.share_outlined, size: 28 + iconSize, color: iconColor),
80
+ icon: Icon(Icons.share_outlined, size: 34, color: iconColor),
110
81
  ),
111
82
  trailing: Text("Share", style: bodySmall),
112
83
  ),
lib/widgets/highlight_button.dart CHANGED
@@ -16,7 +16,7 @@ class HighlightButton extends StatelessWidget {
16
16
  color: color.withOpacity(1),
17
17
  shape: BoxShape.circle,
18
18
  ),
19
- child: const SizedBox(width: 30, height: 30),
19
+ child: const SizedBox(width: 45, height: 45),
20
20
  ),
21
21
  );
22
22
  }
lib/widgets/highlight_sheet.dart ADDED
@@ -0,0 +1,49 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/utils.dart";
3
+ import "package:only_bible_app/widgets/highlight_button.dart";
4
+
5
+ class HighlightSheet extends StatelessWidget {
6
+ const HighlightSheet({super.key});
7
+
8
+ @override
9
+ Widget build(BuildContext context) {
10
+ final isDesktop = isWide(context);
11
+ final height = isDesktop || isIOS() ? 100.0 : 70.0;
12
+ onHighlight(Color c) {
13
+ final verses = context.appEvent.selectedVerses;
14
+ context.appEvent.setHighlight(context, verses, c);
15
+ context.appEvent.closeActions(context);
16
+ }
17
+
18
+ return Container(
19
+ height: height,
20
+ color: Theme.of(context).colorScheme.background,
21
+ padding: EdgeInsets.only(left: 20, right: 20, top: isDesktop ? 10 : 10, bottom: 30),
22
+ child: Row(
23
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
24
+ children: [
25
+ HighlightButton(
26
+ color: const Color(0xFFDAEFFE),
27
+ onColorSelected: onHighlight,
28
+ ),
29
+ HighlightButton(
30
+ color: const Color(0xFFFFFBB1),
31
+ onColorSelected: onHighlight,
32
+ ),
33
+ HighlightButton(
34
+ color: const Color(0xFFFFDEF3),
35
+ onColorSelected: onHighlight,
36
+ ),
37
+ HighlightButton(
38
+ color: const Color(0xFFE6FCC3),
39
+ onColorSelected: onHighlight,
40
+ ),
41
+ HighlightButton(
42
+ color: const Color(0xFFAACCAA),
43
+ onColorSelected: onHighlight,
44
+ ),
45
+ ],
46
+ ),
47
+ );
48
+ }
49
+ }
lib/widgets/note_sheet.dart CHANGED
@@ -1,6 +1,7 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:only_bible_app/models.dart";
3
3
  import "package:only_bible_app/providers/app_model.dart";
4
+ import "package:only_bible_app/utils.dart";
4
5
  import "package:only_bible_app/widgets/modal_button.dart";
5
6
 
6
7
  class NoteSheet extends StatelessWidget {
@@ -24,7 +25,7 @@ class NoteSheet extends StatelessWidget {
24
25
  Padding(
25
26
  padding: const EdgeInsets.only(bottom: 5, left: 15),
26
27
  child: Text(
27
- "Note on ${app.bible.books[verse.book].name} ${verse.chapter + 1}:${verse.index + 1}",
28
+ "Note on ${app.bible.books[verse.book].name(context)} ${verse.chapter + 1}:${verse.index + 1}",
28
29
  style: Theme.of(context).textTheme.headlineMedium,
29
30
  ),
30
31
  ),
@@ -57,7 +58,11 @@ class NoteSheet extends StatelessWidget {
57
58
  mainAxisAlignment: MainAxisAlignment.end,
58
59
  children: [
59
60
  ModalButton(
61
+ onPressed: () {
60
- onPressed: () => app.saveNote(context, verse),
62
+ context.appEvent.saveNote(context, verse);
63
+ // context.chapterEvent.clearSelections();
64
+ // context.appEvent.hideActions(context);
65
+ },
61
66
  icon: Icons.save_outlined,
62
67
  label: "Save",
63
68
  ),
lib/widgets/settings_sheet.dart CHANGED
@@ -1,5 +1,4 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/providers/app_model.dart";
3
2
  import "package:only_bible_app/utils.dart";
4
3
  import "package:settings_ui/settings_ui.dart";
5
4
 
@@ -8,9 +7,6 @@ class SettingsSheet extends StatelessWidget {
8
7
 
9
8
  @override
10
9
  Widget build(BuildContext context) {
11
- final app = AppModel.of(context);
12
- final localizations = AppModel.getLocalizations(context);
13
- final iconColor = Theme.of(context).textTheme.bodyMedium!.color;
14
10
  return SettingsList(
15
11
  contentPadding: EdgeInsets.zero,
16
12
  platform: DevicePlatform.iOS,
@@ -22,26 +18,26 @@ class SettingsSheet extends StatelessWidget {
22
18
  ),
23
19
  sections: [
24
20
  SettingsSection(
25
- title: Text(localizations.settingsTitle, style: Theme.of(context).textTheme.headlineMedium),
21
+ title: Text(context.l10n.settingsTitle, style: context.theme.textTheme.headlineMedium),
26
22
  margin: const EdgeInsetsDirectional.symmetric(horizontal: 20),
27
23
  tiles: [
28
24
  SettingsTile.navigation(
29
25
  leading: const Icon(Icons.book_outlined, color: Colors.blueAccent),
30
- title: Text(localizations.bibleTitle),
26
+ title: Text(context.l10n.bibleTitle),
31
- value: Text(app.bible.name),
27
+ value: Text(context.app.bible.name),
32
- onPressed: app.changeBible,
28
+ onPressed: context.app.changeBible,
33
29
  ),
34
30
  SettingsTile.navigation(
35
31
  leading: const Icon(Icons.color_lens_outlined, color: Colors.pink),
36
- title: Text(localizations.themeTitle),
32
+ title: Text(context.l10n.themeTitle),
37
33
  trailing: ToggleButtons(
38
34
  onPressed: (int index) {
39
- app.toggleDarkMode();
35
+ context.appEvent.toggleDarkMode();
40
36
  },
41
37
  highlightColor: Colors.transparent,
42
38
  borderColor: Colors.grey,
43
39
  borderRadius: const BorderRadius.all(Radius.circular(25)),
44
- selectedColor: app.darkMode ? Colors.lightBlue.shade300 : Colors.yellowAccent.shade700,
40
+ selectedColor: context.app.darkMode ? Colors.lightBlue.shade300 : Colors.yellowAccent.shade700,
45
41
  selectedBorderColor: Colors.grey,
46
42
  color: Colors.grey,
47
43
  fillColor: Colors.transparent,
@@ -49,7 +45,7 @@ class SettingsSheet extends StatelessWidget {
49
45
  minHeight: 36.0,
50
46
  minWidth: 50.0,
51
47
  ),
52
- isSelected: [!app.darkMode, app.darkMode],
48
+ isSelected: [!context.app.darkMode, context.app.darkMode],
53
49
  children: const [
54
50
  Icon(Icons.light_mode),
55
51
  Icon(Icons.dark_mode),
@@ -57,63 +53,59 @@ class SettingsSheet extends StatelessWidget {
57
53
  ),
58
54
  ),
59
55
  SettingsTile(
60
- title: Text(localizations.incrementFontTitle),
56
+ title: Text(context.l10n.incrementFontTitle),
61
- leading: Icon(Icons.font_download, color: iconColor),
57
+ leading: Icon(Icons.font_download, color: context.theme.colorScheme.onBackground),
62
58
  trailing: IconButton(
63
- onPressed: app.increaseFont,
59
+ onPressed: context.appEvent.increaseFont,
64
60
  icon: const Icon(Icons.add_circle_outline, size: 32, color: Colors.redAccent),
65
61
  ),
66
62
  ),
67
63
  SettingsTile(
68
- title: Text(localizations.decrementFontTitle),
64
+ title: Text(context.l10n.decrementFontTitle),
69
- leading: Icon(Icons.font_download, color: iconColor),
65
+ leading: Icon(Icons.font_download, color: context.theme.colorScheme.onBackground),
70
66
  trailing: IconButton(
71
- onPressed: app.decreaseFont,
67
+ onPressed: context.appEvent.decreaseFont,
72
68
  icon: const Icon(Icons.remove_circle_outline, size: 32, color: Colors.blueAccent),
73
69
  ),
74
70
  ),
75
71
  SettingsTile.switchTile(
76
- onToggle: (value) {
77
- app.toggleBold();
78
- },
79
- initialValue: app.fontBold,
72
+ initialValue: context.app.fontBold,
80
- leading: Icon(Icons.format_bold, color: iconColor),
73
+ leading: Icon(Icons.format_bold, color: context.theme.colorScheme.onBackground),
81
- title: Text(localizations.boldFontTitle),
74
+ title: Text(context.l10n.boldFontTitle),
75
+ onToggle: (value) => context.appEvent.toggleBold(),
82
76
  ),
83
77
  SettingsTile.switchTile(
84
- onToggle: (value) {
85
- app.toggleEngBookNames();
86
- },
87
- initialValue: app.engTitles,
78
+ initialValue: context.app.engTitles,
88
- leading: Icon(Icons.abc, color: iconColor),
79
+ leading: Icon(Icons.abc, color: context.theme.colorScheme.onBackground),
89
- title: Text(localizations.engTitles),
80
+ title: Text(context.l10n.engTitles),
81
+ onToggle: (value) => context.appEvent.toggleEngBookNames(),
90
82
  ),
91
83
  ],
92
84
  ),
93
85
  SettingsSection(
94
- // title: Text("About", style: Theme.of(context).textTheme.headlineMedium),
86
+ title: Text(context.l10n.aboutUsTitle, style: context.theme.textTheme.headlineMedium),
95
87
  margin: const EdgeInsetsDirectional.symmetric(horizontal: 20, vertical: 20),
96
88
  tiles: [
97
89
  SettingsTile.navigation(
98
90
  leading: const Icon(Icons.policy_outlined, color: Colors.brown),
99
- title: Text(localizations.privacyPolicyTitle),
91
+ title: Text(context.l10n.privacyPolicyTitle),
100
- onPressed: app.showPrivacyPolicy,
92
+ onPressed: context.appEvent.showPrivacyPolicy,
101
93
  ),
102
94
  SettingsTile.navigation(
103
95
  leading: const Icon(Icons.share_outlined, color: Colors.blueAccent),
104
- title: Text(localizations.shareAppTitle),
96
+ title: Text(context.l10n.shareAppTitle),
105
- onPressed: app.shareAppLink,
97
+ onPressed: context.appEvent.shareAppLink,
106
98
  ),
107
99
  if (!isDesktop()) // TODO: mabe support OSx if we release in that store
108
100
  SettingsTile.navigation(
109
101
  leading: Icon(Icons.star, color: Colors.yellowAccent.shade700),
110
- title: Text(localizations.rateAppTitle),
102
+ title: Text(context.l10n.rateAppTitle),
111
- onPressed: app.rateApp,
103
+ onPressed: context.appEvent.rateApp,
112
104
  ),
113
105
  SettingsTile.navigation(
114
- leading: Icon(Icons.info_outline, color: Theme.of(context).colorScheme.onBackground),
106
+ leading: Icon(Icons.info_outline, color: context.theme.colorScheme.onBackground),
115
- title: Text(localizations.aboutUsTitle),
107
+ title: Text(context.l10n.aboutUsTitle),
116
- onPressed: app.showAboutUs,
108
+ onPressed: context.appEvent.showAboutUs,
117
109
  ),
118
110
  ],
119
111
  ),
lib/widgets/verses_view.dart CHANGED
@@ -3,7 +3,7 @@ import "package:flutter/material.dart";
3
3
  import "package:flutter_swipe_detector/flutter_swipe_detector.dart";
4
4
  import "package:only_bible_app/providers/app_model.dart";
5
5
  import "package:only_bible_app/providers/chapter_view_model.dart";
6
- import "package:provider/provider.dart";
6
+ import "package:only_bible_app/utils.dart";
7
7
 
8
8
  class VersesView extends StatelessWidget {
9
9
  const VersesView({super.key});
@@ -75,7 +75,7 @@ class VersesView extends StatelessWidget {
75
75
  ),
76
76
  TextSpan(
77
77
  text: "${v.text}\n",
78
- style: context.watch<ChapterViewModel>().isVerseSelected(v)
78
+ style: context.app.isVerseSelected(v)
79
79
  ? TextStyle(
80
80
  backgroundColor: app.darkMode ? Colors.grey.shade800 : Colors.grey.shade200,
81
81
  )
@@ -84,7 +84,7 @@ class VersesView extends StatelessWidget {
84
84
  ),
85
85
  recognizer: TapGestureRecognizer()
86
86
  ..onTap = () {
87
- model.onVerseSelected(context, v);
87
+ context.appEvent.onVerseSelected(context, v);
88
88
  // AppModel.ofEvent(context).showHighlightMenu(context, v, details.globalPosition);
89
89
  },
90
90
  ),