@@ -1393,16 +1395,16 @@ class AppView extends connect(store)(LitElement) {
}
}
- const getChatLastSeen=async() => {
+ const getChatLastSeen = async () => {
let items = [];
-
- await chatLastSeen.iterate(function(value, key, iterationNumber) {
-
- items.push({key, timestamp: value});
- })
- store.dispatch(setChatLastSeen(items))
+
+ await chatLastSeen.iterate(function (value, key, iterationNumber) {
+
+ items.push({ key, timestamp: value });
+ })
+ store.dispatch(setChatLastSeen(items))
return items;
- }
+ }
await getOpenTradesBTC()
await appDelay(1000)
@@ -2153,7 +2155,7 @@ class AppView extends connect(store)(LitElement) {
this.addressInfo = state.app.accountInfo.addressInfo
if (sideurl === "minting") {
- this.shadowRoot.getElementById('qminter').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qminter').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qbminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qiminter').hasAttribute('selected')) {
@@ -2186,7 +2188,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "become-minter") {
- this.shadowRoot.getElementById('qbminter').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qbminter').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qiminter').hasAttribute('selected')) {
@@ -2219,7 +2221,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "sponsorship-list") {
- this.shadowRoot.getElementById('qiminter').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qiminter').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2252,7 +2254,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "wallet") {
- this.shadowRoot.getElementById('qwallet').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qwallet').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2285,7 +2287,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "trade-portal") {
- this.shadowRoot.getElementById('qtrade').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qtrade').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2318,7 +2320,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "trade-bot-portal") {
- this.shadowRoot.getElementById('qbot').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qbot').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2351,7 +2353,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "reward-share") {
- this.shadowRoot.getElementById('qrewardshare').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qrewardshare').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2384,7 +2386,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "q-chat") {
- this.shadowRoot.getElementById('qchat').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qchat').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2417,7 +2419,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "name-registration") {
- this.shadowRoot.getElementById('qnamereg').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qnamereg').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2450,7 +2452,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "names-market") {
- this.shadowRoot.getElementById('qnamemarket').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qnamemarket').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2483,7 +2485,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "websites") {
- this.shadowRoot.getElementById('qweb').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qweb').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2516,7 +2518,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "qapps") {
- this.shadowRoot.getElementById('qapp').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qapp').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2549,7 +2551,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "group-management") {
- this.shadowRoot.getElementById('qgroupmange').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qgroupmange').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2582,7 +2584,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "puzzles") {
- this.shadowRoot.getElementById('qpuzzles').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qpuzzles').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2615,7 +2617,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "data-management") {
- this.shadowRoot.getElementById('qdata').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qdata').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
@@ -2648,7 +2650,7 @@ class AppView extends connect(store)(LitElement) {
this.shadowRoot.getElementById('qnode').removeAttribute('selected')
}
} else if (sideurl === "node-management") {
- this.shadowRoot.getElementById('qnode').setAttribute('selected','selected')
+ this.shadowRoot.getElementById('qnode').setAttribute('selected', 'selected')
if (this.shadowRoot.getElementById('qminter').hasAttribute('selected')) {
this.shadowRoot.getElementById('qminter').removeAttribute('selected')
} else if (this.shadowRoot.getElementById('qbminter').hasAttribute('selected')) {
diff --git a/core/src/components/notification-view/notification-bell.js b/core/src/components/notification-view/notification-bell.js
new file mode 100644
index 00000000..9dac3e7f
--- /dev/null
+++ b/core/src/components/notification-view/notification-bell.js
@@ -0,0 +1,265 @@
+import { LitElement, html, css } from 'lit';
+import { connect } from 'pwa-helpers';
+
+import '@vaadin/button';
+import '@vaadin/item';
+import '@vaadin/list-box';
+import '@vaadin/icon';
+import '@vaadin/icons';
+import { store } from '../../store.js';
+import { setNewTab } from '../../redux/app/app-actions.js';
+import { routes } from '../../plugins/routes.js';
+import config from '../../notifications/config.js';
+import '../../../../plugins/plugins/core/components/TimeAgo.js'
+class NotificationBell extends connect(store)(LitElement) {
+
+
+
+
+ static properties = {
+ notifications: { type: Array },
+ showNotifications: { type: Boolean },
+ };
+
+ constructor() {
+ super();
+ this.notifications = [];
+ this.showNotifications = false;
+ this.initialFetch = false
+ }
+
+ firstUpdated() {
+ this.getNotifications();
+ document.addEventListener('click', (event) => {
+ const path = event.composedPath();
+ if (!path.includes(this)) {
+ this.showNotifications = false;
+ }
+ });
+ }
+
+ getApiKey() {
+ const apiNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
+ let apiKey = apiNode.apiKey;
+ return apiKey;
+ }
+
+ async getNotifications() {
+ const myNode =
+ store.getState().app.nodeConfig.knownNodes[
+ store.getState().app.nodeConfig.node
+ ];
+ const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
+
+ let interval = null
+ let stop = false
+
+ const getNewMail = async () => {
+
+ const getMail = async (recipientName, recipientAddress) => {
+ const query = `qortal_qmail_${recipientName.slice(
+ 0,
+ 20
+ )}_${recipientAddress.slice(-6)}_mail_`
+ const url = `${nodeUrl}/arbitrary/resources/search?service=MAIL_PRIVATE&query=${query}&limit=10&includemetadata=true&offset=0&reverse=true&excludeblocked=true`
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ })
+
+ const data = await response.json();
+ return data;
+ };
+
+ if (!stop && !this.showNotifications) {
+ stop = true;
+ try {
+ const address = window.parent.reduxStore.getState().app?.selectedAddress?.address;
+ const name = window.parent.reduxStore.getState().app?.accountInfo?.names[0]?.name
+
+ if (!name || !address) return
+ const mailArray = await getMail(name, address);
+ let notificationsToShow = []
+ if (mailArray.length > 0) {
+ const lastVisited = localStorage.getItem("Q-Mail-last-visited")
+
+ if (lastVisited) {
+ mailArray.forEach((mail) => {
+ if (mail.created > lastVisited) notificationsToShow.push(mail)
+ })
+ } else {
+ notificationsToShow = mailArray
+ }
+
+ }
+ if (!this.initialFetch && notificationsToShow.length > 0) {
+ const mail = notificationsToShow[0]
+ const urlPic = `${nodeUrl}/arbitrary/THUMBNAIL/${mail.name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
+ routes.showNotification({
+ data: { title: "New Q-Mail", type: "qapp", sound: config.messageAlert, url: "", options: { body: `You have an unread mail from ${mail.name}`, icon: urlPic, badge: urlPic } }
+ })
+ } else if (notificationsToShow.length > 0) {
+ if (notificationsToShow[0].created > (this.notifications[0]?.created || 0)) {
+ const mail = notificationsToShow[0]
+ const urlPic = `${nodeUrl}/arbitrary/THUMBNAIL/${mail.name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
+ routes.showNotification({
+ data: { title: "New Q-Mail", type: "qapp", sound: config.messageAlert, url: "", options: { body: `You have an unread mail from ${mail.name}`, icon: urlPic, badge: urlPic } }
+ })
+ }
+ }
+ this.notifications = notificationsToShow
+ if (!this.initialFetch) this.initialFetch = true
+ } catch (error) {
+ console.error(error)
+ }
+ stop = false
+ }
+ };
+ try {
+
+ setTimeout(() => {
+ getNewMail()
+
+ }, 5000)
+
+
+ interval = setInterval(getNewMail, 30000);
+ } catch (error) {
+ console.error(error)
+ }
+
+ }
+ render() {
+
+ return html`
+
+
+
+ ${this.notifications.length}
+
+
+
+ ${this.notifications.map(notification => html`
+
{
+
+ const query = `?service=APP&name=Q-Mail`
+ store.dispatch(setNewTab({
+ url: `qdn/browser/index.html${query}`,
+ id: 'q-mail-notification',
+ myPlugObj: {
+ "url": "qapps",
+ "domain": "core",
+ "page": `qdn/browser/index.html${query}`,
+ "title": "Q-Mail",
+ "icon": "vaadin:desktop",
+ "menus": [],
+ "parent": false
+ }
+ }))
+ this.showNotifications = false
+ this.notifications = []
+ }}>
+
+
+
+
+ `)}
+
+
+
+ `;
+ }
+
+ _toggleNotifications() {
+ if (this.notifications.length === 0) return
+ this.showNotifications = !this.showNotifications;
+ }
+
+ static styles = css`
+ .layout {
+ width: 100px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+ }
+ .count {
+ position: absolute;
+ top: 0;
+ right: 0;
+ background-color: red;
+ color: white;
+ border-radius: 50%;
+ width: 20px;
+ height: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ .popover-panel {
+ position: absolute;
+ width: 200px;
+ padding: 10px;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ 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: #c9d2d9;
+ }
+ p {
+ font-size: 14px;
+ color: #444444;
+ margin: 0px;
+ padding: 0px;
+
+ }
+ `;
+}
+
+customElements.define('notification-bell', NotificationBell);
+
+
+
diff --git a/core/src/components/show-plugin.js b/core/src/components/show-plugin.js
index 72829ec5..e85131b8 100644
--- a/core/src/components/show-plugin.js
+++ b/core/src/components/show-plugin.js
@@ -5,6 +5,7 @@ import { Epml } from '../epml.js'
import { addPluginRoutes } from '../plugins/addPluginRoutes.js'
import { repeat } from 'lit/directives/repeat.js';
import ShortUniqueId from 'short-unique-id';
+import { setNewTab } from '../redux/app/app-actions.js'
class ShowPlugin extends connect(store)(LitElement) {
static get properties() {
@@ -61,13 +62,12 @@ class ShowPlugin extends connect(store)(LitElement) {
.tabs {
display: flex;
justify-content: flex-start;
- height: 35px
- max-height: 35px
- gap: 1em;
padding-top: 0.5em;
padding-left: 0.5em;
background: var(--sidetopbar);
border-bottom: 1px solid var(--black);
+ height: 48px;
+ box-sizing: border-box;
}
.tab {
@@ -84,7 +84,9 @@ class ShowPlugin extends connect(store)(LitElement) {
position: relative;
min-width: 120px;
max-width: 200px;
-
+ overflow: hidden;
+ text-wrap: nowrap;
+ text-overflow: ellipsis;
text-overflow: ellipsis;
}
@@ -165,6 +167,7 @@ class ShowPlugin extends connect(store)(LitElement) {
return myPlug === undefined ? 'about:blank' : `${window.location.origin}/plugin/${myPlug.domain}/${myPlug.page}${this.linkParam}`
}
+
return html`
${this.tabs.map((tab, index) => html`
@@ -172,7 +175,7 @@ class ShowPlugin extends connect(store)(LitElement) {
class="tab ${this.currentTab === index ? 'active' : ''}"
@click=${() => this.currentTab = index}
>
- ${tab.url}
+ ${tab.myPlugObj && tab.myPlugObj.title}
{ this.removeTab(index) }}>x
`)}
@@ -180,9 +183,9 @@ class ShowPlugin extends connect(store)(LitElement) {
class="add-tab-button"
title="Add Tab"
@click=${() => this.addTab({
- url: "",
- id: this.uid()
- })}
+ url: "",
+ id: this.uid()
+ })}
>
+
@@ -191,7 +194,7 @@ class ShowPlugin extends connect(store)(LitElement) {
${repeat(this.tabs, (tab) => tab.id, (tab, index) => html`