From 36ee386c82724980d5eb85d310bf6e5d03a675b0 Mon Sep 17 00:00:00 2001 From: Phillip Date: Thu, 10 Aug 2023 04:19:24 +0300 Subject: [PATCH 1/8] qortalrequest for local q-app notifications --- core/language/us.json | 3 +- core/src/components/show-plugin.js | 9 +- core/src/notifications/dispatcher.js | 6 +- .../notification-actions/index.js | 2 +- .../notification-actions/new-message.js | 154 ++++++++++++++++ core/src/notifications/types.js | 1 + core/src/redux/app/version.js | 2 +- .../core/components/qdn-action-types.js | 11 +- .../plugins/core/qdn/browser/browser.src.js | 169 +++++++++++++++++- 9 files changed, 346 insertions(+), 11 deletions(-) diff --git a/core/language/us.json b/core/language/us.json index cbf85a4f..7fcfc8a1 100644 --- a/core/language/us.json +++ b/core/language/us.json @@ -719,7 +719,8 @@ "bchange43": "Do you give this application permission to add to this list?", "bchange44": "Do you give this application permission to delete from this list?", "bchange45": "Encrypt", - "bchange46": "Do you give this application permission to save the following file" + "bchange46": "Do you give this application permission to save the following file", + "bchange47": "Do you give this application permission to send you notifications" }, "datapage": { "dchange1": "Data Management", diff --git a/core/src/components/show-plugin.js b/core/src/components/show-plugin.js index c1ba4afc..a5c1153b 100644 --- a/core/src/components/show-plugin.js +++ b/core/src/components/show-plugin.js @@ -396,14 +396,15 @@ class ShowPlugin extends connect(store)(LitElement) { icon = 'tab' } - if (tab.myPlugObj && (tab.myPlugObj.url === 'websites' || tab.myPlugObj.url === 'qapps') && this.tabInfo[tab.id]) { - title = this.tabInfo[tab.id].name + if (tab.myPlugObj && (tab.myPlugObj.url === 'myapp') && this.tabInfo[tab.id]) { + title = this.tabInfo[tab.id].name } - if (tab.myPlugObj && (tab.myPlugObj.url === 'websites' || tab.myPlugObj.url === 'qapps') && this.tabInfo[tab.id]) { - count = this.tabInfo[tab.id].count + if (tab.myPlugObj && (tab.myPlugObj.url === 'myapp') && this.tabInfo[tab.id]) { + count = this.tabInfo[tab.id].count } + if (tab.myPlugObj && tab.myPlugObj.url === 'q-chat') { for (const chat of this.chatHeads) { diff --git a/core/src/notifications/dispatcher.js b/core/src/notifications/dispatcher.js index b74e11e1..b0c7713e 100644 --- a/core/src/notifications/dispatcher.js +++ b/core/src/notifications/dispatcher.js @@ -1,5 +1,5 @@ -import { NEW_MESSAGE, NEW_MESSAGE_NOTIFICATION_QAPP } from './types' -import { newMessage, newMessageNotificationQapp } from './notification-actions' +import { NEW_MESSAGE, NEW_MESSAGE_NOTIFICATION_QAPP, NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL } from './types' +import { newMessage, newMessageNotificationQapp, newMessageNotificationQappLocal } from './notification-actions' export const dispatcher = function (notificationState) { @@ -8,6 +8,8 @@ export const dispatcher = function (notificationState) { return newMessage(notificationState.data) case NEW_MESSAGE_NOTIFICATION_QAPP: return newMessageNotificationQapp(notificationState.data) + case NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL: + return newMessageNotificationQappLocal(notificationState.data) default: } } diff --git a/core/src/notifications/notification-actions/index.js b/core/src/notifications/notification-actions/index.js index 02faea66..cc2f8937 100644 --- a/core/src/notifications/notification-actions/index.js +++ b/core/src/notifications/notification-actions/index.js @@ -1 +1 @@ -export { newMessage, newMessageNotificationQapp } from './new-message' +export { newMessage, newMessageNotificationQapp, newMessageNotificationQappLocal } from './new-message' diff --git a/core/src/notifications/notification-actions/new-message.js b/core/src/notifications/notification-actions/new-message.js index 26803216..932da937 100644 --- a/core/src/notifications/notification-actions/new-message.js +++ b/core/src/notifications/notification-actions/new-message.js @@ -1,6 +1,9 @@ import { store } from '../../store.js' import { doPageUrl, setNewTab } from '../../redux/app/app-actions.js' import isElectron from 'is-electron' +import ShortUniqueId from 'short-unique-id'; + +const uid = new ShortUniqueId() export const newMessage = (data) => { const alert = playSound(data.sound) @@ -101,6 +104,157 @@ export const newMessageNotificationQapp = (data) => { } } +} + + const extractComponents= async (url)=> { + if (!url.startsWith("qortal://")) { + return null; + } + + url = url.replace(/^(qortal\:\/\/)/, ""); + if (url.includes("/")) { + let parts = url.split("/"); + const service = parts[0].toUpperCase(); + parts.shift(); + const name = parts[0]; + parts.shift(); + let identifier; + + if (parts.length > 0) { + identifier = parts[0]; // Do not shift yet + // Check if a resource exists with this service, name and identifier combination + let responseObj = await parentEpml.request('apiCall', { + url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${this.getApiKey()}` + }) + + if (responseObj.totalChunkCount > 0) { + // Identifier exists, so don't include it in the path + parts.shift(); + } + else { + identifier = null; + } + } + + const path = parts.join("/"); + + const components = {}; + components["service"] = service; + components["name"] = name; + components["identifier"] = identifier; + components["path"] = path; + return components; + } + + return null; +} + +export const newMessageNotificationQappLocal = (data) => { + const alert = playSound(data.sound) + + // Should I show notification ? + if (store.getState().user.notifications.q_chat.showNotification) { + + // Yes, I can but should I play sound ? + if (store.getState().user.notifications.q_chat.playSound) { + + const notify = new Notification(data.title, data.options) + + notify.onshow = (e) => { + alert.play() + } + + notify.onclick = async(e) => { + const url = data?.url + const value = url + let newQuery = value; + if (newQuery.endsWith('/')) { + newQuery = newQuery.slice(0, -1); + } + const res = await extractComponents(newQuery) + if (!res) return + const { service, name, identifier, path } = res + let query = `?service=${service}` + if (name) { + query = query + `&name=${name}` + } + if (identifier) { + query = query + `&identifier=${identifier}` + } + if (path) { + query = query + `&path=${path}` + } + const tab = { + url: `qdn/browser/index.html${query}`, + id: uid(), + myPlugObj: { + "url": service === 'WEBSITE' ? "websites" : "qapps", + "domain": "core", + "page": `qdn/browser/index.html${query}`, + "title": name, + "icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser', + "mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser', + "menus": [], + "parent": false + } + } + store.dispatch(setNewTab(tab)) + if (!isElectron()) { + window.focus(); + } else { + window.electronAPI.focusApp() + } + + } + } else { + + const notify = new Notification(data.title, data.options) + + notify.onclick = async(e) => { + const url = data?.url + const value = url + let newQuery = value; + if (newQuery.endsWith('/')) { + newQuery = newQuery.slice(0, -1); + } + const res = await extractComponents(newQuery) + if (!res) return + const { service, name, identifier, path } = res + let query = `?service=${service}` + if (name) { + query = query + `&name=${name}` + } + if (identifier) { + query = query + `&identifier=${identifier}` + } + if (path) { + query = query + `&path=${path}` + } + const tab = { + url: `qdn/browser/index.html${query}`, + id: uid(), + myPlugObj: { + "url": service === 'WEBSITE' ? "websites" : "qapps", + "domain": "core", + "page": `qdn/browser/index.html${query}`, + "title": name, + "icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser', + "mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser', + "menus": [], + "parent": false + } + } + store.dispatch(setNewTab(tab)) + if (!isElectron()) { + window.focus(); + } else { + window.electronAPI.focusApp() + } + + } + } + } + } const playSound = (soundUrl) => { diff --git a/core/src/notifications/types.js b/core/src/notifications/types.js index 0f8b1c05..f50b3ec7 100644 --- a/core/src/notifications/types.js +++ b/core/src/notifications/types.js @@ -1,2 +1,3 @@ export const NEW_MESSAGE = 'NEW_MESSAGE' export const NEW_MESSAGE_NOTIFICATION_QAPP = 'NEW_MESSAGE_NOTIFICATION_QAPP' +export const NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL = 'NEW_MESSAGE_NOTIFICATION_QAPP_LOCAL' diff --git a/core/src/redux/app/version.js b/core/src/redux/app/version.js index 91ebdadc..51e897a7 100644 --- a/core/src/redux/app/version.js +++ b/core/src/redux/app/version.js @@ -1 +1 @@ -export const UI_VERSION = "4.0.4"; +export const UI_VERSION = "4.3.0"; diff --git a/plugins/plugins/core/components/qdn-action-types.js b/plugins/plugins/core/components/qdn-action-types.js index fd337ad1..59385690 100644 --- a/plugins/plugins/core/components/qdn-action-types.js +++ b/plugins/plugins/core/components/qdn-action-types.js @@ -50,4 +50,13 @@ export const DECRYPT_DATA_GROUP = 'DECRYPT_DATA_GROUP' export const SAVE_FILE = 'SAVE_FILE' //SET_TAB_NOTIFICATIONS -export const SET_TAB_NOTIFICATIONS = 'SET_TAB_NOTIFICATIONS' \ No newline at end of file +export const SET_TAB_NOTIFICATIONS = 'SET_TAB_NOTIFICATIONS' + +//OPEN_NEW_TAB +export const OPEN_NEW_TAB = 'OPEN_NEW_TAB' + +//NOTIFICATIONS_PERMISSION +export const NOTIFICATIONS_PERMISSION = 'NOTIFICATIONS_PERMISSION' + +//SEND_LOCAL_NOTIFICATION +export const SEND_LOCAL_NOTIFICATION = 'SEND_LOCAL_NOTIFICATION' \ No newline at end of file diff --git a/plugins/plugins/core/qdn/browser/browser.src.js b/plugins/plugins/core/qdn/browser/browser.src.js index db59e4fd..fd3d30de 100644 --- a/plugins/plugins/core/qdn/browser/browser.src.js +++ b/plugins/plugins/core/qdn/browser/browser.src.js @@ -3,6 +3,7 @@ import { render } from 'lit/html.js' import { Epml } from '../../../../epml' import isElectron from 'is-electron' import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' +import ShortUniqueId from 'short-unique-id'; registerTranslateConfig({ loader: (lang) => fetch(`/language/${lang}.json`).then((res) => res.json()) @@ -131,6 +132,7 @@ class WebBrowser extends LitElement { constructor() { super(); this.url = 'about:blank'; + this.uid = new ShortUniqueId() this.myAddress = window.parent.reduxStore.getState().app.selectedAddress this._publicKey = { key: '', hasPubKey: false } const urlParams = new URLSearchParams(window.location.search) @@ -318,6 +320,44 @@ class WebBrowser extends LitElement { } } + async linkOpenNewTab(link) { + + const value = link + let newQuery = value; + if (newQuery.endsWith('/')) { + newQuery = newQuery.slice(0, -1); + } + const res = await this.extractComponents(newQuery) + if (!res) return + const { service, name, identifier, path } = res + let query = `?service=${service}` + if (name) { + query = query + `&name=${name}` + } + if (identifier) { + query = query + `&identifier=${identifier}` + } + if (path) { + query = query + `&path=${path}` + } + + window.parent.reduxStore.dispatch(window.parent.reduxAction.setNewTab({ + url: `qdn/browser/index.html${query}`, + id: this.uid(), + myPlugObj: { + "url": service === 'WEBSITE' ? "websites" : "qapps", + "domain": "core", + "page": `qdn/browser/index.html${query}`, + "title": name, + "icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser', + "mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser', + "menus": [], + "parent": false + } + })) + + } + render() { return html` @@ -1253,8 +1293,91 @@ class WebBrowser extends LitElement { // If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}` break; } + case actions.OPEN_NEW_TAB: { + if(!data.qortalLink){ + const obj = {}; + const errorMsg = 'Please enter a qortal link - qortal://...'; + obj['error'] = errorMsg; + response = JSON.stringify(obj); + break + } + try { + await this.linkOpenNewTab(data.qortalLink) + response = true + } catch (error) { + console.log('error', error) + const obj = {}; + const errorMsg = "Invalid qortal link"; + obj['error'] = errorMsg; + response = JSON.stringify(obj); + break; + } + + } + case actions.NOTIFICATIONS_PERMISSION: { + try { + const res = await showModalAndWait( + actions.NOTIFICATIONS_PERMISSION, + { + name: this.name + } + ); + if (res.action === 'accept'){ + this.addAppToNotificationList(this.name) + response = true + break; + } + + } catch (error) { + break; + } + + } + case actions.SEND_LOCAL_NOTIFICATION: { + const {title, url, icon, message} = data + try { + const id = `appNotificationList-${this.selectedAddress.address}` + const checkData = localStorage.getItem(id) ? JSON.parse(localStorage.getItem(id)) : null; + if(!checkData || !checkData[this.name]) throw new Error('App not on permission list') + const appInfo = checkData[this.name] + const lastNotification = appInfo.lastNotification + const interval = appInfo.interval + if (lastNotification && interval) { + const timeDifference = Date.now() - lastNotification; + + if (timeDifference > interval) { + parentEpml.request('showNotification', { + title, type: "qapp-local-notification", sound: '', url, options: { body: message, icon, badge: icon } + }) + response = true + this.updateLastNotification(id, this.name) + break; + } else { + throw new Error(`duration until another notification can be sent: ${interval - timeDifference}`) + } + } else if(!lastNotification){ + parentEpml.request('showNotification', { + title, type: "qapp-local-notification", sound: '', url, options: { body: message, icon, badge: icon } + }) + response = true + this.updateLastNotification(id) + break; + } else { + throw new Error(`invalid data`) + } + + } catch (error) { + const obj = {}; + const errorMsg = error.message || "error in pushing notification"; + obj['error'] = errorMsg; + response = JSON.stringify(obj); + break; + + } + + } case actions.SEND_CHAT_MESSAGE: { const message = data.message; const recipient = data.destinationAddress; @@ -2707,6 +2830,45 @@ class WebBrowser extends LitElement { use(checkLanguage); } } + addAppToNotificationList(appName) { + const id = `appNotificationList-${this.selectedAddress.address}`; + const checkData = localStorage.getItem(id) ? JSON.parse(localStorage.getItem(id)) : null; + + if (!checkData) { + const newData = { + [appName]: { + interval: 900000, // 15mins in milliseconds + lastNotification: null, + }, + }; + localStorage.setItem(id, JSON.stringify(newData)); + } else { + const copyData = { ...checkData }; + copyData[appName] = { + interval: 900000, // 15mins in milliseconds + lastNotification: null, + }; + localStorage.setItem(id, JSON.stringify(copyData)); + } + } + + updateLastNotification(id, appName) { + const checkData = localStorage.getItem(id) ? JSON.parse(localStorage.getItem(id)) : null; + + if (checkData) { + const copyData = { ...checkData }; + if (copyData[appName]) { + copyData[appName].lastNotification = Date.now(); // Make sure to use Date.now(), not date.now() + } else { + copyData[appName] = { + interval: 900000, // 15mins in milliseconds + lastNotification: Date.now(), + }; + } + localStorage.setItem(id, JSON.stringify(copyData)); + } + } + renderFollowUnfollowButton() { // Only show the follow/unfollow button if we have permission to modify the list on this node @@ -3064,7 +3226,12 @@ async function showModalAndWait(type, data) { ` : ''} - + ${type === actions.NOTIFICATIONS_PERMISSION ? ` + + ` : ''} + ${type === actions.DELETE_LIST_ITEM ? ` +
+ +
+ ` } @@ -155,6 +181,11 @@ class SecurityView extends connect(store)(LitElement) { } } + openDevDialog() { + this.closeSettings() + store.dispatch(setIsOpenDevDialog(true)) + } + async downloadBackup() { let backupname = '' this.backupErrorMessage = '' diff --git a/core/src/components/settings-view/user-settings.js b/core/src/components/settings-view/user-settings.js index 66a8c887..aaaba9b0 100644 --- a/core/src/components/settings-view/user-settings.js +++ b/core/src/components/settings-view/user-settings.js @@ -157,7 +157,9 @@ class UserSettings extends connect(store)(LitElement) { font-size: 16px; text-align: center; min-height: 460px; - height: 60vh; + height: auto; + overflow: auto; + } @media(max-width:700px) { @@ -252,7 +254,7 @@ class UserSettings extends connect(store)(LitElement) { if (selectedView.id === 'info') { return html`` } else if (selectedView.id === 'security') { - return html`` + return html` this.closeSettings()}>` } else if (selectedView.id === 'export') { return html`` } else if (selectedView.id === 'notification') { diff --git a/core/src/components/show-plugin.js b/core/src/components/show-plugin.js index a5c1153b..2738e0d1 100644 --- a/core/src/components/show-plugin.js +++ b/core/src/components/show-plugin.js @@ -6,7 +6,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' +import { setIsOpenDevDialog, setNewTab } from '../redux/app/app-actions.js' import localForage from 'localforage' import FileSaver from 'file-saver' import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' @@ -46,7 +46,8 @@ class ShowPlugin extends connect(store)(LitElement) { tabInfo: { type: Object }, chatLastSeen: { type: Array }, chatHeads: { type: Array }, - proxyPort: { type: Number } + proxyPort: { type: Number }, + isOpenDevDialog: {type: Boolean} } } @@ -334,9 +335,11 @@ class ShowPlugin extends connect(store)(LitElement) { this.chatLastSeen = [] this.chatHeads = [] this.proxyPort = 0 + this.isOpenDevDialog = false } render() { + const plugSrc = (myPlug) => { return myPlug === undefined ? 'about:blank' : `${window.location.origin}/plugin/${myPlug.domain}/${myPlug.page}${this.linkParam}` } @@ -459,11 +462,6 @@ class ShowPlugin extends connect(store)(LitElement) { this.currentTab = lengthOfTabs }} >+ - ${repeat(this.tabs, (tab) => tab.id, (tab, index) => html` @@ -485,7 +483,15 @@ class ShowPlugin extends connect(store)(LitElement) { `)} - + { + this.shadowRoot.getElementById('domainInput').value = '' + this.shadowRoot.getElementById('portInput').value = '' + this.isOpenDevDialog = false + store.dispatch(setIsOpenDevDialog(false)) + }} + >

${translate('tabmenu.tm39')}


@@ -552,12 +558,6 @@ class ShowPlugin extends connect(store)(LitElement) { return true } - openDevDialog() { - this.shadowRoot.getElementById('domainInput').value = '' - this.shadowRoot.getElementById('portInput').value = '' - this.shadowRoot.querySelector("#addDevDialog").show() - } - async getProxyPort() { this.proxyPort = 0 let framework = '' @@ -734,11 +734,9 @@ class ShowPlugin extends connect(store)(LitElement) { const myPlugObj = plugArr.find(pagePlug => { return pagePlug.url === this.url }) - if (this.tabs.length === 0) { this.addTab({ - url: this.url, - myPlugObj, + url: "", id: this.uid() }) } else { @@ -781,7 +779,7 @@ class ShowPlugin extends connect(store)(LitElement) { } if (split[0] === '' && split[1] === 'app' && split[2] === undefined) { - newUrl = 'wallet' + newUrl = '' newLinkParam = '' } else if (split.length === 5 && split[1] === 'app') { newUrl = split[2] @@ -793,7 +791,6 @@ class ShowPlugin extends connect(store)(LitElement) { newUrl = '404' newLinkParam = '' } - if (newUrl !== this.url) { this.url = newUrl } @@ -839,6 +836,9 @@ class ShowPlugin extends connect(store)(LitElement) { //clear newTab } } + if(state.app.isOpenDevDialog){ + this.isOpenDevDialog = state.app.isOpenDevDialog + } } } diff --git a/core/src/redux/app/actions/app-core.js b/core/src/redux/app/actions/app-core.js index 8ed9455f..7c7ab9bd 100644 --- a/core/src/redux/app/actions/app-core.js +++ b/core/src/redux/app/actions/app-core.js @@ -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 } 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 } from '../app-action-types.js' export const doUpdateBlockInfo = (blockObj) => { return (dispatch, getState) => { @@ -126,7 +126,12 @@ export const setNewTab = (payload) => { payload } } - +export const setIsOpenDevDialog = (payload)=> { + return { + type: IS_OPEN_DEV_DIALOG, + payload + } +} export const addTabInfo = (payload) => { return { type: ADD_TAB_INFO, diff --git a/core/src/redux/app/app-action-types.js b/core/src/redux/app/app-action-types.js index 31f9b246..ba2cde2a 100644 --- a/core/src/redux/app/app-action-types.js +++ b/core/src/redux/app/app-action-types.js @@ -27,4 +27,5 @@ export const SET_CHAT_LAST_SEEN = 'SET_CHAT_LAST_SEEN' 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' \ No newline at end of file +export const SET_TAB_NOTIFICATIONS = 'SET_TAB_NOTIFICATIONS' +export const IS_OPEN_DEV_DIALOG = 'IS_OPEN_DEV_DIALOG' \ No newline at end of file diff --git a/core/src/redux/app/app-reducer.js b/core/src/redux/app/app-reducer.js index 0fd3c2cc..11e77486 100644 --- a/core/src/redux/app/app-reducer.js +++ b/core/src/redux/app/app-reducer.js @@ -1,6 +1,6 @@ // 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 } 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 } 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' @@ -49,7 +49,8 @@ const INITIAL_STATE = { qAPPAutoLists: loadStateFromLocalStorage('qAPPAutoLists') || false, chatLastSeen: [], newTab: null, - tabInfo: {} + tabInfo: {}, + isOpenDevDialog: false } export default (state = INITIAL_STATE, action) => { @@ -231,6 +232,12 @@ export default (state = INITIAL_STATE, action) => { newTab: action.payload } } + case IS_OPEN_DEV_DIALOG: { + return { + ...state, + isOpenDevDialog: action.payload + } + } case ADD_TAB_INFO: { const newTabInfo = action.payload if (state.tabInfo[newTabInfo.id] && state.tabInfo[newTabInfo.id].name && newTabInfo.name === state.tabInfo[newTabInfo.id].name) break From 93658b2ba43591e94f2ec1a8b18bdfd1d84e843c Mon Sep 17 00:00:00 2001 From: Phillip Date: Fri, 25 Aug 2023 23:26:56 -0500 Subject: [PATCH 5/8] added notification q-app list --- core/language/us.json | 3 +- .../notification-view/notification-bell.js | 21 ++++++- .../settings-view/notifications-view.js | 57 ++++++++++++++++++- .../plugins/core/components/ChatRightPanel.js | 4 -- .../plugins/core/qdn/browser/browser.src.js | 1 + 5 files changed, 78 insertions(+), 8 deletions(-) diff --git a/core/language/us.json b/core/language/us.json index 7fcfc8a1..206eb418 100644 --- a/core/language/us.json +++ b/core/language/us.json @@ -203,7 +203,8 @@ "exp2": "Export Master Key", "exp3": "Export", "exp4": "Please choose a wallet to backup the private master key.", - "core": "Start Core Settings" + "core": "Start Core Settings", + "qappNotification1": "Q-App notifications" }, "appinfo": { "blockheight": "Block Height", diff --git a/core/src/components/notification-view/notification-bell.js b/core/src/components/notification-view/notification-bell.js index 4ac64130..bb3913c5 100644 --- a/core/src/components/notification-view/notification-bell.js +++ b/core/src/components/notification-view/notification-bell.js @@ -140,7 +140,9 @@ class NotificationBell extends connect(store)(LitElement) { ${this.notificationCount ? html` this._toggleNotifications()} title="Q-Mail"> ` : html` - this._toggleNotifications()} title="Q-Mail"> + { + this._openTabQmail() + }} title="Q-Mail"> `} ${this.notificationCount ? html` @@ -188,6 +190,23 @@ class NotificationBell extends connect(store)(LitElement) { if (this.notifications.length === 0) return this.showNotifications = !this.showNotifications } + _openTabQmail() { + 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:mailbox", + "mwcicon": "mail_outline", + "menus": [], + "parent": false + } + })) + } static styles = css` .layout { diff --git a/core/src/components/settings-view/notifications-view.js b/core/src/components/settings-view/notifications-view.js index ca209c24..1d8fb279 100644 --- a/core/src/components/settings-view/notifications-view.js +++ b/core/src/components/settings-view/notifications-view.js @@ -14,7 +14,8 @@ class NotificationsView extends connect(store)(LitElement) { notificationConfig: { type: Object }, q_chatConfig: { type: Object }, blockConfig: { type: Object }, - theme: { type: String, reflect: true } + theme: { type: String, reflect: true }, + appNotificationList: {type: Array} } } @@ -24,6 +25,11 @@ class NotificationsView extends connect(store)(LitElement) { this.q_chatConfig = {} this.blockConfig = {} this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.appNotificationList = [] // Fetch the list of apps from local storage + } + + firstUpdated(){ + this.appNotificationList = this.getAppsFromStorage(); } static get styles() { @@ -42,7 +48,7 @@ class NotificationsView extends connect(store)(LitElement) { text-align: center; } - @media(min-width: 1150px) { + @media(min-width: 1400px) { .notification-box { display: grid; grid-template-columns: 1fr 1fr; @@ -106,6 +112,18 @@ class NotificationsView extends connect(store)(LitElement) { transition: all .2s; position: relative; } + + .remove-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; + cursor: pointer; + } ` } @@ -145,12 +163,47 @@ class NotificationsView extends connect(store)(LitElement) {
+
+

${translate("settings.qappNotification1")}

+ ${this.appNotificationList.map((app)=> html` +
+ ${app} + +
+ `)} + +
${this.renderSetCoreButton()} ` } + getAppsFromStorage() { + // Your method to fetch the list of apps from local storage + // Example: + const address= store.getState().app.selectedAddress.address + const id = `appNotificationList-${address}`; + const data = localStorage.getItem(id); + return data ? Object.keys(JSON.parse(data)) : []; + } + + removeApp(appName) { + // Remove the app from local storage + this.removeAppFromStorage(appName); + // Update the apps list in the component + this.appNotificationList = this.appNotificationList.filter(app => app !== appName); + } + + removeAppFromStorage(appName) { + // Your method to remove the app from local storage + const address= store.getState().app.selectedAddress.address + const id = `appNotificationList-${address}`; + const data = JSON.parse(localStorage.getItem(id) || '{}'); + delete data[appName]; + localStorage.setItem(id, JSON.stringify(data)); + } + renderSetCoreButton() { if (!isElectron()) { return html`` diff --git a/plugins/plugins/core/components/ChatRightPanel.js b/plugins/plugins/core/components/ChatRightPanel.js index fac86ebf..24a90f59 100644 --- a/plugins/plugins/core/components/ChatRightPanel.js +++ b/plugins/plugins/core/components/ChatRightPanel.js @@ -225,7 +225,6 @@ class ChatRightPanel extends LitElement { if(this.groupMembers.length < 20){ return } - console.log('this.leaveGroupObjp', this.leaveGroupObj) this.getMoreMembers(this.leaveGroupObj.groupId) } } @@ -252,7 +251,6 @@ class ChatRightPanel extends LitElement { activeChatHeadUrl="" .setActiveChatHeadUrl=${(val) => { if (val.address === this.myAddress) return; - console.log({ val }); this.selectedHead = val; this.setOpenUserInfo(true); this.setUserName({ @@ -269,7 +267,6 @@ class ChatRightPanel extends LitElement { activeChatHeadUrl="" .setActiveChatHeadUrl=${(val) => { if (val.address === this.myAddress) return; - console.log({ val }); this.selectedHead = val; this.setOpenUserInfo(true); this.setUserName({ @@ -286,7 +283,6 @@ class ChatRightPanel extends LitElement { activeChatHeadUrl="" .setActiveChatHeadUrl=${(val) => { if (val.address === this.myAddress) return; - console.log({ val }); this.selectedHead = val; this.setOpenUserInfo(true); this.setUserName({ diff --git a/plugins/plugins/core/qdn/browser/browser.src.js b/plugins/plugins/core/qdn/browser/browser.src.js index fd3d30de..94834fd8 100644 --- a/plugins/plugins/core/qdn/browser/browser.src.js +++ b/plugins/plugins/core/qdn/browser/browser.src.js @@ -2831,6 +2831,7 @@ class WebBrowser extends LitElement { } } addAppToNotificationList(appName) { + if(!appName) throw new Error('unknown app name') const id = `appNotificationList-${this.selectedAddress.address}`; const checkData = localStorage.getItem(id) ? JSON.parse(localStorage.getItem(id)) : null; From d989ce6e4ff70948d4bec8ac2951a2ebf6da19c6 Mon Sep 17 00:00:00 2001 From: Phillip Date: Tue, 29 Aug 2023 12:23:59 -0500 Subject: [PATCH 6/8] change url --- electron.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/electron.js b/electron.js index dea2efa6..eabb96eb 100644 --- a/electron.js +++ b/electron.js @@ -845,7 +845,7 @@ function createWindow() { }) myWindow.maximize() myWindow.show() - myWindow.loadURL('http://localhost:12388/app/wallet') + myWindow.loadURL('http://localhost:12388/app') myWindow.on('closed', function () { myWindow = null }) @@ -886,7 +886,7 @@ function createNewWindow() { show: false }) newWindow.show() - newWindow.loadURL('http://localhost:12388/app/wallet') + newWindow.loadURL('http://localhost:12388/app') newWindow.on('closed', function () { newWindow = null }) From 47cc31f6933c6f305dfa5672e0b4141f583da6dc Mon Sep 17 00:00:00 2001 From: Phillip Date: Tue, 29 Aug 2023 12:36:29 -0500 Subject: [PATCH 7/8] add break --- plugins/plugins/core/qdn/browser/browser.src.js | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/plugins/core/qdn/browser/browser.src.js b/plugins/plugins/core/qdn/browser/browser.src.js index 2ab51ea1..1337611b 100644 --- a/plugins/plugins/core/qdn/browser/browser.src.js +++ b/plugins/plugins/core/qdn/browser/browser.src.js @@ -1332,6 +1332,7 @@ class WebBrowser extends LitElement { try { await this.linkOpenNewTab(data.qortalLink) response = true + break; } catch (error) { console.log('error', error) const obj = {}; From cb3d71b779daa1810931de961dfd197474b368e2 Mon Sep 17 00:00:00 2001 From: Phillip Date: Tue, 29 Aug 2023 12:39:26 -0500 Subject: [PATCH 8/8] add another break --- plugins/plugins/core/qdn/browser/browser.src.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/plugins/core/qdn/browser/browser.src.js b/plugins/plugins/core/qdn/browser/browser.src.js index 1337611b..a18f2f37 100644 --- a/plugins/plugins/core/qdn/browser/browser.src.js +++ b/plugins/plugins/core/qdn/browser/browser.src.js @@ -1356,6 +1356,9 @@ class WebBrowser extends LitElement { this.addAppToNotificationList(this.name) response = true break; + } else { + response = false + break; } } catch (error) {