274 lines
8.0 KiB
JavaScript
274 lines
8.0 KiB
JavaScript
const DEFAULT_HISTORY_ITEM_COUNT = 20;
|
|
const EMPTY_HISTORY_MESSAGE = `You haven't visited any sites yet.`;
|
|
let requestedTop = 0;
|
|
let lastRequestSize = 0;
|
|
let itemHeight = 48;
|
|
|
|
const dateStringFormat = new Intl.DateTimeFormat('default', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
|
|
const timeStringFormat = new Intl.DateTimeFormat('default', {
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
|
|
const messageHandler = event => {
|
|
var message = event.data.message;
|
|
var args = event.data.args;
|
|
|
|
switch (message) {
|
|
case commands.MG_GET_HISTORY:
|
|
let entriesContainer = document.getElementById('entries-container');
|
|
if (args.from == 0 && args.items.length) {
|
|
entriesContainer.textContent = '';
|
|
|
|
let clearButton = document.getElementById('btn-clear');
|
|
clearButton.classList.remove('hidden');
|
|
}
|
|
|
|
loadItems(args.items);
|
|
if (args.items.length == lastRequestSize) {
|
|
document.addEventListener('scroll', requestTrigger);
|
|
} else if (entriesContainer.childElementCount == 0) {
|
|
loadUIForEmptyHistory();
|
|
}
|
|
break;
|
|
default:
|
|
console.log(`Unexpected message: ${JSON.stringify(event.data)}`);
|
|
break;
|
|
}
|
|
};
|
|
|
|
const requestTrigger = function(event) {
|
|
let triggerRange = 50;
|
|
let element = document.body;
|
|
|
|
if (element.scrollTop + element.clientHeight >= element.scrollHeight - triggerRange) {
|
|
getMoreHistoryItems();
|
|
event.target.removeEventListener('scroll', requestTrigger);
|
|
}
|
|
};
|
|
|
|
function requestHistoryItems(from, count) {
|
|
let message = {
|
|
message: commands.MG_GET_HISTORY,
|
|
args: {
|
|
from: from,
|
|
count: count || DEFAULT_HISTORY_ITEM_COUNT
|
|
}
|
|
};
|
|
|
|
window.chrome.webview.postMessage(message);
|
|
}
|
|
|
|
function removeItem(id) {
|
|
let message = {
|
|
message: commands.MG_REMOVE_HISTORY_ITEM,
|
|
args: {
|
|
id: id
|
|
}
|
|
};
|
|
|
|
window.chrome.webview.postMessage(message);
|
|
}
|
|
|
|
function createItemElement(item, id, date) {
|
|
let itemContainer = document.createElement('div');
|
|
itemContainer.id = id;
|
|
itemContainer.className = 'item-container';
|
|
|
|
let itemElement = document.createElement('div');
|
|
itemElement.className = 'item';
|
|
|
|
// Favicon
|
|
let faviconElement = document.createElement('div');
|
|
faviconElement.className = 'favicon';
|
|
let faviconImage = document.createElement('img');
|
|
faviconImage.src = item.favicon;
|
|
faviconElement.append(faviconImage);
|
|
itemElement.append(faviconElement);
|
|
|
|
// Title
|
|
let titleLabel = document.createElement('div');
|
|
titleLabel.className = 'label-title';
|
|
let linkElement = document.createElement('a');
|
|
linkElement.href = item.uri;
|
|
linkElement.title = item.title;
|
|
linkElement.textContent = item.title;
|
|
titleLabel.append(linkElement);
|
|
itemElement.append(titleLabel);
|
|
|
|
// URI
|
|
let uriLabel = document.createElement('div');
|
|
uriLabel.className = 'label-uri';
|
|
let textElement = document.createElement('p');
|
|
textElement.title = item.uri;
|
|
textElement.textContent = item.uri;
|
|
uriLabel.append(textElement);
|
|
itemElement.append(uriLabel);
|
|
|
|
// Time
|
|
let timeLabel = document.createElement('div');
|
|
timeLabel.className = 'label-time';
|
|
let timeText = document.createElement('p');
|
|
timeText.textContent = timeStringFormat.format(date);
|
|
timeLabel.append(timeText);
|
|
itemElement.append(timeLabel);
|
|
|
|
// Close button
|
|
let closeButton = document.createElement('div');
|
|
closeButton.className = 'btn-close';
|
|
closeButton.addEventListener('click', function(e) {
|
|
if (itemContainer.parentNode.children.length <= 2) {
|
|
itemContainer.parentNode.remove();
|
|
} else {
|
|
itemContainer.remove();
|
|
}
|
|
|
|
let entriesContainer = document.getElementById('entries-container');
|
|
if (entriesContainer.childElementCount == 0) {
|
|
loadUIForEmptyHistory();
|
|
}
|
|
removeItem(parseInt(id.split('-')[1]));
|
|
});
|
|
itemElement.append(closeButton);
|
|
itemContainer.append(itemElement);
|
|
|
|
return itemContainer;
|
|
}
|
|
|
|
function createDateContainer(id, date) {
|
|
let dateContainer = document.createElement('div');
|
|
dateContainer.id = id;
|
|
|
|
let dateLabel = document.createElement('h3');
|
|
dateLabel.className = 'header-date';
|
|
dateLabel.textContent = dateStringFormat.format(date);
|
|
dateContainer.append(dateLabel);
|
|
|
|
return dateContainer;
|
|
}
|
|
|
|
function loadItems(items) {
|
|
let dateContainer;
|
|
let fragment;
|
|
|
|
items.map((entry) => {
|
|
let id = entry.id;
|
|
let item = entry.item;
|
|
let itemContainerId = `item-${id}`;
|
|
|
|
// Skip the item if already loaded. This could happen if the user
|
|
// visits an item for the current date again before requesting more
|
|
// history items.
|
|
let itemContainer = document.getElementById(itemContainerId);
|
|
if (itemContainer) {
|
|
return;
|
|
}
|
|
|
|
let date = new Date(item.timestamp);
|
|
let day = date.getDate();
|
|
let month = date.getMonth();
|
|
let year = date.getFullYear();
|
|
let dateContainerId = `entries-${month}-${day}-${year}`;
|
|
|
|
// If entry belongs to a new date, append buffered items for previous
|
|
// date.
|
|
if (dateContainer && dateContainer.id != dateContainerId) {
|
|
dateContainer.append(fragment);
|
|
}
|
|
|
|
dateContainer = document.getElementById(dateContainerId);
|
|
if (!dateContainer) {
|
|
dateContainer = createDateContainer(dateContainerId, date);
|
|
fragment = document.createDocumentFragment();
|
|
|
|
let entriesContainer = document.getElementById('entries-container');
|
|
entriesContainer.append(dateContainer);
|
|
} else if (!fragment) {
|
|
fragment = document.createDocumentFragment();
|
|
}
|
|
|
|
itemContainer = createItemElement(item, itemContainerId, date);
|
|
fragment.append(itemContainer);
|
|
});
|
|
|
|
// Append remaining items in buffer
|
|
if (fragment) {
|
|
dateContainer.append(fragment);
|
|
}
|
|
}
|
|
|
|
function getMoreHistoryItems(n) {
|
|
n = n ? n : DEFAULT_HISTORY_ITEM_COUNT;
|
|
|
|
requestHistoryItems(requestedTop, n);
|
|
requestedTop += n;
|
|
lastRequestSize = n;
|
|
document.removeEventListener('scroll', requestTrigger);
|
|
}
|
|
|
|
function addUIListeners() {
|
|
let confirmButton = document.getElementById('prompt-true');
|
|
confirmButton.addEventListener('click', function(event) {
|
|
clearHistory();
|
|
event.stopPropagation();
|
|
});
|
|
|
|
let cancelButton = document.getElementById('prompt-false');
|
|
cancelButton.addEventListener('click', function(event) {
|
|
toggleClearPrompt();
|
|
event.stopPropagation();
|
|
});
|
|
|
|
let promptBox = document.getElementById('prompt-box');
|
|
promptBox.addEventListener('click', function(event) {
|
|
event.stopPropagation();
|
|
});
|
|
|
|
let promptOverlay = document.getElementById('overlay');
|
|
promptOverlay.addEventListener('click', toggleClearPrompt);
|
|
|
|
let clearButton = document.getElementById('btn-clear');
|
|
clearButton.addEventListener('click', toggleClearPrompt);
|
|
}
|
|
|
|
function toggleClearPrompt() {
|
|
let promptOverlay = document.getElementById('overlay');
|
|
promptOverlay.classList.toggle('hidden');
|
|
}
|
|
|
|
function loadUIForEmptyHistory() {
|
|
let entriesContainer = document.getElementById('entries-container');
|
|
entriesContainer.textContent = EMPTY_HISTORY_MESSAGE;
|
|
|
|
let clearButton = document.getElementById('btn-clear');
|
|
clearButton.classList.add('hidden');
|
|
}
|
|
|
|
function clearHistory() {
|
|
toggleClearPrompt();
|
|
loadUIForEmptyHistory();
|
|
|
|
let message = {
|
|
message: commands.MG_CLEAR_HISTORY,
|
|
args: {}
|
|
};
|
|
|
|
window.chrome.webview.postMessage(message);
|
|
}
|
|
|
|
function init() {
|
|
window.chrome.webview.addEventListener('message', messageHandler);
|
|
|
|
let viewportItemsCapacity = Math.round(window.innerHeight / itemHeight);
|
|
addUIListeners();
|
|
getMoreHistoryItems(viewportItemsCapacity);
|
|
}
|
|
|
|
init();
|