Add shortcuts

This commit is contained in:
William Brawner 2022-08-18 01:32:42 +00:00
parent 3cf461c167
commit 7823cee633
7 changed files with 151 additions and 46 deletions

View file

@ -7,7 +7,7 @@
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="application-name" content="Twigs">
<link rel="manifest" href="manifest.webmanifest">
<link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" href="/static/icons/icon-192x192.png">
<link rel="stylesheet" href="/static/css/style.css" />
<script type="text/javascript" src="/static/js/index.js"></script>

View file

@ -1,5 +1,7 @@
{
"name": "Pi-helper",
"description": "A PWA to facillitate enabling/disabling a Pi-hole",
"orientation": "any",
"short_name": "Pi-helper",
"theme_color": "#000000",
"background_color": "#000000",
@ -95,5 +97,55 @@
"type": "image/png",
"purpose": "maskable"
}
],
"shortcuts": [
{
"name": "Enable",
"description": "Enable the Pi-hole",
"url": "/?enable",
"icons": [
{
"src": "static/icons/shortcut-play.png",
"type": "image/png",
"sizes": "96x96"
}
]
},
{
"name": "Disable for 30 seconds",
"description": "Enable the Pi-hole",
"url": "/?disable=30",
"icons": [
{
"src": "static/icons/shortcut-pause.png",
"type": "image/png",
"sizes": "96x96"
}
]
},
{
"name": "Disable for 1 minute",
"description": "Enable the Pi-hole",
"url": "/?disable=60",
"icons": [
{
"src": "static/icons/shortcut-pause.png",
"type": "image/png",
"sizes": "96x96"
}
]
},
{
"name": "Disable for 5 minutes",
"description": "Enable the Pi-hole",
"url": "/?disable=300",
"icons": [
{
"src": "static/icons/shortcut-pause.png",
"type": "image/png",
"sizes": "96x96"
}
]
}
]
}

View file

@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"prebuild": "copyfiles -Ve src/** index.html manifest.webmanifest sw.js **/*.css **/*.png ../dist/public",
"prebuild": "copyfiles -Ve src/** index.html manifest.json sw.js **/*.css **/*.png ../dist/public",
"build": "tsc --project ./",
"test": "echo \"Error: no test specified\" && exit 1"
},

View file

