~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.


.env.example ADDED
@@ -0,0 +1,2 @@
1
+ AZURE_TTS_SUBSCRIPTION_KEY=abcd
2
+ RESEND_API_KEY=abcd
.gitignore CHANGED
@@ -8,4 +8,5 @@ upload-keystore.jks
8
8
  svc.json
9
9
  .dart_tool
10
10
  ui
11
- .flutter-plugins-dependencies
11
+ .flutter-plugins-dependencies
12
+ .env
ios/Podfile.lock CHANGED
@@ -1,6 +1,4 @@
1
1
  PODS:
2
- - app_review (1.0.1):
3
- - Flutter
4
2
  - audio_session (0.0.1):
5
3
  - Flutter
6
4
  - connectivity_plus (0.0.1):
@@ -17,17 +15,10 @@ PODS:
17
15
  - Flutter
18
16
  - share_plus (0.0.1):
19
17
  - Flutter
20
- - shared_preferences_foundation (0.0.1):
21
- - Flutter
22
- - FlutterMacOS
23
18
  - url_launcher_ios (0.0.1):
24
19
  - Flutter
25
- - webview_flutter_wkwebview (0.0.1):
26
- - Flutter
27
- - FlutterMacOS
28
20
 
29
21
  DEPENDENCIES:
30
- - app_review (from `.symlinks/plugins/app_review/ios`)
31
22
  - audio_session (from `.symlinks/plugins/audio_session/ios`)
32
23
  - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
33
24
  - Flutter (from `Flutter`)
@@ -36,13 +27,9 @@ DEPENDENCIES:
36
27
  - just_audio (from `.symlinks/plugins/just_audio/darwin`)
37
28
  - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
38
29
  - share_plus (from `.symlinks/plugins/share_plus/ios`)
39
- - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
40
30
  - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
41
- - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
42
31
 
43
32
  EXTERNAL SOURCES:
44
- app_review:
45
- :path: ".symlinks/plugins/app_review/ios"
46
33
  audio_session:
47
34
  :path: ".symlinks/plugins/audio_session/ios"
48
35
  connectivity_plus:
@@ -59,15 +46,10 @@ EXTERNAL SOURCES:
59
46
  :path: ".symlinks/plugins/package_info_plus/ios"
60
47
  share_plus:
61
48
  :path: ".symlinks/plugins/share_plus/ios"
62
- shared_preferences_foundation:
63
- :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
64
49
  url_launcher_ios:
65
50
  :path: ".symlinks/plugins/url_launcher_ios/ios"
66
- webview_flutter_wkwebview:
67
- :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
68
51
 
69
52
  SPEC CHECKSUMS:
70
- app_review: f75e3250ef5694e328a5ecb3f4c445bc7b32d821
71
53
  audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
72
54
  connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
73
55
  Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
@@ -76,9 +58,7 @@ SPEC CHECKSUMS:
76
58
  just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed
77
59
  package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
78
60
  share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
79
- shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
80
61
  url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
81
- webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d
82
62
 
83
63
  PODFILE CHECKSUM: 251cb053df7158f337c0712f2ab29f4e0fa474ce
84
64
 
lib/dialog.dart CHANGED
@@ -1,9 +1,7 @@
1
1
  import "dart:ui";
2
2
  import "package:flutter/material.dart";
3
- import "package:only_bible_app/gen/bible.gen.dart";
4
3
  import "package:only_bible_app/store/actions_navigation.dart";
5
4
  import "package:only_bible_app/utils.dart";
6
- import "package:only_bible_app/widgets/book_tile.dart";
7
5
 
