Skip to content

Commit 0a13913

Browse files
First pass at implementing bookmarks
- Switch to using chapterNumber and verseNumber instead of sectionNumber - Fixed jumping to id
1 parent 5dbd05e commit 0a13913

15 files changed

Lines changed: 364 additions & 130 deletions

File tree

lib/common/alphanum.dart

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// This code is from https://github.com/ftognetto/alphanum_comparator/
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you
4+
// may not use this file except in compliance with the License. You may
5+
// obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12+
// implied. See the License for the specific language governing permissions
13+
// and limitations under the License.
14+
15+
class AlphanumComparator {
16+
static bool _isDigit(String ch) {
17+
return (ch.codeUnitAt(0) >= 48) && (ch.codeUnitAt(0) <= 57);
18+
}
19+
20+
/// Length of string is passed in for improved efficiency (only need to calculate it once)
21+
static String _getChunk(String s, int slength, int marker) {
22+
final StringBuffer chunk = StringBuffer();
23+
int c = s.codeUnitAt(marker);
24+
chunk.write(c);
25+
marker++;
26+
if (_isDigit(s)) {
27+
while (marker < slength) {
28+
c = s.codeUnitAt(marker);
29+
if (!_isDigit(s)) break;
30+
chunk.write(c);
31+
marker++;
32+
}
33+
} else {
34+
while (marker < slength) {
35+
c = s.codeUnitAt(marker);
36+
if (_isDigit(s)) break;
37+
chunk.write(c);
38+
marker++;
39+
}
40+
}
41+
return chunk.toString();
42+
}
43+
44+
/// Compare using alphanum algorithm
45+
static int compare(String s1, String s2) {
46+
int thisMarker = 0;
47+
int thatMarker = 0;
48+
final int s1Length = s1.length;
49+
final int s2Length = s2.length;
50+
51+
while (thisMarker < s1Length && thatMarker < s2Length) {
52+
final String thisChunk = _getChunk(s1, s1Length, thisMarker);
53+
thisMarker += thisChunk.length;
54+
55+
final String thatChunk = _getChunk(s2, s2Length, thatMarker);
56+
thatMarker += thatChunk.length;
57+
58+
// If both chunks contain numeric characters, sort them numerically
59+
int result = 0;
60+
if (_isDigit(thisChunk) && _isDigit(thatChunk)) {
61+
// Simple chunk comparison by length.
62+
final int thisChunkLength = thisChunk.length;
63+
result = thisChunkLength - thatChunk.length;
64+
// If equal, the first different number counts
65+
if (result == 0) {
66+
for (int i = 0; i < thisChunkLength; i++) {
67+
result = thisChunk.codeUnitAt(i) - thatChunk.codeUnitAt(i);
68+
if (result != 0) {
69+
return result;
70+
}
71+
}
72+
}
73+
} else {
74+
result = thisChunk.compareTo(thatChunk);
75+
}
76+
77+
if (result != 0) return result;
78+
}
79+
80+
return s1Length - s2Length;
81+
}
82+
83+
/// Compare using alphanum algorithm, but sort strings starting with a number firsts
84+
static int compareNumFirsts(String s1, String s2) {
85+
RegExp numberRegEx = RegExp(r'[0-9]');
86+
bool isS1startWithNumber = s1.startsWith(numberRegEx);
87+
bool isS2startWithNumber = s2.startsWith(numberRegEx);
88+
89+
if (isS1startWithNumber && !isS2startWithNumber) {
90+
return -1;
91+
} else if (isS2startWithNumber && !isS1startWithNumber) {
92+
return 1;
93+
}
94+
return compare(s1, s2);
95+
}
96+
}

lib/common/colors.dart

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'package:flutter/material.dart';
22

