~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 only-bible-app
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.


Files changed (3) hide show
  1. lib/app.dart +15 -1
  2. lib/home.dart +21 -80
  3. lib/store/actions_navigation.dart +39 -7
lib/app.dart CHANGED
@@ -4,6 +4,7 @@ import "package:go_router/go_router.dart";
4
4
  import "package:only_bible_app/home.dart";
5
5
  import "package:only_bible_app/store/app_navigator.dart";
6
6
  import "package:only_bible_app/store/app_state.dart";
7
+ import "package:only_bible_app/store/actions_navigation.dart";
7
8
  import "package:only_bible_app/theme.dart";
8
9
 
9
10
  class App extends StatelessWidget {
@@ -25,7 +26,7 @@ class App extends StatelessWidget {
25
26
  final extra = state.extra as ({TextDirection? slideDir, int? scrollToVerse, int navId})?;
26
27
  final slideDir = extra?.slideDir;
27
28
  final scrollToVerse = extra?.scrollToVerse;
28
- final pageKey = ValueKey('$bookIndex-$chapterIndex-${extra?.navId ?? 0}');
29
+ final pageKey = ValueKey("$bookIndex-$chapterIndex-${extra?.navId ?? 0}");
29
30
  if (slideDir != null) {
30
31
  return CustomTransitionPage(
31
32
  key: pageKey,
@@ -51,6 +52,19 @@ class App extends StatelessWidget {
51
52
  ),
52
53
  ],
53
54
  );
55
+ _router.routerDelegate.addListener(_syncCurrentChapter);
56
+ _syncCurrentChapter();
57
+ }
58
+
59
+ void _syncCurrentChapter() {
60
+ final segments = _router.routerDelegate.currentConfiguration.uri.pathSegments;
61
+ if (segments.length != 3 || segments.first != "chapter") return;
62
+
63
+ final book = int.tryParse(segments[1]);
64
+ final chapter = int.tryParse(segments[2]);
65
+ if (book == null || chapter == null) return;
66
+
67
+ store.dispatch(SyncCurrentChapterAction(book, chapter));
54
68
  }
55
69
 
56
70
  @override
lib/home.dart CHANGED
@@ -2,7 +2,6 @@ import "package:flutter/gestures.dart";
2
2
  import "package:flutter/material.dart";
3
3
  import "package:only_bible_app/gen/bible.gen.dart";
4
4
  import "package:only_bible_app/store/app_state.dart";
5
- import "package:only_bible_app/theme.dart";
6
5
  import "package:only_bible_app/widgets/home_app_bar.dart";
7
6
  import "package:only_bible_app/store/actions_navigation.dart";
8
7
  import "package:only_bible_app/store/app_navigator.dart";
@@ -156,7 +155,6 @@ class _HomeState extends State<Home> {
156
155
  appState: appState,
157
156
  buildHeadingSpans: _buildHeadingSpans,
158
157
  fontSize: fontSize,
159
- highlight: widget.scrollToVerse == v.index,
160
158
  ),
161
159
  ),
162
160
  ),
@@ -225,7 +223,7 @@ class _ChapterSwipeDetectorState extends State<_ChapterSwipeDetector> {
225
223
  }
226
224
  }
227
225
 
