Re-implement sorting of the books list

Now the sort is made part of the URL
This commit is contained in:
Kovid Goyal 2017-02-07 23:26:29 +05:30
parent 12f93be515
commit 6e6cadf996
5 changed files with 113 additions and 34 deletions

View File

@ -6,14 +6,40 @@ from ajax import ajax
from session import get_interface_data from session import get_interface_data
from utils import parse_url_params from utils import parse_url_params
from book_list.globals import get_session_data
load_status = {'loading':True, 'ok':False, 'error_html':None, 'current_fetch': None, 'library_id': None} load_status = {'loading':True, 'ok':False, 'error_html':None, 'current_fetch': None}
library_data = {} library_data = {}
current_fetch = None
def current_library_id(): def url_books_query(sd):
return parse_url_params().library_id or get_interface_data().library_id q = parse_url_params()
sd = sd or get_session_data()
lid = q.library_id or get_interface_data().default_library_id
return {
'library_id': lid,
'sort': q.sort or sd.get_library_option(lid, 'sort'),
}
def loaded_books_query():
sr = library_data.search_result
sort = None
if sr:
sort = [s + '.' + o for s, o in zip(sr.sort.split(','), sr.sort_order.split(','))].join(',')
return {
'library_id': sr.library_id if sr else None,
'sort': sort,
}
def current_sorted_field():
if library_data.search_result:
return library_data.search_result.sort, library_data.search_result.sort_order
sort = url_books_query().sort.partition(',')[0]
csf = sort.partition('.')[0]
csfo = sort.partition('.')[2] or 'asc'
return csf, csfo
def update_library_data(data): def update_library_data(data):
@ -29,6 +55,11 @@ 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_library_data(data) update_library_data(data)
sd = get_session_data()
q = loaded_books_query()
sd.set_library_option(q.library_id, 'sort', q.sort)
elif end_type is 'abort':
pass
else: else:
load_status.ok = False load_status.ok = False
load_status.loading = False load_status.loading = False
@ -38,17 +69,16 @@ def on_data_loaded(end_type, xhr, ev):
def fetch_init_data(): def fetch_init_data():
if load_status.current_fetch: if load_status.current_fetch:
load_status.current_fetch.abort() load_status.current_fetch.abort()
load_status.library_id = current_library_id() query = url_books_query()
query = {'library_id': load_status.library_id} load_status.loading = True
url_query = parse_url_params() load_status.ok = False
for key in url_query: load_status.error_html = None
query[key] = url_query[key]
load_status.current_fetch = ajax('interface-data/books-init', on_data_loaded, query=query) load_status.current_fetch = ajax('interface-data/books-init', on_data_loaded, query=query)
load_status.current_fetch.send() load_status.current_fetch.send()
def cover_url(book_id, width, height): def cover_url(book_id, width, height):
return 'get/thumb/{}/{}?sz={}x{}'.format(book_id, current_library_id, Math.ceil(width * window.devicePixelRatio), Math.ceil(height * window.devicePixelRatio)) return 'get/thumb/{}/{}?sz={}x{}'.format(book_id, loaded_books_query().library_id, Math.ceil(width * window.devicePixelRatio), Math.ceil(height * window.devicePixelRatio))
def book_metadata(book_id): def book_metadata(book_id):
@ -56,6 +86,12 @@ def book_metadata(book_id):
def ensure_current_library_data(): def ensure_current_library_data():
# TODO: Handle search/sort parameters as well q = url_books_query()
if load_status.library_id != current_library_id(): loaded = loaded_books_query()
matches = True
for key in q:
if q[key] is not loaded[key]:
matches = False
break
if not matches:
fetch_init_data() fetch_init_data()

View File

