Load the db early during initialization

This commit is contained in:
Kovid Goyal 2017-01-24 07:03:00 +05:30
parent b99e261260
commit 09717b15c4
6 changed files with 120 additions and 47 deletions

View File

@ -21,3 +21,15 @@ def get_current_query(newval):
if newval: if newval:
get_current_query.ans = newval get_current_query.ans = newval
return get_current_query.ans return get_current_query.ans
def get_db(db):
if db:
get_db.db = db
return get_db.db
def get_translations(val):
if val:
get_translations.ans = val
return get_translations.ans

View File

@ -14,8 +14,10 @@ from utils import parse_url_params
from book_list.constants import book_list_container_id, read_book_container_id from book_list.constants import book_list_container_id, read_book_container_id
from book_list.theme import get_color from book_list.theme import get_color
from book_list.router import update_window_title, set_default_mode_handler, apply_url from book_list.router import update_window_title, set_default_mode_handler, apply_url, set_mode_handler
from book_list.globals import get_db, set_session_data
from book_list.ui import apply_url_state as book_list_mode_handler from book_list.ui import apply_url_state as book_list_mode_handler
from read_book.ui import ReadUI
# Register the various panels # Register the various panels
import book_list.home # noqa: unused-import import book_list.home # noqa: unused-import
@ -39,13 +41,17 @@ def onerror(msg, script_url, line_number, column_number, error_object):
except: except:
console.log('There was an error in the unhandled exception handler') console.log('There was an error in the unhandled exception handler')
read_ui = None
def init_ui(): def init_ui():
nonlocal read_ui
install_event_filters() install_event_filters()
set_default_mode_handler(book_list_mode_handler) set_default_mode_handler(book_list_mode_handler)
window.onerror = onerror window.onerror = onerror
translations = get_translations() translations = get_translations()
if translations: if translations:
install(translations) install(translations)
get_translations(translations)
remove_initial_progress_bar() remove_initial_progress_bar()
document.head.appendChild(E.style(get_widget_css())) document.head.appendChild(E.style(get_widget_css()))
set_css(document.body, background_color=get_color('window-background'), color=get_color('window-foreground')) set_css(document.body, background_color=get_color('window-background'), color=get_color('window-foreground'))
@ -54,6 +60,9 @@ def init_ui():
document.body.lastChild.appendChild(E.div(id=read_book_container_id, style='display: none')) document.body.lastChild.appendChild(E.div(id=read_book_container_id, style='display: none'))
create_modal_container() create_modal_container()
update_window_title() update_window_title()
read_ui = ReadUI()
get_db(read_ui.db)
set_mode_handler('read_book', read_ui.apply_url_state.bind(read_ui))
apply_url() apply_url()
def on_data_loaded(end_type, xhr, ev): def on_data_loaded(end_type, xhr, ev):
@ -61,6 +70,9 @@ def on_data_loaded(end_type, xhr, ev):
if end_type is 'load': if end_type is 'load':
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
update_interface_data(data) update_interface_data(data)
interface_data = get_interface_data()
sd = UserSessionData(interface_data.username, interface_data.user_session_data)
set_session_data(sd)
if data.translations: if data.translations:
get_translations(data.translations) get_translations(data.translations)
init_ui() init_ui()
@ -94,4 +106,7 @@ def main():
if get_interface_data().is_default: if get_interface_data().is_default:
load_interface_data() load_interface_data()
else: else:
interface_data = get_interface_data()
sd = UserSessionData(interface_data.username, interface_data.user_session_data)
set_session_data(sd)
init_ui() init_ui()

View File

@ -8,6 +8,7 @@ from utils import parse_url_params
mode_handlers = {} mode_handlers = {}
default_mode_handler = None default_mode_handler = None
read_book_mode = 'read_book'
def set_mode_handler(mode, handler): def set_mode_handler(mode, handler):
@ -24,8 +25,13 @@ def update_window_title(subtitle, title='calibre', sep=' :: '):
document.title = title + extra document.title = title + extra
def is_reading_book():
cq = get_current_query
return cq and cq.mode is read_book_mode
def apply_mode(mode): def apply_mode(mode):
divid = read_book_container_id if (mode or get_current_query().mode) is 'read_book' else book_list_container_id divid = read_book_container_id if is_reading_book() else book_list_container_id
for div in document.getElementById(divid).parentNode.childNodes: for div in document.getElementById(divid).parentNode.childNodes:
div.style.display = 'block' if div.id is divid else 'none' div.style.display = 'block' if div.id is divid else 'none'
@ -37,3 +43,10 @@ def apply_url():
apply_mode() apply_mode()
handler = mode_handlers[data.mode] or default_mode_handler handler = mode_handlers[data.mode] or default_mode_handler
handler(data) handler(data)
def push_state(query, replace=False, mode='book_list'):
query = {k:query[k] for k in query}
if mode is not 'book_list':
query.mode = mode
# TODO: Implement this (see push_state in boos.pyj)

View File

