mirror of
https://github.com/khairul169/honey.git
synced 2025-04-28 14:59:32 +07:00
ui: Add Privacy Panel
This commit is contained in:
parent
6c8724f7ac
commit
1938eb100d
@ -1,6 +1,6 @@
|
||||
body.dark {
|
||||
--color: #EEE;
|
||||
--color2: #EEE9;
|
||||
--color2: #EEE6;
|
||||
--background: #1118;
|
||||
--bg2: #0008;
|
||||
--hover: #FFF1;
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import url(Home.css);
|
||||
@import url(Services.css);
|
||||
@import url(Settings.css);
|
||||
@import url(Settings/Settings.css);
|
||||
|
||||
.page {
|
||||
position: fixed;
|
||||
@ -80,3 +80,36 @@
|
||||
.header .text {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.sub-switch {
|
||||
display: flex;
|
||||
background: var(--bg2);
|
||||
transition: background .2s;
|
||||
margin: 16px 24px 0;
|
||||
z-index: 1;
|
||||
padding: 4px;
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
--id: inherit;
|
||||
}
|
||||
|
||||
.sub-switch::before {
|
||||
content: " ";
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: calc(var(--id) / var(--switches) * 100% + 4px - 4px * var(--id));
|
||||
width: calc(100% / var(--switches) - 4px);
|
||||
height: calc(100% - 8px);
|
||||
opacity: .25;
|
||||
background: var(--color2);
|
||||
border-radius: 12px;
|
||||
transition: left .3s, background .3s;
|
||||
}
|
||||
|
||||
.sub-switch div {
|
||||
padding: 12px;
|
||||
width: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -1,72 +0,0 @@
|
||||
.settings {
|
||||
margin: 32px auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.setting {
|
||||
background: var(--bg2);
|
||||
margin: 8px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
border-radius: 16px;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
transition: background .3s;
|
||||
}
|
||||
|
||||
.setting i {
|
||||
margin-right: 14px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.setting .name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.setting .desc {
|
||||
opacity: .6;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.setting .switch {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
min-width: 44px;
|
||||
height: 24px;
|
||||
background: #8886;
|
||||
border-radius: 100px;
|
||||
margin: 0 4px 0 auto;
|
||||
transition: border .4s, background .3s;
|
||||
}
|
||||
|
||||
.setting .switch:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--color);
|
||||
left: 4px;
|
||||
top: 50%;
|
||||
border-radius: 10px;
|
||||
transform: translateY(-50%);
|
||||
transition: left .2s, background .3s;
|
||||
}
|
||||
|
||||
.setting.checked .switch {
|
||||
background-color: #68F;
|
||||
border-color: #68F;
|
||||
}
|
||||
|
||||
.setting.checked .switch:after {
|
||||
left: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.warn {
|
||||
margin: 24px 0 -8px;
|
||||
color: #F60;
|
||||
}
|
||||
|
||||
.warn.hidden {
|
||||
display: none;
|
||||
}
|
165
css/Pages/Settings/Settings.css
Normal file
165
css/Pages/Settings/Settings.css
Normal file
@ -0,0 +1,165 @@
|
||||
.settings {
|
||||
margin: 32px auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.setting {
|
||||
background: var(--bg2);
|
||||
margin: 8px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
border-radius: 16px;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
transition: background .3s;
|
||||
}
|
||||
|
||||
.setting i {
|
||||
margin-right: 14px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.setting .name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.setting .desc {
|
||||
opacity: .6;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.setting .switch {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
min-width: 44px;
|
||||
height: 24px;
|
||||
background: #8886;
|
||||
border-radius: 100px;
|
||||
margin: 0 4px 0 auto;
|
||||
transition: border .4s, background .3s;
|
||||
}
|
||||
|
||||
.setting .switch:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--color);
|
||||
left: 4px;
|
||||
top: 50%;
|
||||
border-radius: 10px;
|
||||
transform: translateY(-50%);
|
||||
transition: left .2s, background .3s;
|
||||
}
|
||||
|
||||
.setting.checked .switch {
|
||||
background-color: #68F;
|
||||
border-color: #68F;
|
||||
}
|
||||
|
||||
.setting.checked .switch:after {
|
||||
left: calc(100% - 20px);
|
||||
}
|
||||
|
||||
#no-cookies {
|
||||
margin: 24px 0 -8px;
|
||||
color: #F60;
|
||||
}
|
||||
|
||||
#no-cookies.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.subpages {
|
||||
position: relative;
|
||||
transform: translateX(calc(var(--id) * -100%));
|
||||
transition: transform .4s, height .4s;
|
||||
}
|
||||
|
||||
.subpages > div {
|
||||
position: absolute;
|
||||
left: calc(var(--n) * 100%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#report-boxes {
|
||||
display: flex;
|
||||
text-align: left;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#report-boxes i::after {
|
||||
color: var(--color);
|
||||
text-shadow: 0 0 48px var(--color);
|
||||
padding: 16px;
|
||||
font-size: 28px;
|
||||
border-radius: 32px;
|
||||
}
|
||||
|
||||
#report-boxes .icon-https::after {
|
||||
--color: #0C7;
|
||||
content: "lock";
|
||||
}
|
||||
|
||||
#report-boxes .icon-rocket::after {
|
||||
--color: #68F;
|
||||
content: "rocket_launch";
|
||||
}
|
||||
|
||||
#report-boxes .prewarn i::after {
|
||||
--color: #EA0;
|
||||
}
|
||||
|
||||
#report-boxes .warn i::after {
|
||||
--color: #F60;
|
||||
}
|
||||
|
||||
#report-boxes .error i::after {
|
||||
--color: #F22;
|
||||
}
|
||||
|
||||
#report-boxes > div {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
min-width: 256px;
|
||||
flex: 1;
|
||||
padding: 20px 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#report-boxes .title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#report-boxes .subtitle {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.security {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: 600px;
|
||||
padding: 24px;
|
||||
margin: 120px auto 80px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.security i {
|
||||
font-size: 80px;
|
||||
}
|
||||
|
||||
.security #report-score {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.security #report {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.score::after {
|
||||
content: "%";
|
||||
font-size: 18px;
|
||||
}
|
30
index.html
30
index.html
@ -48,16 +48,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings -->
|
||||
<!-- More -->
|
||||
<div class="page" p="settings">
|
||||
<div class="back"><i></i></div>
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<i>settings</i>
|
||||
<div class="text">Settings</div>
|
||||
<div class="sub-switch" id="switch">
|
||||
<div>Privacy</div>
|
||||
<div>Settings</div>
|
||||
</div>
|
||||
<div class="subpages" id="subsettings">
|
||||
|
||||
<!-- Privacy report -->
|
||||
<div>
|
||||
<div class="security">
|
||||
<i>shield</i>
|
||||
<div>
|
||||
<div class="score"><b id="report-score"></b></div>
|
||||
<div id="report"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="report-boxes"></div>
|
||||
</div>
|
||||
|
||||
<!-- Settings -->
|
||||
<div>
|
||||
<div id="no-cookies">WARNING: due to blocked cookies, all settings will be lost after page reload</div>
|
||||
<div class="settings" id="settings"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warn none" id="no-cookies">WARNING: due to blocked cookies, all settings will be lost after page reload</div>
|
||||
<div class="settings" id="settings"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,8 @@
|
||||
import App from "../../App";
|
||||
import * as EVENTS from "./events";
|
||||
import { analyzeService } from "./security";
|
||||
import * as tiles from "./tiles"
|
||||
|
||||
|
||||
export default class Settings {
|
||||
constructor() {
|
||||
@ -10,65 +13,144 @@ export default class Settings {
|
||||
|
||||
init() {
|
||||
this.checkLocalStorage()
|
||||
this.initSecurityPanel()
|
||||
this.initSettings()
|
||||
this.initPager()
|
||||
}
|
||||
|
||||
let darkMode = this.addOnOffTile(
|
||||
initSettings() {
|
||||
let darkMode = tiles.addOnOffTile(this.config,
|
||||
"dark_mode", "Dark mode",
|
||||
"Make the colors more appropriate for low-light environments",
|
||||
"dark_mode", EVENTS.onThemeChange
|
||||
)
|
||||
document.getElementById("theme-switcher").addEventListener("click", () => {
|
||||
darkMode.click()
|
||||
})
|
||||
|
||||
this.addOnOffTile(
|
||||
tiles.addOnOffTile(this.config,
|
||||
"open_in_new", "Open in new tab",
|
||||
"Clicking on application will open it in a new browser tab",
|
||||
"open_new_tab", EVENTS.onNewTabChange
|
||||
)
|
||||
|
||||
this.addOnOffTile(
|
||||
tiles.addOnOffTile(this.config,
|
||||
"blur_on", "Enable blur",
|
||||
"Improves UI sweetness but may have a huge impact on performance",
|
||||
"blur", EVENTS.onBlurChange
|
||||
)
|
||||
|
||||
this.addOnOffTile(
|
||||
tiles.addOnOffTile(this.config,
|
||||
"animation", "Animations",
|
||||
"Show nice and fancy page transitions for improved experience",
|
||||
"animations", EVENTS.onAnimationChange
|
||||
)
|
||||
|
||||
document.getElementById("theme-switcher").addEventListener("click", () => {
|
||||
darkMode.click()
|
||||
})
|
||||
}
|
||||
|
||||
addOnOffTile(icon, name, desc, key, func) {
|
||||
let item = document.createElement("div")
|
||||
item.classList.add("setting")
|
||||
item.innerHTML = `
|
||||
<i>${icon}</i>
|
||||
<div class="text">
|
||||
<div class="name">${name}</div>
|
||||
<div class="desc">${desc}</div>
|
||||
</div>
|
||||
<div class="switch"></div>`
|
||||
initPager() {
|
||||
let switcher = document.getElementById("switch")
|
||||
let buttons = switcher.children
|
||||
let subsettings = document.getElementById("subsettings")
|
||||
|
||||
let handleState = () => {
|
||||
let c = item.classList
|
||||
if (this.config.get(key)) c.add("checked")
|
||||
else c.remove("checked")
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
let button = buttons[i]
|
||||
subsettings.children[i].setAttribute("style", `--n: ${i}`)
|
||||
|
||||
button.addEventListener("click", () => {
|
||||
let calculatedHeight = subsettings.children[i].offsetHeight
|
||||
subsettings.style.height = `${calculatedHeight}px`
|
||||
subsettings.parentNode.setAttribute("style", `--id: ${i}`)
|
||||
switcher.setAttribute("style", `--switches: ${buttons.length}`)
|
||||
})
|
||||
}
|
||||
let write = () => {
|
||||
let target_value = !this.config.get(key)
|
||||
this.config.set(key, target_value)
|
||||
|
||||
buttons[0].click()
|
||||
}
|
||||
|
||||
initSecurityPanel() {
|
||||
let stats = {
|
||||
total: 0,
|
||||
secure: 0,
|
||||
thirdParties: 0
|
||||
}
|
||||
for (let service of this.config.getServices()) {
|
||||
let analysis = analyzeService(service.href)
|
||||
stats.total++
|
||||
stats.secure += analysis.isSecure
|
||||
stats.thirdParties += analysis.isThirdParty
|
||||
}
|
||||
let f = () => {func(this.config)}
|
||||
|
||||
item.addEventListener("click", write)
|
||||
item.addEventListener("click", handleState)
|
||||
if (func) item.addEventListener("click", f)
|
||||
let secure_pp = 100 * stats.secure / stats.total
|
||||
let indepencence_pp = 100 * (stats.total - stats.thirdParties) / stats.total
|
||||
let privacy_score = secure_pp * 0.5 + indepencence_pp * 0.5
|
||||
|
||||
handleState()
|
||||
if (func && this.config.changed(key)) f()
|
||||
document.getElementById("settings").appendChild(item)
|
||||
return item
|
||||
let privacy_report;
|
||||
if (privacy_score == 100) {
|
||||
privacy_report = "Data is sent securely and no third-party services are involved."
|
||||
}
|
||||
else if (privacy_score >= 85) {
|
||||
privacy_report = "Great privacy overall, but at some point it could be better."
|
||||
}
|
||||
else if (privacy_score >= 40) {
|
||||
privacy_report = "Decent privacy. You should pay attention what you're doing."
|
||||
}
|
||||
else privacy_report = "Transferring data is insecure and vulnerable to various threats."
|
||||
|
||||
document.getElementById("report-score").innerText = Math.round(privacy_score)
|
||||
document.getElementById("report").innerText = privacy_report
|
||||
|
||||
const phrase = " of the listed services are using secure connections"
|
||||
tiles.privacyBox(secure_pp, "icon-https", [
|
||||
{
|
||||
"from": 0,
|
||||
"name": "No encryption",
|
||||
"desc": "None" + phrase
|
||||
},
|
||||
{
|
||||
"from": 0.1,
|
||||
"name": "Poor encryption",
|
||||
"desc": "Only some" + phrase
|
||||
},
|
||||
{
|
||||
"from": 60,
|
||||
"name": "Fair encryption",
|
||||
"desc": "Most" + phrase
|
||||
},
|
||||
{
|
||||
"from": 90,
|
||||
"name": "Good encryption",
|
||||
"desc": "Nearly all" + phrase
|
||||
},
|
||||
{
|
||||
"from": 100,
|
||||
"name": "Full encryption",
|
||||
"desc": "All" + phrase
|
||||
}
|
||||
])
|
||||
|
||||
tiles.privacyBox(indepencence_pp, "icon-rocket", [
|
||||
{
|
||||
"from": 0,
|
||||
"name": "Not independent",
|
||||
"desc": "This server lists a lot of 3rd party services"
|
||||
},
|
||||
{
|
||||
"from": 40,
|
||||
"name": "Partially independent",
|
||||
"desc": "This server lists many 3rd party services"
|
||||
},
|
||||
{
|
||||
"from": 65,
|
||||
"name": "Mostly independent",
|
||||
"desc": "This server still lists some 3rd party services"
|
||||
},
|
||||
{
|
||||
"from": 100,
|
||||
"name": "Fully independent",
|
||||
"desc": "This server is free of 3rd party services"
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
checkLocalStorage() {
|
||||
|
32
js/UI/Settings/security.js
Normal file
32
js/UI/Settings/security.js
Normal file
@ -0,0 +1,32 @@
|
||||
export function analyzeService(url) {
|
||||
let isSiteSecure = (
|
||||
window.location.protocol == "https:" ||
|
||||
window.location.hostname == "localhost"
|
||||
)
|
||||
|
||||
let domain_base = window.location.hostname
|
||||
let isSecure = false
|
||||
let isThirdParty = true
|
||||
|
||||
if (url.startsWith("https://")) {
|
||||
isSecure = true
|
||||
}
|
||||
else if (!["http", "https"].includes(url.split("://")[0])) {
|
||||
isSecure = isSiteSecure
|
||||
}
|
||||
|
||||
if (isThirdParty) {
|
||||
let domain = url.split("://")
|
||||
if (domain.length > 1) {
|
||||
domain = domain[1]
|
||||
domain = domain.split("/")[0]
|
||||
domain = domain.split(":")[0]
|
||||
isThirdParty = !domain.includes(domain_base)
|
||||
}
|
||||
else {
|
||||
isThirdParty = false
|
||||
}
|
||||
}
|
||||
|
||||
return {isSecure, isThirdParty}
|
||||
}
|
59
js/UI/Settings/tiles.js
Normal file
59
js/UI/Settings/tiles.js
Normal file
@ -0,0 +1,59 @@
|
||||
export function addOnOffTile(conf, icon, name, desc, key, func) {
|
||||
let item = document.createElement("div")
|
||||
item.classList.add("setting")
|
||||
item.innerHTML = `
|
||||
<i>${icon}</i>
|
||||
<div class="text">
|
||||
<div class="name">${name}</div>
|
||||
<div class="desc">${desc}</div>
|
||||
</div>
|
||||
<div class="switch"></div>`
|
||||
|
||||
let handleState = () => {
|
||||
let c = item.classList
|
||||
if (conf.get(key)) c.add("checked")
|
||||
else c.remove("checked")
|
||||
}
|
||||
let write = () => {
|
||||
let target_value = !conf.get(key)
|
||||
conf.set(key, target_value)
|
||||
}
|
||||
let f = () => {func(conf)}
|
||||
|
||||
item.addEventListener("click", write)
|
||||
item.addEventListener("click", handleState)
|
||||
if (func) item.addEventListener("click", f)
|
||||
|
||||
handleState()
|
||||
if (func && conf.changed(key)) f()
|
||||
document.getElementById("settings").appendChild(item)
|
||||
return item
|
||||
}
|
||||
|
||||
export function privacyBox(privacyScore, icon_class, levels) {
|
||||
let name, desc
|
||||
levels = levels.reverse()
|
||||
|
||||
for (let i in levels) {
|
||||
let item = levels[i]
|
||||
if (item.from <= privacyScore) {
|
||||
name = levels[i].name
|
||||
desc = levels[i].desc
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let item = document.createElement("div")
|
||||
item.innerHTML = `<i class="${icon_class}"></i>
|
||||
<div>
|
||||
<div class="title">${name}</div>
|
||||
<div class="subtitle">${desc}</div>
|
||||
</div>`
|
||||
|
||||
if (privacyScore < 30) item.classList.add("error")
|
||||
else if (privacyScore < 90) item.classList.add("warn")
|
||||
else if (privacyScore < 100) item.classList.add("prewarn")
|
||||
|
||||
document.getElementById("report-boxes").appendChild(item)
|
||||
return item
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user