@ -10,10 +10,9 @@ from modals import create_modal_container, error_dialog
from session import get_interface_data, UserSessionData, update_interface_data, get_translations from session import get_interface_data, UserSessionData, update_interface_data, get_translations
from gettext import gettext as _, install from gettext import gettext as _, install
from popups import install_event_filters from popups import install_event_filters
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.library_data import fetch_init_data from book_list.library_data import fetch_init_data, update_library_data, url_books_query
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, set_mode_handler, on_pop_state from book_list.router import update_window_title, set_default_mode_handler, apply_url, set_mode_handler, on_pop_state
from book_list.globals import get_db, set_session_data from book_list.globals import get_db, set_session_data
@ -77,6 +76,7 @@ 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)
update_library_data(data)
interface_data = get_interface_data() interface_data = get_interface_data()
sd = UserSessionData(interface_data.username, interface_data.user_session_data) sd = UserSessionData(interface_data.username, interface_data.user_session_data)
set_session_data(sd) set_session_data(sd)
@ -99,15 +99,11 @@ def on_data_load_progress(loaded, total):
def load_interface_data(): def load_interface_data():
temp = UserSessionData(None, {}) # So that settings for anonymous users are preserved idata = get_interface_data()
query = {} query = {}
library_id = temp.get('library_id') if not idata.is_default:
if library_id: temp = UserSessionData(None, {}) # So that settings for anonymous users are preserved
query.library_id = library_id query = url_books_query(temp)
query.sort = temp.get_library_option(library_id, 'sort')
url_query = parse_url_params()
for key in url_query:
query[key] = url_query[key]
ajax('interface-data/init', on_data_loaded, on_data_load_progress, query=query).send() ajax('interface-data/init', on_data_loaded, on_data_load_progress, query=query).send()

View File

@ -8,7 +8,6 @@ from elementmaker import E
from book_list.constants import book_list_container_id from book_list.constants import book_list_container_id
from book_list.globals import get_current_query from book_list.globals import get_current_query
from book_list.router import push_state from book_list.router import push_state
from book_list.library_data import current_library_id
panel_handlers = {} panel_handlers = {}
@ -45,13 +44,13 @@ def currently_showing_panel():
return c.dataset.panel return c.dataset.panel
def show_panel(panel, query_data, replace=False): def show_panel(panel, query=None, replace=False):
query = {k:query_data[k] for k in query_data} if query is None:
query = {}
else:
query = {k:query[k] for k in query if k is not 'panel'}
if panel is not 'home': if panel is not 'home':
query.panel = panel query.panel = panel
lid = current_library_id()
if lid:
query.library_id = lid
push_state(query, replace=replace) push_state(query, replace=replace)

View File

