Skip to content

Commit

Permalink
fix(frontend): move from the legacy pagination links to a new compone…
Browse files Browse the repository at this point in the history
…nt with enhanced user experience, and fix the pagination action, which previously responded with some latency (cypht-org#1413)
  • Loading branch information
mercihabam authored Dec 23, 2024
1 parent 0e8bec3 commit 33d7e11
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 34 deletions.
12 changes: 7 additions & 5 deletions modules/core/js_modules/Hm_MessagesStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ class Hm_MessagesStore {
this.path = path;
this.list = path + '_' + page;
this.rows = rows;
this.links = "";
this.count = 0;
this.flagAsReadOnOpen = true;
this.abortController = abortController;
this.pages = 0;
this.page = page;
}

/**
Expand All @@ -41,7 +42,7 @@ class Hm_MessagesStore {
const storedMessages = this.#retrieveFromLocalStorage();
if (storedMessages && !reload) {
this.rows = storedMessages.rows;
this.links = storedMessages.links;
this.pages = parseInt(storedMessages.pages);
this.count = storedMessages.count;
this.flagAsReadOnOpen = storedMessages.flagAsReadOnOpen;
return this;
Expand All @@ -51,10 +52,10 @@ class Hm_MessagesStore {
return this;
}

const { formatted_message_list: updatedMessages, page_links: pageLinks, folder_status, do_not_flag_as_read_on_open } = await this.#fetch(hideLoadingState);
const { formatted_message_list: updatedMessages, pages, folder_status, do_not_flag_as_read_on_open } = await this.#fetch(hideLoadingState);

this.count = folder_status && Object.values(folder_status)[0]?.messages;
this.links = pageLinks;
this.pages = parseInt(pages);
this.rows = updatedMessages;
this.flagAsReadOnOpen = !do_not_flag_as_read_on_open;

Expand Down Expand Up @@ -195,12 +196,13 @@ class Hm_MessagesStore {
}

config.push({ name: "hm_ajax_hook", value: hook });
config.push({ name: "list_page", value: this.page });

return config;
}

#saveToLocalStorage() {
Hm_Utils.save_to_local_storage(this.list, JSON.stringify({ rows: this.rows, links: this.links, count: this.count }));
Hm_Utils.save_to_local_storage(this.list, JSON.stringify({ rows: this.rows, pages: this.pages, count: this.count }));
Hm_Utils.save_to_local_storage('flagAsReadOnOpen', this.flagAsReadOnOpen);
}

Expand Down
60 changes: 60 additions & 0 deletions modules/core/js_modules/actions/pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
function refreshNextButton(current) {
const totalPages = $(".pagination .total").text();
if (current >= totalPages) {
$(".pagination .next").prop('disabled', true);
} else {
$(".pagination .next").prop('disabled', false);
}
}

function refreshPreviousButton(current) {
if (current <= 1) {
$(".pagination .prev").prop('disabled', true);
} else {
$(".pagination .prev").prop('disabled', false);
}
}

async function nextPage() {
const currentPage = $(".pagination .current").text();

const nextPage = parseInt(currentPage) + 1;

await changePage(nextPage, this);
}

async function previousPage() {
const currentPage = $(".pagination .current").text();

const previousPage = parseInt(currentPage) - 1;

await changePage(previousPage, this);

if (previousPage > 1) {
$(this).prop('disabled', false);
}
}

async function changePage(toPage, button) {
$(button).prop('disabled', true);
$(button).addClass('active');

const url = new URL(window.location.href);
url.searchParams.set('list_page', toPage);
history.pushState(history.state, "", url.toString());
window.location.next = url.search;

const messagesStore = new Hm_MessagesStore(getListPathParam(), toPage);
try {
await messagesStore.load();
Hm_Utils.tbody().attr('id', messagesStore.list);
display_imap_mailbox(messagesStore.rows, null, messagesStore.list);
$(".pagination .current").text(toPage);
} catch (error) {
Hm_Utils.add_sys_message("Failed to fetch content", "danger");
} finally {
$(button).removeClass('active');
refreshNextButton(toPage);
refreshPreviousButton(toPage);
}
}
33 changes: 33 additions & 0 deletions modules/core/js_modules/markup/pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const paginationMarkup = (totalPages) => {
const currentPage = getParam('list_page') || 1;
const markup = `
<div class="pagination d-flex align-items-center gap-2 mb-2">
<button class="btn btn-sm rounded-circle prev">
<i class="bi bi-chevron-left"></i>
</button>
<div>
<span class="current">${currentPage}</span>/<span class="total">${totalPages}</span>
</div>
<button class="btn rounded-circle btn-sm next">
<i class="bi bi-chevron-right"></i>
</button>
</div>
`

return markup;
}

function handlePagination() {
$(".pagination .next").on("click", nextPage);
$(".pagination .prev").on("click", previousPage);
}

function showPagination (totalPages) {
if ($('.message_list .pagination').length) {
$('.message_list .pagination').remove();
}
$(paginationMarkup(totalPages)).insertBefore('.message_table');
handlePagination();
refreshNextButton(getParam('list_page') || 1);
refreshPreviousButton(getParam('list_page') || 1);
}
2 changes: 2 additions & 0 deletions modules/core/js_modules/route_handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ function applyInfoPageHandlers() {
function applyMessaleListPageHandlers(routeParams) {
sortHandlerForMessageListAndSearchPage();
Hm_Message_List.set_row_events();
const messagesStore = new Hm_MessagesStore(routeParams.list_path, routeParams.list_page);
Hm_Utils.tbody().attr('id', messagesStore.list);

$('.core_msg_control').on("click", function(e) {
e.preventDefault();
Expand Down
2 changes: 1 addition & 1 deletion modules/core/js_modules/utils/sortable.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function handleMessagesDragAndDrop() {
moveResponses.forEach((response) => {
store.removeRow(response.oldUid);
});
display_imap_mailbox(store.rows, store.links, getListPathParam());
display_imap_mailbox(store.rows, store.list);
}
);

Expand Down
2 changes: 2 additions & 0 deletions modules/core/navigation/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ async function navigate(url) {

window.location.next = url;

scrollTo(0, 0);

const unMountCallback = renderPage(url);

history.pushState({ main: cyphtMain, head }, "", url);
Expand Down
1 change: 1 addition & 0 deletions modules/core/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@
'msg_source' => array(FILTER_UNSAFE_RAW, false),
'msg_parts' => array(FILTER_UNSAFE_RAW, false),
'page_links' => array(FILTER_UNSAFE_RAW, false),
'pages' => array(FILTER_VALIDATE_INT, false),
'folder_status' => array(FILTER_DEFAULT, FILTER_REQUIRE_ARRAY),
'imap_server_id' => array(FILTER_DEFAULT, false),
'imap_service_name' => array(FILTER_DEFAULT, false)
Expand Down
9 changes: 9 additions & 0 deletions modules/core/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -1491,3 +1491,12 @@ a[disabled] {
pointer-events: none;
opacity: 0.6;
}

.pagination .btn {
background-color: #eee;
}

.pagination .btn.active {
background-color: var(--bs-primary);
color: var(--bs-white);
}
29 changes: 21 additions & 8 deletions modules/core/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,18 @@ var Hm_Ajax_Request = function() { return {
if (config.data) {
data = this.format_xhr_data(config.data);
}
const url = window.location.next ?? window.location.href;
xhr.open('POST', url)
const url = new URL(window.location.href);
if (window.location.next) {
url.search = window.location.next.split('?')[1];
}
for (const param of url.searchParams) {
const configItem = config.data.find(item => item.name === param[0]);
if (configItem) {
url.searchParams.set(configItem.name, configItem.value);
}
}

xhr.open('POST', url.toString())
if (config.signal) {
config.signal.addEventListener('abort', function() {
xhr.abort();
Expand Down Expand Up @@ -495,11 +505,11 @@ function Message_List() {
fixLtrInRtl();
};

this.update = function(msgs) {
Hm_Utils.tbody().html('');
this.update = function(msgs, id) {
Hm_Utils.tbody(id).html('');
for (const index in msgs) {
const row = msgs[index][0];
Hm_Utils.tbody().append(row);
Hm_Utils.tbody(id).append(row);
}
};

Expand Down Expand Up @@ -1542,11 +1552,14 @@ var Hm_Utils = {
}
},

rows: function() {
return $('.message_table_body > tr').not('.inline_msg');
rows: function(id) {
return this.tbody(id).find('tr').not('.inline_msg');
},

tbody: function() {
tbody: function(id) {
if (id) {
return $('#'+id);
}
return $('.message_table_body');
},

Expand Down
2 changes: 1 addition & 1 deletion modules/imap/js_modules/route_handlers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function applyImapMessageListPageHandlers(routeParams) {
const setupPageResult = setup_imap_folder_page(routeParams.list_path);
const setupPageResult = setup_imap_folder_page(routeParams.list_path, routeParams.list_page);

imap_setup_snooze();
imap_setup_tags();
Expand Down
2 changes: 2 additions & 0 deletions modules/imap/output_modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,8 @@ protected function output() {
else {
$page_num = ($details['offset']/$details['limit']) + 1;
}
$max_pages = ceil($details['detail']['exists']/$details['limit']);
$this->out('pages', $max_pages);
$this->out('page_links', build_page_links($details['limit'], $page_num, $details['detail']['exists'],
$this->get('imap_mailbox_page_path'), $this->html_safe($this->get('list_filter')), $this->html_safe($this->get('list_sort')), $this->html_safe($this->get('list_keyword'))));
}
Expand Down
40 changes: 21 additions & 19 deletions modules/imap/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,32 +386,35 @@ var remove_from_cached_imap_pages = function(msg_cache_key) {
});
}

async function select_imap_folder(path, reload, processInTheBackground = false, abortController = null) {
const messages = new Hm_MessagesStore(path, Hm_Utils.get_url_page_number(), null, abortController);
async function select_imap_folder(path, page = 1,reload, processInTheBackground = false, abortController = null) {
const messages = new Hm_MessagesStore(path, page, null, abortController);
await messages.load(reload, processInTheBackground).then(() => {
if (processInTheBackground) {
const rows = Object.values(messages.rows);
for (const index in rows) {
const row = rows[index]?.[0];
const rowUid = $(row).data('uid');
const tableRow = Hm_Utils.tbody().find(`tr[data-uid="${rowUid}"]`);
const tableRow = Hm_Utils.tbody(messages.list).find(`tr[data-uid="${rowUid}"]`);
if (!tableRow.length) {
if (Hm_Utils.rows().length >= index) {
Hm_Utils.rows().eq(index).after(row);
if (Hm_Utils.rows(messages.list).length >= index) {
Hm_Utils.rows(messages.list).eq(index).after(row);
} else {
Hm_Utils.tbody().append(row);
Hm_Utils.tbody(messages.list).append(row);
}
} else if (tableRow.attr('class') !== $(row).attr('class')) {
tableRow.replaceWith(row);
}
}
Hm_Utils.rows().each(function() {
Hm_Utils.rows(messages.list).each(function() {
if (!messages.getRowByUid($(this).data('uid'))) {
$(this).remove();
}
});
} else {
display_imap_mailbox(messages.rows, messages.links);
display_imap_mailbox(messages.rows, messages.list);
}
if (messages.pages) {
showPagination(messages.pages);
}
});

Expand All @@ -422,7 +425,7 @@ async function select_imap_folder(path, reload, processInTheBackground = false,
return messages;
};

var setup_imap_folder_page = async function(listPath) {
var setup_imap_folder_page = async function(listPath, listPage = 1) {
$('.remove_source').on("click", remove_imap_combined_source);
$('.add_source').on("click", add_imap_combined_source);
$('.refresh_link').on("click", function(e) {
Expand All @@ -431,7 +434,7 @@ var setup_imap_folder_page = async function(listPath) {
$('#imap_filter_form').trigger('submit');
}
else {
select_imap_folder(listPath, true);
select_imap_folder(listPath, listPage, true);
}
});
$('.imap_filter').on("change", function() { $('#imap_filter_form').trigger('submit'); });
Expand All @@ -442,8 +445,8 @@ var setup_imap_folder_page = async function(listPath) {
$('#imap_filter_form').trigger('submit');
});

const hadLocalData = new Hm_MessagesStore(listPath, Hm_Utils.get_url_page_number()).hasLocalData();
await select_imap_folder(listPath);
const hadLocalData = new Hm_MessagesStore(listPath, listPage).hasLocalData();
await select_imap_folder(listPath, listPage);

handleMessagesDragAndDrop();

Expand All @@ -457,7 +460,7 @@ var setup_imap_folder_page = async function(listPath) {
// Refresh in the background each 30 seconds and abort any pending request when the page unmounts
const backgroundAbortController = new AbortController();
const interval = setInterval(async () => {
select_imap_folder(listPath, true, true, backgroundAbortController);
select_imap_folder(listPath, 1, true, true, backgroundAbortController);
}, 30000);
return [interval, backgroundAbortController];
};
Expand All @@ -470,18 +473,17 @@ $('#imap_filter_form').on('submit', async function(event) {
try {
const messages = new Hm_MessagesStore(getListPathParam(), Hm_Utils.get_url_page_number());
await messages.load(true);
display_imap_mailbox(messages.rows, messages.links);
display_imap_mailbox(messages.rows, messages.list);
} catch (error) {
// Show error message. TODO: No message utility yet, implement it later.
}
});

var display_imap_mailbox = function(rows, links) {
var display_imap_mailbox = function(rows, id) {
Hm_Message_List.toggle_msg_controls();
if (rows) {
Hm_Message_List.update(rows);
Hm_Message_List.update(rows, id);
Hm_Message_List.check_empty_list();
$('.page_links').html(links);
$('input[type=checkbox]').on("click", function(e) {
Hm_Message_List.toggle_msg_controls();
});
Expand Down Expand Up @@ -983,7 +985,7 @@ var imap_perform_move_copy = function(dest_id, context, action = null) {
}
}
if (getListPathParam().substr(0, 4) === 'imap') {
display_imap_mailbox(store.rows, store.links, getListPathParam());
display_imap_mailbox(store.rows, store.list);
}
else {
Hm_Message_List.load_sources();
Expand Down Expand Up @@ -1171,7 +1173,7 @@ var imap_setup_snooze = function() {
store.removeRow(msg);
});
if (getPageNameParam() == 'message_list') {
display_imap_mailbox(store.rows, store.links, getListPathParam());
display_imap_mailbox(store.rows, store.list);
}

Hm_Folders.reload_folders(true);
Expand Down

0 comments on commit 33d7e11

Please sign in to comment.