@ -10,17 +10,26 @@ if ('serviceWorker' in navigator) {
});
}
function monitorChanges() {
const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
socket = new WebSocket(`${protocol}://${location.host}/`)
socket.onopen = (e) => {
console.log('socket opened', e)
if (window.location.search === '?enable') {
enable();
} else if (window.location.search.startsWith('?disable')) {
const duration = window.location.search.split('=')[1];
disable(Number.parseInt(duration));
}
window.history.replaceState(null, '', window.location.pathname);
}
let timeout: NodeJS.Timeout | null = null;
socket.onmessage = (e) => {
if (timeout) {
clearTimeout(timeout!);
}
const data = JSON.parse(e.data)
console.log('message received', data)
switch (data.status) {
case 'enabled':
if (durationInterval) {
@ -28,12 +37,18 @@ function monitorChanges() {
}
animateLogo(false)
showDisable(false)
showEnable(true)
timeout = setTimeout(() => {
showEnable(true);
timeout = null;
}, 500)
break
case 'disabled':
animateLogo(false)
showEnable(false, false)
timeout = setTimeout(() => {
showDisable(true, data.until)
timeout = null;
}, 500)
break
default:
console.error('Unhandled status', data)
@ -80,7 +95,6 @@ function setUnit(unit: string) {
}
function disableCustom() {
showEnable(false, false)
}
@ -97,33 +111,23 @@ function showEnable(show: boolean, showCustom?: boolean) {
const enableDiv = document.getElementById('enabled') as HTMLElement
const disableCustom = document.getElementById('disable-custom') as HTMLElement
if (show) {
if (disableCustom.style.opacity === '1') {
disableCustom.style.opacity = '0'
if (disableCustom.classList.contains('visible')) {
disableCustom.classList.replace('visible', 'hidden');
setTimeout(() => {
disableCustom.style.maxHeight = '0'
enableDiv.style.maxHeight = '100vh'
}, 250)
setTimeout(() => {
enableDiv.style.opacity = '1'
enableDiv.classList.add('visible')
enableDiv.classList.remove('hidden')
}, 500)
} else {
enableDiv.style.maxHeight = '100vh'
setTimeout(() => {
enableDiv.style.opacity = '1'
}, 250)
enableDiv.classList.add('visible')
enableDiv.classList.remove('hidden')
}
} else {
enableDiv.style.opacity = '0'
setTimeout(() => {
enableDiv.style.maxHeight = '0'
}, 250)
enableDiv.classList.replace('visible', 'hidden')
if (showCustom) {
setTimeout(() => {
disableCustom.style.maxHeight = '100vh'
disableCustom.classList.add('visible')
disableCustom.classList.remove('hidden')
}, 250)
setTimeout(() => {
disableCustom.style.opacity = '1'
}, 500)
}
}
}
@ -133,17 +137,11 @@ let durationInterval: any;
function showDisable(show: boolean, timestamp?: number) {
const disableDiv = document.getElementById('disabled') as HTMLElement
if (show) {
disableDiv.style.maxHeight = '100vh'
disableDiv.classList.add('visible');
disableDiv.classList.remove('hidden');
} else {
disableDiv.style.opacity = '0'
disableDiv.classList.replace('visible', 'hidden');
}
setTimeout(() => {
if (show) {
disableDiv.style.opacity = '1'
} else {
disableDiv.style.maxHeight = '0'
}
}, 250)
const duration = document.getElementById('duration') as HTMLElement
if (!timestamp) {
duration.innerText = ''
@ -163,7 +161,6 @@ function showDisable(show: boolean, timestamp?: number) {
return
}
let seconds = Math.ceil(difference / 1000)
console.log(`${until.getTime()} - ${now.getTime()} = ${seconds} seconds`)
let hours = 0
let minutes = 0
let durationText: string = '';
@ -171,12 +168,10 @@ function showDisable(show: boolean, timestamp?: number) {
hours = Math.floor(seconds / 3600)
seconds -= hours * 3600
}
console.log(`hours: ${hours} seconds: ${seconds}`)
if (seconds >= 60) {
minutes = Math.floor(seconds / 60)
seconds -= minutes * 60
}
console.log(`minutes: ${minutes} seconds: ${seconds}`)
if (hours > 0) {
durationText += `${hours.toString().padStart(2, '0')}:`
}

View file

@ -38,7 +38,8 @@
transition: all 0.25s ease;
}
html, body {
html,
body {
box-sizing: border-box;
margin: auto;
padding: 0;
@ -49,7 +50,8 @@ html, body {
color: var(--color-foreground);
}
body, div {
body,
div {
display: flex;
flex-direction: column;
max-width: 400px;
@ -62,7 +64,8 @@ body {
padding: 10px;
}
div, div > * {
div,
div>* {
width: 100%;
}
@ -82,12 +85,14 @@ button {
cursor: pointer;
}
button, p {
button,
p {
margin: 5px 0;
font-size: 1em;
}
#enabled, #disabled, #disable-custom {
.enabled,
.disabled {
max-height: 0;
opacity: 0;
overflow: hidden;
@ -131,8 +136,13 @@ button, p {
}
@keyframes spin {
from { transform: rotate(0deg) }
to { transform: rotate(360deg) }
from {
transform: rotate(0deg)
}
to {
transform: rotate(360deg)
}
}
.custom * {
@ -162,3 +172,51 @@ input {
margin: 5px 0;
padding: 10px;
}
.visible {
animation-name: show;
animation-fill-mode: both;
animation-duration: 500ms;
max-height: 100%;
opacity: 1;
}
.hidden {
animation-name: hide;
animation-fill-mode: forwards;
animation-duration: 500ms;
max-height: 0;
opacity: 0;
}
@keyframes show {
0% {
max-height: 0;
opacity: 0;
}
50% {
max-height: 100vh;
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes hide {
0% {
max-height: 100vh;
opacity: 1;
}
50% {
max-height: 100vh;
opacity: 0;
}
100% {
max-height: 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB