mirror of
https://github.com/Qortal/qortal-ui.git
synced 2025-11-02 21:57:51 +00:00
Merge branch 'resolve-20231003' into master
This commit is contained in:
@@ -42,6 +42,8 @@ import '../functional-components/side-menu.js'
|
||||
import '../functional-components/side-menu-item.js'
|
||||
import './start-minting.js'
|
||||
import './notification-view/notification-bell.js'
|
||||
import './notification-view/notification-bell-general.js'
|
||||
|
||||
|
||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||
|
||||
@@ -556,7 +558,10 @@ class AppView extends connect(store)(LitElement) {
|
||||
<img src="${this.config.coin.logo}" style="height:32px; padding-left:12px;">
|
||||
</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:20px">
|
||||
<notification-bell></notification-bell>
|
||||
<notification-bell-general></notification-bell-general>
|
||||
</div>
|
||||
<div style="display: inline;">
|
||||
<span>
|
||||
<img src="/img/${translate("selectmenu.languageflag")}-flag-round-icon-32.png" style="width: 32px; height: 32px; padding-top: 4px;">
|
||||
|
||||
@@ -15,7 +15,7 @@ import './login-section.js'
|
||||
import '../qort-theme-toggle.js'
|
||||
|
||||
import settings from '../../functional-components/settings-page.js'
|
||||
import { addAutoLoadImageChat, removeAutoLoadImageChat, addChatLastSeen, allowQAPPAutoAuth, removeQAPPAutoAuth, removeQAPPAutoLists, allowQAPPAutoLists, addTabInfo, setTabNotifications, setNewTab } from '../../redux/app/app-actions.js'
|
||||
import { addAutoLoadImageChat, removeAutoLoadImageChat, addChatLastSeen, allowQAPPAutoAuth, removeQAPPAutoAuth, removeQAPPAutoLists, allowQAPPAutoLists, addTabInfo, setTabNotifications, setNewTab, setNewNotification, setSideEffectAction } from '../../redux/app/app-actions.js'
|
||||
|
||||
window.reduxStore = store
|
||||
window.reduxAction = {
|
||||
@@ -28,7 +28,9 @@ window.reduxAction = {
|
||||
removeQAPPAutoLists: removeQAPPAutoLists,
|
||||
addTabInfo: addTabInfo,
|
||||
setTabNotifications: setTabNotifications,
|
||||
setNewTab: setNewTab
|
||||
setNewTab: setNewTab,
|
||||
setNewNotification: setNewNotification,
|
||||
setSideEffectAction: setSideEffectAction
|
||||
}
|
||||
|
||||
const animationDuration = 0.7 // Seconds
|
||||
|
||||
@@ -0,0 +1,527 @@
|
||||
import { LitElement, html, css } 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 { setNewNotification, setNewTab } from '../../redux/app/app-actions.js';
|
||||
import { routes } from '../../plugins/routes.js';
|
||||
import '@material/mwc-icon';
|
||||
import { translate, get } from 'lit-translate';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import config from '../../notifications/config.js';
|
||||
import '../../../../plugins/plugins/core/components/TimeAgo.js';
|
||||
import './popover.js';
|
||||
|
||||
|
||||
|
||||
|
||||
class NotificationBellGeneral extends connect(store)(LitElement) {
|
||||
static properties = {
|
||||
notifications: { type: Array },
|
||||
showNotifications: { type: Boolean },
|
||||
notificationCount: { type: Boolean },
|
||||
theme: { type: String, reflect: true },
|
||||
notifications: { type: Array },
|
||||
currentNotification: { type: Object },
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.notifications = [];
|
||||
this.showNotifications = false;
|
||||
this.notificationCount = false;
|
||||
this.initialFetch = false;
|
||||
this.theme = localStorage.getItem('qortalTheme')
|
||||
? localStorage.getItem('qortalTheme')
|
||||
: 'light';
|
||||
this.currentNotification = null;
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
try {
|
||||
let value = JSON.parse(localStorage.getItem('isFirstTimeUser'));
|
||||
if (!value && value !== false) {
|
||||
value = true;
|
||||
}
|
||||
this.isFirstTimeUser = value;
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
async stateChanged(state) {
|
||||
if (state.app.newNotification) {
|
||||
const newNotification = state.app.newNotification;
|
||||
this.notifications = [newNotification, ...this.notifications];
|
||||
store.dispatch(setNewNotification(null));
|
||||
if (this.isFirstTimeUser) {
|
||||
const target = this.shadowRoot.getElementById(
|
||||
'popover-notification'
|
||||
);
|
||||
const popover =
|
||||
this.shadowRoot.querySelector('popover-component');
|
||||
if (popover) {
|
||||
popover.openPopover(target);
|
||||
}
|
||||
|
||||
localStorage.setItem('isFirstTimeUser', JSON.stringify(false));
|
||||
this.isFirstTimeUser = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
setTimeout(() => {
|
||||
if (!this.shadowRoot.contains(document.activeElement)) {
|
||||
this.showNotifications = false;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
changeStatus(signature, statusTx) {
|
||||
const copyNotifications = [...this.notifications];
|
||||
const findNotification = this.notifications.findIndex(
|
||||
(notification) => notification.reference.signature === signature
|
||||
);
|
||||
if (findNotification !== -1) {
|
||||
copyNotifications[findNotification] = {
|
||||
...copyNotifications[findNotification],
|
||||
status: statusTx,
|
||||
};
|
||||
this.notifications = copyNotifications;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasOngoing = this.notifications.find(
|
||||
(notification) => notification.status !== 'confirmed'
|
||||
);
|
||||
return html`
|
||||
<div class="layout">
|
||||
<popover-component
|
||||
for="popover-notification"
|
||||
message=${get('notifications.explanation')}
|
||||
></popover-component>
|
||||
<div
|
||||
id="popover-notification"
|
||||
@click=${() => this._toggleNotifications()}
|
||||
>
|
||||
${hasOngoing
|
||||
? html`
|
||||
<mwc-icon style="color: green;cursor:pointer"
|
||||
>notifications</mwc-icon
|
||||
>
|
||||
`
|
||||
: html`
|
||||
<mwc-icon
|
||||
style="color: var(--black); cursor:pointer"
|
||||
>notifications</mwc-icon
|
||||
>
|
||||
`}
|
||||
</div>
|
||||
${hasOngoing
|
||||
? html`
|
||||
<span
|
||||
class="count"
|
||||
style="cursor:pointer"
|
||||
@click=${() => this._toggleNotifications()}
|
||||
>
|
||||
<mwc-icon
|
||||
style="color: var(--black);font-size:18px"
|
||||
>pending</mwc-icon
|
||||
>
|
||||
</span>
|
||||
`
|
||||
: ''}
|
||||
|
||||
<div
|
||||
id="notification-panel"
|
||||
class="popover-panel"
|
||||
style="visibility:${this.showNotifications
|
||||
? 'visibile'
|
||||
: 'hidden'}"
|
||||
tabindex="0"
|
||||
@blur=${this.handleBlur}
|
||||
>
|
||||
<div class="notifications-list">
|
||||
${repeat(
|
||||
this.notifications,
|
||||
(notification) => notification.reference.signature, // key function
|
||||
(notification) => html`
|
||||
<notification-item-tx
|
||||
.changeStatus=${(val1, val2) =>
|
||||
this.changeStatus(val1, val2)}
|
||||
status=${notification.status}
|
||||
timestamp=${notification.timestamp}
|
||||
type=${notification.type}
|
||||
signature=${notification.reference
|
||||
.signature}
|
||||
></notification-item-tx>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
_toggleNotifications() {
|
||||
if (this.notifications.length === 0) return;
|
||||
this.showNotifications = !this.showNotifications;
|
||||
if (this.showNotifications) {
|
||||
requestAnimationFrame(() => {
|
||||
this.shadowRoot.getElementById('notification-panel').focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.notifications-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.notification-item {
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
transition: 0.2s all;
|
||||
}
|
||||
|
||||
.notification-item:hover {
|
||||
background: var(--nav-color-hover);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: var(--black);
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
customElements.define('notification-bell-general', NotificationBellGeneral);
|
||||
|
||||
class NotificationItemTx extends connect(store)(LitElement) {
|
||||
static properties = {
|
||||
status: { type: String },
|
||||
type: { type: String },
|
||||
timestamp: { type: Number },
|
||||
signature: { type: String },
|
||||
changeStatus: { attribute: false },
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.nodeUrl = this.getNodeUrl();
|
||||
this.myNode = this.getMyNode();
|
||||
}
|
||||
|
||||
getNodeUrl() {
|
||||
const myNode =
|
||||
store.getState().app.nodeConfig.knownNodes[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
];
|
||||
|
||||
const nodeUrl =
|
||||
myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||
return nodeUrl;
|
||||
}
|
||||
getMyNode() {
|
||||
const myNode =
|
||||
store.getState().app.nodeConfig.knownNodes[
|
||||
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||
];
|
||||
|
||||
return myNode;
|
||||
}
|
||||
|
||||
async getStatus() {
|
||||
let interval = null;
|
||||
let stop = false;
|
||||
const getAnswer = async () => {
|
||||
const getTx = async (minterAddr) => {
|
||||
const url = `${this.nodeUrl}/transactions/signature/${this.signature}`;
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
return data;
|
||||
};
|
||||
|
||||
if (!stop) {
|
||||
stop = true;
|
||||
try {
|
||||
const txTransaction = await getTx();
|
||||
if (!txTransaction.error && txTransaction.signature && txTransaction.blockHeight) {
|
||||
clearInterval(interval);
|
||||
this.changeStatus(this.signature, 'confirmed');
|
||||
}
|
||||
} catch (error) {}
|
||||
stop = false;
|
||||
}
|
||||
};
|
||||
interval = setInterval(getAnswer, 20000);
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.getStatus();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="notification-item" @click=${() => {}}>
|
||||
<div>
|
||||
<p style="margin-bottom:10px; font-weight:bold">
|
||||
${translate('transpage.tchange1')}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="margin-bottom:5px">
|
||||
${translate('walletpage.wchange35')}: ${this.type}
|
||||
</p>
|
||||
<p style="margin-bottom:5px">
|
||||
${translate('tubespage.schange28')}:
|
||||
${this.status === 'confirming'
|
||||
? translate('notifications.notify1')
|
||||
: translate('notifications.notify2')}
|
||||
</p>
|
||||
${this.status !== 'confirmed'
|
||||
? html`
|
||||
<div class="centered">
|
||||
<div class="loader">Loading...</div>
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
<div
|
||||
style="display:flex;justify-content:space-between;align-items:center"
|
||||
>
|
||||
<message-time
|
||||
timestamp=${this.timestamp}
|
||||
style="color:red;font-size:12px"
|
||||
></message-time>
|
||||
${this.status === 'confirmed'
|
||||
? html`
|
||||
<mwc-icon style="color: green;"
|
||||
>done</mwc-icon
|
||||
>
|
||||
`
|
||||
: ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
_toggleNotifications() {
|
||||
if (this.notifications.length === 0) return;
|
||||
this.showNotifications = !this.showNotifications;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.centered {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.layout {
|
||||
width: 100px;
|
||||
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;
|
||||
}
|
||||
|
||||
.notifications-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.notification-item {
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.notification-item:hover {
|
||||
background: var(--nav-color-hover);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: var(--black);
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.loader,
|
||||
.loader:before,
|
||||
.loader:after {
|
||||
border-radius: 50%;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
-webkit-animation: load7 1.8s infinite ease-in-out;
|
||||
animation: load7 1.8s infinite ease-in-out;
|
||||
}
|
||||
.loader {
|
||||
color: var(--black);
|
||||
font-size: 5px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
text-indent: -9999em;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
.loader:before,
|
||||
.loader:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
.loader:before {
|
||||
left: -3.5em;
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
.loader:after {
|
||||
left: 3.5em;
|
||||
}
|
||||
@-webkit-keyframes load7 {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 2.5em 0 -1.3em;
|
||||
}
|
||||
40% {
|
||||
box-shadow: 0 2.5em 0 0;
|
||||
}
|
||||
}
|
||||
@keyframes load7 {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 2.5em 0 -1.3em;
|
||||
}
|
||||
40% {
|
||||
box-shadow: 0 2.5em 0 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
customElements.define('notification-item-tx', NotificationItemTx);
|
||||
@@ -158,7 +158,7 @@ class NotificationBell extends connect(store)(LitElement) {
|
||||
url: `qdn/browser/index.html${query}`,
|
||||
id: 'q-mail-notification',
|
||||
myPlugObj: {
|
||||
"url": "qapps",
|
||||
"url": "myapp",
|
||||
"domain": "core",
|
||||
"page": `qdn/browser/index.html${query}`,
|
||||
"title": "Q-Mail",
|
||||
@@ -196,7 +196,7 @@ class NotificationBell extends connect(store)(LitElement) {
|
||||
url: `qdn/browser/index.html${query}`,
|
||||
id: 'q-mail-notification',
|
||||
myPlugObj: {
|
||||
"url": "qapps",
|
||||
"url": "myapp",
|
||||
"domain": "core",
|
||||
"page": `qdn/browser/index.html${query}`,
|
||||
"title": "Q-Mail",
|
||||
@@ -210,7 +210,6 @@ class NotificationBell extends connect(store)(LitElement) {
|
||||
|
||||
static styles = css`
|
||||
.layout {
|
||||
width: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -220,7 +219,7 @@ class NotificationBell extends connect(store)(LitElement) {
|
||||
.count {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 32px;
|
||||
right: 0px;
|
||||
font-size: 12px;
|
||||
background-color: red;
|
||||
color: white;
|
||||
|
||||
74
core/src/components/notification-view/popover.js
Normal file
74
core/src/components/notification-view/popover.js
Normal file
@@ -0,0 +1,74 @@
|
||||
// popover-component.js
|
||||
import { LitElement, html, css } from 'lit';
|
||||
import { createPopper } from '@popperjs/core';
|
||||
import '@material/mwc-icon'
|
||||
|
||||
export class PopoverComponent extends LitElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: var(--white);
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
z-index: 10;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
color: var(--black);
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
color: var(--black)
|
||||
}
|
||||
`;
|
||||
|
||||
static properties = {
|
||||
for: { type: String, reflect: true },
|
||||
message: { type: String }
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.message = '';
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
// We'll defer the popper attachment to the openPopover() method to ensure target availability
|
||||
}
|
||||
|
||||
attachToTarget(target) {
|
||||
console.log({target})
|
||||
if (!this.popperInstance && target) {
|
||||
this.popperInstance = createPopper(target, this, {
|
||||
placement: 'bottom',
|
||||
strategy: 'fixed'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
openPopover(target) {
|
||||
this.attachToTarget(target);
|
||||
this.style.display = 'block';
|
||||
}
|
||||
|
||||
closePopover() {
|
||||
this.style.display = 'none';
|
||||
if (this.popperInstance) {
|
||||
this.popperInstance.destroy();
|
||||
this.popperInstance = null;
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<span class="close-icon" @click="${this.closePopover}"><mwc-icon style="color: var(--black)">close</mwc-icon></span>
|
||||
<div><mwc-icon style="color: var(--black)">info</mwc-icon> ${this.message}</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('popover-component', PopoverComponent);
|
||||
@@ -97,13 +97,13 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
.hideIframe {
|
||||
display: none;
|
||||
position: absolute;
|
||||
zIndex: -10;
|
||||
z-Index: -10;
|
||||
}
|
||||
|
||||
.showIframe {
|
||||
display: block;
|
||||
display: flex;
|
||||
position: relative;
|
||||
zIndex: 1;
|
||||
z-Index: 1;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
@@ -135,7 +135,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
min-width: 110px;
|
||||
max-width: 220px;
|
||||
overflow: hidden;
|
||||
zIndex: 2;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tabCard {
|
||||
@@ -171,7 +171,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
border-left: 1px solid var(--black);
|
||||
border-right: 1px solid var(--black);
|
||||
border-bottom: 1px solid var(--white);
|
||||
zIndex: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.close {
|
||||
@@ -432,7 +432,9 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
<div
|
||||
id="tab-${tab.id}"
|
||||
class="tab ${this.currentTab === index ? 'active' : ''}"
|
||||
@click="${() => this.currentTab = index}"
|
||||
@click="${() => {
|
||||
this.currentTab = index
|
||||
}}"
|
||||
>
|
||||
<div id="icon-${tab.id}" class="${this.currentTab === index ? "iconActive" : "iconInactive"}">
|
||||
<mwc-icon>${icon}</mwc-icon>
|
||||
@@ -441,10 +443,13 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
${count ? html`
|
||||
<span class="tabTitle ml-30">${title}</span>
|
||||
<span class="count ml-5">${count}</span>
|
||||
<span class="show ml-25"><mwc-icon class="close" @click=${() => {this.removeTab(index, tab.id)}}>close</mwc-icon></span>
|
||||
<span class="show ml-25"><mwc-icon class="close" @click=${(event) => {
|
||||
event.stopPropagation(); this.removeTab(index, tab.id)
|
||||
|
||||
}}>close</mwc-icon></span>
|
||||
` : html`
|
||||
<span class="tabTitle ml-30">${title}</span>
|
||||
<span class="show ml-25"><mwc-icon class="close" @click=${() => {this.removeTab(index, tab.id)}}>close</mwc-icon></span>
|
||||
<span class="show ml-25"><mwc-icon class="close" @click=${(event) => {event.stopPropagation(); this.removeTab(index, tab.id)}}>close</mwc-icon></span>
|
||||
`}
|
||||
</div>
|
||||
</div>
|
||||
@@ -460,6 +465,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
id: this.uid.rnd()
|
||||
})
|
||||
this.currentTab = lengthOfTabs
|
||||
|
||||
}}
|
||||
>+</button>
|
||||
</div>
|
||||
@@ -655,7 +661,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
this.createEpmlInstance(frame, newIndex)
|
||||
}
|
||||
|
||||
removeTab(index, tabA) {
|
||||
removeTab(index, tabA) {
|
||||
const tabB = this.tabs.length - 1
|
||||
const tabC = this.tabs[tabB].id
|
||||
|
||||
@@ -667,10 +673,10 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
let iconId = ''
|
||||
|
||||
this.tabs = this.tabs.filter((tab, tIndex) => tIndex !== index)
|
||||
this.currentTab = this.tabs.length - 1;
|
||||
|
||||
const tabD = this.tabs.length - 1
|
||||
const plugObj = this.tabs[tabD].url
|
||||
|
||||
theId = this.tabs[tabD].id
|
||||
tabId = 'tab-' + theId
|
||||
frameId = 'frame-' + theId
|
||||
@@ -819,7 +825,14 @@ class ShowPlugin extends connect(store)(LitElement) {
|
||||
|
||||
if (state.app.newTab) {
|
||||
const newTab = state.app.newTab
|
||||
if (!this.tabs.find((tab) => tab.id === newTab.id)) {
|
||||
if(newTab.openExisting && this.tabs.find((tab)=> tab.url === newTab.url)){
|
||||
const findIndex = this.tabs.findIndex((tab) => tab.url === newTab.url)
|
||||
if (findIndex !== -1) {
|
||||
this.currentTab = findIndex
|
||||
}
|
||||
|
||||
store.dispatch(setNewTab(null))
|
||||
} else if (!this.tabs.find((tab) => tab.id === newTab.id)) {
|
||||
this.addTab(newTab)
|
||||
this.currentTab = this.tabs.length - 1
|
||||
store.dispatch(setNewTab(null))
|
||||
@@ -893,7 +906,7 @@ class NavBar extends connect(store)(LitElement) {
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
height: 100vh;
|
||||
height: calc(100vh - 120px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -901,7 +914,7 @@ class NavBar extends connect(store)(LitElement) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: color: var(--white);
|
||||
background-color: var(--white);
|
||||
padding: 10px 20px;
|
||||
max-width: 750px;
|
||||
width: 80%;
|
||||
@@ -2276,10 +2289,10 @@ class NavBar extends connect(store)(LitElement) {
|
||||
|
||||
if (service === "APP") {
|
||||
this.changePage({
|
||||
"url": "qapp",
|
||||
"url": "myapp",
|
||||
"domain": "core",
|
||||
"page": `qdn/browser/index.html${query}`,
|
||||
"title": "Q-App",
|
||||
"title": name || "Q-App",
|
||||
"icon": "vaadin:external-browser",
|
||||
"mwcicon": "open_in_browser",
|
||||
"menus": [],
|
||||
@@ -2287,10 +2300,10 @@ class NavBar extends connect(store)(LitElement) {
|
||||
})
|
||||
} else if (service === "WEBSITE") {
|
||||
this.changePage({
|
||||
"url": "websites",
|
||||
"url": "myapp",
|
||||
"domain": "core",
|
||||
"page": `qdn/browser/index.html${query}`,
|
||||
"title": "Website",
|
||||
"title": name || "Website",
|
||||
"icon": "vaadin:desktop",
|
||||
"mwcicon": "desktop_mac",
|
||||
"menus": [],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ const APP_INFO_STATE = 'app_info_state'
|
||||
const CHAT_HEADS_STREAM_NAME = 'chat_heads'
|
||||
const NODE_CONFIG_STREAM_NAME = 'node_config'
|
||||
const CHAT_LAST_SEEN = 'chat_last_seen'
|
||||
const SIDE_EFFECT_ACTION = 'side_effect_action'
|
||||
|
||||
export const loggedInStream = new EpmlStream(LOGIN_STREAM_NAME, () => store.getState().app.loggedIn)
|
||||
export const configStream = new EpmlStream(CONFIG_STREAM_NAME, () => store.getState().config)
|
||||
@@ -16,6 +17,8 @@ export const appInfoStateStream = new EpmlStream(APP_INFO_STATE, () => store.get
|
||||
export const chatHeadsStateStream = new EpmlStream(CHAT_HEADS_STREAM_NAME, () => store.getState().app.chatHeads)
|
||||
export const nodeConfigStream = new EpmlStream(NODE_CONFIG_STREAM_NAME, () => store.getState().app.nodeConfig)
|
||||
export const chatLastSeenStream = new EpmlStream(CHAT_LAST_SEEN, () => store.getState().app.chatLastSeen)
|
||||
export const sideEffectActionStream = new EpmlStream(SIDE_EFFECT_ACTION, () => store.getState().app.sideEffectAction)
|
||||
|
||||
|
||||
|
||||
let oldState = {
|
||||
@@ -56,6 +59,10 @@ store.subscribe(() => {
|
||||
if (oldState.app.appInfo !== state.app.appInfo) {
|
||||
appInfoStateStream.emit(state.app.appInfo)
|
||||
}
|
||||
if (oldState.app.sideEffectAction !== state.app.sideEffectAction) {
|
||||
sideEffectActionStream.emit(state.app.sideEffectAction)
|
||||
}
|
||||
|
||||
|
||||
oldState = state
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Core App Actions here...
|
||||
import { UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, CHAT_HEADS, ACCOUNT_INFO, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN, ALLOW_QAPP_AUTO_LISTS, REMOVE_QAPP_AUTO_LISTS, SET_NEW_TAB, ADD_TAB_INFO, SET_TAB_NOTIFICATIONS, IS_OPEN_DEV_DIALOG } from '../app-action-types.js'
|
||||
import { UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, CHAT_HEADS, ACCOUNT_INFO, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN, ALLOW_QAPP_AUTO_LISTS, REMOVE_QAPP_AUTO_LISTS, SET_NEW_TAB, ADD_TAB_INFO, SET_TAB_NOTIFICATIONS, IS_OPEN_DEV_DIALOG, SET_NEW_NOTIFICATION, SET_SIDE_EFFECT } from '../app-action-types.js'
|
||||
|
||||
export const doUpdateBlockInfo = (blockObj) => {
|
||||
return (dispatch, getState) => {
|
||||
@@ -126,6 +126,12 @@ export const setNewTab = (payload) => {
|
||||
payload
|
||||
}
|
||||
}
|
||||
export const setNewNotification = (payload) => {
|
||||
return {
|
||||
type: SET_NEW_NOTIFICATION,
|
||||
payload
|
||||
}
|
||||
}
|
||||
export const setIsOpenDevDialog = (payload)=> {
|
||||
return {
|
||||
type: IS_OPEN_DEV_DIALOG,
|
||||
@@ -144,4 +150,11 @@ export const setTabNotifications = (payload) => {
|
||||
type: SET_TAB_NOTIFICATIONS,
|
||||
payload
|
||||
}
|
||||
}
|
||||
|
||||
export const setSideEffectAction = (payload)=> {
|
||||
return {
|
||||
type: SET_SIDE_EFFECT,
|
||||
payload
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Node Config Actions here...
|
||||
import { LOAD_NODE_CONFIG, SET_NODE, ADD_NODE } from '../app-action-types.js'
|
||||
import { LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, REMOVE_NODE, EDIT_NODE } from '../app-action-types.js'
|
||||
import { UI_VERSION } from '../version.js'
|
||||
|
||||
const nodeConfigUrl = '/getConfig'
|
||||
@@ -72,6 +72,16 @@ export const doAddNode = (nodeObject) => {
|
||||
return dispatch(addNode(nodeObject))
|
||||
}
|
||||
}
|
||||
export const doRemoveNode = (index) => {
|
||||
return (dispatch, getState) => {
|
||||
return dispatch(removeNode(index))
|
||||
}
|
||||
}
|
||||
export const doEditNode = (index, nodeObject) => {
|
||||
return (dispatch, getState) => {
|
||||
return dispatch(editNode({index, nodeObject}))
|
||||
}
|
||||
}
|
||||
|
||||
const addNode = (payload) => {
|
||||
return {
|
||||
@@ -80,6 +90,18 @@ const addNode = (payload) => {
|
||||
}
|
||||
}
|
||||
|
||||
const editNode = (payload) => {
|
||||
return {
|
||||
type: EDIT_NODE,
|
||||
payload
|
||||
}
|
||||
}
|
||||
const removeNode = (payload) => {
|
||||
return {
|
||||
type: REMOVE_NODE,
|
||||
payload
|
||||
}
|
||||
}
|
||||
const obj1 = {
|
||||
name: 'Local Node',
|
||||
protocol: 'http',
|
||||
|
||||
@@ -12,6 +12,8 @@ export const UPDATE_NODE_STATUS = 'UPDATE_NODE_STATUS'
|
||||
export const UPDATE_NODE_INFO = 'UPDATE_NODE_INFO'
|
||||
export const SET_NODE = 'SET_NODE'
|
||||
export const ADD_NODE = 'ADD_NODE'
|
||||
export const EDIT_NODE = 'EDIT_NODE'
|
||||
export const REMOVE_NODE = 'REMOVE_NODE'
|
||||
export const LOAD_NODE_CONFIG = 'LOAD_NODE_CONFIG'
|
||||
export const PAGE_URL = 'PAGE_URL'
|
||||
export const CHAT_HEADS = 'CHAT_HEADS'
|
||||
@@ -28,4 +30,6 @@ export const ADD_CHAT_LAST_SEEN = 'ADD_CHAT_LAST_SEEN'
|
||||
export const SET_NEW_TAB = 'SET_NEW_TAB'
|
||||
export const ADD_TAB_INFO = 'ADD_TAB_INFO'
|
||||
export const SET_TAB_NOTIFICATIONS = 'SET_TAB_NOTIFICATIONS'
|
||||
export const IS_OPEN_DEV_DIALOG = 'IS_OPEN_DEV_DIALOG'
|
||||
export const IS_OPEN_DEV_DIALOG = 'IS_OPEN_DEV_DIALOG'
|
||||
export const SET_NEW_NOTIFICATION = 'SET_NEW_NOTIFICATION'
|
||||
export const SET_SIDE_EFFECT= 'SET_SIDE_EFFECT'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Loading state, login state, isNavDrawOpen state etc. None of this needs to be saved to localstorage.
|
||||
import { loadStateFromLocalStorage, saveStateToLocalStorage } from '../../localStorageHelpers.js'
|
||||
import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN, ALLOW_QAPP_AUTO_LISTS, REMOVE_QAPP_AUTO_LISTS, SET_NEW_TAB, ADD_TAB_INFO, SET_TAB_NOTIFICATIONS, IS_OPEN_DEV_DIALOG } from './app-action-types.js'
|
||||
import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN, ALLOW_QAPP_AUTO_LISTS, REMOVE_QAPP_AUTO_LISTS, SET_NEW_TAB, ADD_TAB_INFO, SET_TAB_NOTIFICATIONS, IS_OPEN_DEV_DIALOG, REMOVE_NODE, EDIT_NODE, SET_NEW_NOTIFICATION, SET_SIDE_EFFECT } from './app-action-types.js'
|
||||
import { initWorkersReducer } from './reducers/init-workers.js'
|
||||
import { loginReducer } from './reducers/login-reducer.js'
|
||||
import { setNode, addNode } from './reducers/manage-node.js'
|
||||
import { setNode, addNode, removeNode, editNode } from './reducers/manage-node.js'
|
||||
import localForage from "localforage";
|
||||
const chatLastSeen = localForage.createInstance({
|
||||
name: "chat-last-seen",
|
||||
@@ -50,7 +50,9 @@ const INITIAL_STATE = {
|
||||
chatLastSeen: [],
|
||||
newTab: null,
|
||||
tabInfo: {},
|
||||
isOpenDevDialog: false
|
||||
isOpenDevDialog: false,
|
||||
newNotification: null,
|
||||
sideEffectAction: null
|
||||
}
|
||||
|
||||
export default (state = INITIAL_STATE, action) => {
|
||||
@@ -120,6 +122,10 @@ export default (state = INITIAL_STATE, action) => {
|
||||
return setNode(state, action)
|
||||
case ADD_NODE:
|
||||
return addNode(state, action)
|
||||
case EDIT_NODE:
|
||||
return editNode(state, action)
|
||||
case REMOVE_NODE:
|
||||
return removeNode(state, action)
|
||||
case PAGE_URL:
|
||||
return {
|
||||
...state,
|
||||
@@ -273,6 +279,19 @@ export default (state = INITIAL_STATE, action) => {
|
||||
}
|
||||
}
|
||||
|
||||
case SET_NEW_NOTIFICATION: {
|
||||
return {
|
||||
...state,
|
||||
newNotification: action.payload
|
||||
}
|
||||
}
|
||||
case SET_SIDE_EFFECT: {
|
||||
return {
|
||||
...state,
|
||||
sideEffectAction: action.payload
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -20,3 +20,26 @@ export const addNode = (state, action) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const editNode = (state, action) => {
|
||||
const copyKnownNodes = [...state.nodeConfig.knownNodes]
|
||||
copyKnownNodes[action.payload.index] = action.payload.nodeObject
|
||||
return {
|
||||
...state,
|
||||
nodeConfig: {
|
||||
...state.nodeConfig,
|
||||
knownNodes: copyKnownNodes
|
||||
}
|
||||
}
|
||||
}
|
||||
export const removeNode = (state, action) => {
|
||||
const copyKnownNodes = [...state.nodeConfig.knownNodes]
|
||||
copyKnownNodes.splice(action.payload, 1);
|
||||
return {
|
||||
...state,
|
||||
nodeConfig: {
|
||||
...state.nodeConfig,
|
||||
knownNodes: copyKnownNodes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user