@ -1,9 +1,12 @@
# vim:fileencoding=utf-8 # vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import hash_literals from __python__ import hash_literals, bound_methods
from gettext import gettext as _ from gettext import gettext as _
from encodings import base64encode, base64decode from encodings import base64encode, base64decode
from modals import error_dialog
from book_list.router import is_reading_book
def upgrade_schema(idb, old_version, new_version): def upgrade_schema(idb, old_version, new_version):
print('upgrade_schema:', old_version, new_version) print('upgrade_schema:', old_version, new_version)
@ -31,13 +34,59 @@ DB_VERSION = 2
class DB: class DB:
def __init__(self, idb, ui, supports_blobs): def __init__(self, callback, show_read_book_error):
self.interface_data = ui.interface_data self.initialized = False
self.initialize_error_msg = None
self.callback = callback
self.show_read_book_error = show_read_book_error
self.initialize_stage1()
def show_error(self, title, msg, det_msg):
if is_reading_book():
self.show_read_book_error(title, msg, det_msg)
else:
error_dialog(title, msg, det_msg)
def initialize_stage1(self):
if not window.indexedDB:
self.initialize_error_msg = _('Your browser does not support IndexedDB. Cannot read books. Consider using a modern browser, such as Firefox, Chrome or Edge.')
self.initialized = True
self.callback()
return
request = window.indexedDB.open(DB_NAME, DB_VERSION)
request.onupgradeneeded = def(event):
upgrade_schema(event.target.result, event.oldVersion, event.newVersion)
request.onblocked = def(event):
self.initialize_error_msg = _('Please close all other browser tabs with calibre open')
self.initialized = True
self.callback()
request.onerror = def(event):
self.initialize_error_msg = _('You must allow calibre to use IndexedDB storage in your browser to read books')
self.initialized = True
self.callback()
request.onsuccess = def(event):
blob = Blob(['test'], {'type':"text/plain"})
idb = event.target.result
try:
req = idb.transaction(['files'], 'readwrite').objectStore('files').put(blob, ':-test-blob-:')
except Exception:
self.initialize_stage2(idb, False)
req.onsuccess = def(event):
self.initialize_stage2(idb, True)
req.onerror = def(event):
self.initialize_stage2(idb, False)
def initialize_stage2(self, idb, supports_blobs):
self.idb = idb self.idb = idb
self.supports_blobs = supports_blobs self.supports_blobs = supports_blobs
self.initialized = True
if not supports_blobs: if not supports_blobs:
print('IndexedDB does not support Blob storage, using base64 encoding instead') print('WARNING: browser does not support blob storage, calibre falling back to base64 encoding')
self.show_error = ui.show_error.bind(ui)
idb.onerror = def(event): idb.onerror = def(event):
self.display_error(None, event) self.display_error(None, event)
@ -48,8 +97,9 @@ class DB:
idb.onversionchange = def(event): idb.onversionchange = def(event):
idb.close() idb.close()
ui.show_error(_('Database upgraded!'), _( self.show_error(_('Database upgraded!'), _(
'A newer version of calibre is available, please click the reload button in your browser.')) 'A newer version of calibre is available, please click the reload button in your browser.'))
self.callback()
def display_error(self, msg, event): def display_error(self, msg, event):
if event.already_displayed_by_calibre: if event.already_displayed_by_calibre:
@ -225,31 +275,8 @@ class DB:
books.delete(book.key) books.delete(book.key)
next_step() next_step()
def create_db(ui, interface_data):
if not window.indexedDB:
return ui.db_initialized(_('Your browser does not support IndexedDB. Cannot read books. Consider using a modern browser, such as Firefox, Chrome or Edge.'))
request = window.indexedDB.open(DB_NAME, DB_VERSION) def get_db(callback, show_read_book_error):
if not get_db.ans:
request.onupgradeneeded = def(event): get_db.ans = DB(callback)
upgrade_schema(event.target.result, event.oldVersion, event.newVersion) return get_db.ans
request.onblocked = def(event):
alert(_('Please close all other tabs with a calibre book open'))
request.onerror = def(event):
ui.db_initialized(_('You must allow calibre to use IndexedDB storage in your browser to read books'))
request.onsuccess = def(event):
blob = Blob(['test'], {'type':"text/plain"})
idb = event.target.result
try:
req = idb.transaction(['files'], 'readwrite').objectStore('files').put(blob, ':-test-blob-:')
except Exception:
print('WARNING: browser does not support blob storage, calibre falling back to base64 encoding')
return ui.db_initialized(DB(idb, ui, False))
req.onsuccess = def(event):
ui.db_initialized(DB(idb, ui, True))
req.onerror = def(event):
print('WARNING: browser does not support blob storage, calibre falling back to base64 encoding')
ui.db_initialized(DB(idb, ui, False))

View File

