only-bible-app v3.7.2+24
git clone https://git.pyrossh.dev/only-bible-app
The only bible app you will ever need. No ads. No in-app purchases. No distractions.
c2e460a
— Peter John
2026-08-08T13:34:21+05:30
fix navigatino
Generated files not shown: pubspec.lock
- .gitignore +1 -0
- ios/Runner.xcodeproj/project.pbxproj +6 -6
- ios/Runner/Info.plist +1 -5
- lib/app.dart +40 -17
- lib/dialog.dart +24 -5
- lib/home.dart +1 -1
- lib/store/actions_navigation.dart +35 -1
- macos/Runner/Info.plist +0 -4
- pubspec.yaml +2 -3
- test/actions_backup_test.dart +3 -2
- test/app_logic_test.dart +8 -7
.gitignore
CHANGED
|
@@ -17,6 +17,7 @@ node_modules
|
|
|
17
17
|
*.aab
|
|
18
18
|
*.apk
|
|
19
19
|
*.ipa
|
|
20
|
+
*.dSYM.zip
|
|
20
21
|
|
|
21
22
|
# Python
|
|
22
23
|
.venv
|
ios/Runner.xcodeproj/project.pbxproj
CHANGED
|
@@ -485,7 +485,7 @@
|
|
|
485
485
|
CLANG_ENABLE_MODULES = YES;
|
|
486
486
|
CODE_SIGN_IDENTITY = "Apple Development";
|
|
487
487
|
CODE_SIGN_STYLE = Automatic;
|
|
488
|
-
CURRENT_PROJECT_VERSION =
|
|
488
|
+
CURRENT_PROJECT_VERSION = 5;
|
|
489
489
|
DEVELOPMENT_TEAM = FXH8NUGFYY;
|
|
490
490
|
ENABLE_BITCODE = NO;
|
|
491
491
|
INFOPLIST_FILE = Runner/Info.plist;
|
|
@@ -508,7 +508,7 @@
|
|
|
508
508
|
buildSettings = {
|
|
509
509
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
510
510
|
CODE_SIGN_STYLE = Automatic;
|
|
511
|
-
CURRENT_PROJECT_VERSION =
|
|
511
|
+
CURRENT_PROJECT_VERSION = 5;
|
|
512
512
|
GENERATE_INFOPLIST_FILE = YES;
|
|
513
513
|
MARKETING_VERSION = 1.0;
|
|
514
514
|
PRODUCT_BUNDLE_IDENTIFIER = "sh.pyros.only-bible-app.RunnerTests";
|
|
@@ -526,7 +526,7 @@
|
|
|
526
526
|
buildSettings = {
|
|
527
527
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
528
528
|
CODE_SIGN_STYLE = Automatic;
|
|
529
|
-
CURRENT_PROJECT_VERSION =
|
|
529
|
+
CURRENT_PROJECT_VERSION = 5;
|
|
530
530
|
GENERATE_INFOPLIST_FILE = YES;
|
|
531
531
|
MARKETING_VERSION = 1.0;
|
|
532
532
|
PRODUCT_BUNDLE_IDENTIFIER = "sh.pyros.only-bible-app.RunnerTests";
|
|
@@ -542,7 +542,7 @@
|
|
|
542
542
|
buildSettings = {
|
|
543
543
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
544
544
|
CODE_SIGN_STYLE = Automatic;
|
|
545
|
-
CURRENT_PROJECT_VERSION =
|
|
545
|
+
CURRENT_PROJECT_VERSION = 5;
|
|
546
546
|
GENERATE_INFOPLIST_FILE = YES;
|
|
547
547
|
MARKETING_VERSION = 1.0;
|
|
548
548
|
PRODUCT_BUNDLE_IDENTIFIER = "sh.pyros.only-bible-app.RunnerTests";
|
|
@@ -671,7 +671,7 @@
|
|
|
671
671
|
CLANG_ENABLE_MODULES = YES;
|
|
672
672
|
CODE_SIGN_IDENTITY = "Apple Development";
|
|
673
673
|
CODE_SIGN_STYLE = Automatic;
|
|
674
|
-
CURRENT_PROJECT_VERSION =
|
|
674
|
+
CURRENT_PROJECT_VERSION = 5;
|
|
675
675
|
DEVELOPMENT_TEAM = FXH8NUGFYY;
|
|
676
676
|
ENABLE_BITCODE = NO;
|
|
677
677
|
INFOPLIST_FILE = Runner/Info.plist;
|
|
@@ -698,7 +698,7 @@
|
|
|
698
698
|
CODE_SIGN_IDENTITY = "Apple Development";
|
|
699
699
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
|
700
700
|
CODE_SIGN_STYLE = Manual;
|
|
701
|
-
CURRENT_PROJECT_VERSION =
|
|
701
|
+
CURRENT_PROJECT_VERSION = 5;
|
|
702
702
|
DEVELOPMENT_TEAM = "";
|
|
703
703
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = FXH8NUGFYY;
|
|
704
704
|
ENABLE_BITCODE = NO;
|
ios/Runner/Info.plist
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<key>CFBundleSignature</key>
|
|
24
24
|
<string>????</string>
|
|
25
25
|
<key>CFBundleVersion</key>
|
|
26
|
-
<string>
|
|
26
|
+
<string>5</string>
|
|
27
27
|
<key>LSRequiresIPhoneOS</key>
|
|
28
28
|
<true/>
|
|
29
29
|
<key>UIApplicationSceneManifest</key>
|
|
@@ -72,10 +72,6 @@
|
|
|
72
72
|
<false/>
|
|
73
73
|
<key>NSPhotoLibraryUsageDescription</key>
|
|
74
74
|
<string>Only Bible App uses the system file picker to let you choose a backup file to restore your highlights and settings. Depending on your device, this picker may show your photo library, though Only Bible App does not access your photos directly.</string>
|
|
75
|
-
<key>NSCameraUsageDescription</key>
|
|
76
|
-
<string>Only Bible App does not use the camera. This description is required because a bundled library references camera APIs, even though this app does not use them.</string>
|
|
77
|
-
<key>NSLocationWhenInUseUsageDescription</key>
|
|
78
|
-
<string>Only Bible App does not use your location. This description is required because a bundled library references location APIs, even though this app does not use them.</string>
|
|
79
75
|
<key>NSAppleMusicUsageDescription</key>
|
|
80
76
|
<string>Only Bible App does not access your music library. This description is required because a bundled library references media library APIs, even though this app does not use them.</string>
|
|
81
77
|
</dict>
|
lib/app.dart
CHANGED
|
@@ -22,31 +22,53 @@ class App extends StatelessWidget {
|
|
|
22
22
|
path: "/chapter/:bookIndex/:chapterIndex",
|
|
23
23
|
pageBuilder: (context, state) {
|
|
24
24
|
final bookIndex = int.parse(state.pathParameters["bookIndex"]!);
|
|
25
|
+
final chapterIndex = int.parse(
|
|
25
|
-
|
|
26
|
+
state.pathParameters["chapterIndex"]!,
|
|
27
|
+
);
|
|
28
|
+
final extra =
|
|
29
|
+
state.extra
|
|
30
|
+
as ({
|
|
26
|
-
|
|
31
|
+
TextDirection? slideDir,
|
|
32
|
+
int? scrollToVerse,
|
|
33
|
+
int navId,
|
|
34
|
+
})?;
|
|
27
35
|
final slideDir = extra?.slideDir;
|
|
28
36
|
final scrollToVerse = extra?.scrollToVerse;
|
|
37
|
+
final pageKey = ValueKey(
|
|
29
|
-
|
|
38
|
+
"$bookIndex-$chapterIndex-${extra?.navId ?? 0}",
|
|
39
|
+
);
|
|
30
40
|
if (slideDir != null) {
|
|
31
41
|
return CustomTransitionPage(
|
|
32
42
|
key: pageKey,
|
|
43
|
+
child: Home(
|
|
44
|
+
bookIndex: bookIndex,
|
|
45
|
+
chapterIndex: chapterIndex,
|
|
33
|
-
|
|
46
|
+
scrollToVerse: scrollToVerse,
|
|
47
|
+
),
|
|
48
|
+
transitionsBuilder:
|
|
34
|
-
|
|
49
|
+
(context, animation, secondaryAnimation, child) {
|
|
35
|
-
|
|
50
|
+
const begin = Offset(1.0, 0.0);
|
|
36
|
-
|
|
51
|
+
const end = Offset.zero;
|
|
37
|
-
|
|
52
|
+
const curve = Curves.ease;
|
|
53
|
+
final tween = Tween(
|
|
54
|
+
begin: begin,
|
|
55
|
+
end: end,
|
|
38
|
-
|
|
56
|
+
).chain(CurveTween(curve: curve));
|
|
39
|
-
|
|
57
|
+
return SlideTransition(
|
|
40
|
-
|
|
58
|
+
textDirection: slideDir,
|
|
41
|
-
|
|
59
|
+
position: animation.drive(tween),
|
|
42
|
-
|
|
60
|
+
child: child,
|
|
43
|
-
|
|
61
|
+
);
|
|
44
|
-
|
|
62
|
+
},
|
|
45
63
|
);
|
|
46
64
|
}
|
|
47
65
|
return NoTransitionPage(
|
|
48
66
|
key: pageKey,
|
|
67
|
+
child: Home(
|
|
68
|
+
bookIndex: bookIndex,
|
|
69
|
+
chapterIndex: chapterIndex,
|
|
49
|
-
|
|
70
|
+
scrollToVerse: scrollToVerse,
|
|
71
|
+
),
|
|
50
72
|
);
|
|
51
73
|
},
|
|
52
74
|
),
|
|
@@ -57,7 +79,8 @@ class App extends StatelessWidget {
|
|
|
57
79
|
}
|
|
58
80
|
|
|
59
81
|
void _syncCurrentChapter() {
|
|
82
|
+
final segments =
|
|
60
|
-
|
|
83
|
+
_router.routerDelegate.currentConfiguration.uri.pathSegments;
|
|
61
84
|
if (segments.length != 3 || segments.first != "chapter") return;
|
|
62
85
|
|
|
63
86
|
final book = int.tryParse(segments[1]);
|
lib/dialog.dart
CHANGED
|
@@ -4,6 +4,7 @@ import "package:flutter/material.dart";
|
|
|
4
4
|
import "package:only_bible_app/bible_data.dart";
|
|
5
5
|
import "package:only_bible_app/gen/bible.gen.dart";
|
|
6
6
|
import "package:only_bible_app/store/actions_navigation.dart";
|
|
7
|
+
import "package:only_bible_app/store/app_navigator.dart";
|
|
7
8
|
import "package:only_bible_app/store/app_state.dart";
|
|
8
9
|
export "package:shared/shared.dart" show showAlert, showError, showConfirm;
|
|
9
10
|
|
|
@@ -14,7 +15,10 @@ void showBibleSelectDialog(BuildContext context, Bible bible) {
|
|
|
14
15
|
builder: (dialogContext) {
|
|
15
16
|
return AlertDialog(
|
|
16
17
|
title: Text(bible.bibleSelectTitle!),
|
|
17
|
-
contentPadding: const EdgeInsets.symmetric(
|
|
18
|
+
contentPadding: const EdgeInsets.symmetric(
|
|
19
|
+
horizontal: 16,
|
|
20
|
+
vertical: 12,
|
|
21
|
+
),
|
|
18
22
|
content: SizedBox(
|
|
19
23
|
// Capped so the list doesn't stretch edge-to-edge on wide
|
|
20
24
|
// desktop/tablet windows, where double.maxFinite would otherwise
|
|
@@ -23,7 +27,7 @@ void showBibleSelectDialog(BuildContext context, Bible bible) {
|
|
|
23
27
|
height: 400,
|
|
24
28
|
child: ListView.separated(
|
|
25
29
|
itemCount: bibleItems.length,
|
|
26
|
-
separatorBuilder: (_,
|
|
30
|
+
separatorBuilder: (_, _) => const Divider(height: 1),
|
|
27
31
|
itemBuilder: (itemContext, index) {
|
|
28
32
|
final item = bibleItems.values.elementAt(index);
|
|
29
33
|
final isSelected = context.read().bible.name == item.fileName;
|
|
@@ -31,16 +35,31 @@ void showBibleSelectDialog(BuildContext context, Bible bible) {
|
|
|
31
35
|
selected: isSelected,
|
|
32
36
|
tileColor: null,
|
|
33
37
|
leading: const Icon(Icons.menu_book),
|
|
38
|
+
title: Text(
|
|
39
|
+
item.languageNative,
|
|
34
|
-
|
|
40
|
+
style: const TextStyle(fontWeight: FontWeight.w600),
|
|
41
|
+
),
|
|
35
42
|
subtitle: (item.languageNative != item.languageEnglish)
|
|
43
|
+
? Text(
|
|
44
|
+
item.languageEnglish,
|
|
45
|
+
style: const TextStyle(
|
|
46
|
+
fontSize: 15,
|
|
36
|
-
|
|
47
|
+
fontWeight: FontWeight.w400,
|
|
48
|
+
),
|
|
49
|
+
)
|
|
37
50
|
: null,
|
|
38
51
|
trailing: isSelected ? const Icon(Icons.check) : null,
|
|
39
52
|
onTap: () {
|
|
40
53
|
final s = context.read();
|
|
54
|
+
final router = context.router;
|
|
41
55
|
Navigator.of(itemContext).pop();
|
|
42
56
|
context.dispatch(
|
|
43
|
-
UpdateCurrentBibleAction(
|
|
57
|
+
UpdateCurrentBibleAction(
|
|
58
|
+
router,
|
|
59
|
+
item.fileName,
|
|
60
|
+
s.savedBook,
|
|
61
|
+
s.savedChapter,
|
|
62
|
+
),
|
|
44
63
|
);
|
|
45
64
|
},
|
|
46
65
|
);
|
lib/home.dart
CHANGED
|
@@ -132,7 +132,7 @@ class _HomeState extends State<Home> {
|
|
|
132
132
|
onEdgeSwipeRight: () {
|
|
133
133
|
final router = context.router;
|
|
134
134
|
if (router.canPop()) {
|
|
135
|
-
|
|
135
|
+
context.dispatch(PopChapterAction(router));
|
|
136
136
|
} else {
|
|
137
137
|
context.dispatch(PreviousChapterAction(router, bible, chapter.book, chapter.index));
|
|
138
138
|
}
|
lib/store/actions_navigation.dart
CHANGED
|
@@ -131,6 +131,12 @@ class ShowChapterSelectAction extends ReduxAction<AppState> {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
// Tracks the (book, chapter) each GoToChapterAction push navigated away
|
|
135
|
+
// from, so PopChapterAction can restore savedBook/savedChapter synchronously
|
|
136
|
+
// instead of waiting on the routerDelegate listener, which does not fire
|
|
137
|
+
// until a pop's page transition animation completes (see PopChapterAction).
|
|
138
|
+
final List<(int, int)> _chapterHistory = [];
|
|
139
|
+
|
|
134
140
|
class GoToChapterAction extends ReduxAction<AppState> {
|
|
135
141
|
final GoRouter router;
|
|
136
142
|
final int book;
|
|
@@ -153,6 +159,7 @@ class GoToChapterAction extends ReduxAction<AppState> {
|
|
|
153
159
|
} else {
|
|
154
160
|
slideDir = null;
|
|
155
161
|
}
|
|
162
|
+
_chapterHistory.add((state.savedBook, state.savedChapter));
|
|
156
163
|
// Update savedBook/savedChapter before pushing: GoRouter notifies the
|
|
157
164
|
// routerDelegate listener (App._syncCurrentChapter) synchronously as
|
|
158
165
|
// part of push(), which dispatches SyncCurrentChapterAction. Setting
|
|
@@ -167,6 +174,27 @@ class GoToChapterAction extends ReduxAction<AppState> {
|
|
|
167
174
|
}
|
|
168
175
|
}
|
|
169
176
|
|
|
177
|
+
class PopChapterAction extends ReduxAction<AppState> {
|
|
178
|
+
final GoRouter router;
|
|
179
|
+
|
|
180
|
+
PopChapterAction(this.router);
|
|
181
|
+
|
|
182
|
+
@override
|
|
183
|
+
AppState? reduce() {
|
|
184
|
+
// Unlike push(), GoRouterDelegate.pop() only notifies its listener once
|
|
185
|
+
// the page's exit transition finishes, so App._syncCurrentChapter can't
|
|
186
|
+
// be relied on to update savedBook/savedChapter promptly. Restore them
|
|
187
|
+
// from our own history here, before popping, the same way GoToChapterAction
|
|
188
|
+
// updates them before pushing.
|
|
189
|
+
if (_chapterHistory.isNotEmpty) {
|
|
190
|
+
final (book, chapter) = _chapterHistory.removeLast();
|
|
191
|
+
dispatch(UpdateChapterAction(book, chapter));
|
|
192
|
+
}
|
|
193
|
+
router.pop();
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
170
198
|
class SyncCurrentChapterAction extends ReduxAction<AppState> {
|
|
171
199
|
final int book;
|
|
172
200
|
final int chapter;
|
|
@@ -245,16 +273,22 @@ class PreviousChapterAction extends ReduxAction<AppState> {
|
|
|
245
273
|
}
|
|
246
274
|
|
|
247
275
|
class UpdateCurrentBibleAction extends ReduxAction<AppState> {
|
|
276
|
+
final GoRouter router;
|
|
248
277
|
final String name;
|
|
249
278
|
final int book;
|
|
250
279
|
final int chapter;
|
|
251
280
|
|
|
252
|
-
UpdateCurrentBibleAction(this.name, this.book, this.chapter);
|
|
281
|
+
UpdateCurrentBibleAction(this.router, this.name, this.book, this.chapter);
|
|
253
282
|
|
|
254
283
|
@override
|
|
255
284
|
Future<AppState> reduce() async {
|
|
256
285
|
final bible = await loadBible(name);
|
|
257
286
|
final (newBook, newChapter) = clampBookChapter(bible, book, chapter);
|
|
287
|
+
// Keep the route in sync with the clamped book/chapter: otherwise the
|
|
288
|
+
// URL keeps pointing at the pre-switch (possibly now out-of-range)
|
|
289
|
+
// chapter, and a later App._syncCurrentChapter dispatch would clobber
|
|
290
|
+
// savedBook/savedChapter back to those stale path segments.
|
|
291
|
+
router.pushReplacement("/chapter/$newBook/$newChapter");
|
|
258
292
|
return state.copy(
|
|
259
293
|
bible: bible,
|
|
260
294
|
savedBook: newBook,
|
macos/Runner/Info.plist
CHANGED
|
@@ -34,10 +34,6 @@
|
|
|
34
34
|
<string>NSApplication</string>
|
|
35
35
|
<key>NSPhotoLibraryUsageDescription</key>
|
|
36
36
|
<string>Only Bible App uses the system file picker to let you choose a backup file to restore your highlights and settings. Depending on your device, this picker may show your photo library, though Only Bible App does not access your photos directly.</string>
|
|
37
|
-
<key>NSCameraUsageDescription</key>
|
|
38
|
-
<string>Only Bible App does not use the camera. This description is required because a bundled library references camera APIs, even though this app does not use them.</string>
|
|
39
|
-
<key>NSLocationWhenInUseUsageDescription</key>
|
|
40
|
-
<string>Only Bible App does not use your location. This description is required because a bundled library references location APIs, even though this app does not use them.</string>
|
|
41
37
|
<key>NSAppleMusicUsageDescription</key>
|
|
42
38
|
<string>Only Bible App does not access your music library. This description is required because a bundled library references media library APIs, even though this app does not use them.</string>
|
|
43
39
|
</dict>
|
pubspec.yaml
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
name: only_bible_app
|
|
2
2
|
description: The only bible app you will ever need
|
|
3
3
|
publish_to: 'none'
|
|
4
|
-
version: 3.6.
|
|
4
|
+
version: 3.6.1+20
|
|
5
|
-
resolution: workspace
|
|
6
5
|
|
|
7
6
|
environment:
|
|
8
7
|
sdk: '^3.12.2'
|
|
@@ -25,7 +24,7 @@ dependencies:
|
|
|
25
24
|
file_picker: ^11.0.2
|
|
26
25
|
flutter_azure_tts: ^1.0.0
|
|
27
26
|
shared:
|
|
28
|
-
path: ../
|
|
27
|
+
path: ../r2-backed-apps/packages/shared
|
|
29
28
|
|
|
30
29
|
dev_dependencies:
|
|
31
30
|
flutter_test:
|
test/actions_backup_test.dart
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "dart:convert";
|
|
2
2
|
|
|
3
|
+
import "package:flutter/material.dart";
|
|
3
4
|
import "package:flutter_test/flutter_test.dart";
|
|
4
5
|
import "package:only_bible_app/store/actions_backup.dart";
|
|
5
6
|
import "package:only_bible_app/store/app_state.dart";
|
|
@@ -21,7 +22,7 @@ void main() {
|
|
|
21
22
|
final bible = buildTestBible();
|
|
22
23
|
final state = AppState(
|
|
23
24
|
bible: bible,
|
|
24
|
-
|
|
25
|
+
themeMode: ThemeMode.dark,
|
|
25
26
|
fontSize: 22,
|
|
26
27
|
highlights: {"0:0:0": 2},
|
|
27
28
|
highlightHistory: [
|
|
@@ -32,7 +33,7 @@ void main() {
|
|
|
32
33
|
final jsonString = buildBackupJson(state);
|
|
33
34
|
final restored = await parseBackupState(jsonString, loadBibleFn: (_) async => bible);
|
|
34
35
|
|
|
35
|
-
expect(restored.
|
|
36
|
+
expect(restored.themeMode, ThemeMode.dark);
|
|
36
37
|
expect(restored.fontSize, 22);
|
|
37
38
|
expect(restored.highlights["0:0:0"], 2);
|
|
38
39
|
expect(restored.highlightHistory.single.colorIndex, 2);
|
test/app_logic_test.dart
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "dart:io";
|
|
2
2
|
|
|
3
3
|
import "package:async_redux/async_redux.dart";
|
|
4
|
+
import "package:flutter/material.dart";
|
|
4
5
|
import "package:flutter_test/flutter_test.dart";
|
|
5
6
|
import "package:path_provider_platform_interface/path_provider_platform_interface.dart";
|
|
6
7
|
import "package:only_bible_app/env.dart";
|
|
@@ -183,7 +184,7 @@ void main() {
|
|
|
183
184
|
savedBook: 1,
|
|
184
185
|
savedChapter: 0,
|
|
185
186
|
fontSize: 20,
|
|
186
|
-
|
|
187
|
+
themeMode: ThemeMode.dark,
|
|
187
188
|
highlightHistory: [
|
|
188
189
|
HighlightHistoryEntry(book: 0, chapter: 0, verseIndex: 0, colorIndex: 1, timestamp: DateTime.utc(2026, 1, 1)),
|
|
189
190
|
],
|
|
@@ -192,7 +193,7 @@ void main() {
|
|
|
192
193
|
expect(restored.savedBook, 1);
|
|
193
194
|
expect(restored.savedChapter, 0);
|
|
194
195
|
expect(restored.fontSize, 20);
|
|
195
|
-
expect(restored.
|
|
196
|
+
expect(restored.themeMode, ThemeMode.dark);
|
|
196
197
|
expect(restored.highlightHistory.single.book, 0);
|
|
197
198
|
});
|
|
198
199
|
|
|
@@ -208,7 +209,7 @@ void main() {
|
|
|
208
209
|
final state = AppState.fromJson({}, bible);
|
|
209
210
|
expect(state.savedBook, 0);
|
|
210
211
|
expect(state.savedChapter, 0);
|
|
211
|
-
expect(state.
|
|
212
|
+
expect(state.themeMode, ThemeMode.system);
|
|
212
213
|
expect(state.engTitles, false);
|
|
213
214
|
});
|
|
214
215
|
});
|
|
@@ -329,8 +330,8 @@ void main() {
|
|
|
329
330
|
});
|
|
330
331
|
|
|
331
332
|
group("theme", () {
|
|
332
|
-
test("dark theme labelMedium uses the dark
|
|
333
|
+
test("dark theme labelMedium (verse numbers) uses the dark error accent, matching the light theme's dedicated red", () {
|
|
333
|
-
expect(AppTheme.dark.textTheme.labelMedium!.color, darkColorScheme.
|
|
334
|
+
expect(AppTheme.dark.textTheme.labelMedium!.color, darkColorScheme.error);
|
|
334
335
|
});
|
|
335
336
|
});
|
|
336
337
|
|
|
@@ -356,14 +357,14 @@ void main() {
|
|
|
356
357
|
test("persistDifference writes atomically, leaving no leftover temp file", () async {
|
|
357
358
|
final bible = buildTestBible();
|
|
358
359
|
final persistor = AppPersistor();
|
|
359
|
-
final state = AppState(bible: bible, savedBook: 1, savedChapter: 0,
|
|
360
|
+
final state = AppState(bible: bible, savedBook: 1, savedChapter: 0, themeMode: ThemeMode.dark);
|
|
360
361
|
|
|
361
362
|
await persistor.persistDifference(lastPersistedState: null, newState: state);
|
|
362
363
|
|
|
363
364
|
final json = await persistor.readJson();
|
|
364
365
|
expect(json, isNotNull);
|
|
365
366
|
expect(json!["savedBook"], 1);
|
|
366
|
-
expect(json["
|
|
367
|
+
expect(json["themeMode"], "dark");
|
|
367
368
|
expect(await File("${tempDir.path}/app_state.json.tmp").exists(), isFalse);
|
|
368
369
|
});
|
|
369
370
|
|