228
- class _VerseText extends StatefulWidget {
226
+ class _VerseText extends StatelessWidget {
229
227
  final Verse verse;
230
228
  final Bible bible;
231
229
  final TextStyle baseStyle;
@@ -233,7 +231,6 @@ class _VerseText extends StatefulWidget {
233
231
  final AppState appState;
234
232
  final List<InlineSpan> Function(BuildContext, Bible, String, List<Reference>, TextStyle, double) buildHeadingSpans;
235
233
  final double fontSize;
236
- final bool highlight;
237
234
 
238
235
  const _VerseText({
239
236
  required this.verse,
@@ -243,48 +240,13 @@ class _VerseText extends StatefulWidget {
243
240
  required this.appState,
244
241
  required this.buildHeadingSpans,
245
242
  required this.fontSize,
246
- this.highlight = false,
247
243
  });
248
244
 
249
- @override
250
- State<_VerseText> createState() => _VerseTextState();
251
- }
252
-
253
- class _VerseTextState extends State<_VerseText> with SingleTickerProviderStateMixin {
254
- late final AnimationController _controller;
255
-
256
- @override
257
- void initState() {
258
- super.initState();
259
- _controller = AnimationController(
260
- vsync: this,
261
- duration: const Duration(milliseconds: 400),
262
- reverseDuration: const Duration(milliseconds: 500),
263
- );
264
- if (widget.highlight) {
265
- WidgetsBinding.instance.addPostFrameCallback((_) {
266
- _controller.forward().then((_) {
267
- Future.delayed(const Duration(milliseconds: 600), () {
268
- if (mounted) _controller.reverse();
269
- });
270
- });
271
- });
272
- }
273
- }
274
-
275
- @override
276
- void dispose() {
277
- _controller.dispose();
278
- super.dispose();
279
- }
280
-
281
245
  static final _redTagPattern = RegExp(r"<red>(.*?)</red>");
282
246
 
283
247
  @override
284
248
  Widget build(BuildContext context) {
285
- final v = widget.verse;
249
+ final v = verse;
286
- final theme = widget.theme;
287
- final appState = widget.appState;
288
250
 
289
251
  List<TextSpan> buildVerseTextSpans(String text, TextStyle style) {
290
252
  final spans = <TextSpan>[];
@@ -303,47 +265,26 @@ class _VerseTextState extends State<_VerseText> with SingleTickerProviderStateMi
303
265
  return spans;
304
266
  }
305
267
 
306
- Widget buildText(Color? bgColor) {
307
- return Text.rich(
268
+ return Text.rich(
308
- TextSpan(
269
+ TextSpan(
309
- style: widget.baseStyle,
270
+ style: baseStyle,
310
- children: [
271
+ children: [
311
- if (v.heading != null && v.heading!.isNotEmpty)
272
+ if (v.heading != null && v.heading!.isNotEmpty)
312
- ...widget.buildHeadingSpans(
273
+ ...buildHeadingSpans(
313
- context,
274
+ context,
314
- widget.bible,
275
+ bible,
315
- v.heading!,
276
+ v.heading!,
316
- v.headingReferences ?? [],
277
+ v.headingReferences ?? [],
317
- theme.labelLarge!,
278
+ theme.labelLarge!,
318
- widget.fontSize,
279
+ fontSize,
319
- ),
320
- TextSpan(
321
- text: "${v.index + 1} ",
322
- style: theme.labelMedium!.copyWith(
323
- color: const Color(0xFF9A1111),
324
- backgroundColor: bgColor,
325
- ),
326
280
  ),
327
- ...buildVerseTextSpans(
281
+ TextSpan(
328
- v.text ?? "",
282
+ text: "${v.index + 1} ",
329
- bgColor != null
330
- ? appState.getHighlightStyle(context, v).copyWith(backgroundColor: bgColor)
283
+ style: theme.labelMedium!.copyWith(color: const Color(0xFF9A1111)),
331
- : appState.getHighlightStyle(context, v),
332
- ),
284
+ ),
285
+ ...buildVerseTextSpans(v.text ?? "", appState.getHighlightStyle(context, v)),
333
- ],
286
+ ],
334
- ),
287
+ ),
335
- );
336
- }
337
-
338
- if (!widget.highlight) return buildText(null);
339
-
340
- return AnimatedBuilder(
341
- animation: _controller,
342
- builder: (context, _) {
343
- final highlightColor = appState.darkMode ? darkHighlights[1] : lightHighlights[1];
344
- final bgColor = highlightColor.withValues(alpha: _controller.value);
345
- return buildText(bgColor);
346
- },
347
288
  );
348
289
  }
349
290
  }
lib/store/actions_navigation.dart CHANGED
@@ -10,6 +10,8 @@ import "package:only_bible_app/widgets/book_select_sheet.dart";
10
10
  import "package:only_bible_app/widgets/settings_sheet.dart";
11
11
  import "package:share_plus/share_plus.dart";
12
12
 
13
+ String _stripRedTags(String text) => text.replaceAll(RegExp(r"</?red>"), "");
14
+
13
15
  class ShowSettingsAction extends ReduxAction<AppState> {
14
16
  final BuildContext buildContext;
15
17
  final Bible bible;
@@ -77,7 +79,12 @@ class ShowChapterSelectAction extends ReduxAction<AppState> {
77
79
  final Bible bible;
78
80
  final Book book;
79
81
 
80
- ShowChapterSelectAction(this.buildContext, this.router, this.bible, this.book);
82
+ ShowChapterSelectAction(
83
+ this.buildContext,
84
+ this.router,
85
+ this.bible,
86
+ this.book,
87
+ );
81
88
 
82
89
  @override
83
90
  AppState? reduce() {
@@ -116,12 +123,20 @@ class GoToChapterAction extends ReduxAction<AppState> {
116
123
  SoLoud.instance.disposeAllSources();
117
124
  final navId = DateTime.now().millisecondsSinceEpoch;
118
125
  if (book > state.savedBook || (book == state.savedBook && chapter > state.savedChapter)) {
126
+ router.push(
127
+ "/chapter/$book/$chapter",
119
- router.push("/chapter/$book/$chapter", extra: (slideDir: TextDirection.ltr, scrollToVerse: verse, navId: navId));
128
+ extra: (slideDir: TextDirection.ltr, scrollToVerse: verse, navId: navId),
129
+ );
120
130
  } else if (book < state.savedBook || (book == state.savedBook && chapter < state.savedChapter)) {
131
+ router.push(
132
+ "/chapter/$book/$chapter",
121
- router.push("/chapter/$book/$chapter", extra: (slideDir: TextDirection.rtl, scrollToVerse: verse, navId: navId));
133
+ extra: (slideDir: TextDirection.rtl, scrollToVerse: verse, navId: navId),
134
+ );
122
135
  } else {
136
+ router.push(
123
- router.push("/chapter/$book/$chapter",
137
+ "/chapter/$book/$chapter",
124
- extra: (slideDir: null as TextDirection?, scrollToVerse: verse, navId: navId));
138
+ extra: (slideDir: null as TextDirection?, scrollToVerse: verse, navId: navId),
139
+ );
125
140
  }
126
141
  return state.copy(
127
142
  savedBook: book,
@@ -131,6 +146,23 @@ class GoToChapterAction extends ReduxAction<AppState> {
131
146
  }
132
147
  }
133
148
 
149
+ class SyncCurrentChapterAction extends ReduxAction<AppState> {
150
+ final int book;
151
+ final int chapter;
152
+
153
+ SyncCurrentChapterAction(this.book, this.chapter);
154
+
155
+ @override
156
+ AppState? reduce() {
157
+ if (book == state.savedBook && chapter == state.savedChapter) return null;
158
+ return state.copy(
159
+ savedBook: book,
160
+ savedChapter: chapter,
161
+ selectedVerses: [],
162
+ );
163
+ }
164
+ }
165
+
134
166
  class NextChapterAction extends ReduxAction<AppState> {
135
167
  final GoRouter router;
136
168
  final Bible bible;
@@ -235,9 +267,9 @@ class ShareVersesAction extends ReduxAction<AppState> {
235
267
  final chapter = verses.first.chapter + 1;
236
268
  final items = verses.sortedBy((e) => e.index).map((e) => e.index + 1);
237
269
  final versesThrough = items.length >= 3 ? "${items.first}-${items.last}" : items.join(",");
238
- final version = state.bible.languageCode == "en" ? "KJV" : "";
270
+ final version = state.bible.languageCode == "en" ? state.bible.languageNative : "";
239
271
  final title = "$bookName $chapter:$versesThrough $version";
240
- final text = verses.map((e) => e.text ?? "").join("\n");
272
+ final text = verses.map((e) => _stripRedTags(e.text ?? "")).join("\n");
241
273
  await SharePlus.instance.share(
242
274
  ShareParams(title: title, subject: title, text: "$title\n$text"),
243
275
  );