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


c5b56aa0 pyrossh

2 years ago
implement Atom2
lib/actions.dart ADDED
@@ -0,0 +1,8 @@
1
+ import "package:freezed_annotation/freezed_annotation.dart";
2
+ part "actions.freezed.dart";
3
+
4
+ @freezed
5
+ class AppAction with _$AppAction {
6
+ const factory AppAction.firstOpenDone() = FirstOpenDone;
7
+ const factory AppAction.setLanguageCode(String code) = SetLanguageCode;
8
+ }
lib/actions.freezed.dart ADDED
@@ -0,0 +1,309 @@
1
+ // coverage:ignore-file
2
+ // GENERATED CODE - DO NOT MODIFY BY HAND
3
+ // ignore_for_file: type=lint
4
+ // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
5
+
6
+ part of 'actions.dart';
7
+
8
+ // **************************************************************************
9
+ // FreezedGenerator
10
+ // **************************************************************************
11
+
12
+ T _$identity<T>(T value) => value;
13
+
14
+ final _privateConstructorUsedError = UnsupportedError(
15
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
16
+
17
+ /// @nodoc
18
+ mixin _$AppAction {
19
+ @optionalTypeArgs
20
+ TResult when<TResult extends Object?>({
21
+ required TResult Function() firstOpenDone,
22
+ required TResult Function(String code) setLanguageCode,
23
+ }) =>
24
+ throw _privateConstructorUsedError;
25
+ @optionalTypeArgs
26
+ TResult? whenOrNull<TResult extends Object?>({
27
+ TResult? Function()? firstOpenDone,
28
+ TResult? Function(String code)? setLanguageCode,
29
+ }) =>
30
+ throw _privateConstructorUsedError;
31
+ @optionalTypeArgs
32
+ TResult maybeWhen<TResult extends Object?>({
33
+ TResult Function()? firstOpenDone,
34
+ TResult Function(String code)? setLanguageCode,
35
+ required TResult orElse(),
36
+ }) =>
37
+ throw _privateConstructorUsedError;
38
+ @optionalTypeArgs
39
+ TResult map<TResult extends Object?>({
40
+ required TResult Function(FirstOpenDone value) firstOpenDone,
41
+ required TResult Function(SetLanguageCode value) setLanguageCode,
42
+ }) =>
43
+ throw _privateConstructorUsedError;
44
+ @optionalTypeArgs
45
+ TResult? mapOrNull<TResult extends Object?>({
46
+ TResult? Function(FirstOpenDone value)? firstOpenDone,
47
+ TResult? Function(SetLanguageCode value)? setLanguageCode,
48
+ }) =>
49
+ throw _privateConstructorUsedError;
50
+ @optionalTypeArgs
51
+ TResult maybeMap<TResult extends Object?>({
52
+ TResult Function(FirstOpenDone value)? firstOpenDone,
53
+ TResult Function(SetLanguageCode value)? setLanguageCode,
54
+ required TResult orElse(),
55
+ }) =>
56
+ throw _privateConstructorUsedError;
57
+ }
58
+
59
+ /// @nodoc
60
+ abstract class $AppActionCopyWith<$Res> {
61
+ factory $AppActionCopyWith(AppAction value, $Res Function(AppAction) then) =
62
+ _$AppActionCopyWithImpl<$Res, AppAction>;
63
+ }
64
+
65
+ /// @nodoc
66
+ class _$AppActionCopyWithImpl<$Res, $Val extends AppAction>
67
+ implements $AppActionCopyWith<$Res> {
68
+ _$AppActionCopyWithImpl(this._value, this._then);
69
+
70
+ // ignore: unused_field
71
+ final $Val _value;
72
+ // ignore: unused_field
73
+ final $Res Function($Val) _then;
74
+ }
75
+
76
+ /// @nodoc
77
+ abstract class _$$FirstOpenDoneCopyWith<$Res> {
78
+ factory _$$FirstOpenDoneCopyWith(
79
+ _$FirstOpenDone value, $Res Function(_$FirstOpenDone) then) =
80
+ __$$FirstOpenDoneCopyWithImpl<$Res>;
81
+ }
82
+
83
+ /// @nodoc
84
+ class __$$FirstOpenDoneCopyWithImpl<$Res>
85
+ extends _$AppActionCopyWithImpl<$Res, _$FirstOpenDone>
86
+ implements _$$FirstOpenDoneCopyWith<$Res> {
87
+ __$$FirstOpenDoneCopyWithImpl(
88
+ _$FirstOpenDone _value, $Res Function(_$FirstOpenDone) _then)
89
+ : super(_value, _then);
90
+ }
91
+
92
+ /// @nodoc
93
+
94
+ class _$FirstOpenDone implements FirstOpenDone {
95
+ const _$FirstOpenDone();
96
+
97
+ @override
98
+ String toString() {
99
+ return 'AppAction.firstOpenDone()';
100
+ }
101
+
102
+ @override
103
+ bool operator ==(dynamic other) {
104
+ return identical(this, other) ||
105
+ (other.runtimeType == runtimeType && other is _$FirstOpenDone);
106
+ }
107
+
108
+ @override
109
+ int get hashCode => runtimeType.hashCode;
110
+
111
+ @override
112
+ @optionalTypeArgs
113
+ TResult when<TResult extends Object?>({
114
+ required TResult Function() firstOpenDone,
115
+ required TResult Function(String code) setLanguageCode,
116
+ }) {
117
+ return firstOpenDone();
118
+ }
119
+
120
+ @override
121
+ @optionalTypeArgs
122
+ TResult? whenOrNull<TResult extends Object?>({
123
+ TResult? Function()? firstOpenDone,
124
+ TResult? Function(String code)? setLanguageCode,
125
+ }) {
126
+ return firstOpenDone?.call();
127
+ }
128
+
129
+ @override
130
+ @optionalTypeArgs
131
+ TResult maybeWhen<TResult extends Object?>({
132
+ TResult Function()? firstOpenDone,
133
+ TResult Function(String code)? setLanguageCode,
134
+ required TResult orElse(),
135
+ }) {
136
+ if (firstOpenDone != null) {
137
+ return firstOpenDone();
138
+ }
139
+ return orElse();
140
+ }
141
+
142
+ @override
143
+ @optionalTypeArgs
144
+ TResult map<TResult extends Object?>({
145
+ required TResult Function(FirstOpenDone value) firstOpenDone,
146
+ required TResult Function(SetLanguageCode value) setLanguageCode,
147
+ }) {
148
+ return firstOpenDone(this);
149
+ }
150
+
151
+ @override
152
+ @optionalTypeArgs
153
+ TResult? mapOrNull<TResult extends Object?>({
154
+ TResult? Function(FirstOpenDone value)? firstOpenDone,
155
+ TResult? Function(SetLanguageCode value)? setLanguageCode,
156
+ }) {
157
+ return firstOpenDone?.call(this);
158
+ }
159
+
160
+ @override
161
+ @optionalTypeArgs
162
+ TResult maybeMap<TResult extends Object?>({
163
+ TResult Function(FirstOpenDone value)? firstOpenDone,
164
+ TResult Function(SetLanguageCode value)? setLanguageCode,
165
+ required TResult orElse(),
166
+ }) {
167
+ if (firstOpenDone != null) {
168
+ return firstOpenDone(this);
169
+ }
170
+ return orElse();
171
+ }
172
+ }
173
+
174
+ abstract class FirstOpenDone implements AppAction {
175
+ const factory FirstOpenDone() = _$FirstOpenDone;
176
+ }
177
+
178
+ /// @nodoc
179
+ abstract class _$$SetLanguageCodeCopyWith<$Res> {
180
+ factory _$$SetLanguageCodeCopyWith(
181
+ _$SetLanguageCode value, $Res Function(_$SetLanguageCode) then) =
182
+ __$$SetLanguageCodeCopyWithImpl<$Res>;
183
+ @useResult
184
+ $Res call({String code});
185
+ }
186
+
187
+ /// @nodoc
188
+ class __$$SetLanguageCodeCopyWithImpl<$Res>
189
+ extends _$AppActionCopyWithImpl<$Res, _$SetLanguageCode>
190
+ implements _$$SetLanguageCodeCopyWith<$Res> {
191
+ __$$SetLanguageCodeCopyWithImpl(
192
+ _$SetLanguageCode _value, $Res Function(_$SetLanguageCode) _then)
193
+ : super(_value, _then);
194
+
195
+ @pragma('vm:prefer-inline')
196
+ @override
197
+ $Res call({
198
+ Object? code = null,
199
+ }) {
200
+ return _then(_$SetLanguageCode(
201
+ null == code
202
+ ? _value.code
203
+ : code // ignore: cast_nullable_to_non_nullable
204
+ as String,
205
+ ));
206
+ }
207
+ }
208
+
209
+ /// @nodoc
210
+
211
+ class _$SetLanguageCode implements SetLanguageCode {
212
+ const _$SetLanguageCode(this.code);
213
+
214
+ @override
215
+ final String code;
216
+
217
+ @override
218
+ String toString() {
219
+ return 'AppAction.setLanguageCode(code: $code)';
220
+ }
221
+
222
+ @override
223
+ bool operator ==(dynamic other) {
224
+ return identical(this, other) ||
225
+ (other.runtimeType == runtimeType &&
226
+ other is _$SetLanguageCode &&
227
+ (identical(other.code, code) || other.code == code));
228
+ }
229
+
230
+ @override
231
+ int get hashCode => Object.hash(runtimeType, code);
232
+
233
+ @JsonKey(ignore: true)
234
+ @override
235
+ @pragma('vm:prefer-inline')
236
+ _$$SetLanguageCodeCopyWith<_$SetLanguageCode> get copyWith =>
237
+ __$$SetLanguageCodeCopyWithImpl<_$SetLanguageCode>(this, _$identity);
238
+
239
+ @override
240
+ @optionalTypeArgs
241
+ TResult when<TResult extends Object?>({
242
+ required TResult Function() firstOpenDone,
243
+ required TResult Function(String code) setLanguageCode,
244
+ }) {
245
+ return setLanguageCode(code);
246
+ }
247
+
248
+ @override
249
+ @optionalTypeArgs
250
+ TResult? whenOrNull<TResult extends Object?>({
251
+ TResult? Function()? firstOpenDone,
252
+ TResult? Function(String code)? setLanguageCode,
253
+ }) {
254
+ return setLanguageCode?.call(code);
255
+ }
256
+
257
+ @override
258
+ @optionalTypeArgs
259
+ TResult maybeWhen<TResult extends Object?>({
260
+ TResult Function()? firstOpenDone,
261
+ TResult Function(String code)? setLanguageCode,
262
+ required TResult orElse(),
263
+ }) {
264
+ if (setLanguageCode != null) {
265
+ return setLanguageCode(code);
266
+ }
267
+ return orElse();
268
+ }
269
+
270
+ @override
271
+ @optionalTypeArgs
272
+ TResult map<TResult extends Object?>({
273
+ required TResult Function(FirstOpenDone value) firstOpenDone,
274
+ required TResult Function(SetLanguageCode value) setLanguageCode,
275
+ }) {
276
+ return setLanguageCode(this);
277
+ }
278
+
279
+ @override
280
+ @optionalTypeArgs
281
+ TResult? mapOrNull<TResult extends Object?>({
282
+ TResult? Function(FirstOpenDone value)? firstOpenDone,
283
+ TResult? Function(SetLanguageCode value)? setLanguageCode,
284
+ }) {
285
+ return setLanguageCode?.call(this);
286
+ }
287
+
288
+ @override
289
+ @optionalTypeArgs
290
+ TResult maybeMap<TResult extends Object?>({
291
+ TResult Function(FirstOpenDone value)? firstOpenDone,
292
+ TResult Function(SetLanguageCode value)? setLanguageCode,
293
+ required TResult orElse(),
294
+ }) {
295
+ if (setLanguageCode != null) {
296
+ return setLanguageCode(this);
297
+ }
298
+ return orElse();
299
+ }
300
+ }
301
+
302
+ abstract class SetLanguageCode implements AppAction {
303
+ const factory SetLanguageCode(final String code) = _$SetLanguageCode;
304
+
305
+ String get code;
306
+ @JsonKey(ignore: true)
307
+ _$$SetLanguageCodeCopyWith<_$SetLanguageCode> get copyWith =>
308
+ throw _privateConstructorUsedError;
309
+ }
lib/atom.dart CHANGED
@@ -2,6 +2,78 @@ import "package:flutter/material.dart";
2
2
  import "package:flutter/scheduler.dart";
3
3
  import "package:get_storage/get_storage.dart";
4
4
 
5
+ final List<Atom2> atoms = [];
6
+
7
+ void dispatch<A>(A a) {
8
+ for (final atom in atoms) {
9
+ final typeName = a.runtimeType.toString().replaceAll("_\$", "");
10
+ for (final entry in atom.reducer.entries) {
11
+ if (typeName == entry.key.toString()) {
12
+ final newValue = entry.value.call(atom.valueNotifier.value, a);
13
+ atom.valueNotifier.value = newValue;
14
+ if (atom.box != null) {
15
+ if (newValue == null) {
16
+ atom.box!.remove(atom.key);
17
+ } else {
18
+ atom.box!.write(atom.key, newValue);
19
+ }
20
+ atom.box!.save();
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+
27
+ class Atom2<T, A> {
28
+ late ValueNotifier<T> valueNotifier;
29
+ final String key;
30
+ final GetStorage? box;
31
+ final Map<Type, dynamic Function(dynamic, dynamic)> reducer;
32
+
33
+ Atom2({required this.key, required T initialState, this.box, required this.reducer}) {
34
+ valueNotifier = ValueNotifier(box != null ? box!.read<T>(key) ?? initialState : initialState);
35
+ atoms.add(this);
36
+ }
37
+
38
+ get value => valueNotifier.value;
39
+
40
+ T watch(BuildContext context) {
41
+ final elementRef = WeakReference(context as Element);
42
+ final listenerWrapper = _ListenerWrapper();
43
+ listenerWrapper.listener = () {
44
+ assert(
45
+ SchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacks,
46
+ """
47
+ Do not mutate state (by setting the value of the ValueNotifier
48
+ that you are subscribed to) during a `build` method. If you need
49
+ to schedule a value update after `build` has completed, use
50
+ `SchedulerBinding.instance.scheduleTask(updateTask, Priority.idle)`,
51
+ `SchedulerBinding.addPostFrameCallback(updateTask)`, '
52
+ or similar.
53
+ """,
54
+ );
55
+ // If the element has not been garbage collected (causing
56
+ // `elementRef.target` to be null), or unmounted
57
+ if (elementRef.target?.mounted ?? false) {
58
+ // Mark the element as needing to be rebuilt
59
+ elementRef.target!.markNeedsBuild();
60
+ }
61
+ // Remove the listener -- only listen to one change per `build`
62
+ valueNotifier.removeListener(listenerWrapper.listener!);
63
+ };
64
+ valueNotifier.addListener(listenerWrapper.listener!);
65
+ return valueNotifier.value;
66
+ }
67
+
68
+ /// Use this method to notify listeners of deeper changes, e.g. when a value
69
+ /// is added to or removed from a set which is stored in the value of a
70
+ /// `ValueNotifier<Set<T>>`.
71
+ void notifyChanged() {
72
+ // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
73
+ valueNotifier.notifyListeners();
74
+ }
75
+ }
76
+
5
77
  class Atom<T> extends ValueNotifier<T> {
6
78
  final String key;
7
79
  final GetStorage? box;
lib/screens/bible_select_screen.dart CHANGED
@@ -1,4 +1,6 @@
1
1
  import "package:flutter/material.dart";
2
+ import "package:only_bible_app/actions.dart";
3
+ import "package:only_bible_app/atom.dart";
2
4
  import "package:only_bible_app/state.dart";
3
5
  import "package:only_bible_app/utils.dart";
4
6
  import "package:only_bible_app/widgets/scaffold_menu.dart";
@@ -24,15 +26,15 @@ class BibleSelectScreen extends StatelessWidget {
24
26
  ? Column(
25
27
  mainAxisAlignment: MainAxisAlignment.center,
26
28
  children: [
27
- Text(l.localeLanguageTitle),
29
+ Text(l.localeLanguageTitle, textScaleFactor: 1.3),
28
- Text("(${l.languageTitle})", textScaleFactor: 0.7),
30
+ Text("(${l.languageTitle})", textScaleFactor: 0.8),
29
31
  ],
30
32
  )
31
- : Text(l.languageTitle),
33
+ : Text(l.languageTitle, textScaleFactor: 1.1),
32
34
  onPressed: () {
33
35
  if (firstOpen.value) {
34
- firstOpen.set!();
36
+ dispatch(const FirstOpenDone());
35
- } else {}
37
+ }
36
38
  updateCurrentBible(context, l.localeName, l.languageTitle);
37
39
  },
38
40
  );
lib/state.dart CHANGED
@@ -1,10 +1,10 @@
1
- import "dart:convert";
2
1
  import "dart:developer";
3
2
  import "package:firebase_crashlytics/firebase_crashlytics.dart";
4
3
  import "package:flutter/material.dart";
5
4
  import "package:flutter/services.dart";
6
5
  import "package:get_storage/get_storage.dart";
7
6
  import "package:just_audio/just_audio.dart";
7
+ import "package:only_bible_app/actions.dart";
8
8
  import "package:only_bible_app/atom.dart";
9
9
  import "package:only_bible_app/dialog.dart";
10
10
  import "package:only_bible_app/models.dart";
@@ -24,21 +24,21 @@ final bibleAtom = AsyncAtom(
24
24
  callback: loadBible,
25
25
  );
26
26
 
27
- final Atom<bool> firstOpen = Atom<bool>(
27
+ final firstOpen = Atom2<bool, AppAction>(
28
28
  box: box,
29
29
  key: "firstOpen",
30
- initialValue: true,
30
+ initialState: true,
31
- set: () {
31
+ reducer: {
32
- firstOpen.value = false;
32
+ FirstOpenDone: (state, action) => false,
33
33
  },
34
34
  );
35
35
 
36
- final Atom<String> languageCode = Atom<String>(
36
+ final languageCode = Atom2<String, AppAction>(
37
37
  box: box,
38
38
  key: "languageCode",
39
- initialValue: "en",
39
+ initialState: "en",
40
- update: (String v) {
40
+ reducer: {
41
- languageCode.value = v;
41
+ SetLanguageCode: (state, action) => (action as SetLanguageCode).code,
42
42
  },
43
43
  );
44
44
 
@@ -53,31 +53,11 @@ final Atom<String> bibleName = Atom<String>(
53
53
 
54
54
  updateCurrentBible(BuildContext context, String code, String name) async {
55
55
  hideActions(context);
56
- languageCode.value = code;
56
+ dispatch(SetLanguageCode(code));
57
57
  bibleName.update!(name);
58
58
  pushBookChapter(context, name, 0, 0, null);
59
59
  }
60
60
 
61
- Future<Bible> loadBible(String name) async {
62
- final bytes = await rootBundle.load("assets/bibles/$name.txt");
63
- final books = getBibleFromText(name, utf8.decode(bytes.buffer.asUint8List(), allowMalformed: false));
64
- // await Future.delayed(Duration(seconds: 2));
65
- return Bible(
66
- name: name,
67
- books: books,
68
- );
69
- }
70
-
71
- // Trace customTrace;
72
- // if (!isDesktop()) {
73
- // customTrace = FirebasePerformance.instance.newTrace("loadBible");
74
- // await customTrace.start();
75
- // }
76
- // bibleLoading = Future.delayed(const Duration(seconds: 2)).then((value) => getBibleFromAsset(bibleName.value));
77
- // if (!isDesktop()) {
78
- // await customTrace.stop();
79
- // }
80
-
81
61
  final Atom<bool> engTitles = Atom<bool>(
82
62
  box: box,
83
63
  key: "engTitles",
lib/utils.dart CHANGED
@@ -1,5 +1,7 @@
1
- import "dart:typed_data";
1
+ import "dart:convert";
2
+ import "package:flutter/services.dart";
2
3
  import "package:only_bible_app/dialog.dart";
4
+ import "package:only_bible_app/models.dart";
3
5
  import "package:only_bible_app/state.dart";
4
6
  import "package:url_launcher/url_launcher.dart";
5
7
  import "package:flutter/foundation.dart" show defaultTargetPlatform, TargetPlatform;
@@ -181,3 +183,13 @@ Future<Uint8List> convertText(String langCode, String text) async {
181
183
  ));
182
184
  return ttsResponse.audio.buffer.asUint8List();
183
185
  }
186
+
187
+ Future<Bible> loadBible(String name) async {
188
+ final bytes = await rootBundle.load("assets/bibles/$name.txt");
189
+ final books = getBibleFromText(name, utf8.decode(bytes.buffer.asUint8List(), allowMalformed: false));
190
+ // await Future.delayed(Duration(seconds: 2));
191
+ return Bible(
192
+ name: name,
193
+ books: books,
194
+ );
195
+ }