Add autoLoad setting to services and add running indicator to navigation
This commit is contained in:
parent
c390fc906d
commit
670e4cf6f8
146
resources/index.css
Normal file
146
resources/index.css
Normal 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;
|
||||
}
|
@ -1,144 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Tabs</title>
|
||||
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css"
|
||||
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
|
||||
|
||||
<link rel="stylesheet" href="layout.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>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -331,6 +201,10 @@
|
||||
} else {
|
||||
nav.insertBefore(li, nextNavButton);
|
||||
}
|
||||
|
||||
if (service.autoLoad) {
|
||||
loadService(index, service);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
@ -347,34 +221,7 @@
|
||||
function setActiveService(serviceId) {
|
||||
let currentService = services[serviceId];
|
||||
process.nextTick(() => {
|
||||
// Load service if not loaded yet
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
loadService(serviceId, currentService);
|
||||
|
||||
// Hide previous service
|
||||
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() {
|
||||
console.debug('Updating navigation');
|
||||
// Update active list element
|
||||
|
@ -134,6 +134,11 @@
|
||||
<input type="text" name="url" id="url" required>
|
||||
</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">
|
||||
<h2>Service icon</h2>
|
||||
<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() {
|
||||
const searchStr = builtInIconSearchField.value;
|
||||
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() {
|
||||
if (typeof service.name !== 'string' || service.name.length === 0) {
|
||||
console.log('Invalid name');
|
||||
@ -317,27 +345,6 @@
|
||||
}
|
||||
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>
|
||||
</body>
|
||||
</html>
|
@ -33,6 +33,7 @@ Service.requiredProperties = {
|
||||
'isImage': null,
|
||||
'url': null,
|
||||
'useFavicon': true,
|
||||
'autoLoad': false,
|
||||
};
|
||||
|
||||
export default Service;
|
Loading…
Reference in New Issue
Block a user