Merge branch 'develop'

This commit is contained in:
Alice Gaudon 2020-07-14 11:11:10 +02:00
commit dea6c43573
10 changed files with 209 additions and 135 deletions

View File

@ -3,6 +3,7 @@
"files": "ts/files.ts", "files": "ts/files.ts",
"layout": "sass/layout.scss", "layout": "sass/layout.scss",
"index": "sass/index.scss", "index": "sass/index.scss",
"error": "sass/error.scss",
"service-settings": "sass/service-settings.scss" "service-settings": "sass/service-settings.scss"
} }
} }

22
frontend/error.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>An error occured</title>
<meta http-equiv="Content-Security-Policy"
content="style-src 'self' 'unsafe-inline' https://use.fontawesome.com; font-src 'self' https://use.fontawesome.com; script-src 'self'">
<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="css/layout.css">
<link rel="stylesheet" href="css/error.css">
</head>
<body>
<h1>Oops</h1>
<p>An error has occurred while loading this document.</p>
<p>Please check your internet connection and this service's URL.</p>
<p>If you can load the actual URL in your favorite web browser, please file us a bug report.</p>
</body>
</html>

9
frontend/sass/error.scss Normal file
View File

@ -0,0 +1,9 @@
html, body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-self: center;
}

View File

@ -7,6 +7,16 @@ body {
flex-direction: row; flex-direction: row;
} }
#service-buttons {
flex-grow: 1;
overflow: hidden auto;
}
*:focus {
outline-color: rgb(118, 93, 176);
}
#navigation { #navigation {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -16,16 +26,43 @@ body {
body.fullscreen & { body.fullscreen & {
display: none; display: none;
} }
}
#navigation > :not(#service-buttons) { > :not(#service-buttons) {
flex-shrink: 0; flex-shrink: 0;
} }
#service-buttons { button {
flex-grow: 1; position: relative;
display: block;
width: var(--nav-width);
height: var(--nav-width);
margin: 0;
padding: 0;
overflow: hidden auto; color: #fff;
border: 0;
background: transparent;
cursor: pointer;
border-radius: 0;
&:focus {
outline: none;
background: #fff3 !important;
}
&:hover {
background-color: #fff3;
}
i {
font-size: calc(var(--nav-width) / 2);
}
img {
width: calc(var(--nav-width) / 2);
}
}
} }
#service-selector { #service-selector {
@ -34,20 +71,76 @@ body {
padding: 0; padding: 0;
list-style: none; list-style: none;
}
#service-selector::-webkit-scrollbar { &::-webkit-scrollbar {
width: 6px; width: 6px;
} }
#service-selector [draggable] { li {
user-select: none; position: relative;
background-color: rgb(43, 43, 43);
}
#service-selector [draggable] img { button {
-webkit-user-drag: none; border-radius: 0;
user-drag: none; }
&.active button {
background-color: #fff2;
}
&.loading, &.loaded {
button::before {
content: "";
display: block;
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
height: 75%;
border-right: 4px solid #ffffff3e;
}
}
&.loading button::before {
animation: loading-button-after linear 500ms infinite alternate;
height: 45%;
@keyframes loading-button-after {
from {
opacity: 0.1;
}
to {
opacity: 0.5;
}
}
}
}
[draggable] {
user-select: none;
background-color: rgb(43, 43, 43);
img {
-webkit-user-drag: none;
user-drag: none;
}
}
.drag-target-self button::after {
height: var(--nav-width);
border: 1px dashed #fff;
transform: none;
}
.drag-target button::after {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
.drag-target::after {
top: 75% !important;
}
} }
#service-selector .drag-target button::after, #service-selector .drag-target button::after,
@ -60,96 +153,6 @@ body {
background: #fff9; background: #fff9;
} }
#service-selector .drag-target-self button::after {
height: var(--nav-width);
border: 1px dashed #fff;
transform: none;
}
#service-selector .drag-target button::after {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
#service-selector .drag-target::after {
top: 75% !important;
}
*:focus {
outline-color: rgb(118, 93, 176);
}
#navigation button {
position: relative;
display: block;
width: var(--nav-width);
height: var(--nav-width);
margin: 0;
padding: 0;
color: #fff;
border: 0;
background: transparent;
cursor: pointer;
}
#navigation button:focus {
outline: none;
background: #fff3 !important;
}
#navigation button img {
width: calc(var(--nav-width) / 2);
}
#navigation button i {
font-size: calc(var(--nav-width) / 2);
}
#service-selector li {
position: relative;
}
#service-selector li button,
#navigation > button {
border-radius: 0;
}
#service-selector li.active button {
background-color: #fff2;
}
#service-selector li.loaded button::before {
content: "";
display: block;
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
height: 75%;
border-right: 4px solid #ffffff3e;
}
/*#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 { #history {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -71,21 +71,27 @@ form {
margin: 8px; margin: 8px;
grid-template-columns: 0fr auto 0fr; grid-template-columns: 0fr auto 0fr;
}
.form-group > * { &.no-expand {
margin: 8px; display: flex;
padding: 8px; flex-direction: row;
white-space: nowrap; justify-content: center;
align-self: center; }
}
.form-group > :first-child { > * {
justify-self: end; margin: 8px;
} padding: 8px;
white-space: nowrap;
align-self: center;
}
.form-group > :not(:first-child) { > :first-child {
margin-left: 8px; justify-self: end;
}
> :not(:first-child) {
margin-left: 8px;
}
} }
label.form-group { label.form-group {

View File

@ -42,11 +42,18 @@
<textarea name="customCSS" id="custom-css" rows="3"></textarea> <textarea name="customCSS" id="custom-css" rows="3"></textarea>
</div> </div>
<div class="form-group"> <fieldset>
<label for="custom-user-agent">Custom UserAgent (i.e. google services)</label> <legend>Disguise</legend>
<input type="text" name="customUserAgent" id="custom-user-agent"> <div class="form-group">
<button type="button" id="userAgentAutoFill">Auto-fill</button> <label for="custom-user-agent">Custom UserAgent (i.e. google services)</label>
</div> <input type="text" name="customUserAgent" id="custom-user-agent">
</div>
<div class="form-group no-expand">
<button type="button" id="userAgentAutoFillFirefox">Disguise as firefox</button>
<button type="button" id="userAgentAutoFillChrome">Disguise as chrome</button>
</div>
</fieldset>
<div id="icon-choice"> <div id="icon-choice">
<div class="form-group-header"> <div class="form-group-header">

View File

@ -26,7 +26,7 @@ let securityButton: HTMLElement | null,
backButton: HTMLElement | null, backButton: HTMLElement | null,
refreshButton: HTMLElement | null; refreshButton: HTMLElement | null;
let addButton, settingsButton; let addButton, settingsButton;
let emptyPage: string; let emptyPage: string, errorPage: string;
let urlPreview: HTMLElement | null; let urlPreview: HTMLElement | null;
let serviceSelector: HTMLElement | null; let serviceSelector: HTMLElement | null;
@ -128,7 +128,7 @@ function openServiceContextMenu(event: Event, serviceId: number) {
} }
ipcRenderer.on('data', (event, appData, iconSets, actualSelectedService, emptyUrl, config) => { ipcRenderer.on('data', (event, appData, iconSets, actualSelectedService, emptyUrl, errorUrl, config) => {
// App info // App info
appInfo.title = appData.title; appInfo.title = appData.title;
@ -179,6 +179,7 @@ ipcRenderer.on('data', (event, appData, iconSets, actualSelectedService, emptyUr
// Empty // Empty
emptyPage = emptyUrl; emptyPage = emptyUrl;
errorPage = errorUrl;
// Url preview element // Url preview element
urlPreview = document.getElementById("url-preview"); urlPreview = document.getElementById("url-preview");
@ -212,13 +213,15 @@ ipcRenderer.on('data', (event, appData, iconSets, actualSelectedService, emptyUr
document.documentElement.style.setProperty('--nav-width', config.bigNavBar ? '64px' : '48px'); document.documentElement.style.setProperty('--nav-width', config.bigNavBar ? '64px' : '48px');
}); });
function removeServiceFeatures(id: number): HTMLElement | null { function removeServiceFeatures(id: number): Element | null {
// Remove nav // Remove nav
const nav = document.querySelector('#service-selector'); const nav = document.querySelector('#service-selector');
let oldNavButton: HTMLElement | null = null; let oldNavButton: HTMLElement | null = null;
let nextSibling: Element | null = null;
if (nav) { if (nav) {
oldNavButton = nav.querySelector('li:nth-of-type(' + (id + 1) + ')'); oldNavButton = nav.querySelector('li:nth-of-type(' + (id + 1) + ')');
if (oldNavButton) { if (oldNavButton) {
nextSibling = oldNavButton.nextElementSibling;
nav.removeChild(oldNavButton); nav.removeChild(oldNavButton);
} }
} }
@ -228,19 +231,21 @@ function removeServiceFeatures(id: number): HTMLElement | null {
document.querySelector('#services')?.removeChild(services[id].view); document.querySelector('#services')?.removeChild(services[id].view);
} }
return oldNavButton; return nextSibling;
} }
ipcRenderer.on('updateService', (e, id, data) => { ipcRenderer.on('updateService', (e, id, data) => {
if (id === null) { if (id === null) {
console.log('Adding new service');
services.push(data); services.push(data);
createService(services.length - 1); createService(services.length - 1);
} else { } else {
const oldNavButton = removeServiceFeatures(id); console.log('Updating existing service', id);
const nextSibling = removeServiceFeatures(id);
// Create new service // Create new service
services[id] = data; services[id] = data;
createService(id, oldNavButton ? oldNavButton.nextElementSibling : null); createService(id, nextSibling);
if (parseInt(selectedService) === id) { if (parseInt(selectedService) === id) {
setActiveService(id); setActiveService(id);
} }
@ -324,6 +329,8 @@ function createService(index: number, nextNavButton?: Element | null) {
iconProperties.faIcon.split(' ').forEach((cl: string) => { iconProperties.faIcon.split(' ').forEach((cl: string) => {
icon.classList.add(cl); icon.classList.add(cl);
}); });
} else {
icon.classList.add('fas', 'fa-circle');
} }
} }
@ -464,6 +471,7 @@ function loadService(serviceId: number, service: any) {
document.querySelector('#services > .loader')?.classList.remove('hidden'); document.querySelector('#services > .loader')?.classList.remove('hidden');
service.view = document.createElement('webview'); service.view = document.createElement('webview');
updateNavigation(); // Start loading animation
service.view.setAttribute('enableRemoteModule', 'false'); service.view.setAttribute('enableRemoteModule', 'false');
service.view.setAttribute('partition', 'persist:service_' + service.partition); service.view.setAttribute('partition', 'persist:service_' + service.partition);
service.view.setAttribute('autosize', 'true'); service.view.setAttribute('autosize', 'true');
@ -473,6 +481,11 @@ function loadService(serviceId: number, service: any) {
// eventual future human mistakes. // eventual future human mistakes.
service.view.setAttribute('webpreferences', 'contextIsolation=yes'); service.view.setAttribute('webpreferences', 'contextIsolation=yes');
// Error handling
service.view.addEventListener('did-fail-load', (e: Event) => {
service.view.setAttribute('src', errorPage);
});
// Append element to DOM // Append element to DOM
document.querySelector('#services')?.appendChild(service.view); document.querySelector('#services')?.appendChild(service.view);
@ -643,10 +656,16 @@ function updateNavigation() {
for (let i = 0; i < services.length; i++) { for (let i = 0; i < services.length; i++) {
const service = services[i]; const service = services[i];
if (!service.li) continue;
// Active? // Active?
if (parseInt(selectedService) === i) service.li.classList.add('active'); if (parseInt(selectedService) === i) service.li.classList.add('active');
else service.li.classList.remove('active'); else service.li.classList.remove('active');
// Loading?
if (service.view && !service.viewReady) service.li.classList.add('loading');
else service.li.classList.remove('loading');
// Loaded? // Loaded?
if (service.viewReady) service.li.classList.add('loaded'); if (service.viewReady) service.li.classList.add('loaded');
else service.li.classList.remove('loaded'); else service.li.classList.remove('loaded');

View File

@ -72,10 +72,16 @@ document.addEventListener('DOMContentLoaded', () => {
ipcRenderer.send('sync-settings'); ipcRenderer.send('sync-settings');
document.getElementById('userAgentAutoFill')?.addEventListener('click', () => { document.getElementById('userAgentAutoFillFirefox')?.addEventListener('click', () => {
let customUserAgent = document.getElementById('custom-user-agent'); let customUserAgent = document.getElementById('custom-user-agent');
if (customUserAgent) { if (customUserAgent) {
(<HTMLInputElement>customUserAgent).value = 'Mozilla/5.0 (X11; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0'; (<HTMLInputElement>customUserAgent).value = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0';
}
});
document.getElementById('userAgentAutoFillChrome')?.addEventListener('click', () => {
let customUserAgent = document.getElementById('custom-user-agent');
if (customUserAgent) {
(<HTMLInputElement>customUserAgent).value = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36';
} }
}); });
}); });
@ -200,7 +206,7 @@ function save() {
ipcRenderer.send('saveService', serviceId, service); ipcRenderer.send('saveService', serviceId, service);
remote.getCurrentWindow().close(); 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) {

View File

@ -1,6 +1,6 @@
{ {
"name": "tabs", "name": "tabs",
"version": "1.1.3", "version": "1.1.4",
"description": "Persistent and separate browser tabs in one window.", "description": "Persistent and separate browser tabs in one window.",
"author": { "author": {
"name": "Alice Gaudon", "name": "Alice Gaudon",

View File

@ -147,6 +147,7 @@ export default class MainWindow extends Window {
Meta.ICON_SETS, Meta.ICON_SETS,
this.activeService, this.activeService,
path.resolve(Meta.RESOURCES_PATH, 'empty.html'), path.resolve(Meta.RESOURCES_PATH, 'empty.html'),
path.resolve(Meta.RESOURCES_PATH, 'error.html'),
this.config this.config
); );
} }