only-bible-app v3.7.2+24

#kotlin#android#ios

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.


1b816bePeter John 2026-08-09T11:09:22+05:30
fix various bugs
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 = 8;
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 = 8;
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 = 8;
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 = 8;
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 = 8;
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 = 8;
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>8</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(this.router, this.name, this.book, this.chapter);
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 loadBible(name);
313
+ final bible = await loadBibleFn(name);
286
314
  final (newBook, newChapter) = clampBookChapter(bible, book, chapter);
287
- // Keep the route in sync with the clamped book/chapter: otherwise the
315
+ // A translation switch has no meaningful "back" destination, so reset
316
+ // navigation entirely rather than pushReplacement-ing: pushReplacement
288
- // URL keeps pointing at the pre-switch (possibly now out-of-range)
317
+ // only swaps the top of the stack, leaving any earlier GoToChapterAction
289
- // chapter, and a later App._syncCurrentChapter dispatch would clobber
318
+ // push (e.g. from the book/chapter picker) sitting underneath, poppable -
290
- // savedBook/savedChapter back to those stale path segments.
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.pushReplacement("/chapter/$newBook/$newChapter");
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 = "$bookName $chapter:$versesThrough $version";
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.000187">
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="0.000122">
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="0.000138">
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="7.1e-05">
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.003265">
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.472711">
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.169831">
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.869459">
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 ../.. &amp;&amp; flutter build macos --config-only" time="7.146192">
48
+ <testcase classname="fastlane.lanes" name="08: cd ../.. &amp;&amp; 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="86.815746">
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="0.000101">
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.00169">
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="481.245784">
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",