3-
// From https://stackoverflow.com/a/76053830/ (CC BY-SA 4.0)
3+
// Hash code from https://stackoverflow.com/(CC BY-SA 4.0)
44
extension StringColor on String {
55
Color textToColor() {
66
if (isEmpty) {
@@ -10,6 +10,18 @@ extension StringColor on String {
1010
for (var i = 0; i < length; i++) {
1111
hash = codeUnitAt(i) + ((hash << 5) - hash);
1212
}
13-
return Color(hash + 0xFF000000);
13+
return HSLColor.fromColor(Colors.white).withHue(hash % 360.0).withSaturation(0.8).withLightness(0.54).toColor();
14+
}
15+
16+
String textToHslColor() {
17+
if (isEmpty) {
18+
return 'hsl(0, 0%, 0%)';
19+
}
20+
var hash = 0;
21+
for (var i = 0; i < length; i++) {
22+
hash = codeUnitAt(i) + ((hash << 5) - hash);
23+
}
24+
25+
return 'hsl(${hash % 360}, 80%, 54%)';
1426
}
1527
}

lib/models/bibles/kjv_bible.dart

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// ignore_for_file: prefer_single_quotes
2-
1+
import 'dart:developer';
32
import '../../common/enums.dart';
43
import '../json_to_bible.dart';
54

@@ -8,7 +7,7 @@ class KJVBibleImpl extends JsonToBible {
87
KJVBibleImpl(Map<String, dynamic> json) : super(json: json);
98

109
@override
11-
String getBook(String bookCode, ViewBy viewBy) {
10+
String getBook(Area readerArea, String bookCode, List<String> bookmarks, ViewBy viewBy) {
1211
String htmlText = '';
1312
String chapterNumber = '';
1413
List<dynamic> chapterContents = [];
@@ -19,21 +18,27 @@ class KJVBibleImpl extends JsonToBible {
1918
chapterNumber = chapter['chapter'];
2019
chapterContents = chapter['verses'];
2120

22-
String chapterNumberHtml = """<span class="c" id="$bookCode$chapterNumber">$chapterNumber</span>""";
21+
String chapterId = '${readerArea.name}-$bookCode-$chapterNumber';
22+
23+
String chapterNumberHtml = '''<span class="c" id="$chapterId">$chapterNumber</span>''';
2324

2425
for (Map item in chapterContents) {
2526
String verseNumber = item['verse'];
2627
String verseText = item['text'];
2728

29+
String verseId = '${readerArea.name}-$bookCode-$chapterNumber-$verseNumber';
30+
String bookmarkIcon = bookmarkIconHTML(verseId, bookmarks);
31+
2832
if (verseNumber == '1') {
2933
htmlText +=
30-
"""<p class="p" id="$bookCode$chapterNumber:$verseNumber">$chapterNumberHtml<sup>$verseNumber</sup> $verseText</p>""";
34+
"""<p ondblclick=onCreateBookmark("$verseId") class="p" id="$verseId">$chapterNumberHtml$bookmarkIcon<sup>$verseNumber</sup> $verseText</p>""";
3135
} else {
3236
htmlText +=
33-
"""<p class="p" id="$bookCode$chapterNumber:$verseNumber"><sup>$verseNumber</sup> $verseText</p>""";
37+
"""<p ondblclick=onCreateBookmark("$verseId") class="p" id="$verseId">$bookmarkIcon<sup>$verseNumber</sup> $verseText</p>""";
3438
}
3539
}
3640
}
41+
3742
return htmlText;
3843
}
3944
}

lib/models/bibles/oet_lv_bible.dart

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import 'dart:developer';
2-
31
import '../../common/enums.dart';
42
import '../json_to_bible.dart';
53

@@ -9,7 +7,7 @@ class OETLiteralBibleImpl extends JsonToBible {
97

108
// TODO: currently replaces all ' marks with ’.
119
@override
12-
String getBook(String bookCode, ViewBy viewBy) {
10+
String getBook(Area readerArea, String bookCode, List<String> bookmarks, ViewBy viewBy) {
1311
String htmlText = '';
1412
String chapterNumberHtml = '';
1513

@@ -22,7 +20,7 @@ class OETLiteralBibleImpl extends JsonToBible {
2220
for (Map<String, dynamic> chapter in chaptersData) {
2321
chapterNumber = chapter['chapterNumber'];
2422

25-
chapterNumberHtml = '<span class="c" id="$bookCode$chapterNumber">$chapterNumber</span>';
23+
chapterNumberHtml = '<span class="c" id="${readerArea.name}-$bookCode-$chapterNumber">$chapterNumber</span>';
2624

2725
chapterContents = chapter['contents'];
2826

@@ -74,7 +72,7 @@ class OETLiteralBibleImpl extends JsonToBible {
7472
// Add a new break between paragraphs
7573
isNewParagraph = false;
7674
if (verseNumberText == '1') {
77-
htmlText += '<p class="c" id="$bookCode$chapterNumber">';
75+
htmlText += '<p class="c" id="${readerArea.name}-$bookCode-$chapterNumber">';
7876
} else {
7977
// The end of a paragraph
8078
htmlText += '<p/>';
@@ -83,11 +81,15 @@ class OETLiteralBibleImpl extends JsonToBible {
8381
sectionVerseReference = verseNumberText;
8482

8583
if (isSection == false) {
84+
String verseId = '${readerArea.name}-$bookCode-$chapterNumber-$verseNumberText';
85+
String bookmarkIcon = bookmarkIconHTML(verseId, bookmarks);
86+
8687
if (isNewParagraph == false) {
87-
htmlText += '<span><sup id="$bookCode$chapterNumber:$verseNumberText">$verseNumberText</sup>';
88+
htmlText +=
89+
'<span ondblclick=onCreateBookmark("$verseId") class="p">$bookmarkIcon<sup id="$verseId"> $verseNumberText</sup>';
8890
} else {
8991
htmlText +=
90-
'<p class="p">$chapterNumberHtml<sup id="$bookCode$chapterNumber:$verseNumberText">$verseNumberText</sup>';
92+
'<p ondblclick=onCreateBookmark("$verseId") class="p">$chapterNumberHtml$bookmarkIcon<sup id="$verseId"> $verseNumberText</sup>';
9193
}
9294
}
9395
} else if (key == 'verseText') {
@@ -101,13 +103,21 @@ class OETLiteralBibleImpl extends JsonToBible {
101103
.replaceAll('=', ' ');
102104

103105
if (isSection == true && isNext == true) {
104-
htmlText +=
105-
"""<p class="p"><div class="section-box"><p><sup id="$bookCode$chapterNumber:$sectionVerseReference">$chapterNumber:$sectionVerseReference</sup> ${sectionText.replaceAll("'", "")}</p></div><span>$chapterNumberHtml<sup>$sectionVerseReference</sup>${verseText.replaceAll("'", "’")}</span>""";
106+
String verseId = '${readerArea.name}-$bookCode-$chapterNumber-$sectionVerseReference';
107+
String bookmarkIcon = bookmarkIconHTML(verseId, bookmarks);
108+
htmlText += """<p ondblclick=onCreateBookmark("$verseId") class="p">
109+
<div class="section-box">
110+
<p><sup id="$verseId">$chapterNumber:$sectionVerseReference</sup> ${sectionText.replaceAll("'", "’")}</p>
111+
</div>
112+
<span>
113+
$chapterNumberHtml$bookmarkIcon<sup>$sectionVerseReference</sup> ${verseText.replaceAll("'", "’")}
114+
</span>
115+
""";
106116

107117
isSection = false;
108118
isNext = false;
109119
} else {
110-
htmlText += "${verseText.replaceAll("'", "’")}</span>";
120+
htmlText += " ${verseText.replaceAll("'", "’")}</span>";
111121
}
112122
}
113123
}

lib/models/bibles/oet_rv_bible.dart

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import 'dart:developer';
2-
31
import '../../common/enums.dart';
42
import '../json_to_bible.dart';
53

@@ -9,7 +7,7 @@ class OETReadersBibleImpl extends JsonToBible {
97

108
// TODO: currently replaces all ' marks with ’.
119
@override
12-
String getBook(String bookCode, ViewBy viewBy) {
10+
String getBook(Area readerArea, String bookCode, List<String> bookmarks, ViewBy viewBy) {
1311
String htmlText = '';
1412
String chapterNumberHtml = '';
1513

@@ -22,7 +20,7 @@ class OETReadersBibleImpl extends JsonToBible {
2220
for (Map<String, dynamic> chapter in chaptersData) {
2321
chapterNumber = chapter['chapterNumber'];
2422

25-
chapterNumberHtml = '<span class="c" id="$bookCode$chapterNumber">$chapterNumber</span>';
23+
chapterNumberHtml = '<span class="c" id="${readerArea.name}-$bookCode-$chapterNumber">$chapterNumber</span>';
2624

2725
chapterContents = chapter['contents'];
2826

@@ -74,7 +72,7 @@ class OETReadersBibleImpl extends JsonToBible {
7472
// Add a new break between paragraphs
7573
isNewParagraph = false;
7674
if (verseNumberText == '1') {
77-
htmlText += '<p class="c" id="$bookCode$chapterNumber">';
75+
htmlText += '<p class="c" id="${readerArea.name}-$bookCode-$chapterNumber">';
7876
} else {
7977
// The end of a paragraph
8078
htmlText += '<p/>';
@@ -83,11 +81,14 @@ class OETReadersBibleImpl extends JsonToBible {
8381
sectionVerseReference = verseNumberText;
8482

8583
if (isSection == false) {
84+
String verseId = '${readerArea.name}-$bookCode-$chapterNumber-$verseNumberText';
85+
String bookmarkIcon = bookmarkIconHTML(verseId, bookmarks);
8686
if (isNewParagraph == false) {
87-
htmlText += '<span><sup id="$bookCode$chapterNumber:$verseNumberText">$verseNumberText</sup>';
87+
htmlText +=
88+
'<span ondblclick=onCreateBookmark("$verseId") class="p">$bookmarkIcon<sup id="$verseId">$verseNumberText</sup>';
8889
} else {
8990
htmlText +=
90-
'<p class="p">$chapterNumberHtml<sup id="$bookCode$chapterNumber:$verseNumberText">$verseNumberText</sup>';
91+
'<p ondblclick=onCreateBookmark("$verseId") class="p">$chapterNumberHtml$bookmarkIcon<sup id="$verseId">$verseNumberText</sup>';
9192
}
9293
}
9394
} else if (key == 'verseText') {
@@ -101,20 +102,26 @@ class OETReadersBibleImpl extends JsonToBible {
101102
.replaceAll('=', ' ');
102103

103104
if (isSection == true && isNext == true) {
104-
htmlText +=
105-
"""<p class="p"><div class="section-box"><p><sup id="$bookCode$chapterNumber:$sectionVerseReference">$chapterNumber:$sectionVerseReference</sup> ${sectionText.replaceAll("'", "")}</p></div><span>$chapterNumberHtml<sup>$sectionVerseReference</sup>${verseText.replaceAll("'", "’")}</span>""";
105+
String verseId = '${readerArea.name}-$bookCode-$chapterNumber-$sectionVerseReference';
106+
String bookmarkIcon = bookmarkIconHTML(verseId, bookmarks);
107+
htmlText += """<p ondblclick=onCreateBookmark("$verseId") class="p">
108+
<div class="section-box">
109+
<p><sup id="$verseId">$chapterNumber:$sectionVerseReference</sup> ${sectionText.replaceAll("'", "’")}</p>
110+
</div>
111+
<span>
112+
$chapterNumberHtml$bookmarkIcon<sup>$sectionVerseReference</sup> ${verseText.replaceAll("'", "’")}
113+
</span>
114+
""";
106115

107116
isSection = false;
108117
isNext = false;
109118
} else {
110-
htmlText += "${verseText.replaceAll("'", "’")}</span>";
119+
htmlText += " ${verseText.replaceAll("'", "’")}</span>";
111120
}
112121
}
113122
}
114123
}
115124
}
116-
117-
//log(htmlText);
118125
return htmlText;
119126
}
120127
}

lib/models/json_to_bible.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import '../common/colors.dart';
12
import '../common/enums.dart';
23

34
abstract class JsonToBible {
@@ -20,7 +21,14 @@ abstract class JsonToBible {
2021
return listOfStringMatches;
2122
}
2223

23-
String getBook(String bookCode, ViewBy viewBy) {
24+
String bookmarkIconHTML(String verseId, List<String> bookmarks) {
25+
bool isBookmarked = bookmarks.contains(verseId.replaceAll('primary-', '').replaceAll('secondary-', ''));
26+
String svg =
27+
"""<svg id="$verseId-svg" class="svg ${isBookmarked ? "bookmarked" : ""}" style="fill: ${verseId.textToHslColor()} !important;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path d="M184,32H72A16,16,0,0,0,56,48V224a8,8,0,0,0,12.24,6.78L128,193.43l59.77,37.35A8,8,0,0,0,200,224V48A16,16,0,0,0,184,32Zm0,177.57-51.77-32.35a8,8,0,0,0-8.48,0L72,209.57V48H184Z"></path></svg>""";
28+
return svg;
29+
}
30+
31+
String getBook(Area readerArea, String bookCode, List<String> bookmarks, ViewBy viewBy) {
2432
return '~';
2533
}
2634
}

lib/services/bibles_service.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class BiblesService with ListenableServiceMixin {
2222
String get secondaryBible => _settingsService.secondaryBible;
2323
String get bookCode => _settingsService.bookCode;
2424
int get chapterNumber => _settingsService.chapterNumber;
25-
int get sectionNumber => _settingsService.sectionNumber;
25+
int get verseNumber => _settingsService.verseNumber;
2626
List<String> get recentBooks => _settingsService.recentBooks;
2727
ViewBy get viewBy => _settingsService.viewBy;
2828

@@ -49,8 +49,8 @@ class BiblesService with ListenableServiceMixin {
4949
_settingsService.setChapterNumber(newChapter);
5050
}
5151

52-
void setSection(int newSection) {
53-
_settingsService.setSectionNumber(newSection);
52+
void setVerse(int newVerse) {
53+
_settingsService.setVerseNumber(newVerse);
5454
}
5555

5656
void setViewBy(ViewBy viewBy) {

0 commit comments

Comments
 (0)