@ -10,8 +10,9 @@ from gettext import gettext as _
from modals import error_dialog from modals import error_dialog
from utils import human_readable, debounce from utils import human_readable, debounce
from book_list.constants import read_book_container_id
from read_book.db import get_db
from book_list.router import update_window_title from book_list.router import update_window_title
from read_book.db import create_db
from read_book.view import View from read_book.view import View
RENDER_VERSION = __RENDER_VERSION__ RENDER_VERSION = __RENDER_VERSION__
@ -19,19 +20,17 @@ MATHJAX_VERSION = "__MATHJAX_VERSION__"
class ReadUI: class ReadUI:
def __init__(self, interface_data, container): def __init__(self):
self.interface_data = interface_data
self.db = None
self.current_metadata = {'title': _('Unknown book')} self.current_metadata = {'title': _('Unknown book')}
self.current_book_id = None self.current_book_id = None
self.manifest_xhr = None self.manifest_xhr = None
create_db(self, interface_data)
self.pending_load = None self.pending_load = None
self.downloads_in_progress = [] self.downloads_in_progress = []
self.progress_id = 'book-load-progress' self.progress_id = 'book-load-progress'
self.display_id = 'book-iframe-container' self.display_id = 'book-iframe-container'
self.error_id = 'book-global-error-container' self.error_id = 'book-global-error-container'
self.stacked_widgets = [self.progress_id, self.display_id, self.error_id] self.stacked_widgets = [self.progress_id, self.display_id, self.error_id]
container = document.getElementById(read_book_container_id)
container.appendChild(E.div( container.appendChild(E.div(
id=self.progress_id, style='display:none; text-align: center', id=self.progress_id, style='display:none; text-align: center',
@ -51,6 +50,7 @@ class ReadUI:
)) ))
self.view = View(container.lastChild, self) self.view = View(container.lastChild, self)
window.addEventListener('resize', debounce(self.on_resize.bind(self), 250)) window.addEventListener('resize', debounce(self.on_resize.bind(self), 250))
self.db = get_db(self.db_initialized.bind(self), self.show_error.bind(self))
def on_resize(self): def on_resize(self):
self.view.on_resize() self.view.on_resize()
@ -99,7 +99,7 @@ class ReadUI:
def load_book(self, book_id, fmt, metadata, force_reload): def load_book(self, book_id, fmt, metadata, force_reload):
self.base_url_data = {'book_id':book_id, 'fmt':fmt} self.base_url_data = {'book_id':book_id, 'fmt':fmt}
if self.db is None: if not self.db.initialized:
self.pending_load = [book_id, fmt, metadata, force_reload] self.pending_load = [book_id, fmt, metadata, force_reload]
return return
self.start_load(book_id, fmt, metadata, force_reload) self.start_load(book_id, fmt, metadata, force_reload)
@ -126,11 +126,13 @@ class ReadUI:
ans.bookpos = bookpos ans.bookpos = bookpos
return ans return ans
def db_initialized(self, db): def db_initialized(self):
self.db = db
if self.pending_load is not None: if self.pending_load is not None:
pl, self.pending_load = self.pending_load, None pl, self.pending_load = self.pending_load, None
self.start_load(*pl) if self.db.initialize_error_msg:
self.show_error(_('Failed to initialize IndexedDB'), self.db.initialize_error_msg)
else:
self.start_load(*pl)
def start_load(self, book_id, fmt, metadata, force_reload): def start_load(self, book_id, fmt, metadata, force_reload):
self.current_book_id = book_id self.current_book_id = book_id
@ -402,3 +404,6 @@ class ReadUI:
def display_book_stage2(self, book): def display_book_stage2(self, book):
self.show_stack(self.display_id) self.show_stack(self.display_id)
self.view.display_book(book) self.view.display_book(book)
def apply_url_state(self, current_query):
pass

View File

@ -8,7 +8,8 @@ from gettext import gettext as _
from utils import html_escape from utils import html_escape
from modals import error_dialog, warning_dialog from modals import error_dialog, warning_dialog
from book_list.globals import get_session_data, get_boss, main_js from book_list.globals import get_session_data, main_js, get_translations
from book_list.router import push_state, read_book_mode
from read_book.globals import messenger, iframe_id, current_book, set_current_spine_item from read_book.globals import messenger, iframe_id, current_book, set_current_spine_item
from read_book.resources import load_resources from read_book.resources import load_resources
from read_book.overlay import Overlay from read_book.overlay import Overlay
@ -228,7 +229,7 @@ class View:
def on_iframe_ready(self, data): def on_iframe_ready(self, data):
messenger.reset() messenger.reset()
self.send_message('initialize', secret=messenger.secret, translations=self.ui.interface_data.translations) self.send_message('initialize', secret=messenger.secret, translations=get_translations())
self.iframe_ready = True self.iframe_ready = True
if self.pending_load: if self.pending_load:
data = self.pending_load data = self.pending_load
@ -354,7 +355,7 @@ class View:
def on_update_cfi(self, data): def on_update_cfi(self, data):
self.currently_showing.bookpos = data.cfi self.currently_showing.bookpos = data.cfi
get_boss().push_state(replace=data.replace_history) push_state(self.url_data, replace=data.replace_history, mode=read_book_mode)
unkey = username_key(self.ui.interface_data.username) unkey = username_key(self.ui.interface_data.username)
if not self.book.last_read_position: if not self.book.last_read_position:
self.book.last_read_position = {} self.book.last_read_position = {}