~repos /only-bible-app
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.
85fd0483
—
pyrossh 1 month ago
update state
- lib/app.dart +5 -26
- lib/screens/bible_select_screen.dart +5 -4
- lib/screens/book_select_screen.dart +1 -1
- lib/screens/chapter_select_screen.dart +1 -1
- lib/screens/chapter_view_screen.dart +1 -1
- lib/sheets/actions_sheet.dart +12 -10
- lib/sheets/settings_sheet.dart +7 -4
- lib/store/app_state.dart +2 -2
- lib/utils.dart +16 -10
- lib/widgets/verses_view.dart +7 -5
lib/app.dart
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "package:async_redux/async_redux.dart";
|
|
1
|
+
import "package:async_redux/async_redux.dart" show Store;
|
|
2
2
|
import "package:flutter/material.dart";
|
|
3
3
|
import "package:go_router/go_router.dart";
|
|
4
4
|
import "package:only_bible_app/gen/l10n/app_localizations.dart";
|
|
@@ -110,40 +110,19 @@ class App extends StatelessWidget {
|
|
|
110
110
|
return AppNavigatorProvider(
|
|
111
111
|
store: store,
|
|
112
112
|
navigator: navigator,
|
|
113
|
-
child:
|
|
113
|
+
child: Builder(
|
|
114
|
-
vm: () => _AppVmFactory(),
|
|
115
|
-
builder: (context
|
|
114
|
+
builder: (context) => MaterialApp.router(
|
|
116
115
|
routerConfig: _router,
|
|
117
116
|
onGenerateTitle: (context) => context.l.title,
|
|
118
117
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
119
118
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
120
119
|
debugShowCheckedModeBanner: false,
|
|
121
|
-
themeMode:
|
|
120
|
+
themeMode: context.select((s) => s.darkMode) ? ThemeMode.dark : ThemeMode.light,
|
|
122
121
|
theme: lightTheme,
|
|
123
122
|
darkTheme: darkTheme,
|
|
124
|
-
locale: Locale(
|
|
123
|
+
locale: Locale(context.select((s) => s.languageCode)),
|
|
125
124
|
),
|
|
126
125
|
),
|
|
127
126
|
);
|
|
128
127
|
}
|
|
129
128
|
}
|
|
130
|
-
|
|
131
|
-
class _AppVm extends Vm {
|
|
132
|
-
final bool darkMode;
|
|
133
|
-
final String languageCode;
|
|
134
|
-
|
|
135
|
-
_AppVm({
|
|
136
|
-
required this.darkMode,
|
|
137
|
-
required this.languageCode,
|
|
138
|
-
}) : super(equals: [darkMode, languageCode]);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
class _AppVmFactory extends VmFactory<AppState, App, _AppVm> {
|
|
142
|
-
@override
|
|
143
|
-
_AppVm fromStore() {
|
|
144
|
-
return _AppVm(
|
|
145
|
-
darkMode: state.darkMode,
|
|
146
|
-
languageCode: state.languageCode,
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
}
|
lib/screens/bible_select_screen.dart
CHANGED
|
@@ -17,7 +17,7 @@ class BibleSelectScreen extends StatelessWidget {
|
|
|
17
17
|
slivers: [
|
|
18
18
|
SliverHeading(
|
|
19
19
|
title: context.l.bibleSelectTitle,
|
|
20
|
-
showClose: !context.
|
|
20
|
+
showClose: !context.select((s) => s.firstOpen),
|
|
21
21
|
),
|
|
22
22
|
SliverTileGrid(
|
|
23
23
|
listType: ListType.extraLarge,
|
|
@@ -34,15 +34,16 @@ class BibleSelectScreen extends StatelessWidget {
|
|
|
34
34
|
)
|
|
35
35
|
: Text(l.languageTitle, textScaleFactor: 1.1),
|
|
36
36
|
onPressed: () {
|
|
37
|
+
final s = context.read();
|
|
37
|
-
if (
|
|
38
|
+
if (s.firstOpen) {
|
|
38
39
|
context.dispatch(FirstOpenDoneAction());
|
|
39
40
|
}
|
|
40
41
|
context.nav.updateCurrentBible(
|
|
41
42
|
context,
|
|
42
43
|
l.languageTitle,
|
|
43
44
|
l.localeName,
|
|
44
|
-
|
|
45
|
+
s.savedBook,
|
|
45
|
-
|
|
46
|
+
s.savedChapter,
|
|
46
47
|
);
|
|
47
48
|
},
|
|
48
49
|
);
|
lib/screens/book_select_screen.dart
CHANGED
|
@@ -22,7 +22,7 @@ class BookSelectScreen extends StatelessWidget {
|
|
|
22
22
|
|
|
23
23
|
@override
|
|
24
24
|
Widget build(BuildContext context) {
|
|
25
|
-
final bible = context.
|
|
25
|
+
final bible = context.select((s) => s.bible);
|
|
26
26
|
if (bible == null) {
|
|
27
27
|
return ColoredBox(
|
|
28
28
|
color: Theme.of(context).colorScheme.surface,
|
lib/screens/chapter_select_screen.dart
CHANGED
|
@@ -13,7 +13,7 @@ class ChapterSelectScreen extends StatelessWidget {
|
|
|
13
13
|
|
|
14
14
|
@override
|
|
15
15
|
Widget build(BuildContext context) {
|
|
16
|
-
final bible = context.
|
|
16
|
+
final bible = context.select((s) => s.bible);
|
|
17
17
|
if (bible == null) {
|
|
18
18
|
return ColoredBox(
|
|
19
19
|
color: Theme.of(context).colorScheme.surface,
|
lib/screens/chapter_view_screen.dart
CHANGED
|
@@ -17,7 +17,7 @@ class ChapterViewScreen extends StatelessWidget {
|
|
|
17
17
|
|
|
18
18
|
@override
|
|
19
19
|
Widget build(BuildContext context) {
|
|
20
|
-
final bible = context.
|
|
20
|
+
final bible = context.select((s) => s.bible);
|
|
21
21
|
if (bible == null) {
|
|
22
22
|
return ColoredBox(
|
|
23
23
|
color: Theme.of(context).colorScheme.surface,
|
lib/sheets/actions_sheet.dart
CHANGED
|
@@ -13,13 +13,15 @@ class ActionsSheet extends StatelessWidget {
|
|
|
13
13
|
|
|
14
14
|
@override
|
|
15
15
|
Widget build(BuildContext context) {
|
|
16
|
+
final darkMode = context.select((s) => s.darkMode);
|
|
17
|
+
final isPlaying = context.select((s) => s.isPlaying);
|
|
16
|
-
final iconColor =
|
|
18
|
+
final iconColor = darkMode ? Colors.white.withOpacity(0.9) : Colors.black.withOpacity(0.9);
|
|
17
|
-
final audioIcon =
|
|
19
|
+
final audioIcon = isPlaying ? Icons.pause_circle_outline : Icons.play_circle_outline;
|
|
18
20
|
|
|
19
21
|
void onHighlight(int index) {
|
|
20
22
|
context.dispatch(
|
|
21
23
|
SetHighlightAction(
|
|
22
|
-
List<Verse>.from(context.
|
|
24
|
+
List<Verse>.from(context.read().selectedVerses),
|
|
23
25
|
index,
|
|
24
26
|
),
|
|
25
27
|
);
|
|
@@ -41,7 +43,7 @@ class ActionsSheet extends StatelessWidget {
|
|
|
41
43
|
onPressed: () {
|
|
42
44
|
context.dispatch(
|
|
43
45
|
RemoveHighlightAction(
|
|
44
|
-
List<Verse>.from(context.
|
|
46
|
+
List<Verse>.from(context.read().selectedVerses),
|
|
45
47
|
),
|
|
46
48
|
);
|
|
47
49
|
context.nav.hideActions();
|
|
@@ -50,34 +52,34 @@ class ActionsSheet extends StatelessWidget {
|
|
|
50
52
|
),
|
|
51
53
|
HighlightButton(
|
|
52
54
|
index: 0,
|
|
53
|
-
color:
|
|
55
|
+
color: darkMode ? darkHighlights[0] : lightHighlights[0],
|
|
54
56
|
onHighlightSelected: onHighlight,
|
|
55
57
|
),
|
|
56
58
|
HighlightButton(
|
|
57
59
|
index: 1,
|
|
58
|
-
color:
|
|
60
|
+
color: darkMode ? darkHighlights[1] : lightHighlights[1],
|
|
59
61
|
onHighlightSelected: onHighlight,
|
|
60
62
|
),
|
|
61
63
|
HighlightButton(
|
|
62
64
|
index: 2,
|
|
63
|
-
color:
|
|
65
|
+
color: darkMode ? darkHighlights[2] : lightHighlights[2],
|
|
64
66
|
onHighlightSelected: onHighlight,
|
|
65
67
|
),
|
|
66
68
|
HighlightButton(
|
|
67
69
|
index: 3,
|
|
68
|
-
color:
|
|
70
|
+
color: darkMode ? darkHighlights[3] : lightHighlights[3],
|
|
69
71
|
onHighlightSelected: onHighlight,
|
|
70
72
|
),
|
|
71
73
|
IconButton(
|
|
72
74
|
padding: EdgeInsets.zero,
|
|
73
75
|
onPressed: () {
|
|
74
|
-
context.
|
|
76
|
+
context.read().onPlay(context, bible);
|
|
75
77
|
},
|
|
76
78
|
icon: Icon(audioIcon, size: 34, color: iconColor),
|
|
77
79
|
),
|
|
78
80
|
IconButton(
|
|
79
81
|
padding: EdgeInsets.zero,
|
|
80
|
-
onPressed: () => context.nav.shareVerses(context, bible, context.
|
|
82
|
+
onPressed: () => context.nav.shareVerses(context, bible, context.read().selectedVerses),
|
|
81
83
|
icon: Icon(Icons.share_outlined, size: 34, color: iconColor),
|
|
82
84
|
),
|
|
83
85
|
],
|
lib/sheets/settings_sheet.dart
CHANGED
|
@@ -12,6 +12,9 @@ class SettingsSheet extends StatelessWidget {
|
|
|
12
12
|
|
|
13
13
|
@override
|
|
14
14
|
Widget build(BuildContext context) {
|
|
15
|
+
final darkMode = context.select((s) => s.darkMode);
|
|
16
|
+
final boldFont = context.select((s) => s.boldFont);
|
|
17
|
+
final engTitles = context.select((s) => s.engTitles);
|
|
15
18
|
return SettingsList(
|
|
16
19
|
contentPadding: EdgeInsets.zero,
|
|
17
20
|
platform: DevicePlatform.iOS,
|
|
@@ -45,7 +48,7 @@ class SettingsSheet extends StatelessWidget {
|
|
|
45
48
|
highlightColor: Colors.transparent,
|
|
46
49
|
borderColor: Colors.grey,
|
|
47
50
|
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
|
48
|
-
selectedColor:
|
|
51
|
+
selectedColor: darkMode ? Colors.lightBlue.shade300 : Colors.yellowAccent.shade700,
|
|
49
52
|
selectedBorderColor: Colors.grey,
|
|
50
53
|
color: Colors.grey,
|
|
51
54
|
fillColor: Colors.transparent,
|
|
@@ -53,7 +56,7 @@ class SettingsSheet extends StatelessWidget {
|
|
|
53
56
|
minHeight: 36.0,
|
|
54
57
|
minWidth: 50.0,
|
|
55
58
|
),
|
|
56
|
-
isSelected: [!
|
|
59
|
+
isSelected: [!darkMode, darkMode],
|
|
57
60
|
children: const [
|
|
58
61
|
Icon(Icons.light_mode),
|
|
59
62
|
Icon(Icons.dark_mode),
|
|
@@ -91,7 +94,7 @@ class SettingsSheet extends StatelessWidget {
|
|
|
91
94
|
),
|
|
92
95
|
),
|
|
93
96
|
SettingsTile.switchTile(
|
|
94
|
-
initialValue:
|
|
97
|
+
initialValue: boldFont,
|
|
95
98
|
leading: Icon(
|
|
96
99
|
Icons.format_bold,
|
|
97
100
|
color: context.theme.colorScheme.onSurface,
|
|
@@ -100,7 +103,7 @@ class SettingsSheet extends StatelessWidget {
|
|
|
100
103
|
onToggle: (value) => context.dispatch(ToggleBoldFontAction()),
|
|
101
104
|
),
|
|
102
105
|
SettingsTile.switchTile(
|
|
103
|
-
initialValue:
|
|
106
|
+
initialValue: engTitles,
|
|
104
107
|
leading: Icon(Icons.abc, color: context.theme.colorScheme.onSurface),
|
|
105
108
|
title: Text(context.l.engTitles),
|
|
106
109
|
onToggle: (value) => context.dispatch(ToggleEngTitlesAction()),
|
lib/store/app_state.dart
CHANGED
|
@@ -136,8 +136,8 @@ class AppState {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
Future<void> onPlay(BuildContext context, Bible bible) async {
|
|
139
|
-
final versesToPlay = List<Verse>.from(context.
|
|
139
|
+
final versesToPlay = List<Verse>.from(context.read().selectedVerses);
|
|
140
|
-
if (context.
|
|
140
|
+
if (context.read().isPlaying) {
|
|
141
141
|
await player.pause();
|
|
142
142
|
context.dispatch(SetPlayingAction(false));
|
|
143
143
|
} else {
|
lib/utils.dart
CHANGED
|
@@ -41,20 +41,26 @@ extension IterableUtils<E> on Iterable<E> {
|
|
|
41
41
|
Iterable<E> addBy(E e) => toList()..add(e);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
extension
|
|
44
|
+
extension AppStateExtension on BuildContext {
|
|
45
|
-
ThemeData get theme => Theme.of(this);
|
|
46
|
-
|
|
47
|
-
AppState get state =>
|
|
45
|
+
AppState get state => getState<AppState>();
|
|
48
|
-
|
|
46
|
+
AppState read() => getRead<AppState>();
|
|
47
|
+
R select<R>(R Function(AppState state) selector) => getSelect<AppState, R>(selector);
|
|
48
|
+
R? event<R>(Evt<R> Function(AppState state) selector) => getEvent<AppState, R>(selector);
|
|
49
49
|
void dispatch(ReduxAction<AppState> action) => StoreProvider.dispatch<AppState>(this, action);
|
|
50
|
-
|
|
51
50
|
Future<void> dispatchAndWait(ReduxAction<AppState> action) => StoreProvider.dispatchAndWait<AppState>(this, action);
|
|
51
|
+
}
|
|
52
52
|
|
|
53
|
+
extension AppContext on BuildContext {
|
|
54
|
+
ThemeData get theme => Theme.of(this);
|
|
55
|
+
|
|
56
|
+
AppLocalizations get l {
|
|
57
|
+
final s = read();
|
|
53
|
-
|
|
58
|
+
return s.engTitles && s.languageCode != "en"
|
|
54
|
-
|
|
59
|
+
? lookupAppLocalizations(const Locale("en"))
|
|
55
|
-
|
|
60
|
+
: AppLocalizations.of(this)!;
|
|
61
|
+
}
|
|
56
62
|
|
|
57
|
-
AppLocalizations get currentLang => supportedLocalizations.firstWhere((el) => el.languageCode ==
|
|
63
|
+
AppLocalizations get currentLang => supportedLocalizations.firstWhere((el) => el.languageCode == read().languageCode);
|
|
58
64
|
|
|
59
65
|
List<AppLocalizations> get supportedLocalizations {
|
|
60
66
|
return AppLocalizations.supportedLocales
|
lib/widgets/verses_view.dart
CHANGED
|
@@ -13,7 +13,7 @@ class VersesView extends StatelessWidget {
|
|
|
13
13
|
|
|
14
14
|
void _onVerseTap(BuildContext context, Verse v) {
|
|
15
15
|
context.dispatch(SelectVerseAction(v));
|
|
16
|
-
if (context.
|
|
16
|
+
if (context.read().selectedVerses.isNotEmpty) {
|
|
17
17
|
context.nav.showActions(context, bible);
|
|
18
18
|
} else {
|
|
19
19
|
context.nav.hideActions();
|
|
@@ -22,10 +22,12 @@ class VersesView extends StatelessWidget {
|
|
|
22
22
|
|
|
23
23
|
@override
|
|
24
24
|
Widget build(BuildContext context) {
|
|
25
|
+
final (boldFont, textScale, selectedVerses, _, _) =
|
|
26
|
+
context.select((s) => (s.boldFont, s.textScale, s.selectedVerses, s.highlights, s.darkMode));
|
|
25
|
-
final appState = context.
|
|
27
|
+
final appState = context.read();
|
|
26
28
|
final textStyle = DefaultTextStyle.of(context).style;
|
|
27
29
|
final theme = Theme.of(context).textTheme;
|
|
28
|
-
final baseStyle =
|
|
30
|
+
final baseStyle = boldFont ? textStyle.copyWith(fontWeight: FontWeight.w500) : textStyle;
|
|
29
31
|
return SwipeDetector(
|
|
30
32
|
onSwipeLeft: (offset) => context.nav.nextChapter(context, bible, chapter.book, chapter.index),
|
|
31
33
|
onSwipeRight: (offset) => context.nav.previousChapter(context, bible, chapter.book, chapter.index),
|
|
@@ -57,11 +59,11 @@ class VersesView extends StatelessWidget {
|
|
|
57
59
|
),
|
|
58
60
|
],
|
|
59
61
|
),
|
|
60
|
-
textScaler: TextScaler.linear(1.1 +
|
|
62
|
+
textScaler: TextScaler.linear(1.1 + textScale / 2),
|
|
61
63
|
),
|
|
62
64
|
),
|
|
63
65
|
),
|
|
64
|
-
if (
|
|
66
|
+
if (selectedVerses.isNotEmpty) const SizedBox(height: 120),
|
|
65
67
|
],
|
|
66
68
|
),
|
|
67
69
|
),
|