mirror of
https://github.com/Qortal/qortal-ui.git
synced 2025-02-11 09:45:52 +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",
|
"profile24": "You must fill out both field name and field value to add a custom property",
|
||||||
"profile25": "Is your friend",
|
"profile25": "Is your friend",
|
||||||
"profile26": "Add as 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/save-settings-qdn.js'
|
||||||
import './friends-view/core-sync-status.js'
|
import './friends-view/core-sync-status.js'
|
||||||
import './friends-view/profile.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'
|
import './controllers/coin-balances-controller.js'
|
||||||
|
|
||||||
const chatLastSeen = localForage.createInstance({
|
const chatLastSeen = localForage.createInstance({
|
||||||
@ -540,6 +543,26 @@ class AppView extends connect(store)(LitElement) {
|
|||||||
this.myLockScreenPass = ''
|
this.myLockScreenPass = ''
|
||||||
this.myLockScreenSet = ''
|
this.myLockScreenSet = ''
|
||||||
this.helperMessage = ''
|
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() {
|
render() {
|
||||||
@ -570,6 +593,7 @@ class AppView extends connect(store)(LitElement) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;align-items:center;gap:20px">
|
<div style="display:flex;align-items:center;gap:20px">
|
||||||
|
<beginner-checklist></beginner-checklist>
|
||||||
<profile-qdn></profile-qdn>
|
<profile-qdn></profile-qdn>
|
||||||
<friends-side-panel-parent></friends-side-panel-parent>
|
<friends-side-panel-parent></friends-side-panel-parent>
|
||||||
<notification-bell></notification-bell>
|
<notification-bell></notification-bell>
|
||||||
@ -600,6 +624,8 @@ class AppView extends connect(store)(LitElement) {
|
|||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
<show-plugin></show-plugin>
|
<show-plugin></show-plugin>
|
||||||
|
<tour-component .getElements=${this.getTourElements}></tour-component>
|
||||||
|
<sync-indicator ></sync-indicator>
|
||||||
</app-header-layout>
|
</app-header-layout>
|
||||||
</app-drawer-layout>
|
</app-drawer-layout>
|
||||||
<user-info-view></user-info-view>
|
<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() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
|
<div id="core-sync-status-id">
|
||||||
${this.renderSyncStatusIcon()}
|
${this.renderSyncStatusIcon()}
|
||||||
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,8 +139,8 @@ class FriendsView extends connect(store)(LitElement) {
|
|||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
window.removeEventListener('friends-my-friend-list-event', this._updateFriends)
|
window.removeEventListener('friends-my-friend-list-event', this._updateFriends)
|
||||||
window.addEventListener('friends-my-selected-feeds-event', this._updateFeed)
|
window.removeEventListener('friends-my-selected-feeds-event', this._updateFeed)
|
||||||
window.addEventListener('add-friend', this._addFriend)
|
window.removeEventListener('add-friend', this._addFriend)
|
||||||
|
|
||||||
super.disconnectedCallback()
|
super.disconnectedCallback()
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import '@polymer/paper-dialog/paper-dialog.js'
|
|||||||
import '@vaadin/grid'
|
import '@vaadin/grid'
|
||||||
import '@vaadin/text-field'
|
import '@vaadin/text-field'
|
||||||
import '../custom-elements/frag-file-input.js'
|
import '../custom-elements/frag-file-input.js'
|
||||||
|
import { defaultQappsTabs } from '../data/defaultQapps.js'
|
||||||
|
|
||||||
registerTranslateConfig({
|
registerTranslateConfig({
|
||||||
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
||||||
@ -322,6 +323,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.registeredUrls = []
|
this.registeredUrls = []
|
||||||
|
this.initialRegisteredUrls = []
|
||||||
this.currentTab = 0
|
this.currentTab = 0
|
||||||
this.tabs = []
|
this.tabs = []
|
||||||
this.uid = new ShortUniqueId()
|
this.uid = new ShortUniqueId()
|
||||||
@ -339,6 +341,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
<div id="showPluginId" style="width:0px"></div>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
${this.tabs.map((tab, index) => {
|
${this.tabs.map((tab, index) => {
|
||||||
let title = ''
|
let title = ''
|
||||||
@ -1725,7 +1728,9 @@ class NavBar extends connect(store)(LitElement) {
|
|||||||
|
|
||||||
if (localStorage.getItem("myMenuPlugs") === null) {
|
if (localStorage.getItem("myMenuPlugs") === null) {
|
||||||
await appDelay(1000)
|
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)
|
localStorage.setItem("myMenuPlugs", myObj)
|
||||||
this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]")
|
this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]")
|
||||||
} else {
|
} 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
|
||||||
|
}
|
||||||
|
]
|
@ -136,4 +136,5 @@ html[theme="dark"] {
|
|||||||
--app-hr: rgba(255, 255, 255, .3);
|
--app-hr: rgba(255, 255, 255, .3);
|
||||||
--code-block-text-color: #008fd5;
|
--code-block-text-color: #008fd5;
|
||||||
--noavatar: url("/img/noavatar_dark.png");
|
--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",
|
"buffer": "6.0.3",
|
||||||
"compressorjs": "1.2.1",
|
"compressorjs": "1.2.1",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
|
"driver.js": "^1.3.1",
|
||||||
"electron-dl": "3.5.1",
|
"electron-dl": "3.5.1",
|
||||||
"electron-log": "5.0.1",
|
"electron-log": "5.0.1",
|
||||||
"electron-store": "8.1.0",
|
"electron-store": "8.1.0",
|
||||||
@ -5450,6 +5451,11 @@
|
|||||||
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
|
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/ejs": {
|
||||||
"version": "3.1.9",
|
"version": "3.1.9",
|
||||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
"buffer": "6.0.3",
|
"buffer": "6.0.3",
|
||||||
"compressorjs": "1.2.1",
|
"compressorjs": "1.2.1",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
|
"driver.js": "^1.3.1",
|
||||||
"electron-dl": "3.5.1",
|
"electron-dl": "3.5.1",
|
||||||
"electron-log": "5.0.1",
|
"electron-log": "5.0.1",
|
||||||
"electron-store": "8.1.0",
|
"electron-store": "8.1.0",
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import {parentEpml} from '../connect.js'
|
import {parentEpml} from '../connect.js'
|
||||||
|
const MIN_RECONNECT_INTERVAL = 1000; // 1 second
|
||||||
|
const MAX_RECONNECT_INTERVAL = 300000; // 5 minutes
|
||||||
|
|
||||||
let socketObject
|
let socketObject
|
||||||
let activeBlockSocketTimeout
|
let activeBlockSocketTimeout
|
||||||
let initial = 0
|
|
||||||
let closeGracefully = false
|
let closeGracefully = false
|
||||||
let isCalled = false
|
let isCalled = false
|
||||||
let retryOnClose = false
|
let retryOnClose = false
|
||||||
@ -10,11 +11,12 @@ let blockFirstCall = true
|
|||||||
let nodeStatusSocketObject
|
let nodeStatusSocketObject
|
||||||
let nodeStatusSocketTimeout
|
let nodeStatusSocketTimeout
|
||||||
let nodeStatusSocketcloseGracefully = false
|
let nodeStatusSocketcloseGracefully = false
|
||||||
let nodeStatusCount = 0
|
|
||||||
let nodeStatusRetryOnClose = false
|
let nodeStatusRetryOnClose = false
|
||||||
let nodeStateCall = false
|
let nodeStateCall = false
|
||||||
let isLoggedIn = false
|
let isLoggedIn = false
|
||||||
let oldAccountInfo
|
let oldAccountInfo
|
||||||
|
let blockSocketReconnectInterval = MIN_RECONNECT_INTERVAL;
|
||||||
|
let nodeStatusSocketReconnectInterval = MIN_RECONNECT_INTERVAL;
|
||||||
|
|
||||||
parentEpml.subscribe('logged_in', loggedIn => {
|
parentEpml.subscribe('logged_in', loggedIn => {
|
||||||
if (loggedIn === 'true') {
|
if (loggedIn === 'true') {
|
||||||
@ -24,6 +26,7 @@ parentEpml.subscribe('logged_in', loggedIn => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const setAccountInfo = async (addr) => {
|
const setAccountInfo = async (addr) => {
|
||||||
const names = await parentEpml.request('apiCall', {
|
const names = await parentEpml.request('apiCall', {
|
||||||
url: `/names/address/${addr}`
|
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 = () => {
|
const initBlockSocket = () => {
|
||||||
let myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
let myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||||
let nodeUrl = myNode.domain + ":" + myNode.port
|
let nodeUrl = myNode.domain + ":" + myNode.port
|
||||||
@ -127,9 +145,9 @@ const initBlockSocket = () => {
|
|||||||
const activeBlockSocket = new WebSocket(activeBlockSocketLink)
|
const activeBlockSocket = new WebSocket(activeBlockSocketLink)
|
||||||
// Open Connection
|
// Open Connection
|
||||||
activeBlockSocket.onopen = (e) => {
|
activeBlockSocket.onopen = (e) => {
|
||||||
|
blockSocketReconnectInterval = MIN_RECONNECT_INTERVAL;
|
||||||
closeGracefully = false
|
closeGracefully = false
|
||||||
socketObject = activeBlockSocket
|
socketObject = activeBlockSocket
|
||||||
initial = initial + 1
|
|
||||||
}
|
}
|
||||||
// Message Event
|
// Message Event
|
||||||
activeBlockSocket.onmessage = (e) => {
|
activeBlockSocket.onmessage = (e) => {
|
||||||
@ -141,21 +159,10 @@ const initBlockSocket = () => {
|
|||||||
}
|
}
|
||||||
// Closed Event
|
// Closed Event
|
||||||
activeBlockSocket.onclose = () => {
|
activeBlockSocket.onclose = () => {
|
||||||
processBlock({})
|
processBlock({});
|
||||||
blockFirstCall = true
|
blockFirstCall = true;
|
||||||
clearInterval(activeBlockSocketTimeout)
|
attemptReconnectBlockSocket();
|
||||||
|
};
|
||||||
if (closeGracefully === false && initial <= 52) {
|
|
||||||
if (initial <= 52) {
|
|
||||||
retryOnClose = true
|
|
||||||
setTimeout(pingactiveBlockSocket, 10000)
|
|
||||||
initial = initial + 1
|
|
||||||
} else {
|
|
||||||
// ... Stop retrying...
|
|
||||||
retryOnClose = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Error Event
|
// Error Event
|
||||||
activeBlockSocket.onerror = (e) => {
|
activeBlockSocket.onerror = (e) => {
|
||||||
blockFirstCall = true
|
blockFirstCall = true
|
||||||
@ -200,31 +207,26 @@ const initNodeStatusSocket = () => {
|
|||||||
const activeNodeStatusSocket = new WebSocket(activeNodeStatusSocketLink)
|
const activeNodeStatusSocket = new WebSocket(activeNodeStatusSocketLink)
|
||||||
// Open Connection
|
// Open Connection
|
||||||
activeNodeStatusSocket.onopen = (e) => {
|
activeNodeStatusSocket.onopen = (e) => {
|
||||||
|
console.log('onopen')
|
||||||
|
nodeStatusSocketReconnectInterval = MIN_RECONNECT_INTERVAL;
|
||||||
|
|
||||||
nodeStatusSocketcloseGracefully = false
|
nodeStatusSocketcloseGracefully = false
|
||||||
nodeStatusSocketObject = activeNodeStatusSocket
|
nodeStatusSocketObject = activeNodeStatusSocket
|
||||||
nodeStatusCount = nodeStatusCount + 1
|
|
||||||
}
|
}
|
||||||
// Message Event
|
// Message Event
|
||||||
activeNodeStatusSocket.onmessage = (e) => {
|
activeNodeStatusSocket.onmessage = (e) => {
|
||||||
|
console.log('onmessage')
|
||||||
doNodeStatus(JSON.parse(e.data))
|
doNodeStatus(JSON.parse(e.data))
|
||||||
}
|
}
|
||||||
// Closed Event
|
// Closed Event
|
||||||
activeNodeStatusSocket.onclose = () => {
|
activeNodeStatusSocket.onclose = () => {
|
||||||
doNodeStatus({})
|
console.log('onclose')
|
||||||
clearInterval(nodeStatusSocketTimeout)
|
doNodeStatus({});
|
||||||
if (nodeStatusSocketcloseGracefully === false && nodeStatusCount <= 52) {
|
attemptReconnectNodeStatusSocket();
|
||||||
if (nodeStatusCount <= 52) {
|
};
|
||||||
nodeStatusRetryOnClose = true
|
|
||||||
setTimeout(pingNodeStatusSocket, 10000)
|
|
||||||
nodeStatusCount = nodeStatusCount + 1
|
|
||||||
} else {
|
|
||||||
// ... Stop retrying...
|
|
||||||
nodeStatusRetryOnClose = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Error Event
|
// Error Event
|
||||||
activeNodeStatusSocket.onerror = (e) => {
|
activeNodeStatusSocket.onerror = (e) => {
|
||||||
|
console.log('onerror')
|
||||||
doNodeStatus({})
|
doNodeStatus({})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user