~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
@@ -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: StoreConnector<AppState, _AppVm>(
113
+ child: Builder(
114
- vm: () => _AppVmFactory(),
115
- builder: (context, vm) => MaterialApp.router(
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: vm.darkMode ? ThemeMode.dark : ThemeMode.light,
120
+ themeMode: context.select((s) => s.darkMode) ? ThemeMode.dark : ThemeMode.light,
122
121
  theme: lightTheme,
123
122
  darkTheme: darkTheme,
124
- locale: Locale(vm.languageCode),
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.state.firstOpen,
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 (context.state.firstOpen) {
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
- context.state.savedBook,
45
+ s.savedBook,
45
- context.state.savedChapter,
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.state.bible;
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.state.bible;
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.state.bible;
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 = context.state.darkMode ? Colors.white.withOpacity(0.9) : Colors.black.withOpacity(0.9);
18
+ final iconColor = darkMode ? Colors.white.withOpacity(0.9) : Colors.black.withOpacity(0.9);
17
- final audioIcon = context.state.isPlaying ? Icons.pause_circle_outline : Icons.play_circle_outline;
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.state.selectedVerses),
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.state.selectedVerses),
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: context.state.darkMode ? darkHighlights[0] : lightHighlights[0],
55
+ color: darkMode ? darkHighlights[0] : lightHighlights[0],
54
56
  onHighlightSelected: onHighlight,
55
57
  ),
56
58
  HighlightButton(
57
59
  index: 1,
58
- color: context.state.darkMode ? darkHighlights[1] : lightHighlights[1],
60
+ color: darkMode ? darkHighlights[1] : lightHighlights[1],
59
61
  onHighlightSelected: onHighlight,
60
62
  ),
61
63
  HighlightButton(
62
64
  index: 2,
63
- color: context.state.darkMode ? darkHighlights[2] : lightHighlights[2],
65
+ color: darkMode ? darkHighlights[2] : lightHighlights[2],
64
66
  onHighlightSelected: onHighlight,
65
67
  ),
66
68
  HighlightButton(
67
69
  index: 3,
68
- color: context.state.darkMode ? darkHighlights[3] : lightHighlights[3],
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.state.onPlay(context, bible);
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.state.selectedVerses),
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: context.state.darkMode ? Colors.lightBlue.shade300 : Colors.yellowAccent.shade700,
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: [!context.state.darkMode, context.state.darkMode],
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: context.state.boldFont,
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: context.state.engTitles,
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.state.selectedVerses);
139
+ final versesToPlay = List<Verse>.from(context.read().selectedVerses);
140
- if (context.state.isPlaying) {
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 AppContext on BuildContext {
44
+ extension AppStateExtension on BuildContext {
45
- ThemeData get theme => Theme.of(this);
46
-
47
- AppState get state => StoreProvider.state<AppState>(this);
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
- AppLocalizations get l => state.engTitles && state.languageCode != "en"
58
+ return s.engTitles && s.languageCode != "en"
54
- ? lookupAppLocalizations(const Locale("en"))
59
+ ? lookupAppLocalizations(const Locale("en"))
55
- : AppLocalizations.of(this)!;
60
+ : AppLocalizations.of(this)!;
61
+ }
56
62
 
57
- AppLocalizations get currentLang => supportedLocalizations.firstWhere((el) => el.languageCode == state.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.state.selectedVerses.isNotEmpty) {
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.state;
27
+ final appState = context.read();
26
28
  final textStyle = DefaultTextStyle.of(context).style;
27
29
  final theme = Theme.of(context).textTheme;
28
- final baseStyle = appState.boldFont ? textStyle.copyWith(fontWeight: FontWeight.w500) : textStyle;
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 + appState.textScale / 2),
62
+ textScaler: TextScaler.linear(1.1 + textScale / 2),
61
63
  ),
62
64
  ),
63
65
  ),
64
- if (appState.selectedVerses.isNotEmpty) const SizedBox(height: 120),
66
+ if (selectedVerses.isNotEmpty) const SizedBox(height: 120),
65
67
  ],
66
68
  ),
67
69
  ),