From fa1e4692a0f23ce1e6f33ce80f3726499430c24f Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 28 Sep 2023 00:40:29 -0500 Subject: [PATCH] added tx notification for join group --- core/language/us.json | 7 +- core/src/components/app-view.js | 5 + core/src/components/login-view/login-view.js | 5 +- .../notification-bell-general.js | 535 ++++++++++++++++++ .../notification-view/notification-bell.js | 3 +- .../components/notification-view/popover.js | 74 +++ core/src/components/show-plugin.js | 11 +- core/src/redux/app/actions/app-core.js | 8 +- core/src/redux/app/app-action-types.js | 3 +- core/src/redux/app/app-reducer.js | 12 +- package-lock.json | 1 + package.json | 1 + .../core/components/ChatGroupManager.js | 348 ++++++++++++ .../core/components/ChatGroupsModal.js | 98 ++++ plugins/plugins/core/components/ChatPage.js | 2 + .../group-management/group-management.src.js | 20 +- .../core/messaging/q-chat/q-chat-css.src.js | 1 + .../core/messaging/q-chat/q-chat.src.js | 79 ++- 18 files changed, 1186 insertions(+), 27 deletions(-) create mode 100644 core/src/components/notification-view/notification-bell-general.js create mode 100644 core/src/components/notification-view/popover.js create mode 100644 plugins/plugins/core/components/ChatGroupManager.js create mode 100644 plugins/plugins/core/components/ChatGroupsModal.js diff --git a/core/language/us.json b/core/language/us.json index aec0628f..2498cc47 100644 --- a/core/language/us.json +++ b/core/language/us.json @@ -836,7 +836,8 @@ "cchange92": "Unread messages below", "cchange93": "Image copied to clipboard", "cchange94": "loaded", - "cchange95": "Only my resources" + "cchange95": "Only my resources", + "cchange96": "Open Group Management" }, "welcomepage": { "wcchange1": "Welcome to Q-Chat", @@ -1168,5 +1169,9 @@ "lot11": "There are no open lotteries!", "lot12": "There are no finished lotteries!", "lot13": "Players" + }, + "notifications": { + "notify1": "Confirming transaction", + "notify2": "Transaction confirmed" } } \ No newline at end of file diff --git a/core/src/components/app-view.js b/core/src/components/app-view.js index 08b9c019..ca5a6732 100644 --- a/core/src/components/app-view.js +++ b/core/src/components/app-view.js @@ -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) { +
+ +
diff --git a/core/src/components/login-view/login-view.js b/core/src/components/login-view/login-view.js index 24bef336..c6ef8a5b 100644 --- a/core/src/components/login-view/login-view.js +++ b/core/src/components/login-view/login-view.js @@ -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 } from '../../redux/app/app-actions.js' window.reduxStore = store window.reduxAction = { @@ -28,7 +28,8 @@ window.reduxAction = { removeQAPPAutoLists: removeQAPPAutoLists, addTabInfo: addTabInfo, setTabNotifications: setTabNotifications, - setNewTab: setNewTab + setNewTab: setNewTab, + setNewNotification: setNewNotification } const animationDuration = 0.7 // Seconds diff --git a/core/src/components/notification-view/notification-bell-general.js b/core/src/components/notification-view/notification-bell-general.js new file mode 100644 index 00000000..804c63b2 --- /dev/null +++ b/core/src/components/notification-view/notification-bell-general.js @@ -0,0 +1,535 @@ +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 } 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'; + +const currentNotification = { + type: 'JOIN_GROUP', + timestamp: Date.now(), + status: 'confirming', + reference: { + signature: + '5wpPP7ngE13z8x7FKr3tkx5AhMyzWAcFeTmkyefSbddRZ3ieMRcbwt4VDz5bakJzpFaE16NcSofa8w35AGLN4J47', + }, +}; + +const notifications = [currentNotification]; +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` +
+ +
this._toggleNotifications()} + > + ${hasOngoing + ? html` + notifications + ` + : html` + notifications + `} +
+ ${hasOngoing + ? html` + this._toggleNotifications()} + > + pending + + ` + : ''} + +
+
+ ${repeat( + this.notifications, + (notification) => notification.reference.signature, // key function + (notification) => html` + + this.changeStatus(val1, val2)} + status=${notification.status} + timestamp=${notification.timestamp} + type=${notification.type} + signature=${notification.reference + .signature} + > + ` + )} +
+
+
+ `; + } + + _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` +
{}}> +
+

+ ${translate('transpage.tchange1')} +

+
+
+

+ ${translate('walletpage.wchange35')}: ${this.type} +

+

+ ${translate('tubespage.schange28')}: + ${this.status === 'confirming' + ? translate('notifications.notify1') + : translate('notifications.notify2')} +

+ ${this.status !== 'confirmed' + ? html` +
+
Loading...
+
+ ` + : ''} +
+ + ${this.status === 'confirmed' + ? html` + done + ` + : ''} +
+
+
+ `; + } + + _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); diff --git a/core/src/components/notification-view/notification-bell.js b/core/src/components/notification-view/notification-bell.js index cf78f907..8a4b0aec 100644 --- a/core/src/components/notification-view/notification-bell.js +++ b/core/src/components/notification-view/notification-bell.js @@ -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; diff --git a/core/src/components/notification-view/popover.js b/core/src/components/notification-view/popover.js new file mode 100644 index 00000000..43e85a94 --- /dev/null +++ b/core/src/components/notification-view/popover.js @@ -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` + close +
info ${this.message}
+ `; + } +} + +customElements.define('popover-component', PopoverComponent); diff --git a/core/src/components/show-plugin.js b/core/src/components/show-plugin.js index fddcd127..7f8cddd2 100644 --- a/core/src/components/show-plugin.js +++ b/core/src/components/show-plugin.js @@ -759,6 +759,7 @@ class ShowPlugin extends connect(store)(LitElement) { } changePage(page) { + console.log({page}) const copiedTabs = [...this.tabs] copiedTabs[this.currentTab] = { ...copiedTabs[this.currentTab], @@ -819,7 +820,15 @@ class ShowPlugin extends connect(store)(LitElement) { if (state.app.newTab) { const newTab = state.app.newTab - if (!this.tabs.find((tab) => tab.id === newTab.id)) { + console.log('this.tabs', this.tabs) + 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)) diff --git a/core/src/redux/app/actions/app-core.js b/core/src/redux/app/actions/app-core.js index 7c7ab9bd..0b5f59dd 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, 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 } 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, diff --git a/core/src/redux/app/app-action-types.js b/core/src/redux/app/app-action-types.js index d98ff2c6..eb12f9fa 100644 --- a/core/src/redux/app/app-action-types.js +++ b/core/src/redux/app/app-action-types.js @@ -30,4 +30,5 @@ 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' \ No newline at end of file +export const IS_OPEN_DEV_DIALOG = 'IS_OPEN_DEV_DIALOG' +export const SET_NEW_NOTIFICATION = 'SET_NEW_NOTIFICATION' diff --git a/core/src/redux/app/app-reducer.js b/core/src/redux/app/app-reducer.js index d8d8abd2..63a062f0 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, IS_OPEN_DEV_DIALOG, REMOVE_NODE, EDIT_NODE } 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 } from './app-action-types.js' import { initWorkersReducer } from './reducers/init-workers.js' import { loginReducer } from './reducers/login-reducer.js' import { setNode, addNode, removeNode, editNode } from './reducers/manage-node.js' @@ -50,7 +50,8 @@ const INITIAL_STATE = { chatLastSeen: [], newTab: null, tabInfo: {}, - isOpenDevDialog: false + isOpenDevDialog: false, + newNotification: null } export default (state = INITIAL_STATE, action) => { @@ -277,6 +278,13 @@ export default (state = INITIAL_STATE, action) => { } } + case SET_NEW_NOTIFICATION: { + return { + ...state, + newNotification: action.payload + } + } + default: return state } diff --git a/package-lock.json b/package-lock.json index 8d3ff292..b088ddad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@hapi/hapi": "21.3.2", "@hapi/inert": "7.1.0", "@lit-labs/motion": "1.0.4", + "@popperjs/core": "^2.11.8", "@tiptap/core": "2.0.4", "@tiptap/extension-highlight": "2.0.4", "@tiptap/extension-image": "2.0.4", diff --git a/package.json b/package.json index 6af10180..94b6eafe 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@hapi/hapi": "21.3.2", "@hapi/inert": "7.1.0", "@lit-labs/motion": "1.0.4", + "@popperjs/core": "^2.11.8", "@tiptap/core": "2.0.4", "@tiptap/extension-highlight": "2.0.4", "@tiptap/extension-image": "2.0.4", diff --git a/plugins/plugins/core/components/ChatGroupManager.js b/plugins/plugins/core/components/ChatGroupManager.js new file mode 100644 index 00000000..a6fc68c2 --- /dev/null +++ b/plugins/plugins/core/components/ChatGroupManager.js @@ -0,0 +1,348 @@ +import { LitElement, html, css } from 'lit'; +import { Epml } from '../../../epml'; +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import '@polymer/paper-spinner/paper-spinner-lite.js'; +import '@polymer/paper-progress/paper-progress.js'; +import '@material/mwc-icon'; +import '@vaadin/button'; +import './WrapperModal'; +import './TipUser'; +import './UserInfo/UserInfo'; +import './ChatImage'; +import './ReusableImage'; +import { + get +} from 'lit-translate'; + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }); + +class ChatGroupsManager extends LitElement { + static get properties() { + return { + leaveGroupObj: { type: Object }, + error: { type: Boolean }, + chatHeads: { type: Array }, + groupAdmin: { attribute: false }, + groupMembers: { attribute: false }, + selectedHead: { type: Object }, + toggle: { attribute: false }, + getMoreMembers: { attribute: false }, + setOpenPrivateMessage: { attribute: false }, + userName: { type: String }, + walletBalance: { type: Number }, + sendMoneyLoading: { type: Boolean }, + btnDisable: { type: Boolean }, + errorMessage: { type: String }, + successMessage: { type: String }, + setOpenTipUser: { attribute: false }, + setOpenUserInfo: { attribute: false }, + setUserName: { attribute: false }, + chatId: { type: String }, + _chatId: { type: String }, + isReceipient: { type: Boolean }, + groups: { type: Array }, + viewImage: { type: Boolean }, + autoView: {type: Boolean}, + onlyMyImages: {type: Boolean}, + repost: {attribute: false} + }; + } + + constructor() { + super(); + this.leaveGroupObj = {}; + this.leaveFee = 0.001; + this.error = false; + this.chatHeads = []; + this.groupAdmin = []; + this.groupMembers = []; + this.observerHandler = this.observerHandler.bind(this); + this.getGroups = this.getGroups.bind(this); + this.viewElement = ''; + this.downObserverElement = ''; + + this.sendMoneyLoading = false; + this.btnDisable = false; + this.errorMessage = ''; + this.successMessage = ''; + + this.groups = []; + this.viewImage = false; + this.myName = + window.parent.reduxStore.getState().app.accountInfo.names[0].name; + this.myAddress = + window.parent.reduxStore.getState().app.selectedAddress.address; + this.autoView =false + this.onlyMyImages = true + } + + static get styles() { + return css` + .top-bar-icon { + cursor: pointer; + height: 18px; + width: 18px; + transition: 0.2s all; + } + + .top-bar-icon:hover { + color: var(--black); + } + + .modal-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; + } + + .close-row { + width: 100%; + display: flex; + justify-content: flex-end; + height: 50px; + flex: 0; + align-items: center; + } + + .container-body { + width: 100%; + display: flex; + flex-direction: column; + flex-grow: 1; + overflow: auto; + margin-top: 5px; + padding: 0px 6px; + box-sizing: border-box; + } + + .container-body::-webkit-scrollbar-track { + background-color: whitesmoke; + border-radius: 7px; + } + + .container-body::-webkit-scrollbar { + width: 6px; + border-radius: 7px; + background-color: whitesmoke; + } + + .container-body::-webkit-scrollbar-thumb { + background-color: rgb(180, 176, 176); + border-radius: 7px; + transition: all 0.3s ease-in-out; + } + + .container-body::-webkit-scrollbar-thumb:hover { + background-color: rgb(148, 146, 146); + cursor: pointer; + } + + p { + color: var(--black); + margin: 0px; + padding: 0px; + word-break: break-all; + } + + .container { + display: flex; + width: 100%; + flex-direction: column; + height: 100%; + } + + .chat-right-panel-label { + font-family: Montserrat, sans-serif; + color: var(--group-header); + padding: 5px; + font-size: 13px; + user-select: none; + } + + .group-info { + display: flex; + flex-direction: column; + justify-content: flex-start; + gap: 10px; + } + + .group-name { + font-family: Raleway, sans-serif; + font-size: 20px; + color: var(--chat-bubble-msg-color); + text-align: center; + user-select: none; + } + + .group-description { + font-family: Roboto, sans-serif; + color: var(--chat-bubble-msg-color); + letter-spacing: 0.3px; + font-weight: 300; + font-size: 14px; + margin-top: 15px; + word-break: break-word; + user-select: none; + } + + .group-subheader { + font-family: Montserrat, sans-serif; + font-size: 14px; + color: var(--chat-bubble-msg-color); + } + + .group-data { + font-family: Roboto, sans-serif; + letter-spacing: 0.3px; + font-weight: 300; + font-size: 14px; + color: var(--chat-bubble-msg-color); + } + .message-myBg { + background-color: var(--chat-bubble-myBg) !important; + margin-bottom: 15px; + border-radius: 5px; + padding: 5px; + } + .message-data-name { + user-select: none; + color: #03a9f4; + margin-bottom: 5px; + } + .message-user-info { + display: flex; + justify-content: space-between; + width: 100%; + gap: 10px; + } + + .hideImg { + visibility: hidden; + } + .checkbox-row { + position: relative; + display: flex; + align-items: center; + align-content: center; + font-family: Montserrat, sans-serif; + font-weight: 600; + color: var(--black); + padding-left: 5px; + } + `; + } + + async getGroups() { + try { + + let endpoint = `/groups` + + + const groups = await parentEpml.request('apiCall', { + type: 'api', + url: endpoint, + }); + + let list = groups + + this.groups = list + } catch (error) { + console.log(error); + } + } + + firstUpdated() { + // this.viewElement = this.shadowRoot.getElementById('viewElement'); + // this.downObserverElement = + // this.shadowRoot.getElementById('downObserver'); + // this.elementObserver(); + this.getGroups() + } + + + elementObserver() { + const options = { + root: this.viewElement, + rootMargin: '0px', + threshold: 1, + }; + // identify an element to observe + const elementToObserve = this.downObserverElement; + // passing it a callback function + const observer = new IntersectionObserver( + this.observerHandler, + options + ); + // call `observe()` on that MutationObserver instance, + // passing it the element to observe, and the options object + observer.observe(elementToObserve); + } + + observerHandler(entries) { + if (!entries[0].isIntersecting) { + return; + } else { + if (this.images.length < 20) { + return; + } + this.getMoreImages(); + } + } + + selectAuto(e) { + if (e.target.checked) { + this.autoView = false + } else { + this.autoView = true + } + } + + selectMyImages(e) { + if (e.target.checked) { + this.onlyMyImages = false + } else { + this.onlyMyImages = true + } + } + + render() { + console.log('this.groups', this.groups) + return html` + +
+
+ { + this.getGroups() + }} style="color: var(--black); cursor:pointer;">refresh +
+
+ + this.selectAuto(e)} ?checked=${this.autoView}> +
+
+ + this.selectMyImages(e)} ?checked=${this.onlyMyImages}> +
+
+ + +
+
+
+
+ `; + } +} + +customElements.define('chat-groups-manager', ChatGroupsManager); + diff --git a/plugins/plugins/core/components/ChatGroupsModal.js b/plugins/plugins/core/components/ChatGroupsModal.js new file mode 100644 index 00000000..f6a299d0 --- /dev/null +++ b/plugins/plugins/core/components/ChatGroupsModal.js @@ -0,0 +1,98 @@ +import { LitElement, html, css } from 'lit'; +import { + translate, +} from 'lit-translate'; +import '@material/mwc-menu'; +import '@material/mwc-list/mwc-list-item.js'; +import '@material/mwc-dialog' +import './ChatGroupManager' + +export class ChatGroupsModal extends LitElement { + static get properties() { + return { + openDialogGroupsModal: { type: Boolean }, + setOpenDialogGroupsModal: { attribute: false} + }; + } + + static get styles() { + return css` + * { + --mdc-theme-text-primary-on-background: var(--black); + --mdc-dialog-max-width: 85vw; + --mdc-dialog-max-height: 95vh; + } + + .imageContainer { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + } + + @-webkit-keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + @keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + `; + } + + constructor() { + super(); + this.openDialogGroupsModal = false + + } + + + + firstUpdated() { + } + + + render() { + console.log('hello') + return html` + + { + this.setOpenDialogGroupsModal(false) + }}> +
+
+ +
+ { + this.setOpenDialogGroupsModal(false) + }} + > + ${translate('general.close')} + +
+ `; + } +} + +customElements.define('chat-groups-modal', ChatGroupsModal); diff --git a/plugins/plugins/core/components/ChatPage.js b/plugins/plugins/core/components/ChatPage.js index ccc3b70c..351e5a1f 100644 --- a/plugins/plugins/core/components/ChatPage.js +++ b/plugins/plugins/core/components/ChatPage.js @@ -1095,6 +1095,7 @@ class ChatPage extends LitElement { const blobFound = handleTransferIntoURL(event.clipboardData) if (blobFound) { this.insertFile(blobFound) + e.preventDefault(); return } else { const item_list = await navigator.clipboard.read() @@ -1114,6 +1115,7 @@ class ChatPage extends LitElement { type: image_type }) this.insertFile(file) + e.preventDefault(); } catch (error) { console.error(error) let errorMsg = get("chatpage.cchange81") diff --git a/plugins/plugins/core/group-management/group-management.src.js b/plugins/plugins/core/group-management/group-management.src.js index bd35cc58..56e4e7b7 100644 --- a/plugins/plugins/core/group-management/group-management.src.js +++ b/plugins/plugins/core/group-management/group-management.src.js @@ -2849,6 +2849,17 @@ class GroupManagement extends LitElement { } } + setTxNotification(tx){ + window.parent.reduxStore.dispatch( + window.parent.reduxAction.setNewNotification({ + type: 'JOIN_GROUP', + status: 'confirming', + reference: tx, + timestamp: Date.now() + }) + ); + } + async _joinGroup(groupId, groupName) { this.resetDefaultSettings() const joinFeeInput = this.joinFee @@ -2885,7 +2896,8 @@ class GroupManagement extends LitElement { lastReference: lastRef, groupdialog1: groupdialog1, groupdialog2: groupdialog2 - } + }, + apiVersion: 2 }) return myTxnrequest } @@ -2897,6 +2909,12 @@ class GroupManagement extends LitElement { throw new Error(txnResponse) } else if (txnResponse.success === true && !txnResponse.data.error) { this.message = this.renderErr8Text() + this.setTxNotification({ + groupName, + groupId, + timestamp: Date.now(), + ...(txnResponse.data || {}) + }) this.error = false } else { this.error = true diff --git a/plugins/plugins/core/messaging/q-chat/q-chat-css.src.js b/plugins/plugins/core/messaging/q-chat/q-chat-css.src.js index ef1ff3e1..80b11a08 100644 --- a/plugins/plugins/core/messaging/q-chat/q-chat-css.src.js +++ b/plugins/plugins/core/messaging/q-chat/q-chat-css.src.js @@ -160,6 +160,7 @@ export const qchatStyles = css` display: flex; align-items: center; gap: 10px; + justify-content: space-between; } .center { diff --git a/plugins/plugins/core/messaging/q-chat/q-chat.src.js b/plugins/plugins/core/messaging/q-chat/q-chat.src.js index 15ce69d4..f8bcf947 100644 --- a/plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -12,19 +12,22 @@ import Underline from '@tiptap/extension-underline'; import Placeholder from '@tiptap/extension-placeholder' import Highlight from '@tiptap/extension-highlight' import snackbar from '../../components/snackbar.js' +import ShortUniqueId from 'short-unique-id'; import '../../components/ChatWelcomePage.js' import '../../components/ChatHead.js' import '../../components/ChatPage.js' import '../../components/WrapperModal.js' import '../../components/ChatSearchResults.js' - +import '../../components/ChatGroupsModal.js' import '@material/mwc-button' import '@material/mwc-dialog' import '@material/mwc-icon' import '@material/mwc-snackbar' import '@polymer/paper-spinner/paper-spinner-lite.js' import '@vaadin/grid' +import '@vaadin/tooltip'; + passiveSupport({ events: ['touchstart'] }) @@ -55,6 +58,7 @@ class Chat extends LitElement { groupInvites: { type: Array }, loggedInUserName: {type: String}, loggedInUserAddress: {type: String}, + openDialogGroupsModal: {type: Boolean} } } @@ -96,6 +100,9 @@ class Chat extends LitElement { this.userSelected = {} this.groupInvites = [] this.loggedInUserName = "" + this.openDialogGroupsModal = false + this.uid = new ShortUniqueId(); + } async setActiveChatHeadUrl(url) { @@ -188,34 +195,74 @@ class Chat extends LitElement { }) } + setOpenDialogGroupsModal(val){ + this.openDialogGroupsModal = val + } + + openTabToGroupManagement(){ + window.parent.reduxStore.dispatch( + window.parent.reduxAction.setNewTab({ + url: `group-management`, + id: this.uid.rnd(), + myPlugObj: { + "url": "group-management", + "domain": "core", + "page": "group-management/index.html", + "title": "Group Management", + "icon": "vaadin:group", + "mwcicon": "group", + "pluginNumber": "plugin-fJZNpyLGTl", + "menus": [], + "parent": false + }, + openExisting: true + }) + ); + } + render() { return html`