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


lib/app.dart CHANGED
@@ -7,9 +7,10 @@ import "package:only_bible_app/screens/book_select_screen.dart";
7
7
  import "package:only_bible_app/screens/chapter_select_screen.dart";
8
8
  import "package:only_bible_app/screens/chapter_view_screen.dart";
9
9
  import "package:only_bible_app/screens/webview_screen.dart";
10
- import "package:only_bible_app/store/actions.dart";
10
+ import "package:only_bible_app/store/actions_state.dart";
11
11
  import "package:only_bible_app/store/app_navigator.dart";
12
12
  import "package:only_bible_app/store/app_state.dart";
13
+ import "package:only_bible_app/store/actions_navigation.dart";
13
14
  import "package:only_bible_app/theme.dart";
14
15
  import "package:only_bible_app/utils.dart";
15
16
 
lib/dialog.dart CHANGED
@@ -1,7 +1,8 @@
1
1
  import "dart:ui";
2
2
  import "package:flutter/material.dart";
3
- import "package:only_bible_app/store/actions.dart";
3
+ import "package:only_bible_app/store/actions_state.dart";
4
4
  import "package:only_bible_app/store/app_navigator.dart";
5
+ import "package:only_bible_app/store/actions_navigation.dart";
5
6
  import "package:only_bible_app/utils.dart";
6
7
 