8
6
  void showAlert(BuildContext context, String title, String message) {
9
7
  showDialog(
@@ -75,128 +73,6 @@ void showBibleSelectDialog(BuildContext context) {
75
73
  );
76
74
  }
77
75
 
78
- void showBookSelectDialog(BuildContext context, Bible bible) {
79
- void onBookSelected(BuildContext dialogContext, int index) {
80
- final book = bible.books![index];
81
- Navigator.of(dialogContext).pop();
82
- if (book.chapters!.length == 1) {
83
- context.dispatch(GoToChapterAction(bible.name!, index, 0));
84
- } else {
85
- showChapterSelectDialog(context, bible, book);
86
- }
87
- }
88
-
89
- showDialog(
90
- context: context,
91
- barrierColor: Colors.black54,
92
- builder: (_) {
93
- final currentBook = context.read().savedBook;
94
- final oldBooks = bible.getOldBooks();
95
- final newBooks = bible.getNewBooks();
96
-
97
- return AlertDialog(
98
- contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
99
- content: SizedBox(
100
- width: double.maxFinite,
101
- height: 500,
102
- child: ListView(
103
- children: [
104
- Padding(
105
- padding: const EdgeInsets.only(bottom: 8, left: 4),
106
- child: Text(
107
- context.l.oldTestamentTitle,
108
- style: TextStyle(
109
- fontSize: 20,
110
- fontWeight: FontWeight.w500,
111
- color: context.theme.colorScheme.onSurfaceVariant,
112
- ),
113
- ),
114
- ),
115
- GridView.count(
116
- shrinkWrap: true,
117
- physics: const NeverScrollableScrollPhysics(),
118
- crossAxisCount: 5,
119
- crossAxisSpacing: 6,
120
- mainAxisSpacing: 6,
121
- children: oldBooks
122
- .map((book) => BookTile(
123
- label: book.shortName(context.bookNames[book.index]),
124
- isSelected: currentBook == book.index,
125
- onTap: () => onBookSelected(_, book.index)))
126
- .toList(),
127
- ),
128
- Padding(
129
- padding: const EdgeInsets.only(top: 16, bottom: 8, left: 4),
130
- child: Text(
131
- context.l.newTestamentTitle,
132
- style: TextStyle(
133
- fontSize: 20,
134
- fontWeight: FontWeight.w500,
135
- color: context.theme.colorScheme.onSurfaceVariant,
136
- ),
137
- ),
138
- ),
139
- GridView.count(
140
- shrinkWrap: true,
141
- physics: const NeverScrollableScrollPhysics(),
142
- crossAxisCount: 5,
143
- crossAxisSpacing: 6.0,
144
- mainAxisSpacing: 6.0,
145
- children: newBooks
146
- .map((book) => BookTile(
147
- label: book.shortName(context.bookNames[book.index]),
148
- isSelected: currentBook == book.index,
149
- onTap: () => onBookSelected(_, book.index)))
150
- .toList(),
151
- ),
152
- ],
153
- ),
154
- ),
155
- );
156
- },
157
- );
158
- }
159
-
160
- void showChapterSelectDialog(BuildContext context, Bible bible, Book book) {
161
- final currentChapter = context.read().savedChapter;
162
- final currentBook = context.read().savedBook;
163
- showDialog(
164
- context: context,
165
- barrierColor: Colors.black54,
166
- builder: (_) {
167
- return AlertDialog(
168
- title: Text(context.bookNames[book.index]),
169
- contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
170
- content: SizedBox(
171
- width: double.maxFinite,
172
- height: 400,
173
- child: GridView.builder(
174
- gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
175
- crossAxisCount: 5,
176
- crossAxisSpacing: 8.0,
177
- mainAxisSpacing: 8.0,
178
- ),
179
- itemCount: book.chapters!.length,
180
- itemBuilder: (_, index) {
181
- final isSelected = currentBook == book.index && currentChapter == index;
182
- return BookTile(
183
- label: "${index + 1}",
184
- isSelected: isSelected,
185
- onTap: () {
186
- Navigator.of(_).pop();
187
- context.dispatch(
188
- GoToChapterAction(bible.name!, book.index, index),
189
- );
190
- },
191
- );
192
- },
193
- ),
194
- ),
195
- );
196
- },
197
- );
198
- }
199
-
200
76
  void showReportError(BuildContext context, String message, StackTrace? st) {
201
77
  if (message.contains("LateInitializationError") || message.contains("HardwareKeyboard")) {
202
78
  return;
lib/store/actions_navigation.dart CHANGED
@@ -4,6 +4,8 @@ import "package:only_bible_app/gen/bible.gen.dart";
4
4
  import "package:only_bible_app/store/actions_state.dart" show audioPlayer;
5
5
  import "package:only_bible_app/store/app_state.dart";
6
6
  import "package:only_bible_app/utils.dart";
7
+ import "package:only_bible_app/widgets/book_select_sheet.dart";
8
+ import "package:only_bible_app/widgets/chapter_select_sheet.dart";
7
9
  import "package:only_bible_app/widgets/settings_sheet.dart";
8
10
  import "package:share_plus/share_plus.dart";
9
11
 
@@ -27,6 +29,67 @@ class ShowSettingsAction extends ReduxAction<AppState> {
27
29
  }
28
30
  }
29
31
 
32
+ class ShowBookSelectAction extends ReduxAction<AppState> {
33
+ final BuildContext buildContext;
34
+ final Bible bible;
35
+
36
+ ShowBookSelectAction(this.buildContext, this.bible);
37
+
38
+ @override
39
+ AppState? reduce() {
40
+ showModalBottomSheet(
41
+ context: buildContext,
42
+ isDismissible: true,
43
+ enableDrag: true,
44
+ showDragHandle: true,
45
+ useSafeArea: true,
46
+ builder: (_) {
47
+ return BookSelectSheet(
48
+ bible: bible,
49
+ parentContext: buildContext,
50
+ onBookSelected: (BuildContext sheetContext, int index) {
51
+ final book = bible.books![index];
52
+ Navigator.of(sheetContext).pop();
53
+ if (book.chapters!.length == 1) {
54
+ dispatch(GoToChapterAction(bible.name!, index, 0));
55
+ } else {
56
+ dispatch(ShowChapterSelectAction(buildContext, bible, book));
57
+ }
58
+ },
59
+ );
60
+ },
61
+ );
62
+ return null;
63
+ }
64
+ }
65
+
66
+ class ShowChapterSelectAction extends ReduxAction<AppState> {
67
+ final BuildContext buildContext;
68
+ final Bible bible;
69
+ final Book book;
70
+
71
+ ShowChapterSelectAction(this.buildContext, this.bible, this.book);
72
+
73
+ @override
74
+ AppState? reduce() {
75
+ showModalBottomSheet(
76
+ context: buildContext,
77
+ isDismissible: true,
78
+ enableDrag: true,
79
+ showDragHandle: true,
80
+ useSafeArea: true,
81
+ builder: (_) {
82
+ return ChapterSelectSheet(
83
+ bible: bible,
84
+ book: book,
85
+ parentContext: buildContext,
86
+ );
87
+ },
88
+ );
89
+ return null;
90
+ }
91
+ }
92
+
30
93
  class GoToChapterAction extends ReduxAction<AppState> {
31
94
  final String bibleName;
32
95
  final int book;
lib/widgets/book_select_sheet.dart ADDED
@@ -0,0 +1,73 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/gen/bible.gen.dart";
3
+ import "package:only_bible_app/utils.dart";
4
+ import "package:only_bible_app/widgets/book_tile.dart";
5
+
6
+ class BookSelectSheet extends StatefulWidget {
7
+ final Bible bible;
8
+ final BuildContext parentContext;
9
+ final void Function(BuildContext, int) onBookSelected;
10
+
11
+ const BookSelectSheet({
12
+ super.key,
13
+ required this.bible,
14
+ required this.parentContext,
15
+ required this.onBookSelected,
16
+ });
17
+
18
+ @override
19
+ State<BookSelectSheet> createState() => _BookSelectSheetState();
20
+ }
21
+
22
+ class _BookSelectSheetState extends State<BookSelectSheet> {
23
+ bool showOldTestament = true;
24
+
25
+ @override
26
+ Widget build(BuildContext context) {
27
+ final currentBook = widget.parentContext.read().savedBook;
28
+ final books = showOldTestament ? widget.bible.getOldBooks() : widget.bible.getNewBooks();
29
+
30
+ final colorScheme = Theme.of(context).colorScheme;
31
+
32
+ return Padding(
33
+ padding: const EdgeInsets.symmetric(horizontal: 16),
34
+ child: Column(
35
+ children: [
36
+ ToggleButtons(
37
+ onPressed: (int index) {
38
+ setState(() {
39
+ showOldTestament = index == 0;
40
+ });
41
+ },
42
+ borderRadius: const BorderRadius.all(Radius.circular(12)),
43
+ borderColor: colorScheme.outlineVariant,
44
+ selectedBorderColor: colorScheme.primary,
45
+ selectedColor: colorScheme.onPrimary,
46
+ fillColor: colorScheme.primary,
47
+ color: colorScheme.onSurfaceVariant,
48
+ constraints: const BoxConstraints(minHeight: 40, minWidth: 140),
49
+ isSelected: [showOldTestament, !showOldTestament],
50
+ children: [
51
+ Text(widget.parentContext.l.oldTestamentTitle),
52
+ Text(widget.parentContext.l.newTestamentTitle),
53
+ ],
54
+ ),
55
+ const SizedBox(height: 12),
56
+ Expanded(
57
+ child: GridView.count(
58
+ crossAxisCount: 6,
59
+ crossAxisSpacing: 6,
60
+ mainAxisSpacing: 6,
61
+ children: books
62
+ .map((book) => BookTile(
63
+ label: book.shortName(widget.parentContext.bookNames[book.index]),
64
+ isSelected: currentBook == book.index,
65
+ onTap: () => widget.onBookSelected(context, book.index)))
66
+ .toList(),
67
+ ),
68
+ ),
69
+ ],
70
+ ),
71
+ );
72
+ }
73
+ }
lib/widgets/chapter_select_sheet.dart ADDED
@@ -0,0 +1,60 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/gen/bible.gen.dart";
3
+ import "package:only_bible_app/store/actions_navigation.dart";
4
+ import "package:only_bible_app/utils.dart";
5
+ import "package:only_bible_app/widgets/book_tile.dart";
6
+
7
+ class ChapterSelectSheet extends StatelessWidget {
8
+ final Bible bible;
9
+ final Book book;
10
+ final BuildContext parentContext;
11
+
12
+ const ChapterSelectSheet({
13
+ super.key,
14
+ required this.bible,
15
+ required this.book,
16
+ required this.parentContext,
17
+ });
18
+
19
+ @override
20
+ Widget build(BuildContext context) {
21
+ final currentChapter = parentContext.read().savedChapter;
22
+ final currentBook = parentContext.read().savedBook;
23
+
24
+ return Padding(
25
+ padding: const EdgeInsets.symmetric(horizontal: 16),
26
+ child: Column(
27
+ children: [
28
+ Text(
29
+ parentContext.bookNames[book.index],
30
+ style: Theme.of(context).textTheme.headlineMedium,
31
+ ),
32
+ const SizedBox(height: 12),
33
+ Expanded(
34
+ child: GridView.builder(
35
+ gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
36
+ crossAxisCount: 6,
37
+ crossAxisSpacing: 6,
38
+ mainAxisSpacing: 6,
39
+ ),
40
+ itemCount: book.chapters!.length,
41
+ itemBuilder: (sheetContext, index) {
42
+ final isSelected = currentBook == book.index && currentChapter == index;
43
+ return BookTile(
44
+ label: "${index + 1}",
45
+ isSelected: isSelected,
46
+ onTap: () {
47
+ Navigator.of(sheetContext).pop();
48
+ parentContext.dispatch(
49
+ GoToChapterAction(bible.name!, book.index, index),
50
+ );
51
+ },
52
+ );
53
+ },
54
+ ),
55
+ ),
56
+ ],
57
+ ),
58
+ );
59
+ }
60
+ }
lib/widgets/home_app_bar.dart CHANGED
@@ -31,7 +31,7 @@ class HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
31
31
  children: [
32
32
  InkWell(
33
33
  enableFeedback: true,
34
- onTap: () => showBookSelectDialog(context, bible),
34
+ onTap: () => context.dispatch(ShowBookSelectAction(context, bible)),
35
35
  child: Text(
36
36
  bookName,
37
37
  style: Theme.of(context).textTheme.headlineMedium,
@@ -40,7 +40,7 @@ class HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
40
40
  ),
41
41
  InkWell(
42
42
  enableFeedback: true,
43
- onTap: () => showChapterSelectDialog(context, bible, book),
43
+ onTap: () => context.dispatch(ShowChapterSelectAction(context, bible, book)),
44
44
  child: Padding(
45
45
  padding: const EdgeInsets.only(left: 16),
46
46
  child: Text(
lib/widgets/settings_sheet.dart CHANGED
@@ -14,7 +14,7 @@ class SettingsSheet extends StatelessWidget {
14
14
  final darkMode = context.select((s) => s.darkMode);
15
15
  final boldFont = context.select((s) => s.boldFont);
16
16
  final engTitles = context.select((s) => s.engTitles);
17
- final fontSize = context.select((s) => s.fontSize).clamp(10.0, 30.0);
17
+ final fontSize = context.select((s) => s.fontSize);
18
18
  return SettingsList(
19
19
  contentPadding: EdgeInsets.zero,
20
20
  platform: DevicePlatform.iOS,