mirror of
https://codeberg.org/keyoxide/keyoxide-web.git
synced 2024-12-23 07:19:28 -07:00
Begin support for full SSR
This commit is contained in:
parent
c747d39c7d
commit
d3c8f5204d
4 changed files with 285 additions and 100 deletions
|
@ -7,50 +7,6 @@ class Claim extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
// Call super
|
// Call super
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// Shadow root
|
|
||||||
this.attachShadow({mode: 'open'});
|
|
||||||
|
|
||||||
// Details element
|
|
||||||
const details = document.createElement('details');
|
|
||||||
details.setAttribute('class', 'kx-item');
|
|
||||||
|
|
||||||
// Summary element
|
|
||||||
const summary = details.appendChild(document.createElement('summary'));
|
|
||||||
|
|
||||||
// Info
|
|
||||||
const info = summary.appendChild(document.createElement('div'));
|
|
||||||
info.setAttribute('class', 'info');
|
|
||||||
|
|
||||||
// Info > Service provider
|
|
||||||
const serviceProvider = info.appendChild(document.createElement('p'));
|
|
||||||
serviceProvider.setAttribute('class', 'subtitle');
|
|
||||||
|
|
||||||
// Info > Profile
|
|
||||||
const profile = info.appendChild(document.createElement('p'));
|
|
||||||
profile.setAttribute('class', 'title');
|
|
||||||
|
|
||||||
// Icons
|
|
||||||
const icons = summary.appendChild(document.createElement('div'));
|
|
||||||
icons.setAttribute('class', 'icons');
|
|
||||||
|
|
||||||
const icons__verificationStatus = icons.appendChild(document.createElement('div'));
|
|
||||||
icons__verificationStatus.setAttribute('class', 'verificationStatus');
|
|
||||||
|
|
||||||
const icons__verificationStatus__inProgress = icons__verificationStatus.appendChild(document.createElement('div'));
|
|
||||||
icons__verificationStatus__inProgress.setAttribute('class', 'inProgress');
|
|
||||||
|
|
||||||
// Details content
|
|
||||||
const content = details.appendChild(document.createElement('div'));
|
|
||||||
content.setAttribute('class', 'content');
|
|
||||||
|
|
||||||
// Load CSS stylesheet
|
|
||||||
const linkCSS = document.createElement('link');
|
|
||||||
linkCSS.setAttribute('rel', 'stylesheet');
|
|
||||||
linkCSS.setAttribute('href', '/static/kx-styles.css');
|
|
||||||
|
|
||||||
// Attach the elements to the shadow DOM
|
|
||||||
this.shadowRoot.append(linkCSS, details);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeChangedCallback(name, oldValue, newValue) {
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
@ -69,38 +25,38 @@ class Claim extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateContent(value) {
|
updateContent(value) {
|
||||||
const shadow = this.shadowRoot;
|
const root = this;
|
||||||
const claim = new doip.Claim(JSON.parse(value));
|
const claim = new doip.Claim(JSON.parse(value));
|
||||||
|
|
||||||
switch (claim.matches[0].serviceprovider.name) {
|
switch (claim.matches[0].serviceprovider.name) {
|
||||||
case 'dns':
|
case 'dns':
|
||||||
case 'xmpp':
|
case 'xmpp':
|
||||||
case 'irc':
|
case 'irc':
|
||||||
shadow.querySelector('.info .subtitle').innerText = claim.matches[0].serviceprovider.name.toUpperCase();
|
root.querySelector('.info .subtitle').innerText = claim.matches[0].serviceprovider.name.toUpperCase();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
shadow.querySelector('.info .subtitle').innerText = claim.matches[0].serviceprovider.name;
|
root.querySelector('.info .subtitle').innerText = claim.matches[0].serviceprovider.name;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
shadow.querySelector('.info .title').innerText = claim.matches[0].profile.display;
|
root.querySelector('.info .title').innerText = claim.matches[0].profile.display;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (claim.status === 'verified') {
|
if (claim.status === 'verified') {
|
||||||
shadow.querySelector('.icons .verificationStatus').setAttribute('data-value', claim.verification.result ? 'success' : 'failed');
|
root.querySelector('.icons .verificationStatus').setAttribute('data-value', claim.verification.result ? 'success' : 'failed');
|
||||||
} else {
|
} else {
|
||||||
shadow.querySelector('.icons .verificationStatus').setAttribute('data-value', 'running');
|
root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'running');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
shadow.querySelector('.icons .verificationStatus').setAttribute('data-value', 'failed');
|
root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
const elContent = shadow.querySelector('.content');
|
const elContent = root.querySelector('.content');
|
||||||
elContent.innerHTML = ``;
|
elContent.innerHTML = ``;
|
||||||
|
|
||||||
// Handle failed ambiguous claim
|
// Handle failed ambiguous claim
|
||||||
if (claim.status === 'verified' && !claim.verification.result && claim.isAmbiguous()) {
|
if (claim.status === 'verified' && !claim.verification.result && claim.isAmbiguous()) {
|
||||||
shadow.querySelector('.info .subtitle').innerText = '---';
|
root.querySelector('.info .subtitle').innerText = '---';
|
||||||
|
|
||||||
const subsection_alert = elContent.appendChild(document.createElement('div'));
|
const subsection_alert = elContent.appendChild(document.createElement('div'));
|
||||||
subsection_alert.setAttribute('class', 'subsection');
|
subsection_alert.setAttribute('class', 'subsection');
|
||||||
|
|
|
@ -7,40 +7,6 @@ class Key extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
// Call super
|
// Call super
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// Shadow root
|
|
||||||
this.attachShadow({mode: 'open'});
|
|
||||||
|
|
||||||
// Details element
|
|
||||||
const details = document.createElement('details');
|
|
||||||
details.setAttribute('class', 'kx-item');
|
|
||||||
|
|
||||||
// Summary element
|
|
||||||
const summary = details.appendChild(document.createElement('summary'));
|
|
||||||
|
|
||||||
// Info
|
|
||||||
const info = summary.appendChild(document.createElement('div'));
|
|
||||||
info.setAttribute('class', 'info');
|
|
||||||
|
|
||||||
// Info > Protocol
|
|
||||||
const serviceProvider = info.appendChild(document.createElement('p'));
|
|
||||||
serviceProvider.setAttribute('class', 'subtitle');
|
|
||||||
|
|
||||||
// Info > Fingerprint
|
|
||||||
const profile = info.appendChild(document.createElement('p'));
|
|
||||||
profile.setAttribute('class', 'title');
|
|
||||||
|
|
||||||
// Details content
|
|
||||||
const content = details.appendChild(document.createElement('div'));
|
|
||||||
content.setAttribute('class', 'content');
|
|
||||||
|
|
||||||
// Load CSS stylesheet
|
|
||||||
const linkCSS = document.createElement('link');
|
|
||||||
linkCSS.setAttribute('rel', 'stylesheet');
|
|
||||||
linkCSS.setAttribute('href', '/static/kx-styles.css');
|
|
||||||
|
|
||||||
// Attach the elements to the shadow DOM
|
|
||||||
this.shadowRoot.append(linkCSS, details);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeChangedCallback(name, oldValue, newValue) {
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
@ -48,23 +14,23 @@ class Key extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateContent(value) {
|
updateContent(value) {
|
||||||
const shadow = this.shadowRoot;
|
const root = this;
|
||||||
const data = JSON.parse(value);
|
const data = JSON.parse(value);
|
||||||
|
|
||||||
shadow.querySelector('.info .subtitle').innerText = data.key.fetchMethod;
|
root.querySelector('.info .subtitle').innerText = data.key.fetchMethod;
|
||||||
shadow.querySelector('.info .title').innerText = data.fingerprint;
|
root.querySelector('.info .title').innerText = data.fingerprint;
|
||||||
|
|
||||||
const elContent = shadow.querySelector('.content');
|
const elContent = root.querySelector('.content');
|
||||||
elContent.innerHTML = ``;
|
elContent.innerHTML = ``;
|
||||||
|
|
||||||
// Link to key
|
// Link to key
|
||||||
const subsection1 = elContent.appendChild(document.createElement('div'));
|
const subsection_links = elContent.appendChild(document.createElement('div'));
|
||||||
subsection1.setAttribute('class', 'subsection');
|
subsection_links.setAttribute('class', 'subsection');
|
||||||
const subsection1_icon = subsection1.appendChild(document.createElement('img'));
|
const subsection_links_icon = subsection_links.appendChild(document.createElement('img'));
|
||||||
subsection1_icon.setAttribute('src', '/static/img/link.png');
|
subsection_links_icon.setAttribute('src', '/static/img/link.png');
|
||||||
const subsection1_text = subsection1.appendChild(document.createElement('div'));
|
const subsection_links_text = subsection_links.appendChild(document.createElement('div'));
|
||||||
|
|
||||||
const profile_link = subsection1_text.appendChild(document.createElement('p'));
|
const profile_link = subsection_links_text.appendChild(document.createElement('p'));
|
||||||
profile_link.innerHTML = `Key link: <a href="${data.key.uri}">${data.key.uri}</a>`;
|
profile_link.innerHTML = `Key link: <a href="${data.key.uri}">${data.key.uri}</a>`;
|
||||||
|
|
||||||
elContent.appendChild(document.createElement('hr'));
|
elContent.appendChild(document.createElement('hr'));
|
||||||
|
|
|
@ -472,3 +472,228 @@ dialog p {
|
||||||
dialog p:first-of-type {
|
dialog p:first-of-type {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* KX-ITEM */
|
||||||
|
.kx-item details {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.kx-item details p {
|
||||||
|
margin: 0;
|
||||||
|
word-break: break-word;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.kx-item details a {
|
||||||
|
color: var(--blue-700);
|
||||||
|
}
|
||||||
|
.kx-item details hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 2px solid var(--purple-100);
|
||||||
|
}
|
||||||
|
.kx-item details .content {
|
||||||
|
padding: 12px;
|
||||||
|
border: solid 3px var(--purple-100);
|
||||||
|
border-top: 0px;
|
||||||
|
border-radius: 0px 0px 8px 8px;
|
||||||
|
}
|
||||||
|
.kx-item details summary {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: var(--purple-100);
|
||||||
|
border: solid 3px var(--purple-100);
|
||||||
|
border-radius: 8px;
|
||||||
|
list-style: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.kx-item details summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.kx-item details summary:hover, summary:focus {
|
||||||
|
border-color: var(--purple-400);
|
||||||
|
}
|
||||||
|
details[open] summary {
|
||||||
|
border-radius: 8px 8px 0px 0px;
|
||||||
|
}
|
||||||
|
.kx-item details summary .info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.kx-item details summary .info .title {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
.kx-item details summary .claim__description p {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
.kx-item details summary .claim__links p, p.subtle-links {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--grey-700);
|
||||||
|
}
|
||||||
|
.kx-item details summary .claim__links a, summary .claim__links span, p.subtle-links a {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
color: var(--grey-700);
|
||||||
|
}
|
||||||
|
.kx-item details summary .subtitle {
|
||||||
|
color: var(--purple-700);
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 100%;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 2rem;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus::after {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus .inProgress {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.4s ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus[data-value="success"] {
|
||||||
|
content: "v";
|
||||||
|
background-color: var(--green-600);
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus[data-value="success"]::after {
|
||||||
|
content: "✔";
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus[data-value="failed"] {
|
||||||
|
background-color: var(--red-400);
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus[data-value="failed"]::after {
|
||||||
|
content: "✕";
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus[data-value="running"] .inProgress {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kx-item details .subsection {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.kx-item details .subsection > img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kx-item details .inProgress {
|
||||||
|
font-size: 10px;
|
||||||
|
margin: 50px auto;
|
||||||
|
text-indent: -9999em;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--purple-400);
|
||||||
|
background: -moz-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%);
|
||||||
|
background: -webkit-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%);
|
||||||
|
background: -o-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%);
|
||||||
|
background: -ms-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%);
|
||||||
|
background: linear-gradient(to right, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%);
|
||||||
|
position: relative;
|
||||||
|
-webkit-animation: load3 1.4s infinite linear;
|
||||||
|
animation: load3 1.4s infinite linear;
|
||||||
|
-webkit-transform: translateZ(0);
|
||||||
|
-ms-transform: translateZ(0);
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
.kx-item details .inProgress:before {
|
||||||
|
width: 50%;
|
||||||
|
height: 50%;
|
||||||
|
background: var(--purple-400);
|
||||||
|
border-radius: 100% 0 0 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
.kx-item details .inProgress:after {
|
||||||
|
background: var(--purple-100);
|
||||||
|
width: 65%;
|
||||||
|
height: 65%;
|
||||||
|
border-radius: 50%;
|
||||||
|
content: '';
|
||||||
|
margin: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.kx-item details button {
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
margin-right: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background-color: #fff;
|
||||||
|
border: solid 2px var(--purple-400);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.kx-item details button:hover {
|
||||||
|
background-color: var(--purple-500);
|
||||||
|
border-color: var(--purple-500);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
.kx-item details summary .claim__description p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
.kx-item details summary .claim__links a, p.subtle-links a {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 480px) {
|
||||||
|
summary .claim__description p {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.kx-item details summary .verificationStatus {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
.kx-item details .inProgress {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes load3 {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes load3 {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,29 @@ mixin generateUser(user, isPrimary)
|
||||||
if isPrimary
|
if isPrimary
|
||||||
small.primary primary
|
small.primary primary
|
||||||
each claim in user.claims
|
each claim in user.claims
|
||||||
kx-claim(data-claim=claim)
|
kx-claim.kx-item(data-claim=claim)
|
||||||
|
details
|
||||||
|
summary
|
||||||
|
.info
|
||||||
|
p.subtitle= claim.matches[0].serviceprovider.name
|
||||||
|
p.title= claim.matches[0].profile.display
|
||||||
|
.icons
|
||||||
|
.verificationStatus(data-value='running')
|
||||||
|
.inProgress
|
||||||
|
.content
|
||||||
|
.subsection
|
||||||
|
img(src='/static/img/link.png')
|
||||||
|
div
|
||||||
|
if (claim.matches[0].profile.uri)
|
||||||
|
p Profile link:
|
||||||
|
a(rel='me' href=claim.matches[0].profile.uri)= claim.matches[0].profile.uri
|
||||||
|
else
|
||||||
|
p Profile link: not accessible from browser
|
||||||
|
if (claim.matches[0].proof.uri)
|
||||||
|
p Proof link:
|
||||||
|
a(rel='me' href=claim.matches[0].proof.uri)= claim.matches[0].proof.uri
|
||||||
|
else
|
||||||
|
p Proof link: not accessible from browser
|
||||||
|
|
||||||
block js
|
block js
|
||||||
script(type='application/javascript' src='/static/qrcode.min.js' charset='utf-8')
|
script(type='application/javascript' src='/static/qrcode.min.js' charset='utf-8')
|
||||||
|
@ -84,7 +106,23 @@ block content
|
||||||
|
|
||||||
#profileProofs.card.card--transparent
|
#profileProofs.card.card--transparent
|
||||||
h2 Key
|
h2 Key
|
||||||
kx-key(data-keydata=data.keyData)
|
kx-key.kx-item(data-keydata=data.keyData)
|
||||||
|
details
|
||||||
|
summary
|
||||||
|
.info
|
||||||
|
p.subtitle= data.keyData.key.fetchMethod
|
||||||
|
p.title= data.keyData.fingerprint
|
||||||
|
.content
|
||||||
|
.subsection
|
||||||
|
img(src='/static/img/link.png')
|
||||||
|
div
|
||||||
|
p Key link:
|
||||||
|
a(href=data.keyData.key.uri)= data.keyData.key.uri
|
||||||
|
hr
|
||||||
|
.subsection
|
||||||
|
img(src='/static/img/qrcode.png')
|
||||||
|
div
|
||||||
|
button(onClick=`showQR('${data.keyData.fingerprint}', 'fingerprint')`) Show OpenPGP fingerprint QR
|
||||||
|
|
||||||
+generateUser(data.keyData.users[data.keyData.primaryUserIndex], true)
|
+generateUser(data.keyData.users[data.keyData.primaryUserIndex], true)
|
||||||
each user, index in data.keyData.users
|
each user, index in data.keyData.users
|
||||||
|
|
Loading…
Reference in a new issue