7
8
  void showAlert(BuildContext context, String title, String message) {
lib/screens/bible_select_screen.dart CHANGED
@@ -1,6 +1,7 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/store/actions.dart";
2
+ import "package:only_bible_app/store/actions_state.dart";
3
3
  import "package:only_bible_app/store/app_navigator.dart";
4
+ import "package:only_bible_app/store/actions_navigation.dart";
4
5
  import "package:only_bible_app/utils.dart";
5
6
  import "package:only_bible_app/widgets/scaffold_menu.dart";
6
7
  import "package:only_bible_app/widgets/sliver_heading.dart";
lib/screens/book_select_screen.dart CHANGED
@@ -1,6 +1,7 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/store/actions.dart";
2
+ import "package:only_bible_app/store/actions_state.dart";
3
3
  import "package:only_bible_app/store/app_navigator.dart";
4
+ import "package:only_bible_app/store/actions_navigation.dart";
4
5
  import "package:only_bible_app/utils.dart";
5
6
  import "package:only_bible_app/widgets/scaffold_menu.dart";
6
7
  import "package:only_bible_app/widgets/sliver_heading.dart";
lib/screens/chapter_select_screen.dart CHANGED
@@ -1,6 +1,7 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/store/actions.dart";
2
+ import "package:only_bible_app/store/actions_state.dart";
3
3
  import "package:only_bible_app/store/app_navigator.dart";
4
+ import "package:only_bible_app/store/actions_navigation.dart";
4
5
  import "package:only_bible_app/utils.dart";
5
6
  import "package:only_bible_app/widgets/scaffold_menu.dart";
6
7
  import "package:only_bible_app/widgets/sliver_tile_grid.dart";
lib/store/{actions.dart → actions_navigation.dart} RENAMED
@@ -1,165 +1,15 @@
1
- import "dart:developer";
2
-
3
1
  import "package:app_review/app_review.dart";
4
2
  import "package:async_redux/async_redux.dart";
5
3
  import "package:flutter/material.dart";
6
4
  import "package:go_router/go_router.dart";
7
- import "package:only_bible_app/dialog.dart";
8
5
  import "package:only_bible_app/gen/bible.gen.dart";
9
- import "package:only_bible_app/widgets/settings_sheet.dart";
6
+ import "package:only_bible_app/store/actions_state.dart" show audioPlayer;
10
7
  import "package:only_bible_app/store/app_state.dart";
11
- import "package:only_bible_app/store/buffer_audio_source.dart";
12
8
  import "package:only_bible_app/utils.dart";
9
+ import "package:only_bible_app/widgets/settings_sheet.dart";
13
10
  import "package:only_bible_app/widgets/verses_view.dart";
14
11
  import "package:share_plus/share_plus.dart";
15
12
 
16
- class FirstOpenDoneAction extends ReduxAction<AppState> {
17
- @override
18
- AppState reduce() => state.copy(firstOpen: false);
19
- }
20
-
21
- class UpdateBibleAction extends ReduxAction<AppState> {
22
- final String name;
23
- final String code;
24
-
25
- UpdateBibleAction(this.name, this.code);
26
-
27
- @override
28
- AppState reduce() => state.copy(bibleName: name, languageCode: code);
29
- }
30
-
31
- class ToggleEngTitlesAction extends ReduxAction<AppState> {
32
- @override
33
- AppState reduce() => state.copy(engTitles: !state.engTitles);
34
- }
35
-
36
- class ToggleBoldFontAction extends ReduxAction<AppState> {
37
- @override
38
- AppState reduce() => state.copy(boldFont: !state.boldFont);
39
- }
40
-
41
- class ToggleDarkModeAction extends ReduxAction<AppState> {
42
- @override
43
- AppState reduce() => state.copy(darkMode: !state.darkMode);
44
- }
45
-
46
- class UpdateTextScaleAction extends ReduxAction<AppState> {
47
- final double value;
48
-
49
- UpdateTextScaleAction(this.value);
50
-
51
- @override
52
- AppState reduce() => state.copy(textScale: state.textScale + value);
53
- }
54
-
55
- class UpdateChapterAction extends ReduxAction<AppState> {
56
- final int book;
57
- final int chapter;
58
-
59
- UpdateChapterAction(this.book, this.chapter);
60
-
61
- @override
62
- AppState reduce() => state.copy(savedBook: book, savedChapter: chapter);
63
- }
64
-
65
- class TogglePlayAction extends ReduxAction<AppState> {
66
- final BuildContext buildContext;
67
- final Bible bible;
68
-
69
- TogglePlayAction(this.buildContext, this.bible);
70
-
71
- @override
72
- Future<AppState?> reduce() async {
73
- if (audioPlayer.playing) {
74
- await audioPlayer.pause();
75
- return null;
76
- }
77
- final versesToPlay = List<Verse>.from(state.selectedVerses);
78
- final audioVoice = buildContext.currentLang.audioVoice;
79
- final audioError = buildContext.l.audioError;
80
- for (final v in versesToPlay) {
81
- final pathname = "${bible.name!}_${v.book}_${v.chapter}_${v.index}";
82
- try {
83
- final data = await convertText(audioVoice, v.text ?? "");
84
- await audioPlayer.setAudioSource(BufferAudioSource(data));
85
- await audioPlayer.play();
86
- await audioPlayer.stop();
87
- } catch (err) {
88
- log(
89
- "Could not play audio",
90
- name: "play",
91
- error: (err.toString(), pathname),
92
- );
93
- recordError((err.toString(), pathname).toString(), null);
94
- if (buildContext.mounted) {
95
- showError(buildContext, audioError);
96
- }
97
- return null;
98
- } finally {
99
- await audioPlayer.pause();
100
- }
101
- }
102
- return null;
103
- }
104
- }
105
-
106
- class SelectVerseAction extends ReduxAction<AppState> {
107
- final Verse verse;
108
-
109
- SelectVerseAction(this.verse);
110
-
111
- @override
112
- AppState reduce() {
113
- final isSelected = state.selectedVerses.any(
114
- (el) => el.book == verse.book && el.chapter == verse.chapter && el.index == verse.index,
115
- );
116
- final newVerses = isSelected
117
- ? state.selectedVerses.where((it) => it.index != verse.index).toList()
118
- : [...state.selectedVerses, verse];
119
- return state.copy(
120
- selectedVerses: newVerses,
121
- );
122
- }
123
- }
124
-
125
- class ClearSelectedVersesAction extends ReduxAction<AppState> {
126
- @override
127
- AppState reduce() => state.copy(selectedVerses: []);
128
- }
129
-
130
- class SetHighlightAction extends ReduxAction<AppState> {
131
- final List<Verse> verses;
132
- final int colorIndex;
133
-
134
- SetHighlightAction(this.verses, this.colorIndex);
135
-
136
- @override
137
- AppState reduce() {
138
- final updated = Map<String, int>.from(state.highlights);
139
- for (final v in verses) {
140
- updated["${v.book}:${v.chapter}:${v.index}"] = colorIndex;
141
- }
142
- return state.copy(highlights: updated);
143
- }
144
- }
145
-
146
- class RemoveHighlightAction extends ReduxAction<AppState> {
147
- final List<Verse> verses;
148
-
149
- RemoveHighlightAction(this.verses);
150
-
151
- @override
152
- AppState reduce() {
153
- final updated = Map<String, int>.from(state.highlights);
154
- for (final v in verses) {
155
- updated.remove("${v.book}:${v.chapter}:${v.index}");
156
- }
157
- return state.copy(highlights: updated);
158
- }
159
- }
160
-
161
- // ─── Navigation actions ──────────────────────────────────────────────────────
162
-
163
13
  class HideActionsAction extends ReduxAction<AppState> {
164
14
  @override
165
15
  AppState? reduce() {
@@ -310,18 +160,6 @@ class ChangeBibleAction extends ReduxAction<AppState> {
310
160
 
311
161
  ChangeBibleAction(this.router);
312
162
 
313
- @override
314
- AppState? reduce() {
315
- router.go("/bible");
316
- return null;
317
- }
318
- }
319
-
320
- class PushChangeBibleAction extends ReduxAction<AppState> {
321
- final GoRouter router;
322
-
323
- PushChangeBibleAction(this.router);
324
-
325
163
  @override
326
164
  AppState? reduce() {
327
165
  router.push("/bible");
@@ -387,7 +225,7 @@ class ShowAboutUsAction extends ReduxAction<AppState> {
387
225
 
388
226
  @override
389
227
  AppState? reduce() {
390
- router.push("/webview", extra: "https://onlybible.app/about-us");
228
+ router.push("/webview", extra: "https://pyrossh.dev/only-bible-app");
391
229
  return null;
392
230
  }
393
231
  }
@@ -399,7 +237,7 @@ class ShowPrivacyPolicyAction extends ReduxAction<AppState> {
399
237
 
400
238
  @override
401
239
  AppState? reduce() {
402
- router.push("/webview", extra: "https://onlybible.app/privacy-policy");
240
+ router.push("/webview", extra: "https://pyrossh.dev/only-bible-app/privacy-policy");
403
241
  return null;
404
242
  }
405
243
  }
@@ -411,7 +249,7 @@ class ShowTermsAndConditionsAction extends ReduxAction<AppState> {
411
249
 
412
250
  @override
413
251
  AppState? reduce() {
414
- router.push("/webview", extra: "https://onlybible.app/terms-and-conditions");
252
+ router.push("/webview", extra: "https://pyrossh.dev/only-bible-app/terms-and-conditions");
415
253
  return null;
416
254
  }
417
255
  }
lib/store/actions_state.dart ADDED
@@ -0,0 +1,157 @@
1
+ import "dart:developer";
2
+
3
+ import "package:async_redux/async_redux.dart";
4
+ import "package:just_audio/just_audio.dart";
5
+ import "package:flutter/material.dart";
6
+ import "package:only_bible_app/dialog.dart";
7
+ import "package:only_bible_app/gen/bible.gen.dart";
8
+ import "package:only_bible_app/store/app_state.dart";
9
+ import "package:only_bible_app/store/buffer_audio_source.dart";
10
+ import "package:only_bible_app/utils.dart";
11
+
12
+ final audioPlayer = AudioPlayer();
13
+
14
+ class FirstOpenDoneAction extends ReduxAction<AppState> {
15
+ @override
16
+ AppState reduce() => state.copy(firstOpen: false);
17
+ }
18
+
19
+ class UpdateBibleAction extends ReduxAction<AppState> {
20
+ final String name;
21
+ final String code;
22
+
23
+ UpdateBibleAction(this.name, this.code);
24
+
25
+ @override
26
+ AppState reduce() => state.copy(bibleName: name, languageCode: code);
27
+ }
28
+
29
+ class ToggleEngTitlesAction extends ReduxAction<AppState> {
30
+ @override
31
+ AppState reduce() => state.copy(engTitles: !state.engTitles);
32
+ }
33
+
34
+ class ToggleBoldFontAction extends ReduxAction<AppState> {
35
+ @override
36
+ AppState reduce() => state.copy(boldFont: !state.boldFont);
37
+ }
38
+
39
+ class ToggleDarkModeAction extends ReduxAction<AppState> {
40
+ @override
41
+ AppState reduce() => state.copy(darkMode: !state.darkMode);
42
+ }
43
+
44
+ class UpdateTextScaleAction extends ReduxAction<AppState> {
45
+ final double value;
46
+
47
+ UpdateTextScaleAction(this.value);
48
+
49
+ @override
50
+ AppState reduce() => state.copy(textScale: state.textScale + value);
51
+ }
52
+
53
+ class UpdateChapterAction extends ReduxAction<AppState> {
54
+ final int book;
55
+ final int chapter;
56
+
57
+ UpdateChapterAction(this.book, this.chapter);
58
+
59
+ @override
60
+ AppState reduce() => state.copy(savedBook: book, savedChapter: chapter);
61
+ }
62
+
63
+ class TogglePlayAction extends ReduxAction<AppState> {
64
+ final BuildContext buildContext;
65
+ final Bible bible;
66
+
67
+ TogglePlayAction(this.buildContext, this.bible);
68
+
69
+ @override
70
+ Future<AppState?> reduce() async {
71
+ if (audioPlayer.playing) {
72
+ await audioPlayer.pause();
73
+ return null;
74
+ }
75
+ final versesToPlay = List<Verse>.from(state.selectedVerses);
76
+ final audioVoice = buildContext.currentLang.audioVoice;
77
+ final audioError = buildContext.l.audioError;
78
+ for (final v in versesToPlay) {
79
+ final pathname = "${bible.name!}_${v.book}_${v.chapter}_${v.index}";
80
+ try {
81
+ final data = await convertText(audioVoice, v.text ?? "");
82
+ await audioPlayer.setAudioSource(BufferAudioSource(data));
83
+ await audioPlayer.play();
84
+ await audioPlayer.stop();
85
+ } catch (err) {
86
+ log(
87
+ "Could not play audio",
88
+ name: "play",
89
+ error: (err.toString(), pathname),
90
+ );
91
+ recordError((err.toString(), pathname).toString(), null);
92
+ if (buildContext.mounted) {
93
+ showError(buildContext, audioError);
94
+ }
95
+ return null;
96
+ } finally {
97
+ await audioPlayer.pause();
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+ }
103
+
104
+ class SelectVerseAction extends ReduxAction<AppState> {
105
+ final Verse verse;
106
+
107
+ SelectVerseAction(this.verse);
108
+
109
+ @override
110
+ AppState reduce() {
111
+ final isSelected = state.selectedVerses.any(
112
+ (el) => el.book == verse.book && el.chapter == verse.chapter && el.index == verse.index,
113
+ );
114
+ final newVerses = isSelected
115
+ ? state.selectedVerses.where((it) => it.index != verse.index).toList()
116
+ : [...state.selectedVerses, verse];
117
+ return state.copy(
118
+ selectedVerses: newVerses,
119
+ );
120
+ }
121
+ }
122
+
123
+ class ClearSelectedVersesAction extends ReduxAction<AppState> {
124
+ @override
125
+ AppState reduce() => state.copy(selectedVerses: []);
126
+ }
127
+
128
+ class SetHighlightAction extends ReduxAction<AppState> {
129
+ final List<Verse> verses;
130
+ final int colorIndex;
131
+
132
+ SetHighlightAction(this.verses, this.colorIndex);
133
+
134
+ @override
135
+ AppState reduce() {
136
+ final updated = Map<String, int>.from(state.highlights);
137
+ for (final v in verses) {
138
+ updated["${v.book}:${v.chapter}:${v.index}"] = colorIndex;
139
+ }
140
+ return state.copy(highlights: updated);
141
+ }
142
+ }
143
+
144
+ class RemoveHighlightAction extends ReduxAction<AppState> {
145
+ final List<Verse> verses;
146
+
147
+ RemoveHighlightAction(this.verses);
148
+
149
+ @override
150
+ AppState reduce() {
151
+ final updated = Map<String, int>.from(state.highlights);
152
+ for (final v in verses) {
153
+ updated.remove("${v.book}:${v.chapter}:${v.index}");
154
+ }
155
+ return state.copy(highlights: updated);
156
+ }
157
+ }
lib/theme.dart CHANGED
@@ -19,20 +19,19 @@ const lightColorScheme = ColorScheme.light(
19
19
  onSurface: Color(0xFF010101),
20
20
  primary: Color(0xFF9A1111),
21
21
  secondary: Color(0xFFFFC351),
22
- surfaceTint: Colors.black,
22
+ surfaceTint: Color(0xFFF8F6F6),
23
23
  shadow: Colors.black,
24
24
  outline: Colors.grey,
25
25
  );
26
26
 
27
27
  const darkColorScheme = ColorScheme.dark(
28
28
  surface: Color(0xFF1F1F22),
29
- onSurface: Color(0xFFBCBEC4),
29
+ onSurface: Colors.white,
30
- primary: Color(0xFFBC86FC),
30
+ primary: Colors.white,
31
31
  secondary: Color(0xFFFFC351),
32
32
  tertiary: Color(0xFF323232),
33
33
  onTertiary: Color(0xFFBA50AB),
34
- //E24DE2
35
- surfaceTint: Colors.white,
34
+ surfaceTint: Color.fromARGB(255, 133, 2, 133),
36
35
  shadow: Colors.white,
37
36
  outline: Color(0xAA5D4979),
38
37
  );
@@ -137,7 +136,7 @@ final lightTheme = ThemeData(
137
136
  ),
138
137
  elevation: 2,
139
138
  shadowColor: Colors.black,
140
- backgroundColor: lightColorScheme.surface,
139
+ backgroundColor: lightColorScheme.surfaceTint,
141
140
  foregroundColor: Colors.black,
142
141
  textStyle: const TextStyle(
143
142
  fontSize: 18,
@@ -258,13 +257,10 @@ final darkTheme = lightTheme.copyWith(
258
257
  ),
259
258
  elevation: 0,
260
259
  shadowColor: Colors.white,
261
- backgroundColor: darkColorScheme.surface,
260
+ backgroundColor: darkColorScheme.surfaceTint,
262
261
  foregroundColor: darkColorScheme.primary,
263
262
  ),
264
263
  ),
265
- // shadowColor: Colors.black,
266
- // backgroundColor: lightColorScheme.surface,
267
- // foregroundColor: Colors.black,
268
264
  textTheme: TextTheme(
269
265
  bodyMedium: lightTheme.textTheme.bodyMedium!.copyWith(
270
266
  color: darkColorScheme.onSurface,
@@ -281,5 +277,8 @@ final darkTheme = lightTheme.copyWith(
281
277
  labelMedium: lightTheme.textTheme.labelMedium!.copyWith(
282
278
  color: darkColorScheme.onTertiary,
283
279
  ),
280
+ labelLarge: lightTheme.textTheme.labelLarge!.copyWith(
281
+ color: darkColorScheme.onTertiary,
282
+ ),
284
283
  ),
285
284
  );
lib/widgets/chapter_app_bar.dart CHANGED
@@ -1,7 +1,8 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:only_bible_app/gen/bible.gen.dart";
3
- import "package:only_bible_app/store/actions.dart";
3
+ import "package:only_bible_app/store/actions_state.dart";
4
4
  import "package:only_bible_app/store/app_navigator.dart";
5
+ import "package:only_bible_app/store/actions_navigation.dart";
5
6
  import "package:only_bible_app/utils.dart";
6
7
 
7
8
  class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
@@ -54,6 +55,18 @@ class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
54
55
  child: Row(
55
56
  mainAxisAlignment: MainAxisAlignment.end,
56
57
  children: [
58
+ InkWell(
59
+ enableFeedback: true,
60
+ onTap: () => context.dispatch(ChangeBibleAction(context.router)),
61
+ child: Padding(
62
+ padding: const EdgeInsets.only(left: 16),
63
+ child: Text(
64
+ context.l.languageTitle,
65
+ style: Theme.of(context).textTheme.headlineMedium,
66
+ key: const Key("bible"),
67
+ ),
68
+ ),
69
+ ),
57
70
  Padding(
58
71
  padding: const EdgeInsets.only(left: 10),
59
72
  child: IconButton(
lib/widgets/highlight_button.dart CHANGED
@@ -14,8 +14,9 @@ class HighlightButton extends StatelessWidget {
14
14
  child: Container(
15
15
  padding: const EdgeInsets.symmetric(horizontal: 10),
16
16
  decoration: BoxDecoration(
17
- color: color.withOpacity(1),
17
+ color: color.withAlpha(230),
18
18
  shape: BoxShape.circle,
19
+ border: Border.all(color: Theme.of(context).colorScheme.onSurface, width: 1),
19
20
  ),
20
21
  child: const SizedBox(width: 24, height: 24),
21
22
  ),
lib/widgets/{actions_sheet.dart → menu_overlay.dart} RENAMED
@@ -1,6 +1,7 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:only_bible_app/gen/bible.gen.dart";
3
- import "package:only_bible_app/store/actions.dart";
3
+ import "package:only_bible_app/store/actions_state.dart";
4
+ import "package:only_bible_app/store/actions_navigation.dart";
4
5
  import "package:only_bible_app/theme.dart";
5
6
  import "package:only_bible_app/utils.dart";
6
7
  import "package:only_bible_app/widgets/highlight_button.dart";
@@ -28,9 +29,13 @@ class MenuOverlay extends StatelessWidget {
28
29
  }
29
30
 
30
31
  return Material(
31
- elevation: 4,
32
+ elevation: 1,
32
- borderRadius: BorderRadius.circular(28),
33
33
  color: Theme.of(context).colorScheme.surface,
34
+ shadowColor: Theme.of(context).colorScheme.shadow,
35
+ shape: RoundedRectangleBorder(
36
+ borderRadius: BorderRadius.circular(28),
37
+ side: BorderSide(color: Theme.of(context).colorScheme.outline, width: 1),
38
+ ),
34
39
  child: Container(
35
40
  height: 50,
36
41
  padding: const EdgeInsets.only(left: 8, right: 8),
lib/widgets/settings_sheet.dart CHANGED
@@ -1,7 +1,8 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:only_bible_app/gen/bible.gen.dart";
3
- import "package:only_bible_app/store/actions.dart";
3
+ import "package:only_bible_app/store/actions_state.dart";
4
4
  import "package:only_bible_app/store/app_navigator.dart";
5
+ import "package:only_bible_app/store/actions_navigation.dart";
5
6
  import "package:only_bible_app/utils.dart";
6
7
  import "package:settings_ui/settings_ui.dart";
7
8
 
lib/widgets/verses_view.dart CHANGED
@@ -1,14 +1,12 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:flutter_swipe_detector/flutter_swipe_detector.dart";
3
- import "package:just_audio/just_audio.dart";
4
3
  import "package:only_bible_app/gen/bible.gen.dart";
4
+ import "package:only_bible_app/store/actions_navigation.dart";
5
- import "package:only_bible_app/widgets/actions_sheet.dart";
5
+ import "package:only_bible_app/widgets/menu_overlay.dart";
6
- import "package:only_bible_app/store/actions.dart";
6
+ import "package:only_bible_app/store/actions_state.dart";
7
7
  import "package:only_bible_app/store/app_navigator.dart";
8
8
  import "package:only_bible_app/utils.dart";
9
9
 
10
- final audioPlayer = AudioPlayer();
11
-
12
10
  class VersesView extends StatelessWidget {
13
11
  final Bible bible;
14
12
  final Chapter chapter;