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.
1b816be
— Peter John
2026-08-09T11:09:22+05:30
fix various bugs
- ios/Runner.xcodeproj/project.pbxproj +6 -6
- ios/Runner/Info.plist +1 -1
- lib/store/actions_navigation.dart +42 -17
- macos/fastlane/report.xml +13 -13
- test/app_logic_test.dart +65 -0
ios/Runner.xcodeproj/project.pbxproj
CHANGED
|
@@ -393,7 +393,7 @@
|
|
|
393
393
|
CLANG_ENABLE_MODULES = YES;
|
|
394
394
|
CODE_SIGN_IDENTITY = "Apple Development";
|
|
395
395
|
CODE_SIGN_STYLE = Automatic;
|
|
396
|
-
CURRENT_PROJECT_VERSION =
|
|
396
|
+
CURRENT_PROJECT_VERSION = 9;
|
|
397
397
|
DEVELOPMENT_TEAM = FXH8NUGFYY;
|
|
398
398
|
ENABLE_BITCODE = NO;
|
|
399
399
|
INFOPLIST_FILE = Runner/Info.plist;
|
|
@@ -415,7 +415,7 @@
|
|
|
415
415
|
buildSettings = {
|
|
416
416
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
417
417
|
CODE_SIGN_STYLE = Automatic;
|
|
418
|
-
CURRENT_PROJECT_VERSION =
|
|
418
|
+
CURRENT_PROJECT_VERSION = 9;
|
|
419
419
|
GENERATE_INFOPLIST_FILE = YES;
|
|
420
420
|
MARKETING_VERSION = 1.0;
|
|
421
421
|
PRODUCT_BUNDLE_IDENTIFIER = "sh.pyros.only-bible-app.RunnerTests";
|
|
@@ -432,7 +432,7 @@
|
|
|
432
432
|
buildSettings = {
|
|
433
433
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
434
434
|
CODE_SIGN_STYLE = Automatic;
|
|
435
|
-
CURRENT_PROJECT_VERSION =
|
|
435
|
+
CURRENT_PROJECT_VERSION = 9;
|
|
436
436
|
GENERATE_INFOPLIST_FILE = YES;
|
|
437
437
|
MARKETING_VERSION = 1.0;
|
|
438
438
|
PRODUCT_BUNDLE_IDENTIFIER = "sh.pyros.only-bible-app.RunnerTests";
|
|
@@ -447,7 +447,7 @@
|
|
|
447
447
|
buildSettings = {
|
|
448
448
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
449
449
|
CODE_SIGN_STYLE = Automatic;
|
|
450
|
-
CURRENT_PROJECT_VERSION =
|
|
450
|
+
CURRENT_PROJECT_VERSION = 9;
|
|
451
451
|
GENERATE_INFOPLIST_FILE = YES;
|
|
452
452
|
MARKETING_VERSION = 1.0;
|
|
453
453
|
PRODUCT_BUNDLE_IDENTIFIER = "sh.pyros.only-bible-app.RunnerTests";
|
|
@@ -576,7 +576,7 @@
|
|
|
576
576
|
CLANG_ENABLE_MODULES = YES;
|
|
577
577
|
CODE_SIGN_IDENTITY = "Apple Development";
|
|
578
578
|
CODE_SIGN_STYLE = Automatic;
|
|
579
|
-
CURRENT_PROJECT_VERSION =
|
|
579
|
+
CURRENT_PROJECT_VERSION = 9;
|
|
580
580
|
DEVELOPMENT_TEAM = FXH8NUGFYY;
|
|
581
581
|
ENABLE_BITCODE = NO;
|
|
582
582
|
INFOPLIST_FILE = Runner/Info.plist;
|
|
@@ -603,7 +603,7 @@
|
|
|
603
603
|
CODE_SIGN_IDENTITY = "Apple Development";
|
|
604
604
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
|
605
605
|
CODE_SIGN_STYLE = Manual;
|
|
606
|
-
CURRENT_PROJECT_VERSION =
|
|
606
|
+
CURRENT_PROJECT_VERSION = 9;
|
|
607
607
|
DEVELOPMENT_TEAM = "";
|
|
608
608
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = FXH8NUGFYY;
|
|
609
609
|
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>9</string>
|
|
27
27
|
<key>LSRequiresIPhoneOS</key>
|
|
28
28
|
<true/>
|
|
29
29
|
<key>UIApplicationSceneManifest</key>
|
lib/store/actions_navigation.dart
CHANGED
|
@@ -13,6 +13,27 @@ import "package:share_plus/share_plus.dart";
|
|
|
13
13
|
|
|
14
14
|
String _stripRedTags(String text) => text.replaceAll(RegExp(r"</?red>"), "");
|
|
15
15
|
|
|
16
|
+
/// Builds the share title (e.g. "John 3:1-2 King James Version") and body
|
|
17
|
+
/// text for [verses], sorted by verse index regardless of the order they
|
|
18
|
+
/// were selected/tapped in - otherwise selecting verse 2 then verse 1 would
|
|
19
|
+
/// show a correctly-ordered "1,2" title but the verse 2 text before verse 1's.
|
|
20
|
+
(String title, String text) buildShareContent(
|
|
21
|
+
List<Verse> verses,
|
|
22
|
+
String bookName,
|
|
23
|
+
Bible bible,
|
|
24
|
+
) {
|
|
25
|
+
final sortedVerses = verses.sortedBy((e) => e.index).toList();
|
|
26
|
+
final chapter = sortedVerses.first.chapter + 1;
|
|
27
|
+
final items = sortedVerses.map((e) => e.index + 1);
|
|
28
|
+
final versesThrough = items.length >= 3
|
|
29
|
+
? "${items.first}-${items.last}"
|
|
30
|
+
: items.join(",");
|
|
31
|
+
final version = bible.languageCode == "en" ? bible.languageNative : "";
|
|
32
|
+
final title = "$bookName $chapter:$versesThrough $version";
|
|
33
|
+
final text = sortedVerses.map((e) => _stripRedTags(e.text ?? "")).join("\n");
|
|
34
|
+
return (title, text);
|
|
35
|
+
}
|
|
36
|
+
|
|
16
37
|
class ShowHighlightHistoryAction extends ReduxAction<AppState> {
|
|
17
38
|
final BuildContext buildContext;
|
|
18
39
|
final Bible bible;
|
|
@@ -231,7 +252,7 @@ class NextChapterAction extends ReduxAction<AppState> {
|
|
|
231
252
|
extra: (
|
|
232
253
|
slideDir: TextDirection.ltr,
|
|
233
254
|
scrollToVerse: null as int?,
|
|
234
|
-
navId: DateTime.now().millisecondsSinceEpoch
|
|
255
|
+
navId: DateTime.now().millisecondsSinceEpoch,
|
|
235
256
|
),
|
|
236
257
|
);
|
|
237
258
|
return state.copy(
|
|
@@ -261,7 +282,7 @@ class PreviousChapterAction extends ReduxAction<AppState> {
|
|
|
261
282
|
extra: (
|
|
262
283
|
slideDir: TextDirection.rtl,
|
|
263
284
|
scrollToVerse: null as int?,
|
|
264
|
-
navId: DateTime.now().millisecondsSinceEpoch
|
|
285
|
+
navId: DateTime.now().millisecondsSinceEpoch,
|
|
265
286
|
),
|
|
266
287
|
);
|
|
267
288
|
return state.copy(
|
|
@@ -277,18 +298,29 @@ class UpdateCurrentBibleAction extends ReduxAction<AppState> {
|
|
|
277
298
|
final String name;
|
|
278
299
|
final int book;
|
|
279
300
|
final int chapter;
|
|
301
|
+
final Future<Bible> Function(String) loadBibleFn;
|
|
280
302
|
|
|
281
|
-
UpdateCurrentBibleAction(
|
|
303
|
+
UpdateCurrentBibleAction(
|
|
304
|
+
this.router,
|
|
305
|
+
this.name,
|
|
306
|
+
this.book,
|
|
307
|
+
this.chapter, {
|
|
308
|
+
this.loadBibleFn = loadBible,
|
|
309
|
+
});
|
|
282
310
|
|
|
283
311
|
@override
|
|
284
312
|
Future<AppState> reduce() async {
|
|
285
|
-
final bible = await
|
|
313
|
+
final bible = await loadBibleFn(name);
|
|
286
314
|
final (newBook, newChapter) = clampBookChapter(bible, book, chapter);
|
|
287
|
-
//
|
|
315
|
+
// A translation switch has no meaningful "back" destination, so reset
|
|
316
|
+
// navigation entirely rather than pushReplacement-ing: pushReplacement
|
|
288
|
-
//
|
|
317
|
+
// only swaps the top of the stack, leaving any earlier GoToChapterAction
|
|
289
|
-
//
|
|
318
|
+
// push (e.g. from the book/chapter picker) sitting underneath, poppable -
|
|
290
|
-
//
|
|
319
|
+
// the next edge-swipe-right would then treat that stale leftover entry
|
|
320
|
+
// as "go back" instead of "previous chapter", landing back on the
|
|
321
|
+
// pre-switch chapter. go() discards the whole imperative history instead.
|
|
322
|
+
_chapterHistory.clear();
|
|
291
|
-
router.
|
|
323
|
+
router.go("/chapter/$newBook/$newChapter");
|
|
292
324
|
return state.copy(
|
|
293
325
|
bible: bible,
|
|
294
326
|
savedBook: newBook,
|
|
@@ -306,14 +338,7 @@ class ShareVersesAction extends ReduxAction<AppState> {
|
|
|
306
338
|
|
|
307
339
|
@override
|
|
308
340
|
Future<AppState?> reduce() async {
|
|
309
|
-
final chapter = verses.first.chapter + 1;
|
|
310
|
-
final items = verses.sortedBy((e) => e.index).map((e) => e.index + 1);
|
|
311
|
-
final versesThrough =
|
|
312
|
-
items.length >= 3 ? "${items.first}-${items.last}" : items.join(",");
|
|
313
|
-
final version =
|
|
314
|
-
state.bible.languageCode == "en" ? state.bible.languageNative : "";
|
|
315
|
-
final title =
|
|
341
|
+
final (title, text) = buildShareContent(verses, bookName, state.bible);
|
|
316
|
-
final text = verses.map((e) => _stripRedTags(e.text ?? "")).join("\n");
|
|
317
342
|
await SharePlus.instance.share(
|
|
318
343
|
ShareParams(title: title, subject: title, text: "$title\n$text"),
|
|
319
344
|
);
|
macos/fastlane/report.xml
CHANGED
|
@@ -5,67 +5,67 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
<testcase classname="fastlane.lanes" name="00: default_platform" time="0.
|
|
8
|
+
<testcase classname="fastlane.lanes" name="00: default_platform" time="0.000176">
|
|
9
9
|
|
|
10
10
|
</testcase>
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
<testcase classname="fastlane.lanes" name="01: Switch to mac build lane" time="
|
|
13
|
+
<testcase classname="fastlane.lanes" name="01: Switch to mac build lane" time="7.9e-05">
|
|
14
14
|
|
|
15
15
|
</testcase>
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
<testcase classname="fastlane.lanes" name="02: Switch to mac certs lane" time="
|
|
18
|
+
<testcase classname="fastlane.lanes" name="02: Switch to mac certs lane" time="6.0e-05">
|
|
19
19
|
|
|
20
20
|
</testcase>
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
<testcase classname="fastlane.lanes" name="03: Switch to mac api_key lane" time="
|
|
23
|
+
<testcase classname="fastlane.lanes" name="03: Switch to mac api_key lane" time="6.2e-05">
|
|
24
24
|
|
|
25
25
|
</testcase>
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
<testcase classname="fastlane.lanes" name="04: app_store_connect_api_key" time="0.
|
|
28
|
+
<testcase classname="fastlane.lanes" name="04: app_store_connect_api_key" time="0.002017">
|
|
29
29
|
|
|
30
30
|
</testcase>
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
<testcase classname="fastlane.lanes" name="05: cert" time="1.
|
|
33
|
+
<testcase classname="fastlane.lanes" name="05: cert" time="1.504193">
|
|
34
34
|
|
|
35
35
|
</testcase>
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
<testcase classname="fastlane.lanes" name="06: sigh" time="1.
|
|
38
|
+
<testcase classname="fastlane.lanes" name="06: sigh" time="1.25945">
|
|
39
39
|
|
|
40
40
|
</testcase>
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
<testcase classname="fastlane.lanes" name="07: cert" time="1.
|
|
43
|
+
<testcase classname="fastlane.lanes" name="07: cert" time="1.440071">
|
|
44
44
|
|
|
45
45
|
</testcase>
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
<testcase classname="fastlane.lanes" name="08: cd ../.. && flutter build macos --config-only" time="
|
|
48
|
+
<testcase classname="fastlane.lanes" name="08: cd ../.. && flutter build macos --config-only" time="8.49617">
|
|
49
49
|
|
|
50
50
|
</testcase>
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
<testcase classname="fastlane.lanes" name="09: build_mac_app" time="
|
|
53
|
+
<testcase classname="fastlane.lanes" name="09: build_mac_app" time="88.046487">
|
|
54
54
|
|
|
55
55
|
</testcase>
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
<testcase classname="fastlane.lanes" name="10: Switch to mac api_key lane" time="
|
|
58
|
+
<testcase classname="fastlane.lanes" name="10: Switch to mac api_key lane" time="9.2e-05">
|
|
59
59
|
|
|
60
60
|
</testcase>
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
<testcase classname="fastlane.lanes" name="11: app_store_connect_api_key" time="0.
|
|
63
|
+
<testcase classname="fastlane.lanes" name="11: app_store_connect_api_key" time="0.001318">
|
|
64
64
|
|
|
65
65
|
</testcase>
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
<testcase classname="fastlane.lanes" name="12: upload_to_app_store" time="
|
|
68
|
+
<testcase classname="fastlane.lanes" name="12: upload_to_app_store" time="157.083489">
|
|
69
69
|
|
|
70
70
|
</testcase>
|
|
71
71
|
|
test/app_logic_test.dart
CHANGED
|
@@ -503,6 +503,71 @@ void main() {
|
|
|
503
503
|
});
|
|
504
504
|
});
|
|
505
505
|
|
|
506
|
+
group("UpdateCurrentBibleAction", () {
|
|
507
|
+
testWidgets(
|
|
508
|
+
"switching translation after navigating to another chapter doesn't leave a stale "
|
|
509
|
+
"chapter poppable underneath (regression: switching Bible used to revert to it)",
|
|
510
|
+
(tester) async {
|
|
511
|
+
final bible = buildTestBible();
|
|
512
|
+
final store = Store<AppState>(
|
|
513
|
+
initialState: AppState(bible: bible, savedBook: 0, savedChapter: 0),
|
|
514
|
+
);
|
|
515
|
+
final router = buildTestRouter();
|
|
516
|
+
await tester.pumpWidget(MaterialApp.router(routerConfig: router));
|
|
517
|
+
|
|
518
|
+
// Navigate to another chapter the way the book/chapter picker does
|
|
519
|
+
// (push, not pushReplacement) - this is what used to leave a stale
|
|
520
|
+
// entry behind after a bible switch.
|
|
521
|
+
await store.dispatchAndWait(GoToChapterAction(router, 2, 1));
|
|
522
|
+
await tester.pumpAndSettle();
|
|
523
|
+
expect(store.state.savedBook, 2);
|
|
524
|
+
expect(store.state.savedChapter, 1);
|
|
525
|
+
|
|
526
|
+
await store.dispatchAndWait(
|
|
527
|
+
UpdateCurrentBibleAction(
|
|
528
|
+
router,
|
|
529
|
+
"other_bible",
|
|
530
|
+
store.state.savedBook,
|
|
531
|
+
store.state.savedChapter,
|
|
532
|
+
loadBibleFn: (_) async => buildTestBible(),
|
|
533
|
+
),
|
|
534
|
+
);
|
|
535
|
+
await tester.pumpAndSettle();
|
|
536
|
+
expect(store.state.savedBook, 2);
|
|
537
|
+
expect(store.state.savedChapter, 1);
|
|
538
|
+
|
|
539
|
+
// The pre-switch chapter must not still be reachable via "back" -
|
|
540
|
+
// otherwise the next edge-swipe-right treats it as pop-to-previous
|
|
541
|
+
// instead of previous-chapter, landing back on the stale position.
|
|
542
|
+
expect(router.canPop(), isFalse);
|
|
543
|
+
expect(router.routerDelegate.currentConfiguration.matches.length, 1);
|
|
544
|
+
},
|
|
545
|
+
);
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
group("buildShareContent", () {
|
|
549
|
+
test(
|
|
550
|
+
"sorts verses by index for both the title and the text, regardless of selection order",
|
|
551
|
+
() {
|
|
552
|
+
final bible = buildTestBible();
|
|
553
|
+
final chapter = bible.bookAt(0)!.chapterAt(0)!;
|
|
554
|
+
final verse0 = chapter.verseAt(0)!; // "In the beginning..."
|
|
555
|
+
final verse1 = chapter.verseAt(1)!; // "And the earth..."
|
|
556
|
+
|
|
557
|
+
// Selected out of order: verse 2 (index 1) tapped before verse 1
|
|
558
|
+
// (index 0) - the exact repro for the reported bug.
|
|
559
|
+
final (title, text) = buildShareContent(
|
|
560
|
+
[verse1, verse0],
|
|
561
|
+
"Genesis",
|
|
562
|
+
bible,
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
expect(title, "Genesis 1:1,2 English");
|
|
566
|
+
expect(text, "In the beginning...\nAnd the earth...");
|
|
567
|
+
},
|
|
568
|
+
);
|
|
569
|
+
});
|
|
570
|
+
|
|
506
571
|
group("theme", () {
|
|
507
572
|
test(
|
|
508
573
|
"dark theme labelMedium (verse numbers) uses the dark error accent, matching the light theme's dedicated red",
|