forked from Qortal/qortal-ui
PhilReact
9 months ago
15 changed files with 1279 additions and 36 deletions
@ -0,0 +1,281 @@
|
||||
import { LitElement, html, css } from 'lit'; |
||||
import { store } from '../../store'; |
||||
import { connect } from 'pwa-helpers'; |
||||
import '@material/mwc-icon'; |
||||
import { translate } from '../../../translate'; |
||||
import { parentEpml } from '../show-plugin'; |
||||
|
||||
class SyncIndicator extends connect(store)(LitElement) { |
||||
static get properties() { |
||||
return { |
||||
isBehind: { type: Boolean }, |
||||
blocksBehind: { type: Number }, |
||||
isSynchronizing: { type: Boolean }, |
||||
hasCoreRunning: { type: Boolean }, |
||||
}; |
||||
} |
||||
|
||||
constructor() { |
||||
super(); |
||||
this.isBehind = null; |
||||
this.blocksBehind = 0; |
||||
this.nodeUrl = this.getNodeUrl(); |
||||
this.myNode = this.getMyNode(); |
||||
this.interval = null; |
||||
this.hasCoreRunning = true; |
||||
this.seenWelcomeSync = false; |
||||
this.numberOfTries = 0; |
||||
} |
||||
|
||||
static get styles() { |
||||
return css` |
||||
* { |
||||
--mdc-theme-text-primary-on-background: var(--black); |
||||
box-sizing: border-box; |
||||
} |
||||
:host { |
||||
box-sizing: border-box; |
||||
position: fixed; |
||||
bottom: 25px; |
||||
right: 25px; |
||||
z-index: 50000; |
||||
} |
||||
.parent { |
||||
width: 360px; |
||||
padding: 10px; |
||||
border-radius: 8px; |
||||
border: 1px solid var(--black); |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 10px; |
||||
user-select: none; |
||||
background: var(--white); |
||||
} |
||||
.row { |
||||
display: flex; |
||||
gap: 10px; |
||||
width: 100%; |
||||
} |
||||
.column { |
||||
display: flex; |
||||
flex-direction: column; |
||||
gap: 10px; |
||||
width: 100%; |
||||
} |
||||
.bootstrap-button { |
||||
font-family: Roboto, sans-serif; |
||||
font-size: 16px; |
||||
color: var(--mdc-theme-primary); |
||||
background-color: transparent; |
||||
padding: 8px 10px; |
||||
border-radius: 5px; |
||||
border: none; |
||||
transition: all 0.3s ease-in-out; |
||||
} |
||||
|
||||
.bootstrap-button:hover { |
||||
cursor: pointer; |
||||
background-color: #03a8f475; |
||||
} |
||||
`;
|
||||
} |
||||
async firstUpdated() { |
||||
const seenWelcomeSync = JSON.parse( |
||||
localStorage.getItem('welcome-sync') || 'false' |
||||
); |
||||
this.seenWelcomeSync = seenWelcomeSync; |
||||
} |
||||
|
||||
getNodeUrl() { |
||||
const myNode = |
||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ |
||||
window.parent.reduxStore.getState().app.nodeConfig.node |
||||
]; |
||||
|
||||
const nodeUrl = |
||||
myNode.protocol + '://' + myNode.domain + ':' + myNode.port; |
||||
return nodeUrl; |
||||
} |
||||
getMyNode() { |
||||
const myNode = |
||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ |
||||
window.parent.reduxStore.getState().app.nodeConfig.node |
||||
]; |
||||
|
||||
return myNode; |
||||
} |
||||
|
||||
async getDaySummary() { |
||||
try { |
||||
const endpoint = `${this.nodeUrl}/admin/summary/?apiKey=${this.myNode.apiKey}`; |
||||
const res = await fetch(endpoint); |
||||
const data = await res.json(); |
||||
let blockTimeInSeconds = null; |
||||
if (data.blockCount) { |
||||
const blockTime = 1440 / data.blockCount; |
||||
blockTimeInSeconds = blockTime * 60; |
||||
} |
||||
|
||||
const endpointLastBlock = `${this.nodeUrl}/blocks/last`; |
||||
const resLastBlock = await fetch(endpointLastBlock); |
||||
const dataLastBlock = await resLastBlock.json(); |
||||
const timestampNow = Date.now(); |
||||
const currentBlockTimestamp = dataLastBlock.timestamp; |
||||
if (blockTimeInSeconds && currentBlockTimestamp < timestampNow) { |
||||
const diff = timestampNow - currentBlockTimestamp; |
||||
const inSeconds = diff / 1000; // millisecs to secs
|
||||
const inBlocks = inSeconds / blockTimeInSeconds; |
||||
this.blocksBehind = parseInt(inBlocks); |
||||
if (inBlocks >= 1000) { |
||||
this.isBehind = true; |
||||
} else { |
||||
this.isBehind = false; |
||||
} |
||||
} else { |
||||
this.blocksBehind = 0; |
||||
this.isBehind = false; |
||||
} |
||||
} catch (error) {} |
||||
} |
||||
|
||||
async checkHowManyBlocksBehind() { |
||||
try { |
||||
this.getDaySummary(); |
||||
this.interval = setInterval(() => { |
||||
if (this.isBehind === false) { |
||||
this.isBehind = null; |
||||
clearInterval(this.interval); |
||||
} |
||||
this.getDaySummary(); |
||||
}, 60000); |
||||
} catch (error) {} |
||||
} |
||||
|
||||
stateChanged(state) { |
||||
console.log({state}) |
||||
if ( |
||||
state.app.nodeStatus && |
||||
Object.keys(state.app.nodeStatus).length === 0 |
||||
) { |
||||
if (this.numberOfTries > 5) { |
||||
this.hasCoreRunning = false; |
||||
} else { |
||||
this.numberOfTries = this.numberOfTries + 1; |
||||
} |
||||
} else if ( |
||||
state.app.nodeStatus && |
||||
state.app.nodeStatus.syncPercent !== this.syncPercentage |
||||
) { |
||||
this.syncPercentage = state.app.nodeStatus.syncPercent; |
||||
|
||||
if (state.app.nodeStatus.syncPercent !== 100) { |
||||
this.isSynchronizing = true; |
||||
} else { |
||||
this.isSynchronizing = false; |
||||
} |
||||
if ( |
||||
this.isBehind === null && |
||||
state.app.nodeStatus.syncPercent === 100 |
||||
) { |
||||
this.isBehind = false; |
||||
this.blocksBehind = 0; |
||||
if (!this.seenWelcomeSync) { |
||||
this.dispatchEvent( |
||||
new CustomEvent('open-welcome-modal-sync', { |
||||
bubbles: true, |
||||
composed: true, |
||||
}) |
||||
); |
||||
} |
||||
} else if ( |
||||
!this.interval && |
||||
this.isBehind === null && |
||||
state.app.nodeStatus.isSynchronizing && |
||||
state.app.nodeStatus.syncPercent !== 100 |
||||
) { |
||||
this.checkHowManyBlocksBehind(); |
||||
} |
||||
} else { |
||||
this.hasCoreRunning = true; |
||||
} |
||||
} |
||||
|
||||
async bootstrap(){ |
||||
try { |
||||
const endpoint = `${this.nodeUrl}/admin/bootstrap/?apiKey=${this.myNode.apiKey}`; |
||||
const res = await fetch(endpoint); |
||||
const data = await res.json(); |
||||
if(data === true){ |
||||
parentEpml.request('showSnackBar', get('tour.tour22')); |
||||
} |
||||
console.log({data}) |
||||
} catch (error) { |
||||
|
||||
} |
||||
} |
||||
|
||||
render() { |
||||
return html` |
||||
${!this.hasCoreRunning |
||||
? html` |
||||
<div class="parent"> |
||||
<span |
||||
><mwc-icon |
||||
id="notification-general-icon" |
||||
style="color: red; cursor:pointer;user-select:none" |
||||
>priority_high</mwc-icon |
||||
></span |
||||
> |
||||
<p> |
||||
${translate("tour.tour17")} |
||||
</p> |
||||
</div> |
||||
` |
||||
: (this.isBehind && this.isSynchronizing) |
||||
? html` |
||||
<div class="parent"> |
||||
<div class="column"> |
||||
<div class="row"> |
||||
<span |
||||
><img |
||||
src="/img/syncing.png" |
||||
style="height: 24px; width: 24px;" |
||||
/></span> |
||||
<p> |
||||
${this.blocksBehind} ${translate("tour.tour20")} |
||||
</p> |
||||
</div> |
||||
<div |
||||
class="row" |
||||
style="justify-content: center" |
||||
> |
||||
<button |
||||
class="bootstrap-button" |
||||
@click="${() => { |
||||
this.bootstrap() |
||||
}}" |
||||
> |
||||
${translate("tour.tour18")} |
||||
</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
` |
||||
: this.isSynchronizing |
||||
? html` |
||||
<div class="parent"> |
||||
<span |
||||
><img |
||||
src="/img/syncing.png" |
||||
style="height: 24px; width: 24px;" |
||||
/></span> |
||||
<p> |
||||
${translate("tour.tour19")} ${this.blocksBehind ? this.blocksBehind : ""} ${this.blocksBehind ? translate("tour.tour21"): ""}
|
||||
</p> |
||||
</div> |
||||
` |
||||
: "" } |
||||
`;
|
||||
} |
||||
} |
||||
customElements.define('sync-indicator', SyncIndicator); |
@ -0,0 +1,396 @@
|
||||
import { LitElement, html, css } from 'lit'; |
||||
import { driver } from 'driver.js'; |
||||
import 'driver.js/dist/driver.css'; |
||||
import '@material/mwc-icon'; |
||||
import '@polymer/paper-spinner/paper-spinner-lite.js'; |
||||
import '@vaadin/tooltip'; |
||||
import '@material/mwc-button'; |
||||
import { get, translate } from '../../../translate/index.js'; |
||||
import '@polymer/paper-dialog/paper-dialog.js'; |
||||
import { setNewTab } from '../../redux/app/app-actions.js'; |
||||
import { store } from '../../store.js'; |
||||
import { connect } from 'pwa-helpers'; |
||||
import './tour.css'; |
||||
class TourComponent extends connect(store)(LitElement) { |
||||
static get properties() { |
||||
return { |
||||
getElements: { attribute: false }, |
||||
dialogOpenedCongrats: { type: Boolean }, |
||||
hasViewedTour: { type: Boolean }, |
||||
}; |
||||
} |
||||
|
||||
constructor() { |
||||
super(); |
||||
this.dialogOpenedCongrats = false; |
||||
this._controlOpenWelcomeModal = |
||||
this._controlOpenWelcomeModal.bind(this); |
||||
this.hasName = false; |
||||
this.nodeUrl = this.getNodeUrl(); |
||||
this.myNode = this.getMyNode(); |
||||
} |
||||
|
||||
static get styles() { |
||||
return css` |
||||
* { |
||||
--mdc-theme-primary: rgb(3, 169, 244); |
||||
--mdc-theme-secondary: var(--mdc-theme-primary); |
||||
--mdc-theme-surface: var(--white); |
||||
--mdc-dialog-content-ink-color: var(--black); |
||||
box-sizing: border-box; |
||||
color: var(--black); |
||||
background: var(--white); |
||||
} |
||||
|
||||
:host { |
||||
box-sizing: border-box; |
||||
position: fixed; |
||||
bottom: 25px; |
||||
right: 25px; |
||||
z-index: 50000; |
||||
} |
||||
|
||||
.full-info-wrapper { |
||||
width: 100%; |
||||
min-width: 600px; |
||||
max-width: 600px; |
||||
text-align: center; |
||||
background: var(--white); |
||||
border: 1px solid var(--black); |
||||
border-radius: 15px; |
||||
padding: 25px; |
||||
box-shadow: 0px 10px 15px rgba(0, 0, 0, 0.1); |
||||
display: block !important; |
||||
} |
||||
|
||||
.buttons { |
||||
display: inline; |
||||
} |
||||
.accept-button { |
||||
font-family: Roboto, sans-serif; |
||||
letter-spacing: 0.3px; |
||||
font-weight: 300; |
||||
padding: 8px 5px; |
||||
border-radius: 3px; |
||||
text-align: center; |
||||
color: var(--black); |
||||
transition: all 0.3s ease-in-out; |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 10px; |
||||
font-size: 18px; |
||||
justify-content: center; |
||||
outline: 1px solid var(--black); |
||||
} |
||||
|
||||
.accept-button:hover { |
||||
cursor: pointer; |
||||
background-color: #03a8f485; |
||||
} |
||||
|
||||
.close-button { |
||||
font-family: Roboto, sans-serif; |
||||
letter-spacing: 0.3px; |
||||
font-weight: 300; |
||||
padding: 8px 5px; |
||||
border-radius: 3px; |
||||
text-align: center; |
||||
color: #f44336; |
||||
transition: all 0.3s ease-in-out; |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 10px; |
||||
font-size: 18px; |
||||
width:auto; |
||||
} |
||||
|
||||
.close-button:hover { |
||||
cursor: pointer; |
||||
background-color: #f4433663; |
||||
} |
||||
`;
|
||||
} |
||||
|
||||
_controlOpenWelcomeModal() { |
||||
this.isSynced = true |
||||
|
||||
const seenWelcomeSync = JSON.parse( |
||||
localStorage.getItem('welcome-sync') || 'false' |
||||
); |
||||
if (this.hasName) return; |
||||
if (seenWelcomeSync) return; |
||||
if(!this.hasViewedTour) return |
||||
this.dialogOpenedCongrats = true; |
||||
} |
||||
|
||||
openWelcomeModal() { |
||||
this.dispatchEvent( |
||||
new CustomEvent('send-tour-finished', { |
||||
bubbles: true, |
||||
composed: true, |
||||
}) |
||||
); |
||||
const seenWelcomeSync = JSON.parse( |
||||
localStorage.getItem('welcome-sync') || 'false' |
||||
); |
||||
if (this.hasName) return; |
||||
if (seenWelcomeSync) return; |
||||
if(!this.isSynced) return |
||||
this.dialogOpenedCongrats = true; |
||||
} |
||||
|
||||
connectedCallback() { |
||||
super.connectedCallback(); |
||||
window.addEventListener( |
||||
'open-welcome-modal-sync', |
||||
this._controlOpenWelcomeModal |
||||
); |
||||
} |
||||
|
||||
disconnectedCallback() { |
||||
window.removeEventListener( |
||||
'open-welcome-modal-sync', |
||||
this._controlOpenWelcomeModal |
||||
); |
||||
|
||||
super.disconnectedCallback(); |
||||
} |
||||
|
||||
getNodeUrl() { |
||||
const myNode = |
||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ |
||||
window.parent.reduxStore.getState().app.nodeConfig.node |
||||
]; |
||||
|
||||
const nodeUrl = |
||||
myNode.protocol + '://' + myNode.domain + ':' + myNode.port; |
||||
return nodeUrl; |
||||
} |
||||
getMyNode() { |
||||
const myNode = |
||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ |
||||
window.parent.reduxStore.getState().app.nodeConfig.node |
||||
]; |
||||
|
||||
return myNode; |
||||
} |
||||
|
||||
async getName(recipient) { |
||||
try { |
||||
const endpoint = `${this.nodeUrl}/names/address/${recipient}`; |
||||
const res = await fetch(endpoint); |
||||
const getNames = await res.json(); |
||||
|
||||
if (Array.isArray(getNames) && getNames.length > 0) { |
||||
return getNames[0].name; |
||||
} else { |
||||
return ''; |
||||
} |
||||
} catch (error) { |
||||
return ''; |
||||
} |
||||
} |
||||
async firstUpdated() { |
||||
const hasViewedTour = JSON.parse( |
||||
localStorage.getItem('hasViewedTour') || 'false' |
||||
); |
||||
const selectedAddress = store.getState().app.selectedAddress || ''; |
||||
const name = await this.getName(selectedAddress.address); |
||||
if (name) { |
||||
this.hasName = true; |
||||
} |
||||
this.hasViewedTour = hasViewedTour; |
||||
if (!hasViewedTour) { |
||||
try { |
||||
if (name) { |
||||
this.hasViewedTour = true; |
||||
this.hasName = true; |
||||
localStorage.setItem("hasViewedTour", JSON.stringify(true)) |
||||
} |
||||
} catch (error) { |
||||
console.log({ error }); |
||||
} |
||||
} |
||||
await new Promise((res) => { |
||||
setTimeout(() => { |
||||
res(); |
||||
}, 1000); |
||||
}); |
||||
if (!this.hasViewedTour) { |
||||
const elements = this.getElements(); |
||||
let steps = [ |
||||
{ |
||||
popover: { |
||||
title: get("tour.tour6"), |
||||
description: ` |
||||
<div style="display:flex;justify-content:center;gap:15px"> |
||||
<img style="height:40px;width:auto;margin:15px 0px;" src="/img/qort.png" /> |
||||
</div> |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;"> |
||||
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div> <p style="margin:0px;padding:0px">${get("tour.tour7")}</p> |
||||
</div> |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;"> |
||||
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div> <p style="margin:0px;padding:0px">${get("tour.tour8")}</p> |
||||
</div> |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px"> |
||||
<div style="height:6px;width:6px;border-radius:50%;background:var(--black)"></div> <p style="margin:0px;padding:0px">${get("tour.tour9")}</p> |
||||
</div> |
||||
`,
|
||||
// ... other options
|
||||
}, |
||||
}, |
||||
]; |
||||
const step2 = elements['core-sync-status-id']; |
||||
const step3 = elements['tab']; |
||||
const step4 = elements['checklist']; |
||||
|
||||
if (step2) { |
||||
steps.push({ |
||||
element: step2, |
||||
popover: { |
||||
title: get("tour.tour5"), |
||||
description: ` |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px"> |
||||
<p style="margin:0px;padding:0px">${get("tour.tour1")}</p> |
||||
</div> |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;"> |
||||
<span><img src="/img/synced.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span> |
||||
<p style="margin:0px;padding:0px">${get("tour.tour2")}</p> |
||||
</div> |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;"> |
||||
<span><img src="/img/synced_minting.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span> |
||||
<p style="margin:0px;padding:0px">${get("tour.tour3")}</p> |
||||
</div> |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px"> |
||||
<span><img src="/img/syncing.png" style="height: 24px; width: 24px; padding-top: 4px;" /></span> |
||||
<p style="margin:0px;padding:0px">${get("tour.tour4")}</p> |
||||
</div> |
||||
|
||||
`,
|
||||
}, |
||||
}); |
||||
} |
||||
if (step3) { |
||||
steps.push({ |
||||
element: step3, |
||||
popover: { |
||||
title: 'Tab View', |
||||
description: ` |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;margin-bottom:30px"> |
||||
<p style="margin:0px;padding:0px">${get("tour.tour10")} |
||||
</p> |
||||
</div> |
||||
<div style="display:flex;gap:15px;align-items:center;margin-top:15px;"> |
||||
<span><img src="/img/addplugin.webp" style="height: 36px; width: 36px; padding-top: 4px;" /></span> |
||||
<p style="margin:0px;padding:0px">You can also bookmark other Q-Apps and Plugins by clicking on the ${get( |
||||
'tabmenu.tm19' |
||||
)} button</p> |
||||
</div> |
||||
`,
|
||||
}, |
||||
}); |
||||
} |
||||
if (step4) { |
||||
steps.push( |
||||
{ |
||||
element: step4, |
||||
popover: { |
||||
title: get("tour.tour11"), |
||||
description: get("tour.tour12"), |
||||
}, |
||||
} |
||||
);this.hasViewedTour |
||||
} |
||||
let currentStepIndex = 0; |
||||
const driverObj = driver({ |
||||
popoverClass: 'driverjs-theme', |
||||
showProgress: true, |
||||
showButtons: ['next', 'previous'], |
||||
steps: steps, |
||||
allowClose: false, |
||||
onDestroyed: () => { |
||||
localStorage.setItem("hasViewedTour", JSON.stringify(true)) |
||||
this.hasViewedTour = true; |
||||
this.openWelcomeModal(); |
||||
} |
||||
}); |
||||
|
||||
driverObj.drive(); |
||||
} else { |
||||
this.dispatchEvent( |
||||
new CustomEvent('send-tour-finished', { |
||||
bubbles: true, |
||||
composed: true, |
||||
}) |
||||
); |
||||
} |
||||
} |
||||
|
||||
visitQtube() { |
||||
this.onClose(); |
||||
const query = `?service=APP&name=Q-Tube`; |
||||
store.dispatch( |
||||
setNewTab({ |
||||
url: `qdn/browser/index.html${query}`, |
||||
id: 'q-mail-notification', |
||||
myPlugObj: { |
||||
url: 'myapp', |
||||
domain: 'core', |
||||
page: `qdn/browser/index.html${query}`, |
||||
title: 'Q-Tube', |
||||
menus: [], |
||||
parent: false, |
||||
}, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
onClose() { |
||||
localStorage.setItem("welcome-sync", JSON.stringify(true)) |
||||
this.dialogOpenedCongrats = false; |
||||
} |
||||
|
||||
render() { |
||||
return html` |
||||
<!-- Profile read-view --> |
||||
${this.dialogOpenedCongrats && this.hasViewedTour |
||||
? html` |
||||
<paper-dialog |
||||
class="full-info-wrapper" |
||||
?opened="${this.dialogOpenedCongrats}" |
||||
> |
||||
<h3>Congratulations!</h3> |
||||
<div |
||||
style="display:flex;gap:15px;justify-content:center;margin-top:10px" |
||||
> |
||||
${translate("tour.tour13")} |
||||
</div> |
||||
<div |
||||
style="display:flex;gap:15px;justify-content:center;margin-top:10px" |
||||
> |
||||
${translate("tour.tour14")} |
||||
</div> |
||||
|
||||
<div |
||||
class="accept-button" |
||||
@click=${this.visitQtube} |
||||
> |
||||
${translate("tour.tour15")} |
||||
</div> |
||||
<div style="width:100%;display:flex;justify-content:center;margin-top:10px"> |
||||
<div |
||||
class="close-button" |
||||
@click=${()=> { |
||||
this.dialogOpenedCongrats = false |
||||
}} |
||||
> |
||||
${translate("general.close")} |
||||
</div> |
||||
</div> |
||||
</paper-dialog> |
||||
` |
||||
: ''} |
||||
`;
|
||||
} |
||||
} |
||||
customElements.define('tour-component', TourComponent); |
@ -0,0 +1,77 @@
|
||||
.driver-popover.driverjs-theme { |
||||
background-color: var(--white); |
||||
color: var(--black); |
||||
max-width: 500px; |
||||
width: auto; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-title { |
||||
font-size: 20px; |
||||
text-align: center; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-title, |
||||
.driver-popover.driverjs-theme .driver-popover-description, |
||||
.driver-popover.driverjs-theme .driver-popover-progress-text { |
||||
color: var(--black); |
||||
font-family: Roboto, sans-serif; |
||||
} |
||||
.driver-popover.driverjs-theme .driver-popover-description { |
||||
font-size: 16px; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme button { |
||||
flex: 1; |
||||
text-align: center; |
||||
background-color: #000; |
||||
color: #ffffff; |
||||
border: 2px solid #000; |
||||
text-shadow: none; |
||||
font-size: 14px; |
||||
padding: 5px 8px; |
||||
border-radius: 6px; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .test-span { |
||||
color: green; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme button:hover { |
||||
background-color: #000; |
||||
color: #ffffff; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-navigation-btns { |
||||
justify-content: space-between; |
||||
gap: 3px; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-close-btn { |
||||
color: #9b9b9b; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-close-btn:hover { |
||||
color: #000; |
||||
} |
||||
.driver-popover.driverjs-theme .driver-popover-footer { |
||||
gap: 20px; |
||||
} |
||||
.driver-popover.driverjs-theme .driver-popover-footer button { |
||||
background-color: #000 !important; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-arrow-side-left.driver-popover-arrow { |
||||
border-left-color: #fde047; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-arrow-side-right.driver-popover-arrow { |
||||
border-right-color: #fde047; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-arrow-side-top.driver-popover-arrow { |
||||
border-top-color: #fde047; |
||||
} |
||||
|
||||
.driver-popover.driverjs-theme .driver-popover-arrow-side-bottom.driver-popover-arrow { |
||||
border-bottom-color: #fde047; |
||||
} |
@ -0,0 +1,343 @@
|
||||
import { css, html, LitElement } from 'lit'; |
||||
import { connect } from 'pwa-helpers'; |
||||
|
||||
import '@vaadin/item'; |
||||
import '@vaadin/list-box'; |
||||
import '@polymer/paper-icon-button/paper-icon-button.js'; |
||||
import '@polymer/iron-icons/iron-icons.js'; |
||||
import { store } from '../../store.js'; |
||||
import { setNewTab } from '../../redux/app/app-actions.js'; |
||||
import '@material/mwc-icon'; |
||||
import { get, translate } from '../../../translate/index.js'; |
||||
import { repeat } from 'lit/directives/repeat.js'; |
||||
import '../../../../plugins/plugins/core/components/TimeAgo.js'; |
||||
import '../notification-view/popover.js'; |
||||
import ShortUniqueId from 'short-unique-id'; |
||||
|
||||
class BeginnerChecklist extends connect(store)(LitElement) { |
||||
static properties = { |
||||
notifications: { type: Array }, |
||||
showChecklist: { type: Boolean }, |
||||
theme: { type: String, reflect: true }, |
||||
isSynced: { type: Boolean }, |
||||
hasName: { type: Boolean }, |
||||
hasTourFinished: { type: Boolean }, |
||||
}; |
||||
|
||||
constructor() { |
||||
super(); |
||||
this.showChecklist = false; |
||||
this.initialFetch = false; |
||||
this.theme = localStorage.getItem('qortalTheme') |
||||
? localStorage.getItem('qortalTheme') |
||||
: 'light'; |
||||
this.isSynced = false; |
||||
this.hasName = null; |
||||
this.nodeUrl = this.getNodeUrl(); |
||||
this.myNode = this.getMyNode(); |
||||
this.hasTourFinished = false; |
||||
this._controlTourFinished = this._controlTourFinished.bind(this); |
||||
this.uid = new ShortUniqueId(); |
||||
} |
||||
|
||||
_controlTourFinished() { |
||||
this.hasTourFinished = true; |
||||
} |
||||
|
||||
connectedCallback() { |
||||
super.connectedCallback(); |
||||
window.addEventListener( |
||||
'send-tour-finished', |
||||
this._controlTourFinished |
||||
); |
||||
} |
||||
|
||||
disconnectedCallback() { |
||||
window.removeEventListener( |
||||
'send-tour-finished', |
||||
this._controlTourFinished |
||||
); |
||||
|
||||
super.disconnectedCallback(); |
||||
} |
||||
|
||||
getNodeUrl() { |
||||
const myNode = |
||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ |
||||
window.parent.reduxStore.getState().app.nodeConfig.node |
||||
]; |
||||
|
||||
const nodeUrl = |
||||
myNode.protocol + '://' + myNode.domain + ':' + myNode.port; |
||||
return nodeUrl; |
||||
} |
||||
getMyNode() { |
||||
const myNode = |
||||
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ |
||||
window.parent.reduxStore.getState().app.nodeConfig.node |
||||
]; |
||||
|
||||
return myNode; |
||||
} |
||||
|
||||
async getName(recipient) { |
||||
try { |
||||
const endpoint = `${this.nodeUrl}/names/address/${recipient}`; |
||||
const res = await fetch(endpoint); |
||||
const getNames = await res.json(); |
||||
|
||||
if (Array.isArray(getNames) && getNames.length > 0) { |
||||
this.hasName = true; |
||||
} else { |
||||
this.hasName = false; |
||||
} |
||||
} catch (error) { |
||||
return ''; |
||||
} |
||||
} |
||||
|
||||
stateChanged(state) { |
||||
if ( |
||||
state.app.nodeStatus && |
||||
state.app.nodeStatus.syncPercent !== this.syncPercentage |
||||
) { |
||||
this.syncPercentage = state.app.nodeStatus.syncPercent; |
||||
|
||||
if ( |
||||
!this.hasAttempted && |
||||
state.app.nodeStatus.syncPercent === 100 |
||||
) { |
||||
this.hasAttempted = true; |
||||
this.getName(); |
||||
} |
||||
} |
||||
if ( |
||||
state.app.accountInfo && |
||||
state.app.accountInfo.names.length && |
||||
state.app.nodeStatus && |
||||
state.app.nodeStatus.syncPercent === 100 && |
||||
this.hasName === false && |
||||
this.hasAttempted && |
||||
state.app.accountInfo && |
||||
state.app.accountInfo.names && |
||||
state.app.accountInfo.names.length > 0 |
||||
) { |
||||
this.hasName = true; |
||||
} |
||||
} |
||||
|
||||
handleBlur() { |
||||
setTimeout(() => { |
||||
if (!this.shadowRoot.contains(document.activeElement)) { |
||||
this.showChecklist = false; |
||||
} |
||||
}, 0); |
||||
} |
||||
|
||||
|
||||
|
||||
render() { |
||||
|
||||
return this.hasName === false || !this.hasTourFinished |
||||
? html` |
||||
<div class="layout"> |
||||
<popover-component |
||||
for="popover-checklist" |
||||
message=${get('tour.tour16')} |
||||
></popover-component> |
||||
<div |
||||
id="popover-checklist" |
||||
@click=${() => this._toggleChecklist()} |
||||
> |
||||
<mwc-icon |
||||
id="checklist-general-icon" |
||||
style=${`color: ${!this.hasName ? 'red' : 'var(--black)'}; cursor:pointer;user-select:none`} |
||||
>checklist</mwc-icon |
||||
> |
||||
<vaadin-tooltip |
||||
for="checklist-general-icon" |
||||
position="bottom" |
||||
hover-delay=${400} |
||||
hide-delay=${1} |
||||
text=${get('tour.tour16')} |
||||
> |
||||
</vaadin-tooltip> |
||||
</div> |
||||
|
||||
|
||||
<div |
||||
id="checklist-panel" |
||||
class="popover-panel" |
||||
style="visibility:${this.showChecklist |
||||
? 'visibile' |
||||
: 'hidden'}" |
||||
tabindex="0" |
||||
@blur=${this.handleBlur} |
||||
> |
||||
<div class="list"> |
||||
<div class="task-list-item"> |
||||
<p>Are you synced?</p> |
||||
${this.syncPercentage === 100 |
||||
? html` |
||||
<mwc-icon |
||||
id="checklist-general-icon" |
||||
style="color: green; user-select:none" |
||||
>task_alt</mwc-icon |
||||
> |
||||
` |
||||
: html` |
||||
<mwc-icon |
||||
id="checklist-general-icon" |
||||
style="color: red; user-select:none" |
||||
>radio_button_unchecked</mwc-icon |
||||
> |
||||
`}
|
||||
</div> |
||||
|
||||
<div class="task-list-item" style="cursor:pointer" @click=${()=> { |
||||
store.dispatch( |
||||
setNewTab({ |
||||
url: `group-management`, |
||||
id: this.uid.rnd(), |
||||
myPlugObj: { |
||||
url: 'name-registration', |
||||
domain: 'core', |
||||
page: 'name-registration/index.html', |
||||
title: 'Name Registration', |
||||
icon: 'vaadin:user-check', |
||||
mwcicon: 'manage_accounts', |
||||
pluginNumber: |
||||
'plugin-qCmtXAQmtu', |
||||
menus: [], |
||||
parent: false, |
||||
}, |
||||
openExisting: true, |
||||
}) |
||||
); |
||||
this.handleBlur() |
||||
}}> |
||||
<p>Do you have a name registered?</p> |
||||
${this.hasName |
||||
? html` |
||||
<mwc-icon |
||||
id="checklist-general-icon" |
||||
style="color: green; user-select:none" |
||||
>task_alt</mwc-icon |
||||
> |
||||
` |
||||
: html` |
||||
<mwc-icon |
||||
id="checklist-general-icon" |
||||
style="color: red; user-select:none" |
||||
>radio_button_unchecked</mwc-icon |
||||
> |
||||
`}
|
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
` |
||||
: ''; |
||||
} |
||||
|
||||
_toggleChecklist() { |
||||
this.showChecklist = !this.showChecklist; |
||||
if (this.showChecklist) { |
||||
requestAnimationFrame(() => { |
||||
this.shadowRoot.getElementById('checklist-panel').focus(); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
static styles = css` |
||||
.layout { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
position: relative; |
||||
} |
||||
|
||||
.count { |
||||
position: absolute; |
||||
top: -5px; |
||||
right: -5px; |
||||
font-size: 12px; |
||||
background-color: red; |
||||
color: white; |
||||
border-radius: 50%; |
||||
width: 16px; |
||||
height: 16px; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.nocount { |
||||
display: none; |
||||
} |
||||
|
||||
.popover-panel { |
||||
position: absolute; |
||||
width: 200px; |
||||
padding: 10px; |
||||
background-color: var(--white); |
||||
border: 1px solid var(--black); |
||||
border-radius: 4px; |
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
||||
top: 40px; |
||||
max-height: 350px; |
||||
overflow: auto; |
||||
scrollbar-width: thin; |
||||
scrollbar-color: #6a6c75 #a1a1a1; |
||||
} |
||||
|
||||
.popover-panel::-webkit-scrollbar { |
||||
width: 11px; |
||||
} |
||||
|
||||
.popover-panel::-webkit-scrollbar-track { |
||||
background: #a1a1a1; |
||||
} |
||||
|
||||
.popover-panel::-webkit-scrollbar-thumb { |
||||
background-color: #6a6c75; |
||||
border-radius: 6px; |
||||
border: 3px solid #a1a1a1; |
||||
} |
||||
|
||||
.list { |
||||
display: flex; |
||||
flex-direction: column; |
||||
gap: 15px; |
||||
} |
||||
|
||||
.task-list-item { |
||||
display: flex; |
||||
gap: 15px; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
} |
||||
|
||||
.checklist-item { |
||||
padding: 5px; |
||||
border-bottom: 1px solid; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
cursor: pointer; |
||||
transition: 0.2s all; |
||||
} |
||||
|
||||
.checklist-item:hover { |
||||
background: var(--nav-color-hover); |
||||
} |
||||
|
||||
p { |
||||
font-size: 16px; |
||||
color: var(--black); |
||||
margin: 0px; |
||||
padding: 0px; |
||||
} |
||||
`;
|
||||
} |
||||
|
||||
customElements.define('beginner-checklist', BeginnerChecklist); |
@ -0,0 +1,79 @@
|
||||
export const defaultQappsTabs = [ |
||||
{ |
||||
"url": "myapp", |
||||
"domain": "core", |
||||
"page": "qdn/browser/index.html?name=Q-Tube&service=APP", |
||||
"title": "Q-Tube", |
||||
"icon": "vaadin:external-browser", |
||||
"mwcicon": "apps", |
||||
"pluginNumber": "plugin-04tlGhUzzd", |
||||
"menus": [], |
||||
"parent": false |
||||
}, |
||||
{ |
||||
"url": "myapp", |
||||
"domain": "core", |
||||
"page": "qdn/browser/index.html?name=Q-Shop&service=APP", |
||||
"title": "Q-Shop", |
||||
"icon": "vaadin:external-browser", |
||||
"mwcicon": "apps", |
||||
"pluginNumber": "plugin-gAogkIS5z6", |
||||
"menus": [], |
||||
"parent": false |
||||
}, |
||||
{ |
||||
"url": "myapp", |
||||
"domain": "core", |
||||
"page": "qdn/browser/index.html?name=Q-Blog&service=APP", |
||||
"title": "Q-Blog", |
||||
"icon": "vaadin:external-browser", |
||||
"mwcicon": "rss_feed", |
||||
"pluginNumber": "plugin-4KX5PHKSbM", |
||||
"menus": [], |
||||
"parent": false |
||||
}, |
||||
{ |
||||
"url": "myapp", |
||||
"domain": "core", |
||||
"page": "qdn/browser/index.html?name=Q-Fund&service=APP", |
||||
"title": "Q-Fund", |
||||
"icon": "vaadin:external-browser", |
||||
"mwcicon": "apps", |
||||
"pluginNumber": "plugin-POw2jHDdHo", |
||||
"menus": [], |
||||
"parent": false |
||||
}, |
||||
{ |
||||
"url": "myapp", |
||||
"domain": "core", |
||||
"page": "qdn/browser/index.html?name=Q-Share&service=APP", |
||||
"title": "Q-Share", |
||||
"icon": "vaadin:external-browser", |
||||
"mwcicon": "apps", |
||||
"pluginNumber": "plugin-POw24oDdHo", |
||||
"menus": [], |
||||
"parent": false |
||||
}, |
||||
{ |
||||
"url": "myapp", |
||||
"domain": "core", |
||||
"page": "qdn/browser/index.html?name=Ear-Bump&service=APP", |
||||
"title": "Ear-Bump", |
||||
"icon": "vaadin:external-browser", |
||||
"mwcicon": "apps", |
||||
"pluginNumber": "plugin-POx8jHDdHo", |
||||
"menus": [], |
||||
"parent": false |
||||
}, |
||||
{ |
||||
"url": "myapp", |
||||
"domain": "core", |
||||
"page": "qdn/browser/index.html?name=Q-Mail&service=APP", |
||||
"title": "Q-Mail", |
||||
"icon": "vaadin:external-browser", |
||||
"mwcicon": "mail", |
||||
"pluginNumber": "plugin-GGHiHzW6pe", |
||||
"menus": [], |
||||
"parent": false |
||||
} |
||||
] |
After Width: | Height: | Size: 572 B |
Loading…
Reference in new issue