@ -15,10 +15,11 @@ from widgets import create_button, create_spinner
from book_list.globals import get_session_data from book_list.globals import get_session_data
from book_list.cover_grid import cover_grid_css, create_item as create_cover_grid_item, init as init_cover_grid, append_item as cover_grid_append_item from book_list.cover_grid import cover_grid_css, create_item as create_cover_grid_item, init as init_cover_grid, append_item as cover_grid_append_item
from book_list.top_bar import create_top_bar from book_list.top_bar import create_top_bar, add_button
from book_list.router import back from book_list.router import back
from book_list.ui import set_panel_handler from book_list.ui import set_panel_handler, show_panel
from book_list.library_data import current_library_id, load_status, ensure_current_library_data, library_data from book_list.library_data import load_status, ensure_current_library_data, library_data, current_sorted_field, loaded_books_query, url_books_query
from book_list.item_list import create_item_list, create_item
ALLOWED_MODES = {'cover_grid'} ALLOWED_MODES = {'cover_grid'}
DEFAULT_MODE = 'cover_grid' DEFAULT_MODE = 'cover_grid'
@ -76,6 +77,8 @@ def apply_view_mode(mode=DEFAULT_MODE):
render_ids() render_ids()
# More button {{{
def update_fetching_status(): def update_fetching_status():
more = document.getElementById(book_list_data.container_id).lastChild more = document.getElementById(book_list_data.container_id).lastChild
if book_list_data.fetching_more_books: if book_list_data.fetching_more_books:
@ -123,7 +126,7 @@ def get_more_books():
data[key] = library_data.search_result[key] data[key] = library_data.search_result[key]
book_list_data.fetching_more_books = ajax_send( book_list_data.fetching_more_books = ajax_send(
'interface-data/more-books', data, got_more_books, 'interface-data/more-books', data, got_more_books,
query={'library_id':current_library_id()} query={'library_id':loaded_books_query().library_id}
) )
update_fetching_status() update_fetching_status()
@ -142,6 +145,9 @@ def create_more_button(more):
update_fetching_status() update_fetching_status()
# }}}
def create_books_list(container): def create_books_list(container):
book_list_data.container_id = ensure_id(container) book_list_data.container_id = ensure_id(container)
book_list_data.shown_book_ids = set() book_list_data.shown_book_ids = set()
@ -151,6 +157,7 @@ def create_books_list(container):
container.appendChild(E.div()), container.appendChild(E.div()) container.appendChild(E.div()), container.appendChild(E.div())
apply_view_mode(get_session_data().get('view_mode')) apply_view_mode(get_session_data().get('view_mode'))
create_more_button(container.lastChild) create_more_button(container.lastChild)
add_button(container.parentNode.id, icon='sort-amount-desc', action=show_panel.bind(None, 'book_list^sort'), tooltip=_('Sort Books'))
def check_for_books_loaded(): def check_for_books_loaded():
@ -169,7 +176,7 @@ def check_for_books_loaded():
err, err,
E.div( E.div(
style='margin-top: 1em; border-top: solid 1px currentColor; padding-top: 1ex;', style='margin-top: 1em; border-top: solid 1px currentColor; padding-top: 1ex;',
E.a(onclick=back, href='javascript: void(0)', style='color: blue', _('Go back to the home page'))) E.a(onclick=back, href='javascript: void(0)', style='color: blue', _('Go back to the home page')))
), ),
) )
return return
@ -180,7 +187,7 @@ def init(container_id):
interface_data = get_interface_data() interface_data = get_interface_data()
ensure_current_library_data() ensure_current_library_data()
container = document.getElementById(container_id) container = document.getElementById(container_id)
lid = container.dataset.library_id = current_library_id() lid = container.dataset.library_id = url_books_query().library_id
title = interface_data.library_map[lid] title = interface_data.library_map[lid]
create_top_bar(container_id, title=title, action=back, icon='home') create_top_bar(container_id, title=title, action=back, icon='home')
container.appendChild(E.div()) container.appendChild(E.div())
@ -188,4 +195,44 @@ def init(container_id):
conditional_timeout(container_id, 5, check_for_books_loaded) conditional_timeout(container_id, 5, check_for_books_loaded)
# Sorting {{{
def change_sort(field, order):
abort_get_more_books()
sd = get_session_data()
so = sd.get('last_sort_order')
order = order or so[field] or 'asc'
order = 'asc' if order is 'asc' else 'desc'
q = loaded_books_query()
q.sort = field + '.' + order
so[field] = order
sd.set('last_sort_order', so)
sd.set_library_option(q.library_id, 'sort', q.sort)
show_panel('book_list', query=q, replace=True)
def create_sort_panel(container_id):
if not library_data.sortable_fields:
show_panel('book_list', replace=True)
return
create_top_bar(container_id, title=_('Sort books by…'), action=back, icon='close')
items = []
csf, csf_order = current_sorted_field()
new_sort_order = 'desc' if csf_order is 'asc' else 'asc'
if csf is 'date':
csf = 'timestamp'
for field, name in library_data.sortable_fields:
subtitle = icon_name = None
if field is csf:
subtitle = _('Reverse current sort order')
icon_name = 'sort-amount-asc' if csf_order is 'asc' else 'sort-amount-desc'
action = change_sort.bind(None, field, new_sort_order)
else:
action = change_sort.bind(None, field, None)
items.push(create_item(name, subtitle=subtitle, icon=icon_name, action=action))
create_item_list(document.getElementById(container_id), items, _('Change how the list of books is sorted'))
# }}}
set_panel_handler('book_list', init) set_panel_handler('book_list', init)
set_panel_handler('book_list^sort', create_sort_panel)

View File

@ -8,6 +8,7 @@ defaults = {
# Book list settings # Book list settings
'view_mode': 'cover_grid', 'view_mode': 'cover_grid',
'sort': 'timestamp.desc', # comma separated list of items of the form: field.order 'sort': 'timestamp.desc', # comma separated list of items of the form: field.order
'last_sort_order': {},
# Tag Browser settings # Tag Browser settings
'partition_method': 'first letter', # other choices: 'disable', 'partition' 'partition_method': 'first letter', # other choices: 'disable', 'partition'