mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #1374 from itegulov/books-mobile-support
Adapt book plugin for mobile devices
This commit is contained in:
commit
16fe6d3530
2 changed files with 92 additions and 41 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
import browser from 'browser';
|
||||||
import loading from 'loading';
|
import loading from 'loading';
|
||||||
import keyboardnavigation from 'keyboardnavigation';
|
import keyboardnavigation from 'keyboardnavigation';
|
||||||
import dialogHelper from 'dialogHelper';
|
import dialogHelper from 'dialogHelper';
|
||||||
|
@ -18,6 +19,8 @@ export class BookPlayer {
|
||||||
|
|
||||||
this.onDialogClosed = this.onDialogClosed.bind(this);
|
this.onDialogClosed = this.onDialogClosed.bind(this);
|
||||||
this.openTableOfContents = this.openTableOfContents.bind(this);
|
this.openTableOfContents = this.openTableOfContents.bind(this);
|
||||||
|
this.prevChapter = this.prevChapter.bind(this);
|
||||||
|
this.nextChapter = this.nextChapter.bind(this);
|
||||||
this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
|
this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,26 +125,6 @@ export class BookPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTouchStart(e) {
|
|
||||||
// TODO: depending on the event this can be the document or the rendition itself
|
|
||||||
const rendition = this._rendition || this;
|
|
||||||
const book = rendition.book;
|
|
||||||
|
|
||||||
// check that the event is from the book or the document
|
|
||||||
if (!book || this._loaded === false) return;
|
|
||||||
|
|
||||||
// epubjs stores pages off the screen or something for preloading
|
|
||||||
// get the modulus of the touch event to account for the increased width
|
|
||||||
if (!e.touches || e.touches.length === 0) return;
|
|
||||||
|
|
||||||
const touch = e.touches[0].clientX % dom.getWindowSize().innerWidth;
|
|
||||||
if (touch < dom.getWindowSize().innerWidth / 2) {
|
|
||||||
book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev();
|
|
||||||
} else {
|
|
||||||
book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onDialogClosed() {
|
onDialogClosed() {
|
||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
@ -150,27 +133,33 @@ export class BookPlayer {
|
||||||
const elem = this._mediaElement;
|
const elem = this._mediaElement;
|
||||||
|
|
||||||
elem.addEventListener('close', this.onDialogClosed, {once: true});
|
elem.addEventListener('close', this.onDialogClosed, {once: true});
|
||||||
elem.querySelector('.btnBookplayerExit').addEventListener('click', this.onDialogClosed, {once: true});
|
elem.querySelector('#btnBookplayerExit').addEventListener('click', this.onDialogClosed, {once: true});
|
||||||
elem.querySelector('.btnBookplayerToc').addEventListener('click', this.openTableOfContents);
|
elem.querySelector('#btnBookplayerToc').addEventListener('click', this.openTableOfContents);
|
||||||
|
if (browser.mobile) {
|
||||||
|
elem.querySelector('#btnBookplayerPrev').addEventListener('click', this.prevChapter);
|
||||||
|
elem.querySelector('#btnBookplayerNext').addEventListener('click', this.nextChapter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
this.bindMediaElementEvents();
|
this.bindMediaElementEvents();
|
||||||
|
|
||||||
document.addEventListener('keyup', this.onWindowKeyUp);
|
document.addEventListener('keyup', this.onWindowKeyUp);
|
||||||
document.addEventListener('touchstart', this.onTouchStart);
|
|
||||||
|
|
||||||
// FIXME: I don't really get why document keyup event is not triggered when epub is in focus
|
// FIXME: I don't really get why document keyup event is not triggered when epub is in focus
|
||||||
this._rendition.on('keyup', this.onWindowKeyUp);
|
this._rendition.on('keyup', this.onWindowKeyUp);
|
||||||
this._rendition.on('touchstart', this.onTouchStart);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unbindMediaElementEvents() {
|
unbindMediaElementEvents() {
|
||||||
const elem = this._mediaElement;
|
const elem = this._mediaElement;
|
||||||
|
|
||||||
elem.removeEventListener('close', this.onDialogClosed);
|
elem.removeEventListener('close', this.onDialogClosed);
|
||||||
elem.querySelector('.btnBookplayerExit').removeEventListener('click', this.onDialogClosed);
|
elem.querySelector('#btnBookplayerExit').removeEventListener('click', this.onDialogClosed);
|
||||||
elem.querySelector('.btnBookplayerToc').removeEventListener('click', this.openTableOfContents);
|
elem.querySelector('#btnBookplayerToc').removeEventListener('click', this.openTableOfContents);
|
||||||
|
if (browser.mobile) {
|
||||||
|
elem.querySelector('#btnBookplayerPrev').removeEventListener('click', this.prevChapter);
|
||||||
|
elem.querySelector('#btnBookplayerNext').removeEventListener('click', this.nextChapter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unbindEvents() {
|
unbindEvents() {
|
||||||
|
@ -179,11 +168,9 @@ export class BookPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.removeEventListener('keyup', this.onWindowKeyUp);
|
document.removeEventListener('keyup', this.onWindowKeyUp);
|
||||||
document.removeEventListener('touchstart', this.onTouchStart);
|
|
||||||
|
|
||||||
if (this._rendition) {
|
if (this._rendition) {
|
||||||
this._rendition.off('keyup', this.onWindowKeyUp);
|
this._rendition.off('keyup', this.onWindowKeyUp);
|
||||||
this._rendition.off('touchstart', this.onTouchStart);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +180,16 @@ export class BookPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevChapter(e) {
|
||||||
|
this._rendition.prev();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
nextChapter(e) {
|
||||||
|
this._rendition.next();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
createMediaElement() {
|
createMediaElement() {
|
||||||
let elem = this._mediaElement;
|
let elem = this._mediaElement;
|
||||||
if (elem) {
|
if (elem) {
|
||||||
|
@ -213,13 +210,22 @@ export class BookPlayer {
|
||||||
elem.id = 'bookPlayer';
|
elem.id = 'bookPlayer';
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
html += '<div class="topRightActionButtons">';
|
|
||||||
html += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerExit hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon close"></i></button>';
|
if (browser.mobile) {
|
||||||
|
html += '<div class="button-wrapper top-button"><button id="btnBookplayerPrev" is="paper-icon-button-light" class="autoSize bookplayerButton hide-mouse-idle-tv"><i class="material-icons bookplayerButtonIcon navigate_before"></i> Prev</button></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '<div id="viewer">';
|
||||||
|
html += '<div class="topButtons">';
|
||||||
|
html += '<button is="paper-icon-button-light" id="btnBookplayerToc" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon toc"></i></button>';
|
||||||
|
html += '<button is="paper-icon-button-light" id="btnBookplayerExit" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon close"></i></button>';
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
html += '<div class="topLeftActionButtons">';
|
|
||||||
html += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerToc hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon toc"></i></button>';
|
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
|
if (browser.mobile) {
|
||||||
|
html += '<div class="button-wrapper bottom-button"><button id="btnBookplayerNext" is="paper-icon-button-light" class="autoSize bookplayerButton hide-mouse-idle-tv">Next <i class="material-icons bookplayerButtonIcon navigate_next"></i></button></div>';
|
||||||
|
}
|
||||||
|
|
||||||
elem.innerHTML = html;
|
elem.innerHTML = html;
|
||||||
|
|
||||||
dialogHelper.open(elem);
|
dialogHelper.open(elem);
|
||||||
|
@ -230,6 +236,21 @@ export class BookPlayer {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render(elem, book) {
|
||||||
|
if (browser.mobile) {
|
||||||
|
return book.renderTo(elem, {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
flow: 'scrolled-doc'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return book.renderTo(elem, {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setCurrentSrc(elem, options) {
|
setCurrentSrc(elem, options) {
|
||||||
const item = options.items[0];
|
const item = options.items[0];
|
||||||
this._currentItem = item;
|
this._currentItem = item;
|
||||||
|
@ -248,7 +269,7 @@ export class BookPlayer {
|
||||||
import('epubjs').then(({default: epubjs}) => {
|
import('epubjs').then(({default: epubjs}) => {
|
||||||
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
|
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
|
||||||
const book = epubjs(downloadHref, {openAs: 'epub'});
|
const book = epubjs(downloadHref, {openAs: 'epub'});
|
||||||
const rendition = book.renderTo(elem, {width: '100%', height: '97%'});
|
const rendition = this.render('viewer', book);
|
||||||
|
|
||||||
this._currentSrc = downloadHref;
|
this._currentSrc = downloadHref;
|
||||||
this._rendition = rendition;
|
this._rendition = rendition;
|
||||||
|
|
|
@ -7,18 +7,20 @@
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topRightActionButtons {
|
.topButtons {
|
||||||
right: 0.5vh;
|
|
||||||
top: 0.5vh;
|
top: 0.5vh;
|
||||||
z-index: 1002;
|
z-index: 1002;
|
||||||
position: absolute;
|
position: sticky;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topLeftActionButtons {
|
#btnBookplayerToc {
|
||||||
left: 0.5vh;
|
float: left;
|
||||||
top: 0.5vh;
|
margin-left: 2vw;
|
||||||
z-index: 1002;
|
}
|
||||||
position: absolute;
|
|
||||||
|
#btnBookplayerExit {
|
||||||
|
float: right;
|
||||||
|
margin-right: 2vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bookplayerButtonIcon {
|
.bookplayerButtonIcon {
|
||||||
|
@ -37,3 +39,31 @@
|
||||||
.bookplayerErrorMsg {
|
.bookplayerErrorMsg {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#viewer {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#btnBookplayerPrev {
|
||||||
|
margin: 0.5vh 0.5vh;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#btnBookplayerNext {
|
||||||
|
margin: 0.5vh 0.5vh;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-wrapper {
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-button {
|
||||||
|
margin: 0.5vh 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-button {
|
||||||
|
margin: 2em 0.5vh;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue