Add autoLoad setting to services and add running indicator to navigation

This commit is contained in:
Alice Gaudon 2020-01-07 11:48:38 +01:00
parent c390fc906d
commit 670e4cf6f8
4 changed files with 236 additions and 202 deletions

146
resources/index.css Normal file
View File

@ -0,0 +1,146 @@
body {
display: flex;
flex-direction: row;
}
#service-selector {
flex-grow: 1;
display: block;
margin: 0;
padding: 0;
}
*:focus {
outline-color: rgb(118, 93, 176);
}
#navigation button {
display: block;
width: 100%;
padding: 16px;
margin: 0;
height: 72px;
color: #fff;
border: 0;
background: transparent;
cursor: pointer;
}
#navigation button img {
width: 32px;
}
#navigation button i {
font-size: 32px;
}
#service-selector li {
position: relative;
}
#service-selector li.active button {
position: relative;
background-color: #fff2;
}
#service-selector li.active button::before {
content: "";
display: block;
position: absolute;
left: 0;
top: 0;
height: 100%;
border-left: 4px solid #ffffff2e;
}
#service-selector li.loaded::after {
content: "";
position: absolute;
top: 50%;
right: 2px;
transform: translateY(-50%);
width: 4px;
height: 4px;
background-color: #fff;
border-radius: 100%;
}
#navigation button:hover {
background-color: #fff3;
}
#history {
padding: 8px;
}
#history button {
display: inline;
width: 24px;
height: 24px;
padding: initial;
font-size: 12px;
background: #fff1;
border: 1px solid #fff4;
border-radius: 72px;
color: #fff;
cursor: pointer;
}
#history button i {
font-size: inherit;
}
#history button.disabled {
color: #888;
border: transparent;
background: transparent;
cursor: initial;
}
#history button:focus,
#history button:hover {
outline: none;
border-color: #fff9;
}
#history button:hover:not(.disabled) {
outline: none;
background-color: #fff2;
}
#history button:active:not(.disabled) {
background-color: #fff4;
}
#navigation #add-button:not(:hover) {
opacity: 0.75;
}
#services {
position: relative;
flex-grow: 1;
}
#services > *:not(.loader) {
height: 100%;
}
#services > :not(.active):not(.loader) {
display: none;
}
#services > .loader {
width: 64px;
height: 64px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: -1;
}

View File

@ -1,144 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8">
<title>Tabs</title> <title>Tabs</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css"
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous"> integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<link rel="stylesheet" href="layout.css"> <link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="index.css">
<style>
body {
display: flex;
flex-direction: row;
}
#service-selector {
flex-grow: 1;
display: block;
margin: 0;
padding: 0;
}
*:focus {
outline-color: rgb(118, 93, 176);
}
#navigation button {
display: block;
width: 100%;
padding: 16px;
margin: 0;
height: 72px;
color: #fff;
border: 0;
background: transparent;
cursor: pointer;
}
#navigation button img {
width: 32px;
}
#navigation button i {
font-size: 32px;
}
#service-selector li.active button {
position: relative;
background-color: #fff2;
}
#service-selector li.active button::before {
content: "";
display: block;
position: absolute;
left: 0;
top: 0;
height: 100%;
border-left: 4px solid #ffffff2e;
}
#navigation button:hover {
background-color: #fff3;
}
#history {
padding: 8px;
}
#history button {
display: inline;
width: 24px;
height: 24px;
padding: initial;
font-size: 12px;
background: #fff1;
border: 1px solid #fff4;
border-radius: 72px;
color: #fff;
cursor: pointer;
}
#history button i {
font-size: inherit;
}
#history button.disabled {
color: #888;
border: transparent;
background: transparent;
cursor: initial;
}
#history button:focus,
#history button:hover {
outline: none;
border-color: #fff9;
}
#history button:hover:not(.disabled) {
outline: none;
background-color: #fff2;
}
#history button:active:not(.disabled) {
background-color: #fff4;
}
#navigation #add-button:not(:hover) {
opacity: 0.75;
}
#services {
position: relative;
flex-grow: 1;
}
#services > *:not(.loader) {
height: 100%;
}
#services > :not(.active):not(.loader) {
display: none;
}
#services > .loader {
width: 64px;
height: 64px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: -1;
}
</style>
</head> </head>
<body> <body>
@ -331,6 +201,10 @@
} else { } else {
nav.insertBefore(li, nextNavButton); nav.insertBefore(li, nextNavButton);
} }
if (service.autoLoad) {
loadService(index, service);
}
} }
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
@ -347,34 +221,7 @@
function setActiveService(serviceId) { function setActiveService(serviceId) {
let currentService = services[serviceId]; let currentService = services[serviceId];
process.nextTick(() => { process.nextTick(() => {
// Load service if not loaded yet loadService(serviceId, currentService);
if (!currentService.view) {
currentService.view = document.createElement('webview');
currentService.view.setAttribute('src', currentService.url);
currentService.view.setAttribute('partition', 'persist:service_' + currentService.partition);
currentService.view.setAttribute('autosize', "true");
document.querySelector('#services').appendChild(currentService.view);
currentService.view.addEventListener('dom-ready', event => {
currentService.viewReady = true;
updateNavigation();
});
currentService.view.addEventListener('page-favicon-updated', event => {
console.debug('Loaded favicons for', currentService.name, event.favicons);
if (event.favicons.length > 0) {
ipcRenderer.send('setServiceFavicon', serviceId, event.favicons[0]);
if (currentService.useFavicon) {
const img = document.createElement('img');
img.src = event.favicons[0];
img.alt = currentService.name;
img.onload = () => {
currentService.li.button.innerHTML = '';
currentService.li.button.appendChild(img);
};
}
}
});
}
// Hide previous service // Hide previous service
if (services[selectedService] && services[selectedService].view) { if (services[selectedService] && services[selectedService].view) {
@ -392,6 +239,39 @@
}); });
} }
function loadService(serviceId, service) {
// Load service if not loaded yet
if (!service.view) {
service.view = document.createElement('webview');
service.view.setAttribute('src', service.url);
service.view.setAttribute('partition', 'persist:service_' + service.partition);
service.view.setAttribute('autosize', "true");
document.querySelector('#services').appendChild(service.view);
service.view.addEventListener('dom-ready', () => {
service.viewReady = true;
updateNavigation();
});
service.view.addEventListener('page-favicon-updated', event => {
console.debug('Loaded favicons for', service.name, event.favicons);
if (event.favicons.length > 0) {
ipcRenderer.send('setServiceFavicon', serviceId, event.favicons[0]);
if (service.useFavicon) {
const img = document.createElement('img');
img.src = event.favicons[0];
img.alt = service.name;
img.onload = () => {
service.li.button.innerHTML = '';
service.li.button.appendChild(img);
};
}
}
});
service.li.classList.add('loaded');
}
}
function updateNavigation() { function updateNavigation() {
console.debug('Updating navigation'); console.debug('Updating navigation');
// Update active list element // Update active list element

View File

@ -134,6 +134,11 @@
<input type="text" name="url" id="url" required> <input type="text" name="url" id="url" required>
</div> </div>
<div class="form-group">
<label for="auto-load">Automatically load service in the background</label>
<input type="checkbox" name="autoLoad" id="auto-load">
</div>
<div id="icon-choice"> <div id="icon-choice">
<h2>Service icon</h2> <h2>Service icon</h2>
<div class="form-group"> <div class="form-group">
@ -216,27 +221,6 @@
}); });
}); });
function loadServiceValues() {
if (!service || !isImageCheckbox) {
return;
}
document.getElementById('name').value = service.name;
document.getElementById('url').value = service.url;
document.getElementById('use-favicon').checked = service.useFavicon;
isImageCheckbox.checked = service.isImage;
if (service.isImage) {
iconUrlField.value = service.icon;
} else {
builtInIconSearchField.value = service.icon;
updateIconSearchResults();
const icon = Array.from(iconSelect.querySelectorAll('label')).find(i => i.dataset.icon === service.icon);
if (icon) {
selectIcon(icon);
}
}
}
function updateIconSearchResults() { function updateIconSearchResults() {
const searchStr = builtInIconSearchField.value; const searchStr = builtInIconSearchField.value;
iconSelect.childNodes.forEach(c => { iconSelect.childNodes.forEach(c => {
@ -298,6 +282,50 @@
} }
} }
function loadServiceValues() {
if (!service || !isImageCheckbox) {
return;
}
document.getElementById('name').value = service.name;
document.getElementById('url').value = service.url;
document.getElementById('use-favicon').checked = service.useFavicon;
document.getElementById('auto-load').checked = service.autoLoad;
isImageCheckbox.checked = service.isImage;
if (service.isImage) {
iconUrlField.value = service.icon;
} else {
builtInIconSearchField.value = service.icon;
updateIconSearchResults();
const icon = Array.from(iconSelect.querySelectorAll('label')).find(i => i.dataset.icon === service.icon);
if (icon) {
selectIcon(icon);
}
}
}
function save() {
const formData = new FormData(document.querySelector('form'));
service.name = formData.get('name');
if (typeof service.partition !== 'string' || service.partition.length === 0) {
service.partition = service.name.replace(/ /g, '-');
service.partition = service.partition.replace(/[^a-zA-Z-_]/g, '');
}
service.url = formData.get('url');
service.isImage = formData.get('isImage') === 'on';
service.icon = formData.get('icon');
service.useFavicon = formData.get('useFavicon') === 'on';
service.autoLoad = formData.get('autoLoad') === 'on';
if (!isValid()) {
return;
}
ipcRenderer.send('saveService', serviceId, service);
remote.getCurrentWindow().close();
}
function isValid() { function isValid() {
if (typeof service.name !== 'string' || service.name.length === 0) { if (typeof service.name !== 'string' || service.name.length === 0) {
console.log('Invalid name'); console.log('Invalid name');
@ -317,27 +345,6 @@
} }
return true; return true;
} }
function save() {
const formData = new FormData(document.querySelector('form'));
service.name = formData.get('name');
if (typeof service.partition !== 'string' || service.partition.length === 0) {
service.partition = service.name.replace(/ /g, '-');
service.partition = service.partition.replace(/[^a-zA-Z-_]/g, '');
}
service.url = formData.get('url');
service.isImage = formData.get('isImage') === 'on';
service.icon = formData.get('icon');
service.useFavicon = formData.get('useFavicon') === 'on';
if (!isValid()) {
return;
}
ipcRenderer.send('saveService', serviceId, service);
remote.getCurrentWindow().close();
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -33,6 +33,7 @@ Service.requiredProperties = {
'isImage': null, 'isImage': null,
'url': null, 'url': null,
'useFavicon': true, 'useFavicon': true,
'autoLoad': false,
}; };
export default Service; export default Service;