mirror of
https://github.com/cupcakearmy/ora.git
synced 2024-12-21 23:56:31 +00:00
dismiss & use svelte in client
This commit is contained in:
parent
a4e0bf1532
commit
4c0a60b14a
@ -30,8 +30,7 @@
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["./src/client/index.js"],
|
||||
"css": ["./src/client/index.css"]
|
||||
"js": ["./src/client/index.js"]
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": ["./icons/watch.png", "./icons/watch-alt.png", "./src/dashboard/index.html"]
|
||||
|
@ -4,6 +4,7 @@ import dayjs from 'dayjs'
|
||||
import { dashboard } from '../shared/utils'
|
||||
import { insertLog, normalizeTimestamp, DB } from '../shared/db'
|
||||
import { getSettingsWithDefaults, getUsageForHost, percentagesToBool } from '../shared/lib'
|
||||
import { DismissValidator, checkForErrors } from '../shared/validation'
|
||||
|
||||
browser.browserAction.onClicked.addListener(() => browser.tabs.create({ url: dashboard, active: true }))
|
||||
|
||||
@ -65,5 +66,13 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
case 'report':
|
||||
DB.settings.put({ key: 'lastActivity', value: new Date() })
|
||||
break
|
||||
case 'dismiss':
|
||||
const entry = {
|
||||
host: message.host,
|
||||
timestamp: new Date(),
|
||||
duration: message.duration,
|
||||
}
|
||||
if (!checkForErrors(DismissValidator, entry)) DB.dismiss.put(entry)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
85
src/client/App.svelte
Normal file
85
src/client/App.svelte
Normal file
@ -0,0 +1,85 @@
|
||||
<script>
|
||||
import { blocked } from './blocked'
|
||||
import { init } from './reporter'
|
||||
import { buttons, dismiss } from './dismiss'
|
||||
|
||||
init()
|
||||
</script>
|
||||
|
||||
<div class="wrapper" class:hidden={!$blocked}>
|
||||
<div>
|
||||
<h1>Overtime</h1>
|
||||
<div>You have no time left on this website.</div>
|
||||
<hr />
|
||||
<div class="dismiss">
|
||||
<div>
|
||||
<i> dismiss for... </i>
|
||||
</div>
|
||||
<div class="links">
|
||||
{#each buttons as button}
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<a on:click={() => dismiss(button.duration)}>{button.label}</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
position: fixed;
|
||||
color: #111;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #fefefe;
|
||||
z-index: 999999999;
|
||||
font-size: 1rem;
|
||||
padding: 1rem;
|
||||
user-select: none;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
|
||||
'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.wrapper.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.wrapper {
|
||||
background-color: #111;
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper > div {
|
||||
margin: 3rem auto;
|
||||
max-width: 25rem;
|
||||
}
|
||||
|
||||
.wrapper h1 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.dismiss {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.dismiss .links {
|
||||
margin-left: 1rem;
|
||||
margin-top: -0.1rem;
|
||||
}
|
||||
|
||||
.dismiss .links a {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
padding: 0.1em;
|
||||
transition: all 100ms ease-in-out;
|
||||
}
|
||||
|
||||
.dismiss .links a:hover {
|
||||
transform: translateX(-0.5em);
|
||||
}
|
||||
</style>
|
17
src/client/blocked.js
Normal file
17
src/client/blocked.js
Normal file
@ -0,0 +1,17 @@
|
||||
import browser from 'webextension-polyfill'
|
||||
import { readable } from 'svelte/store'
|
||||
|
||||
async function check(set) {
|
||||
if (window.document.hidden) return
|
||||
const isBlocked = await browser.runtime.sendMessage({
|
||||
type: 'check',
|
||||
host: window.location.host,
|
||||
})
|
||||
set(isBlocked)
|
||||
}
|
||||
|
||||
export const blocked = new readable(false, (set) => {
|
||||
check(set)
|
||||
const interval = setInterval(() => check(set), 1000)
|
||||
return () => clearInterval(interval)
|
||||
})
|
20
src/client/dismiss.js
Normal file
20
src/client/dismiss.js
Normal file
@ -0,0 +1,20 @@
|
||||
import dayjs from 'dayjs'
|
||||
import * as duration from 'dayjs/plugin/duration'
|
||||
dayjs.extend(duration)
|
||||
|
||||
import browser from 'webextension-polyfill'
|
||||
|
||||
export const buttons = [
|
||||
{ label: '1 minute', duration: dayjs.duration({ minutes: 1 }) },
|
||||
{ label: '5 minutes', duration: dayjs.duration({ minutes: 5 }) },
|
||||
{ label: '15 minutes', duration: dayjs.duration({ minutes: 15 }) },
|
||||
{ label: '1 hours', duration: dayjs.duration({ hours: 1 }) },
|
||||
]
|
||||
|
||||
export function dismiss(duration) {
|
||||
browser.runtime.sendMessage({
|
||||
type: 'dismiss',
|
||||
duration: duration.asMilliseconds(),
|
||||
host: window.location.host,
|
||||
})
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
.ora--wrapper {
|
||||
display: none;
|
||||
position: fixed;
|
||||
color: #111;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #eee;
|
||||
z-index: 999999999;
|
||||
font-size: 1rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.ora--wrapper {
|
||||
background-color: #111;
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.ora--wrapper div {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
|
||||
'Helvetica Neue', sans-serif;
|
||||
margin: 3rem auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ora--wrapper div h1 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
@ -1,45 +1,5 @@
|
||||
import browser from 'webextension-polyfill'
|
||||
import App from './App.svelte'
|
||||
|
||||
let wrapper
|
||||
let lastReported = 0
|
||||
|
||||
function init() {
|
||||
wrapper = window.document.createElement('div')
|
||||
wrapper.classList.add('ora--wrapper')
|
||||
wrapper.classList.add('hidden')
|
||||
|
||||
const inner = window.document.createElement('div')
|
||||
inner.innerHTML = `
|
||||
<h1>Overtime ⏱</h1>
|
||||
<p>You have no time left on this website 🥺</p>
|
||||
`
|
||||
wrapper.appendChild(inner)
|
||||
window.document.body.appendChild(wrapper)
|
||||
}
|
||||
|
||||
async function check() {
|
||||
if (window.document.hidden) return
|
||||
const isBlocked = await browser.runtime.sendMessage({
|
||||
type: 'check',
|
||||
host: window.location.host,
|
||||
})
|
||||
wrapper.style.display = isBlocked ? 'initial' : 'none'
|
||||
}
|
||||
|
||||
init()
|
||||
check()
|
||||
setInterval(check, 2000)
|
||||
|
||||
function logActivity() {
|
||||
const now = Date.now()
|
||||
// Limit reports to once every second
|
||||
if (now - lastReported < 1000) return
|
||||
lastReported = now
|
||||
browser.runtime.sendMessage({
|
||||
type: 'report',
|
||||
})
|
||||
}
|
||||
|
||||
window.document.addEventListener('mousemove', logActivity, false)
|
||||
window.document.addEventListener('keydown', logActivity, false)
|
||||
window.document.addEventListener('scroll', logActivity, false)
|
||||
new App({ target: wrapper })
|
||||
|
19
src/client/reporter.js
Normal file
19
src/client/reporter.js
Normal file
@ -0,0 +1,19 @@
|
||||
import browser from 'webextension-polyfill'
|
||||
|
||||
let lastReported = 0
|
||||
|
||||
function logActivity() {
|
||||
const now = Date.now()
|
||||
// Limit reports to once every second
|
||||
if (now - lastReported < 1000) return
|
||||
lastReported = now
|
||||
browser.runtime.sendMessage({
|
||||
type: 'report',
|
||||
})
|
||||
}
|
||||
|
||||
export function init() {
|
||||
window.document.addEventListener('mousemove', logActivity, false)
|
||||
window.document.addEventListener('keydown', logActivity, false)
|
||||
window.document.addEventListener('scroll', logActivity, false)
|
||||
}
|
@ -18,6 +18,7 @@ DB.version(2).stores({
|
||||
|
||||
DB.version(3).stores({
|
||||
settings: `key, value`,
|
||||
dismiss: `host, timestamp, duration`,
|
||||
})
|
||||
|
||||
export function normalizeTimestamp(timestamp) {
|
||||
|
@ -52,6 +52,12 @@ export function getUsageForRules(host, rules) {
|
||||
}
|
||||
|
||||
export async function getUsageForHost(host) {
|
||||
const dismiss = await DB.dismiss.where({ host }).first()
|
||||
if (dismiss) {
|
||||
const isDismissed = dj().isBefore(dj(dismiss.timestamp).add(dismiss.duration, 'ms'))
|
||||
if (isDismissed) return []
|
||||
}
|
||||
|
||||
const limit = await DB.limits.where({ host }).first()
|
||||
if (!limit) return []
|
||||
return await Promise.all(getUsageForRules(host, limit.rules))
|
||||
|
@ -20,6 +20,12 @@ export const LogValidator = Joi.object({
|
||||
timestamp: Joi.date(),
|
||||
})
|
||||
|
||||
export const DismissValidator = Joi.object({
|
||||
host: Joi.string(),
|
||||
timestamp: Joi.date(),
|
||||
duration: Joi.number(),
|
||||
})
|
||||
|
||||
export const SettingsValidator = Joi.object({
|
||||
lastActivity: Joi.date()
|
||||
.default(() => new Date())
|
||||
@ -29,9 +35,10 @@ export const SettingsValidator = Joi.object({
|
||||
})
|
||||
|
||||
export const DBValidator = Joi.object({
|
||||
limits: Joi.array().items(LimitValidator),
|
||||
logs: Joi.array().items(LogValidator),
|
||||
limits: Joi.array().items(LimitValidator).optional(),
|
||||
logs: Joi.array().items(LogValidator).optional(),
|
||||
settings: SettingsValidator.optional(),
|
||||
dismiss: Joi.array().items(DismissValidator).optional(),
|
||||
})
|
||||
|
||||
export function checkForErrors(validator, data) {
|
||||
|
Loading…
Reference in New Issue
Block a user