johnfactotum / foliate-js

Render e-books in the browser
https://johnfactotum.github.io/foliate-js/reader.html
MIT License
318 stars 43 forks source link

Store and retrieve document location to resume document reading #24

Open svera opened 2 months ago

svera commented 2 months ago

Hi @johnfactotum

First of all, thank you for this library, it's really awesome :)

I'm trying to store the current page of a document to the browser's local storage, so the user can resume reading where he or she left off. My idea was to update the values in the local storage every time the onRelocate event is triggered, as seen in the reader.js example reader, and then retrieve this value when loading again the document.

Going through the documentation, there is .goTo({ index, anchor }) method in the renderer, but I don't know the format of the attributes it requires, even after looking to the resolveHref() mentioned.

How can I achieve this?

johnfactotum commented 2 months ago

CFI is the recommended way of storing references to specific locations in a book. You should store the value of event.detail.cfi from the relocate event dispatched by the <foliate-view> element. To restore the location, you should call view.init({ lastLocation: 'your CFI string here' }), where view is the <foliate-view> element.

(The view.init() method differs from view.goTo() in that if lastLocation is undefined or cannot be resolved, it will display the first page of the book, or the first page of the main body text, i.e. the first chapter, if showTextStart is true.)

Going through the documentation, there is .goTo({ index, anchor }) method in the renderer, but I don't know the format of the attributes it requires, even after looking to the resolveHref() mentioned.

index is an integer representing the index of the target section in the section array. anchor(doc) is a function. It takes one argument, a Document object, and returns either an Element or a Range that will be displayed. If the return value is null, it will show the start of the section.

svera commented 2 months ago

Hi @johnfactotum thank you for the comprehensive explanation. I've implemented your suggestion and it works great, but sometimes the reader crashes with the following error, showing the book cover although the paginator is in the right position. This is reproducible refreshing the page several times:


paginator.js:367 Uncaught (in promise) 
TypeError: Failed to execute 'unobserve' on 'ResizeObserver': parameter 1 is not of type 'Element'.
    at View.destroy (paginator.js:367:43)
    at #createView (paginator.js:555:24)
    at #display (paginator.js:868:42)
    at async #goTo (paginator.js:904:13)
    at async View.init (view.js:154:13)
```
johnfactotum commented 2 months ago

Hard to say without reproducing the error. It might be that it's a sort of race condition where sometimes View.init() is called before the a previous call to display a page is resolved. This is just a guess, though.

laosuan commented 1 month ago

Hi @johnfactotum thank you for the comprehensive explanation. I've implemented your suggestion and it works great, but sometimes the reader crashes with the following error, showing the book cover although the paginator is in the right position. This is reproducible refreshing the page several times:

paginator.js:367 Uncaught (in promise) 
TypeError: Failed to execute 'unobserve' on 'ResizeObserver': parameter 1 is not of type 'Element'.
    at View.destroy (paginator.js:367:43)
    at #createView (paginator.js:555:24)
    at #display (paginator.js:868:42)
    at async #goTo (paginator.js:904:13)
    at async View.init (view.js:154:13)
```

Could you share your implemention? I'm looking forward to achieve this function, let's find out the problem together.

svera commented 2 weeks ago

@johnfactotum @laosuan Here you have my current code with the suggestions made by @johnfactotum . It is basically the reader example provided in the Foliate, with some changes removing some features and making it translatable. Unfortunately, I does not work now, it always opens the document from the beginning, not the last stored position.

https://github.com/svera/coreander/blob/v4/internal/webserver/embedded/js/foliate-js/reader.js

From what I gather, the onrelocate observer is called several times. When reloading the document after moving to another position in the document, the location is retrieved correctly, but finally it goes back to the beginning.

This is the PR with the changes related to this exact feature: https://github.com/svera/coreander/pull/95/files

Any clue what might be wrong in the code?

johnfactotum commented 1 week ago

Can't say what's wrong exactly, but you might need to await the view.init() call.

svera commented 1 week ago

Can't say what's wrong exactly, but you might need to await the view.init() call.

Thank you @johnfactotum , I'll try that and let everyone know if that works.

UPDATE: It works with the await!