Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicate / skipping / missing content fix #2776

Merged
merged 8 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/bibleview-js/src/components/BibleView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const {currentVerse} = useVerseNotifier(config, calculatedConfig, mounted, andro
const customFeatures = useCustomFeatures(android);
provide(customFeaturesKey, customFeatures);

useInfiniteScroll(android, documents);
const {documentsCleared} = useInfiniteScroll(android, documents);
const loadingCount = ref(0);

function addDocuments(...docs: AnyDocument[]) {
Expand Down Expand Up @@ -178,6 +178,7 @@ setupEventBusListener("config_changed", async (deferred: Deferred) => {

setupEventBusListener("clear_document", function clearDocument() {
footNoteCount = 0;
documentsCleared();
resetHighlights();
closeModals();
clearLog();
Expand Down
6 changes: 3 additions & 3 deletions app/bibleview-js/src/composables/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
StudyPadItem,
StudyPadTextItem
} from "@/types/client-objects";
import {BibleDocumentType} from "@/types/documents";
import {AnyDocument, BibleDocumentType} from "@/types/documents";
import {isBibleBookmark, isGenericBookmark} from "@/composables/bookmarks";

export type BibleJavascriptInterface = {
Expand Down Expand Up @@ -261,11 +261,11 @@ export function useAndroid({bookmarks}: { bookmarks: Ref<BaseBookmark[]> }, conf
return returnValue
}

async function requestPreviousChapter(): Promise<BibleDocumentType> {
async function requestPreviousChapter(): Promise<Nullable<AnyDocument>> {
return deferredCall((callId) => window.android.requestMoreToBeginning(callId));
}

async function requestNextChapter(): Promise<BibleDocumentType> {
async function requestNextChapter(): Promise<Nullable<AnyDocument>> {
return deferredCall((callId) => window.android.requestMoreToEnd(callId));
}

Expand Down
110 changes: 84 additions & 26 deletions app/bibleview-js/src/composables/infinite-scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,87 @@
*/

import {computed, nextTick, onMounted, watch} from "vue";
import {setupWindowEventListener} from "@/utils";
import {filterNotNull, setupWindowEventListener} from "@/utils";
import {UseAndroid} from "@/composables/android";
import {AnyDocument, BibleViewDocumentType, isOsisDocument} from "@/types/documents";
import {AnyDocument, isOsisDocument} from "@/types/documents";
import {Nullable} from "@/types/common";
import {BookCategory} from "@/types/client-objects";

export function useInfiniteScroll(
{requestPreviousChapter, requestNextChapter}: UseAndroid,
documents: AnyDocument[]
bibleViewDocuments: AnyDocument[]
) {
const enabledCategories: Set<BookCategory> = new Set(["BIBLE", "GENERAL_BOOK"]);
let
currentPos: number,
lastAddMoreTime = 0,
addMoreAtTopOnTouchUp = false,
bottomElem: HTMLElement,
touchDown = false,
textToBeInsertedAtTop: Nullable<AnyDocument> = null;
let currentPos: number;
let addMoreAtTopOnTouchUp = false;
let bottomElem: HTMLElement;
let touchDown = false;
let textToBeInsertedAtTop: Nullable<AnyDocument[]> = null;
let isProcessing = false;
const addChaptersToTop: Promise<Nullable<AnyDocument>>[] = [];
const addChaptersToEnd: Promise<Nullable<AnyDocument>>[] = [];

console.log("inf: Queues", {addChaptersToTop, addChaptersToEnd});

let clearDocumentCount = 0;

function documentsCleared() {
addChaptersToTop.splice(0);
addChaptersToEnd.splice(0);
clearDocumentCount ++;
}

async function processQueues() {
if(isProcessing) return;
console.log("inf: processQueues")
isProcessing = true;
// noinspection UnnecessaryLocalVariableJS
const clearCountStart = clearDocumentCount;
try {
do {
const endPromises =addChaptersToEnd.splice(0);
const topPromises = addChaptersToTop.splice(0);
console.log("inf: Waiting for chapters", {endPromises, topPromises});
const [endChaps, topChaps] = await Promise.all([
Promise.all(endPromises),
Promise.all(topPromises)
]);
console.log("inf: Received chapters")
if(clearCountStart > clearDocumentCount) {
console.log("inf: Document cleared in between, stopping")
return;
}
if(endChaps.length > 0) {
console.log("inf: Displaying received chapters at end")
insertThisTextAtEnd(...filterNotNull(endChaps));
await nextTick();
}
if(topChaps.length > 0) {
console.log("inf: Displaying received chapters at top")
await insertThisTextAtTop(filterNotNull(topChaps));
await nextTick();
}
} while ((addChaptersToEnd.length > 0 || addChaptersToTop.length > 0))
} finally {
isProcessing = false;
console.log("inf: finally isProcessing = false")
}
}

function loadTextAtTop() {
addChaptersToTop.push(requestPreviousChapter())
processQueues();
}

function loadTextAtEnd() {
addChaptersToEnd.push(requestNextChapter())
processQueues();
}

const
isEnabled = computed(() => {
if(documents.length === 0) return false;
const doc = documents[0];
if(bibleViewDocuments.length === 0) return false;
const doc = bibleViewDocuments[0];
if(isOsisDocument(doc)) {
return enabledCategories.has(doc.bookCategory)
} else {
Expand All @@ -56,16 +114,14 @@ export function useInfiniteScroll(
bodyHeight = () => document.body.scrollHeight,
scrollPosition = () => window.pageYOffset,
setScrollPosition = (offset: number) => window.scrollTo(0, offset),
loadTextAtTop = async () => insertThisTextAtTop(await requestPreviousChapter()),
loadTextAtEnd = async () => insertThisTextAtEnd(await requestNextChapter()),
addMoreAtEnd = () => {
if (!isEnabled.value) return;
return loadTextAtEnd();
},
addMoreAtTop = () => {
if (!isEnabled.value) return;
if (touchDown) {
// adding at top is tricky and if the user is stil holding there seems no way to set the scroll position after insert
// adding at top is tricky and if the user is still holding there seems no way to set the scroll position after insert
addMoreAtTopOnTouchUp = true;
} else {
loadTextAtTop();
Expand All @@ -78,21 +134,25 @@ export function useInfiniteScroll(
touchDown = false;
if (textToBeInsertedAtTop) {
insertThisTextAtTop(textToBeInsertedAtTop);
textToBeInsertedAtTop = null;
}
if (addMoreAtTopOnTouchUp) {
addMoreAtTopOnTouchUp = false;
addMoreAtTop()
}
}

async function insertThisTextAtTop(document: AnyDocument) {
async function insertThisTextAtTop(docs: AnyDocument[]) {
if (touchDown) {
textToBeInsertedAtTop = document;
textToBeInsertedAtTop = docs;
} else {
const priorHeight = bodyHeight();
const origPosition = scrollPosition();

if (document) documents.unshift({...document});
if (docs) {
docs.reverse();
bibleViewDocuments.unshift(...docs);
}
await nextTick();

// do no try to get scrollPosition here because it has not settled
Expand All @@ -101,22 +161,18 @@ export function useInfiniteScroll(
}
}

function insertThisTextAtEnd(document: AnyDocument) {
if (document) documents.push({...document});
function insertThisTextAtEnd(...docs: AnyDocument[]) {
if (docs) bibleViewDocuments.push(...docs);
}

function scrollHandler() {
const previousPos = currentPos;
currentPos = scrollPosition();
const scrollingUp = currentPos < previousPos;
const scrollingDown = currentPos > previousPos;
if (scrollingDown
&& currentPos >= (bottomElem.offsetTop - window.innerHeight) - DOWN_MARGIN
&& Date.now() > lastAddMoreTime + 1000) {
lastAddMoreTime = Date.now();
if (scrollingDown && currentPos >= (bottomElem.offsetTop - window.innerHeight) - DOWN_MARGIN) {
addMoreAtEnd();
} else if (scrollingUp && currentPos < UP_MARGIN && Date.now() > lastAddMoreTime + 1000) {
lastAddMoreTime = Date.now();
} else if (scrollingUp && currentPos < UP_MARGIN) {
addMoreAtTop();
}
currentPos = scrollPosition();
Expand All @@ -134,4 +190,6 @@ export function useInfiniteScroll(
currentPos = scrollPosition();
bottomElem = document.getElementById("bottom")!;
});

return {documentsCleared};
}
4 changes: 4 additions & 0 deletions app/bibleview-js/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,3 +661,7 @@ export function formatExportLink({ref, v11n, doc}: {ref: string, v11n: string, d
// this is parsed in MainBibleActivity::openLink(uri: Uri) function for intent parsing
return `https://stepbible.org/?q=reference=${ref}${docStr}&v11n=${v11n}`
}

export function filterNotNull<T>(args: (T|null)[]): T[] {
return args.filter(v => v !== null) as T[];
}
56 changes: 38 additions & 18 deletions app/src/main/java/net/bible/android/view/activity/page/BibleView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1760,46 +1760,66 @@ class BibleView(val mainBibleActivity: MainBibleActivity,
return CommonUtils.getWholeChapters(key.versification, key.book, minChapter, maxChapter)
}

fun requestMoreToBeginning(callId: Long) = scope.launch(Dispatchers.IO) {
fun requestMoreToBeginning(callId: Long) = synchronized(this) {
Log.i(TAG, "requestMoreTextAtTop")
if (isBible) {
val newChap = minChapter - 1

if(newChap < 1) return@launch
if (newChap < 1) {
executeJavascriptOnUiThread("bibleView.response($callId, null);")
return@synchronized
}
addChapter(newChap)

val currentPage = window.pageManager.currentBible
val doc = currentPage.getDocumentForChapter(newChap)
addChapter(newChap)
executeJavascriptOnUiThread("bibleView.response($callId, ${doc.asJson});")
scope.launch(Dispatchers.IO) {
val doc = currentPage.getDocumentForChapter(newChap)
executeJavascriptOnUiThread("bibleView.response($callId, ${doc.asJson});")
}
} else {
val currentPage = window.pageManager.currentGeneralBook
firstKey ?: return@launch
firstKey ?: run {
executeJavascriptOnUiThread("bibleView.response($callId, null);")
return@synchronized
}
val prevKey = currentPage.getKeyPlus(firstKey, -1)
firstKey = prevKey
val doc = currentPage.getPageContent(prevKey)
executeJavascriptOnUiThread("bibleView.response($callId, ${doc.asJson});")

scope.launch(Dispatchers.IO) {
val doc = currentPage.getPageContent(prevKey)
executeJavascriptOnUiThread("bibleView.response($callId, ${doc.asJson});")
}
}
}

fun requestMoreToEnd(callId: Long) = scope.launch(Dispatchers.IO) {
fun requestMoreToEnd(callId: Long) = synchronized(this) {
Log.i(TAG, "requestMoreTextAtEnd")
if (isBible) {
val newChap = maxChapter + 1
val currentPage = window.pageManager.currentBible
val newChap = maxChapter + 1
val verse = currentPage.currentBibleVerse.verse
val lastChap = verse.versification.getLastChapter(verse.book)

if(newChap > lastChap) return@launch
val doc = currentPage.getDocumentForChapter(newChap)
if (newChap > lastChap) {
executeJavascriptOnUiThread("bibleView.response($callId, null);")
return@synchronized
}
addChapter(newChap)
executeJavascriptOnUiThread("bibleView.response($callId, ${doc.asJson});")

scope.launch(Dispatchers.IO) {
val doc = currentPage.getDocumentForChapter(newChap)
executeJavascriptOnUiThread("bibleView.response($callId, ${doc.asJson});")
}
} else {
val currentPage = window.pageManager.currentGeneralBook
lastKey ?: return@launch
lastKey ?: run {
executeJavascriptOnUiThread("bibleView.response($callId, null);")
return@synchronized
}
val nextKey = currentPage.getKeyPlus(lastKey, 1)
lastKey = nextKey
val doc = currentPage.getPageContent(nextKey)
executeJavascriptOnUiThread("bibleView.response($callId, ${doc.asJson});")
scope.launch(Dispatchers.IO) {
val doc = currentPage.getPageContent(nextKey)
executeJavascriptOnUiThread("bibleView.response($callId, ${doc.asJson});")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ class WorkspaceSelectorActivity: ActivityBase() {
workspacesToBeDeleted.forEach {
dao.deleteWorkspace(it)
}
dao.updateWorkspaces(dataSet.filter { changedWorkspaces.contains(it.id) && !workspacesToBeDeleted.contains(it.id) })
dao.updateWorkspaces(dataSet.filter { changedWorkspaces.contains(it.id) })
}

private fun cancelChanges() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ object SwordContentFacade {
fun getKey(): Key? {
val k =
try { PassageKeyFactory.instance().getKey(v11n, searchRef) }
catch (e: NoSuchKeyException) { null }
catch (e: Exception) { null }
if(k != null && k.getRangeAt(0, RestrictionType.NONE)?.start?.chapter == 0) {
return null
}
Expand Down