mirror of
https://github.com/Qortal/qortal-ui.git
synced 2025-02-11 17:55:51 +00:00
added tour and checklist
This commit is contained in:
parent
b17f0f09a8
commit
eb22b0cda6
@ -1254,5 +1254,29 @@
|
||||
"profile24": "You must fill out both field name and field value to add a custom property",
|
||||
"profile25": "Is your friend",
|
||||
"profile26": "Add as friend"
|
||||
},
|
||||
"tour": {
|
||||
"tour1": "In order to use Qortal, the Core must be synced. This icon will be in blue when it's synced.",
|
||||
"tour2": "Synced",
|
||||
"tour3": "Synced and minting",
|
||||
"tour4": "Syncing",
|
||||
"tour5": "Sync your core",
|
||||
"tour6": "The Unstoppable Force of Qortal",
|
||||
"tour7": "Only you control your data on Qortal",
|
||||
"tour8": "Qortal cannot be taken down",
|
||||
"tour9": "Completely peer-to-peer with no centralized intermediaries",
|
||||
"tour10": "This is the default tab view where you can access important Qortal settings and Q-apps such as Q-Tube.",
|
||||
"tour11": "Get the full experience",
|
||||
"tour12": "To get the full Qortal experience, we recommend following this checklist.",
|
||||
"tour13": "You are fully synced! You can now experience the power of the Qortal blockchain.",
|
||||
"tour14":"Let's try visiting Q-Tube!",
|
||||
"tour15":"Visit Q-Tube",
|
||||
"tour16": "Checklist",
|
||||
"tour17": "Please start the Core to access the Qortal blockchain.",
|
||||
"tour18": "Bootstrap",
|
||||
"tour19":"Currently syncing... please wait to use Qortal to its full potential.",
|
||||
"tour20": "blocks behind. Would you like to bootstrap to speed up the syncing process?",
|
||||
"tour21": "blocks remaining.",
|
||||
"tour22": "Bootstrap requested. Please wait."
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,9 @@ import './friends-view/friends-side-panel-parent.js'
|
||||
import './friends-view/save-settings-qdn.js'
|
||||
import './friends-view/core-sync-status.js'
|
||||
import './friends-view/profile.js'
|
||||
import './beginner-tour/tour-component.js'
|
||||
import './beginner-tour/sync-indicator.js'
|
||||
import './friends-view/beginner-checklist.js'
|
||||
import './controllers/coin-balances-controller.js'
|
||||
|
||||
const chatLastSeen = localForage.createInstance({
|
||||
@ -540,6 +543,26 @@ class AppView extends connect(store)(LitElement) {
|
||||
this.myLockScreenPass = ''
|
||||
this.myLockScreenSet = ''
|
||||
this.helperMessage = ''
|
||||
this.getTourElements = this.getTourElements.bind(this)
|
||||
}
|
||||
|
||||
getTourElements(){
|
||||
let els = {}
|
||||
console.log('this.shadowRoot.querySelector("core-sync-status")', this.shadowRoot.querySelector("core-sync-status"))
|
||||
const el1 = this.shadowRoot.querySelector("core-sync-status").shadowRoot.getElementById("core-sync-status-id")
|
||||
const el2 = this.shadowRoot.querySelector("show-plugin").shadowRoot.getElementById("showPluginId")
|
||||
const el3 = this.shadowRoot.querySelector("beginner-checklist").shadowRoot.getElementById("popover-notification")
|
||||
if(el1) {
|
||||
els['core-sync-status-id'] = el1
|
||||
}
|
||||
if(el2) {
|
||||
els['tab'] = el2
|
||||
}
|
||||
if(el3) {
|
||||
els['checklist'] = el3
|
||||
}
|
||||
|
||||
return els
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -570,6 +593,7 @@ class AppView extends connect(store)(LitElement) {
|
||||
</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:20px">
|
||||
<beginner-checklist></beginner-checklist>
|
||||
<profile-qdn></profile-qdn>
|
||||
<friends-side-panel-parent></friends-side-panel-parent>
|
||||
<notification-bell></notification-bell>
|
||||
@ -600,6 +624,8 @@ class AppView extends connect(store)(LitElement) {
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<show-plugin></show-plugin>
|
||||
<tour-component .getElements=${this.getTourElements}></tour-component>
|
||||
<sync-indicator ></sync-indicator>
|
||||
</app-header-layout>
|
||||
</app-drawer-layout>
|
||||
<user-info-view></user-info-view>
|
||||
|
281
core/src/components/beginner-tour/sync-indicator.js
Normal file
281
core/src/components/beginner-tour/sync-indicator.js
Normal file
@ -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);
|
396
core/src/components/beginner-tour/tour-component.js
Normal file
396
core/src/components/beginner-tour/tour-component.js
Normal file
@ -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);
|
77
core/src/components/beginner-tour/tour.css
Normal file
77
core/src/components/beginner-tour/tour.css
Normal file
@ -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;
|
||||
}
|
343
core/src/components/friends-view/beginner-checklist.js
Normal file
343
core/src/components/friends-view/beginner-checklist.js
Normal file
@ -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);
|
@ -81,7 +81,9 @@ class CoreSyncStatus extends connect(store)(LitElement) {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div id="core-sync-status-id">
|
||||
${this.renderSyncStatusIcon()}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
|
@ -139,8 +139,8 @@ class FriendsView extends connect(store)(LitElement) {
|
||||
|
||||
disconnectedCallback() {
|
||||
window.removeEventListener('friends-my-friend-list-event', this._updateFriends)
|
||||
window.addEventListener('friends-my-selected-feeds-event', this._updateFeed)
|
||||
window.addEventListener('add-friend', this._addFriend)
|
||||
window.removeEventListener('friends-my-selected-feeds-event', this._updateFeed)
|
||||
window.removeEventListener('add-friend', this._addFriend)
|
||||
|
||||
super.disconnectedCallback()
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import '@polymer/paper-dialog/paper-dialog.js'
|
||||
import '@vaadin/grid'
|
||||
import '@vaadin/text-field'
|
||||
import '../custom-elements/frag-file-input.js'
|
||||
import { defaultQappsTabs } from '../data/defaultQapps.js'
|
||||
|
||||
registerTranslateConfig({
|
||||
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
||||
@ -322,6 +323,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
constructor() {
|
||||
super()
|
||||
this.registeredUrls = []
|
||||
this.initialRegisteredUrls = []
|
||||
this.currentTab = 0
|
||||
this.tabs = []
|
||||
this.uid = new ShortUniqueId()
|
||||
@ -339,6 +341,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
}
|
||||
|
||||
return html`
|
||||
<div id="showPluginId" style="width:0px"></div>
|
||||
<div class="tabs">
|
||||
${this.tabs.map((tab, index) => {
|
||||
let title = ''
|
||||
@ -1725,7 +1728,9 @@ class NavBar extends connect(store)(LitElement) {
|
||||
|
||||
if (localStorage.getItem("myMenuPlugs") === null) {
|
||||
await appDelay(1000)
|
||||
const myObj = JSON.stringify(this.menuList)
|
||||
const listOfPlugins = this.menuList.filter(plugin=> plugin.url !== "puzzles")
|
||||
const addQapps = [...listOfPlugins, ...defaultQappsTabs]
|
||||
const myObj = JSON.stringify(addQapps)
|
||||
localStorage.setItem("myMenuPlugs", myObj)
|
||||
this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]")
|
||||
} else {
|
||||
|
79
core/src/data/defaultQapps.js
Normal file
79
core/src/data/defaultQapps.js
Normal file
@ -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
|
||||
}
|
||||
]
|
@ -137,3 +137,4 @@ html[theme="dark"] {
|
||||
--code-block-text-color: #008fd5;
|
||||
--noavatar: url("/img/noavatar_dark.png");
|
||||
}
|
||||
|
||||
|
BIN
img/addplugin.webp
Normal file
BIN
img/addplugin.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 572 B |
6
package-lock.json
generated
6
package-lock.json
generated
@ -26,6 +26,7 @@
|
||||
"buffer": "6.0.3",
|
||||
"compressorjs": "1.2.1",
|
||||
"crypto-js": "4.2.0",
|
||||
"driver.js": "^1.3.1",
|
||||
"electron-dl": "3.5.1",
|
||||
"electron-log": "5.0.1",
|
||||
"electron-store": "8.1.0",
|
||||
@ -5450,6 +5451,11 @@
|
||||
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/driver.js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/driver.js/-/driver.js-1.3.1.tgz",
|
||||
"integrity": "sha512-MvUdXbqSgEsgS/H9KyWb5Rxy0aE6BhOVT4cssi2x2XjmXea6qQfgdx32XKVLLSqTaIw7q/uxU5Xl3NV7+cN6FQ=="
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
||||
|
@ -48,6 +48,7 @@
|
||||
"buffer": "6.0.3",
|
||||
"compressorjs": "1.2.1",
|
||||
"crypto-js": "4.2.0",
|
||||
"driver.js": "^1.3.1",
|
||||
"electron-dl": "3.5.1",
|
||||
"electron-log": "5.0.1",
|
||||
"electron-store": "8.1.0",
|
||||
|
@ -1,8 +1,9 @@
|
||||
import {parentEpml} from '../connect.js'
|
||||
const MIN_RECONNECT_INTERVAL = 1000; // 1 second
|
||||
const MAX_RECONNECT_INTERVAL = 300000; // 5 minutes
|
||||
|
||||
let socketObject
|
||||
let activeBlockSocketTimeout
|
||||
let initial = 0
|
||||
let closeGracefully = false
|
||||
let isCalled = false
|
||||
let retryOnClose = false
|
||||
@ -10,11 +11,12 @@ let blockFirstCall = true
|
||||
let nodeStatusSocketObject
|
||||
let nodeStatusSocketTimeout
|
||||
let nodeStatusSocketcloseGracefully = false
|
||||
let nodeStatusCount = 0
|
||||
let nodeStatusRetryOnClose = false
|
||||
let nodeStateCall = false
|
||||
let isLoggedIn = false
|
||||
let oldAccountInfo
|
||||
let blockSocketReconnectInterval = MIN_RECONNECT_INTERVAL;
|
||||
let nodeStatusSocketReconnectInterval = MIN_RECONNECT_INTERVAL;
|
||||
|
||||
parentEpml.subscribe('logged_in', loggedIn => {
|
||||
if (loggedIn === 'true') {
|
||||
@ -24,6 +26,7 @@ parentEpml.subscribe('logged_in', loggedIn => {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const setAccountInfo = async (addr) => {
|
||||
const names = await parentEpml.request('apiCall', {
|
||||
url: `/names/address/${addr}`
|
||||
@ -115,6 +118,21 @@ const initNodeStatusCall = (nodeConfig) => {
|
||||
}
|
||||
}
|
||||
|
||||
function attemptReconnectBlockSocket() {
|
||||
setTimeout(() => {
|
||||
initBlockSocket();
|
||||
blockSocketReconnectInterval = Math.min(blockSocketReconnectInterval * 2, MAX_RECONNECT_INTERVAL);
|
||||
}, blockSocketReconnectInterval);
|
||||
}
|
||||
|
||||
function attemptReconnectNodeStatusSocket() {
|
||||
setTimeout(() => {
|
||||
initNodeStatusSocket();
|
||||
nodeStatusSocketReconnectInterval = Math.min(nodeStatusSocketReconnectInterval * 2, MAX_RECONNECT_INTERVAL);
|
||||
}, nodeStatusSocketReconnectInterval);
|
||||
}
|
||||
|
||||
|
||||
const initBlockSocket = () => {
|
||||
let myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||
let nodeUrl = myNode.domain + ":" + myNode.port
|
||||
@ -127,9 +145,9 @@ const initBlockSocket = () => {
|
||||
const activeBlockSocket = new WebSocket(activeBlockSocketLink)
|
||||
// Open Connection
|
||||
activeBlockSocket.onopen = (e) => {
|
||||
blockSocketReconnectInterval = MIN_RECONNECT_INTERVAL;
|
||||
closeGracefully = false
|
||||
socketObject = activeBlockSocket
|
||||
initial = initial + 1
|
||||
}
|
||||
// Message Event
|
||||
activeBlockSocket.onmessage = (e) => {
|
||||
@ -141,21 +159,10 @@ const initBlockSocket = () => {
|
||||
}
|
||||
// Closed Event
|
||||
activeBlockSocket.onclose = () => {
|
||||
processBlock({})
|
||||
blockFirstCall = true
|
||||
clearInterval(activeBlockSocketTimeout)
|
||||
|
||||
if (closeGracefully === false && initial <= 52) {
|
||||
if (initial <= 52) {
|
||||
retryOnClose = true
|
||||
setTimeout(pingactiveBlockSocket, 10000)
|
||||
initial = initial + 1
|
||||
} else {
|
||||
// ... Stop retrying...
|
||||
retryOnClose = false
|
||||
}
|
||||
}
|
||||
}
|
||||
processBlock({});
|
||||
blockFirstCall = true;
|
||||
attemptReconnectBlockSocket();
|
||||
};
|
||||
// Error Event
|
||||
activeBlockSocket.onerror = (e) => {
|
||||
blockFirstCall = true
|
||||
@ -200,31 +207,26 @@ const initNodeStatusSocket = () => {
|
||||
const activeNodeStatusSocket = new WebSocket(activeNodeStatusSocketLink)
|
||||
// Open Connection
|
||||
activeNodeStatusSocket.onopen = (e) => {
|
||||
console.log('onopen')
|
||||
nodeStatusSocketReconnectInterval = MIN_RECONNECT_INTERVAL;
|
||||
|
||||
nodeStatusSocketcloseGracefully = false
|
||||
nodeStatusSocketObject = activeNodeStatusSocket
|
||||
nodeStatusCount = nodeStatusCount + 1
|
||||
}
|
||||
// Message Event
|
||||
activeNodeStatusSocket.onmessage = (e) => {
|
||||
console.log('onmessage')
|
||||
doNodeStatus(JSON.parse(e.data))
|
||||
}
|
||||
// Closed Event
|
||||
activeNodeStatusSocket.onclose = () => {
|
||||
doNodeStatus({})
|
||||
clearInterval(nodeStatusSocketTimeout)
|
||||
if (nodeStatusSocketcloseGracefully === false && nodeStatusCount <= 52) {
|
||||
if (nodeStatusCount <= 52) {
|
||||
nodeStatusRetryOnClose = true
|
||||
setTimeout(pingNodeStatusSocket, 10000)
|
||||
nodeStatusCount = nodeStatusCount + 1
|
||||
} else {
|
||||
// ... Stop retrying...
|
||||
nodeStatusRetryOnClose = false
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('onclose')
|
||||
doNodeStatus({});
|
||||
attemptReconnectNodeStatusSocket();
|
||||
};
|
||||
// Error Event
|
||||
activeNodeStatusSocket.onerror = (e) => {
|
||||
console.log('onerror')
|
||||
doNodeStatus({})
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user