mirror of
https://github.com/Qortal/qortal-ui.git
synced 2025-02-11 17:55:51 +00:00
Merge pull request #197 from Philreact/feature/q-chat-groups
Feature/q chat groups
This commit is contained in:
commit
6d9984476b
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
/node_modules
|
@ -3,6 +3,7 @@
|
|||||||
"browser": true,
|
"browser": true,
|
||||||
"es2021": true
|
"es2021": true
|
||||||
},
|
},
|
||||||
|
"plugins": ["lit", "wc"],
|
||||||
"extends": ["eslint:recommended", "plugin:lit/recommended", "plugin:wc/recommended"],
|
"extends": ["eslint:recommended", "plugin:lit/recommended", "plugin:wc/recommended"],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 12,
|
"ecmaVersion": 12,
|
||||||
|
@ -200,6 +200,8 @@
|
|||||||
"snack3": "Successfully added and saved custom node",
|
"snack3": "Successfully added and saved custom node",
|
||||||
"snack4": "Nodes successfully saved as",
|
"snack4": "Nodes successfully saved as",
|
||||||
"snack5": "Nodes successfully imported",
|
"snack5": "Nodes successfully imported",
|
||||||
|
"snack6": "Successfully removed custom node",
|
||||||
|
"snack7": "Successfully edited custom node",
|
||||||
"exp1": "Export Private Master Key",
|
"exp1": "Export Private Master Key",
|
||||||
"exp2": "Export Master Key",
|
"exp2": "Export Master Key",
|
||||||
"exp3": "Export",
|
"exp3": "Export",
|
||||||
@ -519,7 +521,8 @@
|
|||||||
"nchange44": "To the new name",
|
"nchange44": "To the new name",
|
||||||
"nchange45": "On pressing confirm, the name update request will be sent!",
|
"nchange45": "On pressing confirm, the name update request will be sent!",
|
||||||
"nchange46": "Name Sale History",
|
"nchange46": "Name Sale History",
|
||||||
"nchange47": "Name Update Successful!"
|
"nchange47": "Name Update Successful!",
|
||||||
|
"nchange48": "Warning! If you update your name, you will forfeit the resources associated with the original name. In other words, you will lose ownership of the content under the original name in the QDN. Proceed with caution!"
|
||||||
},
|
},
|
||||||
"websitespage": {
|
"websitespage": {
|
||||||
"schange1": "Browse Websites",
|
"schange1": "Browse Websites",
|
||||||
@ -829,7 +832,14 @@
|
|||||||
"cchange80": "This image has been deleted",
|
"cchange80": "This image has been deleted",
|
||||||
"cchange81": "This image type is not supported",
|
"cchange81": "This image type is not supported",
|
||||||
"cchange82": "This attachment has been deleted",
|
"cchange82": "This attachment has been deleted",
|
||||||
"cchange90": "No messages"
|
"cchange90": "No messages",
|
||||||
|
"cchange91": "Sending...",
|
||||||
|
"cchange92": "Unread messages below",
|
||||||
|
"cchange93": "Image copied to clipboard",
|
||||||
|
"cchange94": "loaded",
|
||||||
|
"cchange95": "Only my resources",
|
||||||
|
"cchange96": "Open Group Management",
|
||||||
|
"cchange97": "Join group link copied to clipboard"
|
||||||
},
|
},
|
||||||
"welcomepage": {
|
"welcomepage": {
|
||||||
"wcchange1": "Welcome to Q-Chat",
|
"wcchange1": "Welcome to Q-Chat",
|
||||||
@ -1161,5 +1171,10 @@
|
|||||||
"lot11": "There are no open lotteries!",
|
"lot11": "There are no open lotteries!",
|
||||||
"lot12": "There are no finished lotteries!",
|
"lot12": "There are no finished lotteries!",
|
||||||
"lot13": "Players"
|
"lot13": "Players"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"notify1": "Confirming transaction",
|
||||||
|
"notify2": "Transaction confirmed",
|
||||||
|
"explanation": "Your transaction is getting confirmed. To track its progress, click on the bell icon."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -42,6 +42,8 @@ import '../functional-components/side-menu.js'
|
|||||||
import '../functional-components/side-menu-item.js'
|
import '../functional-components/side-menu-item.js'
|
||||||
import './start-minting.js'
|
import './start-minting.js'
|
||||||
import './notification-view/notification-bell.js'
|
import './notification-view/notification-bell.js'
|
||||||
|
import './notification-view/notification-bell-general.js'
|
||||||
|
|
||||||
|
|
||||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
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;">
|
<img src="${this.config.coin.logo}" style="height:32px; padding-left:12px;">
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:20px">
|
||||||
<notification-bell></notification-bell>
|
<notification-bell></notification-bell>
|
||||||
|
<notification-bell-general></notification-bell-general>
|
||||||
|
</div>
|
||||||
<div style="display: inline;">
|
<div style="display: inline;">
|
||||||
<span>
|
<span>
|
||||||
<img src="/img/${translate("selectmenu.languageflag")}-flag-round-icon-32.png" style="width: 32px; height: 32px; padding-top: 4px;">
|
<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 '../qort-theme-toggle.js'
|
||||||
|
|
||||||
import settings from '../../functional-components/settings-page.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.reduxStore = store
|
||||||
window.reduxAction = {
|
window.reduxAction = {
|
||||||
@ -28,7 +28,9 @@ window.reduxAction = {
|
|||||||
removeQAPPAutoLists: removeQAPPAutoLists,
|
removeQAPPAutoLists: removeQAPPAutoLists,
|
||||||
addTabInfo: addTabInfo,
|
addTabInfo: addTabInfo,
|
||||||
setTabNotifications: setTabNotifications,
|
setTabNotifications: setTabNotifications,
|
||||||
setNewTab: setNewTab
|
setNewTab: setNewTab,
|
||||||
|
setNewNotification: setNewNotification,
|
||||||
|
setSideEffectAction: setSideEffectAction
|
||||||
}
|
}
|
||||||
|
|
||||||
const animationDuration = 0.7 // Seconds
|
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}`,
|
url: `qdn/browser/index.html${query}`,
|
||||||
id: 'q-mail-notification',
|
id: 'q-mail-notification',
|
||||||
myPlugObj: {
|
myPlugObj: {
|
||||||
"url": "qapps",
|
"url": "myapp",
|
||||||
"domain": "core",
|
"domain": "core",
|
||||||
"page": `qdn/browser/index.html${query}`,
|
"page": `qdn/browser/index.html${query}`,
|
||||||
"title": "Q-Mail",
|
"title": "Q-Mail",
|
||||||
@ -196,7 +196,7 @@ class NotificationBell extends connect(store)(LitElement) {
|
|||||||
url: `qdn/browser/index.html${query}`,
|
url: `qdn/browser/index.html${query}`,
|
||||||
id: 'q-mail-notification',
|
id: 'q-mail-notification',
|
||||||
myPlugObj: {
|
myPlugObj: {
|
||||||
"url": "qapps",
|
"url": "myapp",
|
||||||
"domain": "core",
|
"domain": "core",
|
||||||
"page": `qdn/browser/index.html${query}`,
|
"page": `qdn/browser/index.html${query}`,
|
||||||
"title": "Q-Mail",
|
"title": "Q-Mail",
|
||||||
@ -210,7 +210,6 @@ class NotificationBell extends connect(store)(LitElement) {
|
|||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
.layout {
|
.layout {
|
||||||
width: 100px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -220,7 +219,7 @@ class NotificationBell extends connect(store)(LitElement) {
|
|||||||
.count {
|
.count {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
right: 32px;
|
right: 0px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
background-color: red;
|
background-color: red;
|
||||||
color: white;
|
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 {
|
.hideIframe {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
zIndex: -10;
|
z-Index: -10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.showIframe {
|
.showIframe {
|
||||||
display: block;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
zIndex: 1;
|
z-Index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
@ -135,7 +135,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
min-width: 110px;
|
min-width: 110px;
|
||||||
max-width: 220px;
|
max-width: 220px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
zIndex: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabCard {
|
.tabCard {
|
||||||
@ -171,7 +171,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
border-left: 1px solid var(--black);
|
border-left: 1px solid var(--black);
|
||||||
border-right: 1px solid var(--black);
|
border-right: 1px solid var(--black);
|
||||||
border-bottom: 1px solid var(--white);
|
border-bottom: 1px solid var(--white);
|
||||||
zIndex: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
@ -432,7 +432,9 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
<div
|
<div
|
||||||
id="tab-${tab.id}"
|
id="tab-${tab.id}"
|
||||||
class="tab ${this.currentTab === index ? 'active' : ''}"
|
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"}">
|
<div id="icon-${tab.id}" class="${this.currentTab === index ? "iconActive" : "iconInactive"}">
|
||||||
<mwc-icon>${icon}</mwc-icon>
|
<mwc-icon>${icon}</mwc-icon>
|
||||||
@ -441,10 +443,13 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
${count ? html`
|
${count ? html`
|
||||||
<span class="tabTitle ml-30">${title}</span>
|
<span class="tabTitle ml-30">${title}</span>
|
||||||
<span class="count ml-5">${count}</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`
|
` : html`
|
||||||
<span class="tabTitle ml-30">${title}</span>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
@ -460,6 +465,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
id: this.uid.rnd()
|
id: this.uid.rnd()
|
||||||
})
|
})
|
||||||
this.currentTab = lengthOfTabs
|
this.currentTab = lengthOfTabs
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>+</button>
|
>+</button>
|
||||||
</div>
|
</div>
|
||||||
@ -655,7 +661,7 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
this.createEpmlInstance(frame, newIndex)
|
this.createEpmlInstance(frame, newIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTab(index, tabA) {
|
removeTab(index, tabA) {
|
||||||
const tabB = this.tabs.length - 1
|
const tabB = this.tabs.length - 1
|
||||||
const tabC = this.tabs[tabB].id
|
const tabC = this.tabs[tabB].id
|
||||||
|
|
||||||
@ -667,10 +673,10 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
let iconId = ''
|
let iconId = ''
|
||||||
|
|
||||||
this.tabs = this.tabs.filter((tab, tIndex) => tIndex !== index)
|
this.tabs = this.tabs.filter((tab, tIndex) => tIndex !== index)
|
||||||
|
this.currentTab = this.tabs.length - 1;
|
||||||
|
|
||||||
const tabD = this.tabs.length - 1
|
const tabD = this.tabs.length - 1
|
||||||
const plugObj = this.tabs[tabD].url
|
const plugObj = this.tabs[tabD].url
|
||||||
|
|
||||||
theId = this.tabs[tabD].id
|
theId = this.tabs[tabD].id
|
||||||
tabId = 'tab-' + theId
|
tabId = 'tab-' + theId
|
||||||
frameId = 'frame-' + theId
|
frameId = 'frame-' + theId
|
||||||
@ -819,7 +825,14 @@ class ShowPlugin extends connect(store)(LitElement) {
|
|||||||
|
|
||||||
if (state.app.newTab) {
|
if (state.app.newTab) {
|
||||||
const newTab = 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.addTab(newTab)
|
||||||
this.currentTab = this.tabs.length - 1
|
this.currentTab = this.tabs.length - 1
|
||||||
store.dispatch(setNewTab(null))
|
store.dispatch(setNewTab(null))
|
||||||
@ -893,7 +906,7 @@ class NavBar extends connect(store)(LitElement) {
|
|||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
height: 100vh;
|
height: calc(100vh - 120px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,7 +914,7 @@ class NavBar extends connect(store)(LitElement) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: color: var(--white);
|
background-color: var(--white);
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
max-width: 750px;
|
max-width: 750px;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
@ -2276,10 +2289,10 @@ class NavBar extends connect(store)(LitElement) {
|
|||||||
|
|
||||||
if (service === "APP") {
|
if (service === "APP") {
|
||||||
this.changePage({
|
this.changePage({
|
||||||
"url": "qapp",
|
"url": "myapp",
|
||||||
"domain": "core",
|
"domain": "core",
|
||||||
"page": `qdn/browser/index.html${query}`,
|
"page": `qdn/browser/index.html${query}`,
|
||||||
"title": "Q-App",
|
"title": name || "Q-App",
|
||||||
"icon": "vaadin:external-browser",
|
"icon": "vaadin:external-browser",
|
||||||
"mwcicon": "open_in_browser",
|
"mwcicon": "open_in_browser",
|
||||||
"menus": [],
|
"menus": [],
|
||||||
@ -2287,10 +2300,10 @@ class NavBar extends connect(store)(LitElement) {
|
|||||||
})
|
})
|
||||||
} else if (service === "WEBSITE") {
|
} else if (service === "WEBSITE") {
|
||||||
this.changePage({
|
this.changePage({
|
||||||
"url": "websites",
|
"url": "myapp",
|
||||||
"domain": "core",
|
"domain": "core",
|
||||||
"page": `qdn/browser/index.html${query}`,
|
"page": `qdn/browser/index.html${query}`,
|
||||||
"title": "Website",
|
"title": name || "Website",
|
||||||
"icon": "vaadin:desktop",
|
"icon": "vaadin:desktop",
|
||||||
"mwcicon": "desktop_mac",
|
"mwcicon": "desktop_mac",
|
||||||
"menus": [],
|
"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 CHAT_HEADS_STREAM_NAME = 'chat_heads'
|
||||||
const NODE_CONFIG_STREAM_NAME = 'node_config'
|
const NODE_CONFIG_STREAM_NAME = 'node_config'
|
||||||
const CHAT_LAST_SEEN = 'chat_last_seen'
|
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 loggedInStream = new EpmlStream(LOGIN_STREAM_NAME, () => store.getState().app.loggedIn)
|
||||||
export const configStream = new EpmlStream(CONFIG_STREAM_NAME, () => store.getState().config)
|
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 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 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 chatLastSeenStream = new EpmlStream(CHAT_LAST_SEEN, () => store.getState().app.chatLastSeen)
|
||||||
|
export const sideEffectActionStream = new EpmlStream(SIDE_EFFECT_ACTION, () => store.getState().app.sideEffectAction)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let oldState = {
|
let oldState = {
|
||||||
@ -56,6 +59,10 @@ store.subscribe(() => {
|
|||||||
if (oldState.app.appInfo !== state.app.appInfo) {
|
if (oldState.app.appInfo !== state.app.appInfo) {
|
||||||
appInfoStateStream.emit(state.app.appInfo)
|
appInfoStateStream.emit(state.app.appInfo)
|
||||||
}
|
}
|
||||||
|
if (oldState.app.sideEffectAction !== state.app.sideEffectAction) {
|
||||||
|
sideEffectActionStream.emit(state.app.sideEffectAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
oldState = state
|
oldState = state
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Core App Actions here...
|
// 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) => {
|
export const doUpdateBlockInfo = (blockObj) => {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -126,6 +126,12 @@ export const setNewTab = (payload) => {
|
|||||||
payload
|
payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const setNewNotification = (payload) => {
|
||||||
|
return {
|
||||||
|
type: SET_NEW_NOTIFICATION,
|
||||||
|
payload
|
||||||
|
}
|
||||||
|
}
|
||||||
export const setIsOpenDevDialog = (payload)=> {
|
export const setIsOpenDevDialog = (payload)=> {
|
||||||
return {
|
return {
|
||||||
type: IS_OPEN_DEV_DIALOG,
|
type: IS_OPEN_DEV_DIALOG,
|
||||||
@ -144,4 +150,11 @@ export const setTabNotifications = (payload) => {
|
|||||||
type: SET_TAB_NOTIFICATIONS,
|
type: SET_TAB_NOTIFICATIONS,
|
||||||
payload
|
payload
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setSideEffectAction = (payload)=> {
|
||||||
|
return {
|
||||||
|
type: SET_SIDE_EFFECT,
|
||||||
|
payload
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
// Node Config Actions here...
|
// 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'
|
import { UI_VERSION } from '../version.js'
|
||||||
|
|
||||||
const nodeConfigUrl = '/getConfig'
|
const nodeConfigUrl = '/getConfig'
|
||||||
@ -72,6 +72,16 @@ export const doAddNode = (nodeObject) => {
|
|||||||
return dispatch(addNode(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) => {
|
const addNode = (payload) => {
|
||||||
return {
|
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 = {
|
const obj1 = {
|
||||||
name: 'Local Node',
|
name: 'Local Node',
|
||||||
protocol: 'http',
|
protocol: 'http',
|
||||||
|
@ -12,6 +12,8 @@ export const UPDATE_NODE_STATUS = 'UPDATE_NODE_STATUS'
|
|||||||
export const UPDATE_NODE_INFO = 'UPDATE_NODE_INFO'
|
export const UPDATE_NODE_INFO = 'UPDATE_NODE_INFO'
|
||||||
export const SET_NODE = 'SET_NODE'
|
export const SET_NODE = 'SET_NODE'
|
||||||
export const ADD_NODE = 'ADD_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 LOAD_NODE_CONFIG = 'LOAD_NODE_CONFIG'
|
||||||
export const PAGE_URL = 'PAGE_URL'
|
export const PAGE_URL = 'PAGE_URL'
|
||||||
export const CHAT_HEADS = 'CHAT_HEADS'
|
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 SET_NEW_TAB = 'SET_NEW_TAB'
|
||||||
export const ADD_TAB_INFO = 'ADD_TAB_INFO'
|
export const ADD_TAB_INFO = 'ADD_TAB_INFO'
|
||||||
export const SET_TAB_NOTIFICATIONS = 'SET_TAB_NOTIFICATIONS'
|
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.
|
// Loading state, login state, isNavDrawOpen state etc. None of this needs to be saved to localstorage.
|
||||||
import { loadStateFromLocalStorage, saveStateToLocalStorage } from '../../localStorageHelpers.js'
|
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 { initWorkersReducer } from './reducers/init-workers.js'
|
||||||
import { loginReducer } from './reducers/login-reducer.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";
|
import localForage from "localforage";
|
||||||
const chatLastSeen = localForage.createInstance({
|
const chatLastSeen = localForage.createInstance({
|
||||||
name: "chat-last-seen",
|
name: "chat-last-seen",
|
||||||
@ -50,7 +50,9 @@ const INITIAL_STATE = {
|
|||||||
chatLastSeen: [],
|
chatLastSeen: [],
|
||||||
newTab: null,
|
newTab: null,
|
||||||
tabInfo: {},
|
tabInfo: {},
|
||||||
isOpenDevDialog: false
|
isOpenDevDialog: false,
|
||||||
|
newNotification: null,
|
||||||
|
sideEffectAction: null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (state = INITIAL_STATE, action) => {
|
export default (state = INITIAL_STATE, action) => {
|
||||||
@ -120,6 +122,10 @@ export default (state = INITIAL_STATE, action) => {
|
|||||||
return setNode(state, action)
|
return setNode(state, action)
|
||||||
case ADD_NODE:
|
case ADD_NODE:
|
||||||
return addNode(state, action)
|
return addNode(state, action)
|
||||||
|
case EDIT_NODE:
|
||||||
|
return editNode(state, action)
|
||||||
|
case REMOVE_NODE:
|
||||||
|
return removeNode(state, action)
|
||||||
case PAGE_URL:
|
case PAGE_URL:
|
||||||
return {
|
return {
|
||||||
...state,
|
...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:
|
default:
|
||||||
return state
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
954
package-lock.json
generated
954
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
77
package.json
77
package.json
@ -27,18 +27,31 @@
|
|||||||
"build-electron": "electron-builder build --publish never",
|
"build-electron": "electron-builder build --publish never",
|
||||||
"deploy-electron": "electron-builder build --win --publish never",
|
"deploy-electron": "electron-builder build --win --publish never",
|
||||||
"release": "NODE_ENV=production electron-builder build --publish never",
|
"release": "NODE_ENV=production electron-builder build --publish never",
|
||||||
"publish": "electron-builder -p always"
|
"publish": "electron-builder -p always",
|
||||||
|
"lint": "eslint './**/*.{js,mjs}'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@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",
|
||||||
|
"@tiptap/extension-placeholder": "2.0.4",
|
||||||
|
"@tiptap/extension-underline": "2.0.4",
|
||||||
|
"@tiptap/html": "2.0.4",
|
||||||
|
"@tiptap/pm": "2.0.4",
|
||||||
|
"@tiptap/starter-kit": "2.0.4",
|
||||||
"asmcrypto.js": "2.3.2",
|
"asmcrypto.js": "2.3.2",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"buffer": "6.0.3",
|
"buffer": "6.0.3",
|
||||||
"compressorjs": "1.2.1",
|
"compressorjs": "1.2.1",
|
||||||
"crypto-js": "4.1.1",
|
"crypto-js": "4.1.1",
|
||||||
"electron-log": "4.4.8",
|
|
||||||
"electron-updater": "6.1.4",
|
|
||||||
"electron-dl": "3.5.0",
|
"electron-dl": "3.5.0",
|
||||||
|
"electron-log": "4.4.8",
|
||||||
"electron-store": "8.1.0",
|
"electron-store": "8.1.0",
|
||||||
|
"electron-updater": "6.1.4",
|
||||||
"emoji-picker-js": "https://github.com/Qortal/emoji-picker-js",
|
"emoji-picker-js": "https://github.com/Qortal/emoji-picker-js",
|
||||||
"extract-zip": "2.0.1",
|
"extract-zip": "2.0.1",
|
||||||
"jssha": "3.3.1",
|
"jssha": "3.3.1",
|
||||||
@ -57,40 +70,9 @@
|
|||||||
"prosemirror-transform": "1.7.5",
|
"prosemirror-transform": "1.7.5",
|
||||||
"prosemirror-view": "1.31.7",
|
"prosemirror-view": "1.31.7",
|
||||||
"sass": "1.66.1",
|
"sass": "1.66.1",
|
||||||
"short-unique-id": "5.0.2",
|
"short-unique-id": "5.0.2"
|
||||||
"@hapi/hapi": "21.3.2",
|
|
||||||
"@hapi/inert": "7.1.0",
|
|
||||||
"@lit-labs/motion": "1.0.4",
|
|
||||||
"@tiptap/pm": "2.0.4",
|
|
||||||
"@tiptap/core": "2.0.4",
|
|
||||||
"@tiptap/extension-highlight": "2.0.4",
|
|
||||||
"@tiptap/extension-image": "2.0.4",
|
|
||||||
"@tiptap/extension-placeholder": "2.0.4",
|
|
||||||
"@tiptap/extension-underline": "2.0.4",
|
|
||||||
"@tiptap/html": "2.0.4",
|
|
||||||
"@tiptap/starter-kit": "2.0.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "1.5.0",
|
|
||||||
"electron": "26.2.0",
|
|
||||||
"electron-builder": "24.6.4",
|
|
||||||
"electron-packager": "17.1.2",
|
|
||||||
"epml": "0.3.3",
|
|
||||||
"file-saver": "2.0.5",
|
|
||||||
"highcharts": "11.1.0",
|
|
||||||
"html-escaper": "3.0.3",
|
|
||||||
"is-electron": "2.2.2",
|
|
||||||
"lit": "2.8.0",
|
|
||||||
"lit-translate": "2.0.1",
|
|
||||||
"pwa-helpers": "0.9.1",
|
|
||||||
"passive-events-support": "1.1.0",
|
|
||||||
"redux": "4.2.1",
|
|
||||||
"redux-thunk": "2.4.2",
|
|
||||||
"rollup": "3.29.0",
|
|
||||||
"rollup-plugin-node-globals": "1.4.0",
|
|
||||||
"rollup-plugin-progress": "1.1.2",
|
|
||||||
"rollup-plugin-scss": "3.0.0",
|
|
||||||
"shelljs": "0.8.5",
|
|
||||||
"@babel/core": "7.22.17",
|
"@babel/core": "7.22.17",
|
||||||
"@material/mwc-button": "0.27.0",
|
"@material/mwc-button": "0.27.0",
|
||||||
"@material/mwc-checkbox": "0.27.0",
|
"@material/mwc-checkbox": "0.27.0",
|
||||||
@ -140,7 +122,30 @@
|
|||||||
"@vaadin/icons": "24.1.7",
|
"@vaadin/icons": "24.1.7",
|
||||||
"@vaadin/password-field": "24.1.7",
|
"@vaadin/password-field": "24.1.7",
|
||||||
"@vaadin/tooltip": "24.1.7",
|
"@vaadin/tooltip": "24.1.7",
|
||||||
"@zip.js/zip.js": "2.7.29"
|
"@zip.js/zip.js": "2.7.29",
|
||||||
|
"axios": "1.5.0",
|
||||||
|
"electron": "26.2.0",
|
||||||
|
"electron-builder": "24.6.4",
|
||||||
|
"electron-packager": "17.1.2",
|
||||||
|
"epml": "0.3.3",
|
||||||
|
"eslint": "^8.50.0",
|
||||||
|
"eslint-plugin-lit": "^1.9.1",
|
||||||
|
"eslint-plugin-wc": "^2.0.3",
|
||||||
|
"file-saver": "2.0.5",
|
||||||
|
"highcharts": "11.1.0",
|
||||||
|
"html-escaper": "3.0.3",
|
||||||
|
"is-electron": "2.2.2",
|
||||||
|
"lit": "2.8.0",
|
||||||
|
"lit-translate": "2.0.1",
|
||||||
|
"passive-events-support": "1.1.0",
|
||||||
|
"pwa-helpers": "0.9.1",
|
||||||
|
"redux": "4.2.1",
|
||||||
|
"redux-thunk": "2.4.2",
|
||||||
|
"rollup": "3.29.0",
|
||||||
|
"rollup-plugin-node-globals": "1.4.0",
|
||||||
|
"rollup-plugin-progress": "1.1.2",
|
||||||
|
"rollup-plugin-scss": "3.0.0",
|
||||||
|
"shelljs": "0.8.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.16.1"
|
"node": ">=18.16.1"
|
||||||
|
348
plugins/plugins/core/components/ChatGroupManager.js
Normal file
348
plugins/plugins/core/components/ChatGroupManager.js
Normal file
@ -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`
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="close-row" style="margin-top: 15px">
|
||||||
|
<mwc-icon @click=${()=> {
|
||||||
|
this.getGroups()
|
||||||
|
}} style="color: var(--black); cursor:pointer;">refresh</mwc-icon>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
||||||
|
${get('chatpage.cchange69')}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.selectAuto(e)} ?checked=${this.autoView}></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
||||||
|
${get('chatpage.cchange95')}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.selectMyImages(e)} ?checked=${this.onlyMyImages}></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
<div id="viewElement" class="container-body">
|
||||||
|
|
||||||
|
|
||||||
|
<div id='downObserver'></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('chat-groups-manager', ChatGroupsManager);
|
||||||
|
|
98
plugins/plugins/core/components/ChatGroupsModal.js
Normal file
98
plugins/plugins/core/components/ChatGroupsModal.js
Normal file
@ -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`
|
||||||
|
|
||||||
|
<mwc-dialog
|
||||||
|
id="showDialogGroupsModal"
|
||||||
|
?open=${this.openDialogGroupsModal}
|
||||||
|
@closed=${() => {
|
||||||
|
this.setOpenDialogGroupsModal(false)
|
||||||
|
}}>
|
||||||
|
<div class="dialog-header"></div>
|
||||||
|
<div class="dialog-container ">
|
||||||
|
<chat-groups-manager></chat-groups-manager>
|
||||||
|
</div>
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
dialogAction="cancel"
|
||||||
|
class="red"
|
||||||
|
@click=${() => {
|
||||||
|
this.setOpenDialogGroupsModal(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${translate('general.close')}
|
||||||
|
</mwc-button>
|
||||||
|
</mwc-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('chat-groups-modal', ChatGroupsModal);
|
353
plugins/plugins/core/components/ChatImage.js
Normal file
353
plugins/plugins/core/components/ChatImage.js
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
import { LitElement, html, css } from 'lit';
|
||||||
|
import {
|
||||||
|
get,
|
||||||
|
translate,
|
||||||
|
} from 'lit-translate';
|
||||||
|
import axios from 'axios'
|
||||||
|
import { RequestQueueWithPromise } from '../../utils/queue';
|
||||||
|
import '@material/mwc-menu';
|
||||||
|
import '@material/mwc-list/mwc-list-item.js'
|
||||||
|
import { Epml } from '../../../epml';
|
||||||
|
const requestQueue = new RequestQueueWithPromise(5);
|
||||||
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
|
export class ChatImage extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
resource: { type: Object },
|
||||||
|
isReady: { type: Boolean},
|
||||||
|
status: {type: Object},
|
||||||
|
setOpenDialogImage: { attribute: false}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
* {
|
||||||
|
--mdc-theme-text-primary-on-background: var(--black);
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-width:45vh;
|
||||||
|
max-height:40vh;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.smallLoading,
|
||||||
|
.smallLoading:after {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 2px;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallLoading {
|
||||||
|
border-width: 0.8em;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2)
|
||||||
|
rgba(3, 169, 244, 0.2) rgb(3, 169, 244);
|
||||||
|
font-size: 30px;
|
||||||
|
position: relative;
|
||||||
|
text-indent: -9999em;
|
||||||
|
transform: translateZ(0px);
|
||||||
|
animation: 1.1s linear 0s infinite normal none running loadingAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
|
.defaultSize {
|
||||||
|
width: 45vh;
|
||||||
|
height: 40vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-menu {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@-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.resource = {
|
||||||
|
identifier: "",
|
||||||
|
name: "",
|
||||||
|
service: ""
|
||||||
|
}
|
||||||
|
this.status = {
|
||||||
|
status: ''
|
||||||
|
}
|
||||||
|
this.url = ""
|
||||||
|
this.isReady = false
|
||||||
|
this.nodeUrl = this.getNodeUrl()
|
||||||
|
this.myNode = this.getMyNode()
|
||||||
|
this.hasCalledWhenDownloaded = false
|
||||||
|
this.isFetching = false
|
||||||
|
|
||||||
|
this.observer = new IntersectionObserver(entries => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.isIntersecting && this.status.status !== 'READY') {
|
||||||
|
this._fetchImage();
|
||||||
|
// Stop observing after the image has started loading
|
||||||
|
this.observer.unobserve(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getNodeUrl(){
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||||
|
|
||||||
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
return nodeUrl
|
||||||
|
}
|
||||||
|
getMyNode(){
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||||
|
|
||||||
|
return myNode
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode =
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||||
|
];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchResource() {
|
||||||
|
try {
|
||||||
|
if(this.isFetching) return
|
||||||
|
this.isFetching = true
|
||||||
|
await axios.get(`${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
||||||
|
this.isFetching = false
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.isFetching = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchVideoUrl() {
|
||||||
|
|
||||||
|
this.fetchResource()
|
||||||
|
this.url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?async=true&apiKey=${this.myNode.apiKey}`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchStatus(){
|
||||||
|
let isCalling = false
|
||||||
|
let percentLoaded = 0
|
||||||
|
let timer = 24
|
||||||
|
const response = await axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
||||||
|
if(response && response.data && response.data.status === 'READY'){
|
||||||
|
this.status = response.data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (isCalling) return
|
||||||
|
isCalling = true
|
||||||
|
|
||||||
|
const data = await requestQueue.enqueue(() => {
|
||||||
|
return axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
||||||
|
});
|
||||||
|
const res = data.data
|
||||||
|
|
||||||
|
isCalling = false
|
||||||
|
if (res.localChunkCount) {
|
||||||
|
if (res.percentLoaded) {
|
||||||
|
if (
|
||||||
|
res.percentLoaded === percentLoaded &&
|
||||||
|
res.percentLoaded !== 100
|
||||||
|
) {
|
||||||
|
timer = timer - 5
|
||||||
|
} else {
|
||||||
|
timer = 24
|
||||||
|
}
|
||||||
|
if (timer < 0) {
|
||||||
|
timer = 24
|
||||||
|
isCalling = true
|
||||||
|
this.status = {
|
||||||
|
...res,
|
||||||
|
status: 'REFETCHING'
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
isCalling = false
|
||||||
|
this.fetchResource()
|
||||||
|
}, 25000)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
percentLoaded = res.percentLoaded
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = res
|
||||||
|
if(this.status.status === 'DOWNLOADED'){
|
||||||
|
this.fetchResource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if progress is 100% and clear interval if true
|
||||||
|
if (res.status === 'READY') {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
this.status = res
|
||||||
|
this.isReady = true
|
||||||
|
}
|
||||||
|
}, 5000) // 1 second interval
|
||||||
|
}
|
||||||
|
|
||||||
|
async _fetchImage() {
|
||||||
|
try {
|
||||||
|
this.fetchVideoUrl({
|
||||||
|
name: this.resource.name,
|
||||||
|
service: this.resource.service,
|
||||||
|
identifier: this.resource.identifier
|
||||||
|
})
|
||||||
|
this.fetchStatus()
|
||||||
|
} catch (error) { /* empty */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated(){
|
||||||
|
this.observer.observe(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldUpdate(changedProperties) {
|
||||||
|
if (changedProperties.has('setOpenDialogImage') && changedProperties.size === 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
showContextMenu(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const contextMenu = this.shadowRoot.getElementById('contextMenu');
|
||||||
|
const containerRect = e.currentTarget.getBoundingClientRect();
|
||||||
|
|
||||||
|
// Adjusting the positions
|
||||||
|
const adjustedX = e.clientX - containerRect.left;
|
||||||
|
const adjustedY = e.clientY - containerRect.top;
|
||||||
|
|
||||||
|
contextMenu.style.top = `${adjustedY}px`;
|
||||||
|
contextMenu.style.left = `${adjustedX}px`;
|
||||||
|
|
||||||
|
contextMenu.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async handleCopy(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const image = this.shadowRoot.querySelector('img');
|
||||||
|
|
||||||
|
// Create a canvas and draw the image on it.
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = image.naturalWidth;
|
||||||
|
canvas.height = image.naturalHeight;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(image, 0, 0);
|
||||||
|
|
||||||
|
// Convert canvas image to blob
|
||||||
|
canvas.toBlob(blob => {
|
||||||
|
try {
|
||||||
|
const clipboardItem = new ClipboardItem({ 'image/png': blob });
|
||||||
|
navigator.clipboard.write([clipboardItem]).then(() => {
|
||||||
|
const msg = get("chatpage.cchange93")
|
||||||
|
parentEpml.request('showSnackBar', msg)
|
||||||
|
console.log('Image copied to clipboard');
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Failed to copy image: ', err);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error copying the image: ', error);
|
||||||
|
}
|
||||||
|
}, 'image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
handleMenuBlur() {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.isMenuItemClicked) {
|
||||||
|
const contextMenu = this.shadowRoot.getElementById('contextMenu');
|
||||||
|
contextMenu.open = false;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class=${[
|
||||||
|
`image-container`,
|
||||||
|
this.status.status !== 'READY'
|
||||||
|
? 'defaultSize'
|
||||||
|
: '',
|
||||||
|
this.status.status !== 'READY'
|
||||||
|
? 'hideImg'
|
||||||
|
: '',
|
||||||
|
].join(' ')}
|
||||||
|
>
|
||||||
|
${
|
||||||
|
this.status.status !== 'READY'
|
||||||
|
? html`
|
||||||
|
<div
|
||||||
|
style="display:flex;flex-direction:column;width:100%;height:100%;justify-content:center;align-items:center;position:absolute;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class=${`smallLoading`}
|
||||||
|
></div>
|
||||||
|
<p style="color: var(--black)">${`${Math.round(this.status.percentLoaded || 0
|
||||||
|
).toFixed(0)}% `}${translate('chatpage.cchange94')}</p>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
${this.status.status === 'READY' ? html`
|
||||||
|
<div class="customContextMenuDiv" @contextmenu="${this.showContextMenu}" style="position:relative">
|
||||||
|
<img crossOrigin="anonymous" @click=${()=> this.setOpenDialogImage(true)} src=${this.url} />
|
||||||
|
<mwc-menu id="contextMenu" @blur="${this.handleMenuBlur}">
|
||||||
|
<mwc-list-item @click="${this.handleCopy}">Copy</mwc-list-item>
|
||||||
|
</mwc-menu>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('chat-image', ChatImage);
|
@ -9,29 +9,29 @@ import '@material/mwc-dialog'
|
|||||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
class ChatModals extends LitElement {
|
class ChatModals extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
openDialogPrivateMessage: {type: Boolean},
|
openDialogPrivateMessage: { type: Boolean },
|
||||||
openDialogBlockUser: {type: Boolean},
|
openDialogBlockUser: { type: Boolean },
|
||||||
isLoading: { type: Boolean },
|
isLoading: { type: Boolean },
|
||||||
nametodialog: { type: String, attribute: true },
|
nametodialog: { type: String, attribute: true },
|
||||||
hidePrivateMessageModal: {type: Function},
|
hidePrivateMessageModal: { type: Function },
|
||||||
hideBlockUserModal: {type: Function},
|
hideBlockUserModal: { type: Function },
|
||||||
toblockaddress: { type: String, attribute: true },
|
toblockaddress: { type: String, attribute: true },
|
||||||
chatBlockedAdresses: { type: Array }
|
chatBlockedAdresses: { type: Array }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
this.hidePrivateMessageModal = () => {}
|
this.hidePrivateMessageModal = () => { }
|
||||||
this.hideBlockUserModal = () => {}
|
this.hideBlockUserModal = () => { }
|
||||||
this.chatBlockedAdresses = []
|
this.chatBlockedAdresses = []
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
.input {
|
.input {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
border: none;
|
border: none;
|
||||||
@ -60,52 +60,48 @@ class ChatModals extends LitElement {
|
|||||||
--mdc-theme-primary: red;
|
--mdc-theme-primary: red;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
|
||||||
|
|
||||||
const stopKeyEventPropagation = (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.shadowRoot.getElementById('sendTo').addEventListener('keydown', stopKeyEventPropagation);
|
firstUpdated() {
|
||||||
this.shadowRoot.getElementById('messageBox').addEventListener('keydown', stopKeyEventPropagation);
|
|
||||||
|
const stopKeyEventPropagation = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shadowRoot.getElementById('sendTo').addEventListener('keydown', stopKeyEventPropagation);
|
||||||
|
this.shadowRoot.getElementById('messageBox').addEventListener('keydown', stopKeyEventPropagation);
|
||||||
|
|
||||||
|
parentEpml.ready().then(() => {
|
||||||
|
parentEpml.subscribe('selected_address', async selectedAddress => {
|
||||||
|
this.selectedAddress = {}
|
||||||
|
selectedAddress = JSON.parse(selectedAddress)
|
||||||
|
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
||||||
|
this.selectedAddress = selectedAddress
|
||||||
|
})
|
||||||
|
|
||||||
parentEpml.ready().then(() => {
|
|
||||||
parentEpml.subscribe('selected_address', async selectedAddress => {
|
|
||||||
this.selectedAddress = {}
|
|
||||||
selectedAddress = JSON.parse(selectedAddress)
|
|
||||||
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
|
||||||
this.selectedAddress = selectedAddress
|
|
||||||
})
|
})
|
||||||
parentEpml.request('apiCall', {
|
parentEpml.imReady()
|
||||||
url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}`
|
|
||||||
}).then(res => {
|
|
||||||
this.balance = res
|
|
||||||
})
|
|
||||||
})
|
|
||||||
parentEpml.imReady()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send Private Message
|
// Send Private Message
|
||||||
|
|
||||||
_sendMessage() {
|
_sendMessage() {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
const recipient = this.shadowRoot.getElementById('sendTo').value;
|
const recipient = this.shadowRoot.getElementById('sendTo').value;
|
||||||
const messageBox = this.shadowRoot.getElementById('messageBox');
|
const messageBox = this.shadowRoot.getElementById('messageBox');
|
||||||
const messageText = messageBox.value;
|
const messageText = messageBox.value;
|
||||||
|
|
||||||
if (recipient.length === 0) {
|
if (recipient.length === 0) {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
} else if (messageText.length === 0) {
|
} else if (messageText.length === 0) {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
} else {
|
} else {
|
||||||
this.sendMessage()
|
this.sendMessage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async sendMessage() {
|
async sendMessage() {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
@ -172,77 +168,77 @@ class ChatModals extends LitElement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendMessageRequest = async (isEncrypted, _publicKey) => {
|
const sendMessageRequest = async (isEncrypted, _publicKey) => {
|
||||||
const messageObject = {
|
const messageObject = {
|
||||||
messageText,
|
messageText,
|
||||||
images: [''],
|
images: [''],
|
||||||
repliedTo: '',
|
repliedTo: '',
|
||||||
version: 1
|
version: 1
|
||||||
}
|
|
||||||
const stringifyMessageObject = JSON.stringify(messageObject)
|
|
||||||
let chatResponse = await parentEpml.request('chat', {
|
|
||||||
type: 18,
|
|
||||||
nonce: this.selectedAddress.nonce,
|
|
||||||
params: {
|
|
||||||
timestamp: sendTimestamp,
|
|
||||||
recipient: recipient,
|
|
||||||
recipientPublicKey: _publicKey,
|
|
||||||
hasChatReference: 0,
|
|
||||||
message: stringifyMessageObject,
|
|
||||||
lastReference: reference,
|
|
||||||
proofOfWorkNonce: 0,
|
|
||||||
isEncrypted: isEncrypted,
|
|
||||||
isText: 1
|
|
||||||
}
|
}
|
||||||
})
|
const stringifyMessageObject = JSON.stringify(messageObject)
|
||||||
_computePow(chatResponse)
|
let chatResponse = await parentEpml.request('chat', {
|
||||||
}
|
type: 18,
|
||||||
|
nonce: this.selectedAddress.nonce,
|
||||||
|
params: {
|
||||||
|
timestamp: sendTimestamp,
|
||||||
|
recipient: recipient,
|
||||||
|
recipientPublicKey: _publicKey,
|
||||||
|
hasChatReference: 0,
|
||||||
|
message: stringifyMessageObject,
|
||||||
|
lastReference: reference,
|
||||||
|
proofOfWorkNonce: 0,
|
||||||
|
isEncrypted: isEncrypted,
|
||||||
|
isText: 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
_computePow(chatResponse)
|
||||||
|
}
|
||||||
|
|
||||||
const _computePow = async (chatBytes) => {
|
const _computePow = async (chatBytes) => {
|
||||||
|
|
||||||
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; })
|
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; })
|
||||||
const chatBytesArray = new Uint8Array(_chatBytesArray)
|
const chatBytesArray = new Uint8Array(_chatBytesArray)
|
||||||
const chatBytesHash = new window.parent.Sha256().process(chatBytesArray).finish().result
|
const chatBytesHash = new window.parent.Sha256().process(chatBytesArray).finish().result
|
||||||
const hashPtr = window.parent.sbrk(32, window.parent.heap)
|
const hashPtr = window.parent.sbrk(32, window.parent.heap)
|
||||||
const hashAry = new Uint8Array(window.parent.memory.buffer, hashPtr, 32)
|
const hashAry = new Uint8Array(window.parent.memory.buffer, hashPtr, 32)
|
||||||
hashAry.set(chatBytesHash)
|
hashAry.set(chatBytesHash)
|
||||||
|
|
||||||
const difficulty = this.balance < 4 ? 18 : 8
|
const difficulty = this.balance < 4 ? 18 : 8
|
||||||
|
|
||||||
const workBufferLength = 8 * 1024 * 1024;
|
const workBufferLength = 8 * 1024 * 1024;
|
||||||
const workBufferPtr = window.parent.sbrk(workBufferLength, window.parent.heap)
|
const workBufferPtr = window.parent.sbrk(workBufferLength, window.parent.heap)
|
||||||
|
|
||||||
let nonce = window.parent.computePow(hashPtr, workBufferPtr, workBufferLength, difficulty)
|
let nonce = window.parent.computePow(hashPtr, workBufferPtr, workBufferLength, difficulty)
|
||||||
|
|
||||||
let _response = await parentEpml.request('sign_chat', {
|
let _response = await parentEpml.request('sign_chat', {
|
||||||
nonce: this.selectedAddress.nonce,
|
nonce: this.selectedAddress.nonce,
|
||||||
chatBytesArray: chatBytesArray,
|
chatBytesArray: chatBytesArray,
|
||||||
chatNonce: nonce
|
chatNonce: nonce
|
||||||
})
|
})
|
||||||
getSendChatResponse(_response)
|
getSendChatResponse(_response)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSendChatResponse = (response) => {
|
const getSendChatResponse = (response) => {
|
||||||
|
|
||||||
if (response === true) {
|
if (response === true) {
|
||||||
messageBox.value = ''
|
messageBox.value = ''
|
||||||
let err2string = get('welcomepage.wcchange8')
|
let err2string = get('welcomepage.wcchange8')
|
||||||
parentEpml.request('showSnackBar', `${err2string}`)
|
parentEpml.request('showSnackBar', `${err2string}`)
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
this.shadowRoot.querySelector('#startPmDialog').close()
|
this.shadowRoot.querySelector('#startPmDialog').close()
|
||||||
} else if (response.error) {
|
} else if (response.error) {
|
||||||
parentEpml.request('showSnackBar', response.message)
|
parentEpml.request('showSnackBar', response.message)
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
this.shadowRoot.querySelector('#startPmDialog').close()
|
this.shadowRoot.querySelector('#startPmDialog').close()
|
||||||
} else {
|
} else {
|
||||||
let err3string = get('welcomepage.wcchange9')
|
let err3string = get('welcomepage.wcchange9')
|
||||||
parentEpml.request('showSnackBar', `${err3string}`)
|
parentEpml.request('showSnackBar', `${err3string}`)
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
this.shadowRoot.querySelector('#startPmDialog').close()
|
this.shadowRoot.querySelector('#startPmDialog').close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
getAddressPublicKey()
|
getAddressPublicKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
_textArea(e) {
|
_textArea(e) {
|
||||||
@ -276,8 +272,8 @@ class ChatModals extends LitElement {
|
|||||||
fetch(`${nodeUrl}/names/address/${item}?limit=0&reverse=true`).then(res => {
|
fetch(`${nodeUrl}/names/address/${item}?limit=0&reverse=true`).then(res => {
|
||||||
return res.json()
|
return res.json()
|
||||||
}).then(jsonRes => {
|
}).then(jsonRes => {
|
||||||
if(jsonRes.length) {
|
if (jsonRes.length) {
|
||||||
jsonRes.map (item => {
|
jsonRes.map(item => {
|
||||||
obj.push(item)
|
obj.push(item)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -291,7 +287,7 @@ class ChatModals extends LitElement {
|
|||||||
|
|
||||||
relMessages() {
|
relMessages() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = window.location.href.split( '#' )[0]
|
window.location.href = window.location.href.split('#')[0]
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,8 +341,8 @@ class ChatModals extends LitElement {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<mwc-dialog
|
<mwc-dialog
|
||||||
id='sendPMDialog'
|
id='sendPMDialog'
|
||||||
tabindex='0'
|
tabindex='0'
|
||||||
@ -366,9 +362,9 @@ class ChatModals extends LitElement {
|
|||||||
<textarea class='textarea' @keydown=${(e) => this._textArea(e)} ?disabled=${this.isLoading} id='messageBox' placeholder='${translate('welcomepage.wcchange5')}' rows='1'></textarea>
|
<textarea class='textarea' @keydown=${(e) => this._textArea(e)} ?disabled=${this.isLoading} id='messageBox' placeholder='${translate('welcomepage.wcchange5')}' rows='1'></textarea>
|
||||||
</p>
|
</p>
|
||||||
<mwc-button ?disabled='${this.isLoading}' slot='primaryAction' @click=${() => {
|
<mwc-button ?disabled='${this.isLoading}' slot='primaryAction' @click=${() => {
|
||||||
this._sendMessage();
|
this._sendMessage();
|
||||||
}
|
}
|
||||||
}>${translate('welcomepage.wcchange6')}
|
}>${translate('welcomepage.wcchange6')}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
?disabled='${this.isLoading}'
|
?disabled='${this.isLoading}'
|
||||||
@ -409,7 +405,7 @@ class ChatModals extends LitElement {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
</mwc-dialog>
|
</mwc-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('chat-modals', ChatModals)
|
customElements.define('chat-modals', ChatModals)
|
1154
plugins/plugins/core/components/ChatPage-css.js
Normal file
1154
plugins/plugins/core/components/ChatPage-css.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
670
plugins/plugins/core/components/ChatRightPanelResources.js
Normal file
670
plugins/plugins/core/components/ChatRightPanelResources.js
Normal file
@ -0,0 +1,670 @@
|
|||||||
|
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,
|
||||||
|
translate,
|
||||||
|
} from 'lit-translate';
|
||||||
|
import { generateIdFromAddresses } from '../../utils/id-generation';
|
||||||
|
|
||||||
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent });
|
||||||
|
|
||||||
|
class ChatRightPanelResources 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 },
|
||||||
|
images: { 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.getMoreImages = this.getMoreImages.bind(this);
|
||||||
|
this.viewElement = '';
|
||||||
|
this.downObserverElement = '';
|
||||||
|
|
||||||
|
this.sendMoneyLoading = false;
|
||||||
|
this.btnDisable = false;
|
||||||
|
this.errorMessage = '';
|
||||||
|
this.successMessage = '';
|
||||||
|
|
||||||
|
this.images = [];
|
||||||
|
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 getMoreImages(reset) {
|
||||||
|
try {
|
||||||
|
if(reset){
|
||||||
|
this.images = []
|
||||||
|
}
|
||||||
|
const groupPart = this.isReceipient
|
||||||
|
? `direct_${generateIdFromAddresses(this._chatId, this.myAddress)}`
|
||||||
|
: `group_${this._chatId}`;
|
||||||
|
|
||||||
|
let offset = reset ? 0 : this.images.length;
|
||||||
|
let endpoint = `/arbitrary/resources/search?service=QCHAT_IMAGE&identifier=qchat_${groupPart}&reverse=true&limit=20&reverse=true&offset=${offset}`
|
||||||
|
if(this.onlyMyImages){
|
||||||
|
endpoint = endpoint + `&name=${this.myName}`
|
||||||
|
}
|
||||||
|
const qchatImages = await parentEpml.request('apiCall', {
|
||||||
|
type: 'api',
|
||||||
|
url: endpoint,
|
||||||
|
});
|
||||||
|
|
||||||
|
let list = []
|
||||||
|
if(reset){
|
||||||
|
list = qchatImages
|
||||||
|
} else {
|
||||||
|
list = [...this.images, ...qchatImages]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.images = list
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.viewElement = this.shadowRoot.getElementById('viewElement');
|
||||||
|
this.downObserverElement =
|
||||||
|
this.shadowRoot.getElementById('downObserver');
|
||||||
|
this.elementObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
async updated(changedProperties) {
|
||||||
|
if (changedProperties && changedProperties.has('_chatId')) {
|
||||||
|
this.images = [];
|
||||||
|
this.getMoreImages(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties && changedProperties.has('onlyMyImages')) {
|
||||||
|
this.getMoreImages(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
return html`
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="close-row" style="margin-top: 15px">
|
||||||
|
<mwc-icon @click=${()=> {
|
||||||
|
this.getMoreImages(true)
|
||||||
|
}} style="color: var(--black); cursor:pointer;">refresh</mwc-icon>
|
||||||
|
<vaadin-icon class="top-bar-icon" @click=${() =>
|
||||||
|
this.toggle(
|
||||||
|
false
|
||||||
|
)} style="margin: 0px 10px" icon="vaadin:close" slot="icon"></vaadin-icon>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
||||||
|
${get('chatpage.cchange69')}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.selectAuto(e)} ?checked=${this.autoView}></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<label for="authButton" id="authButtonLabel" style="color: var(--black);">
|
||||||
|
${get('chatpage.cchange95')}
|
||||||
|
</label>
|
||||||
|
<mwc-checkbox style="margin-right: -15px;" id="authButton" @click=${(e) => this.selectMyImages(e)} ?checked=${this.onlyMyImages}></mwc-checkbox>
|
||||||
|
</div>
|
||||||
|
<div id="viewElement" class="container-body">
|
||||||
|
|
||||||
|
${this.images.map((image) => {
|
||||||
|
return html`<image-parent .repost=${this.repost} .image=${image} ?autoView=${this.autoView}></image-parent>`;
|
||||||
|
})}
|
||||||
|
<div id='downObserver'></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('chat-right-panel-resources', ChatRightPanelResources);
|
||||||
|
|
||||||
|
class ImageParent 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 },
|
||||||
|
images: { type: Array },
|
||||||
|
viewImage: { type: Boolean },
|
||||||
|
image: { type: Object },
|
||||||
|
autoView: {type: Boolean},
|
||||||
|
repost: {attribute: false},
|
||||||
|
isImgLoaded: {type: Boolean}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.leaveGroupObj = {};
|
||||||
|
this.leaveFee = 0.001;
|
||||||
|
this.error = false;
|
||||||
|
this.chatHeads = [];
|
||||||
|
this.groupAdmin = [];
|
||||||
|
this.groupMembers = [];
|
||||||
|
this.viewElement = '';
|
||||||
|
|
||||||
|
this.sendMoneyLoading = false;
|
||||||
|
this.btnDisable = false;
|
||||||
|
this.errorMessage = '';
|
||||||
|
this.successMessage = '';
|
||||||
|
this.isImgLoaded = false
|
||||||
|
this.images = [];
|
||||||
|
this.viewImage = false;
|
||||||
|
this.myName =
|
||||||
|
window.parent.reduxStore.getState().app.accountInfo.names[0].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
gap: 20px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
.image-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.repost-btn {
|
||||||
|
margin-top: 4px;
|
||||||
|
max-height: 28px;
|
||||||
|
padding: 5px 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #03a9f4;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {}
|
||||||
|
|
||||||
|
async updated(changedProperties) {
|
||||||
|
if (changedProperties && changedProperties.has('chatId')) {
|
||||||
|
// const autoSeeChatList =
|
||||||
|
// window.parent.reduxStore.getState().app.autoLoadImageChats;
|
||||||
|
// if (autoSeeChatList.includes(this.chatId)) {
|
||||||
|
// this.viewImage = true;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(){
|
||||||
|
this.isImgLoaded = true
|
||||||
|
this.requestUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
${!this.autoView && !this.viewImage && this.myName !== this.image.name
|
||||||
|
? html`
|
||||||
|
<div class="message-myBg">
|
||||||
|
<div class="message-user-info">
|
||||||
|
<span class="message-data-name">
|
||||||
|
${this.image.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
@click=${() => {
|
||||||
|
this.viewImage = true;
|
||||||
|
}}
|
||||||
|
class=${[`image-container`].join(' ')}
|
||||||
|
style="height: 200px"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black);"
|
||||||
|
>
|
||||||
|
${translate('chatpage.cchange40')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
${this.autoView || this.viewImage || this.myName === this.image.name
|
||||||
|
? html`
|
||||||
|
<div class="message-myBg">
|
||||||
|
<div class="message-user-info">
|
||||||
|
<span class="message-data-name">
|
||||||
|
${this.image.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<reusable-image
|
||||||
|
.resource=${{
|
||||||
|
name: this.image.name,
|
||||||
|
service: this.image.service,
|
||||||
|
identifier: this.image.identifier,
|
||||||
|
}}
|
||||||
|
.onLoad=${()=> this.onLoad()}
|
||||||
|
></reusable-image>
|
||||||
|
${this.isImgLoaded ? html`
|
||||||
|
<div class="actions-parent">
|
||||||
|
<button class="repost-btn" @click=${()=> this.repost(this.image)}>repost</button>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('image-parent', ImageParent);
|
@ -43,6 +43,10 @@ export const chatStyles = css`
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 20px 17px;
|
padding: 20px 17px;
|
||||||
}
|
}
|
||||||
|
.message-sending {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: progress;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-list {
|
.chat-list {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@ -256,7 +260,6 @@ export const chatStyles = css`
|
|||||||
.message-parent {
|
.message-parent {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: rgba(245, 245, 245, 0);
|
background: rgba(245, 245, 245, 0);
|
||||||
transition: all 0.1s ease-in-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-parent:hover {
|
.message-parent:hover {
|
||||||
@ -364,7 +367,6 @@ export const chatStyles = css`
|
|||||||
background:#fff;
|
background:#fff;
|
||||||
color: #000;
|
color: #000;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -410,7 +412,6 @@ export const chatStyles = css`
|
|||||||
width: 150px;
|
width: 150px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 3px 8px;
|
padding: 3px 8px;
|
||||||
box-shadow: rgba(77, 77, 82, 0.2) 0px 7px 29px 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-user:hover {
|
.block-user:hover {
|
||||||
@ -753,6 +754,17 @@ export const chatStyles = css`
|
|||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unread-divider {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
color: var(--black);
|
||||||
|
border-bottom: 1px solid var(--black);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.blink-bg{
|
.blink-bg{
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
animation: blinkingBackground 3s;
|
animation: blinkingBackground 3s;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -36,7 +36,8 @@ class ChatTextEditor extends LitElement {
|
|||||||
isEnabledChatEnter: {type: Boolean},
|
isEnabledChatEnter: {type: Boolean},
|
||||||
openGifModal: { type: Boolean },
|
openGifModal: { type: Boolean },
|
||||||
setOpenGifModal: { attribute: false },
|
setOpenGifModal: { attribute: false },
|
||||||
chatId: {type: String}
|
chatId: {type: String},
|
||||||
|
messageQueue: {type: Array}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +171,7 @@ class ChatTextEditor extends LitElement {
|
|||||||
padding: 0px 10px;
|
padding: 0px 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: safe center;
|
||||||
}
|
}
|
||||||
.element::-webkit-scrollbar-track {
|
.element::-webkit-scrollbar-track {
|
||||||
background-color: whitesmoke;
|
background-color: whitesmoke;
|
||||||
@ -376,6 +377,8 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
|
|||||||
this.userName = window.parent.reduxStore.getState().app.accountInfo.names[0]
|
this.userName = window.parent.reduxStore.getState().app.accountInfo.names[0]
|
||||||
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
|
||||||
this.editor = null
|
this.editor = null
|
||||||
|
this.messageQueue = []
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -499,7 +502,11 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
|
|||||||
accept="image/*, .doc, .docx, .pdf, .zip, .pdf, .txt, .odt, .ods, .xls, .xlsx, .ppt, .pptx" />
|
accept="image/*, .doc, .docx, .pdf, .zip, .pdf, .txt, .odt, .ods, .xls, .xlsx, .ppt, .pptx" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea style="color: var(--black);" tabindex='1' ?autofocus=${true} ?disabled=${this.isLoading || this.isLoadingMessages} id="messageBox" rows="1"></textarea>
|
<textarea style="color: var(--black);" tabindex='1' ?autofocus=${true} ?disabled=${this.isLoading || this.isLoadingMessages} id="messageBox" rows="1"
|
||||||
|
|
||||||
|
>
|
||||||
|
|
||||||
|
></textarea>
|
||||||
<div id=${this.iframeId}
|
<div id=${this.iframeId}
|
||||||
class=${["element", this.iframeId === "privateMessage" ? "privateMessageMargin" : ""].join(" ")}
|
class=${["element", this.iframeId === "privateMessage" ? "privateMessageMargin" : ""].join(" ")}
|
||||||
></div>
|
></div>
|
||||||
@ -515,7 +522,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
|
|||||||
icon="vaadin:check"
|
icon="vaadin:check"
|
||||||
slot="icon"
|
slot="icon"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.sendMessageFunc();
|
this.sendMessageFunc(this.messageQueue);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
</vaadin-icon>
|
</vaadin-icon>
|
||||||
@ -538,7 +545,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
|
|||||||
alt="send-icon"
|
alt="send-icon"
|
||||||
class="send-icon"
|
class="send-icon"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.sendMessageFunc();
|
this.sendMessageFunc(this.messageQueue);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
` :
|
` :
|
||||||
@ -569,6 +576,29 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handlePasteEvent(e) {
|
||||||
|
if (e.type === 'paste') {
|
||||||
|
e.preventDefault();
|
||||||
|
const item_list = await navigator.clipboard.read();
|
||||||
|
let image_type; // we will feed this later
|
||||||
|
const item = item_list.find( item => // choose the one item holding our image
|
||||||
|
item.types.some( type => {
|
||||||
|
if (type.startsWith( 'image/')) {
|
||||||
|
image_type = type;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if(item){
|
||||||
|
const blob = item && await item.getType( image_type );
|
||||||
|
var file = new File([blob], "name", {
|
||||||
|
type: image_type
|
||||||
|
});
|
||||||
|
this.insertFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async firstUpdated() {
|
async firstUpdated() {
|
||||||
window.addEventListener('storage', () => {
|
window.addEventListener('storage', () => {
|
||||||
const checkTheme = localStorage.getItem('qortalTheme');
|
const checkTheme = localStorage.getItem('qortalTheme');
|
||||||
@ -648,7 +678,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.chatMessageSize = 0;
|
this.chatMessageSize = 0;
|
||||||
this._sendMessage(props, this.editor.getJSON());
|
this._sendMessage(props, this.editor.getJSON(), this.messageQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMessageSize(message){
|
getMessageSize(message){
|
||||||
|
@ -217,9 +217,9 @@ class ChatWelcomePage extends LitElement {
|
|||||||
<div
|
<div
|
||||||
class="start-chat"
|
class="start-chat"
|
||||||
@click="${() => this.setOpenPrivateMessage({
|
@click="${() => this.setOpenPrivateMessage({
|
||||||
name: "",
|
name: "",
|
||||||
open: true
|
open: true
|
||||||
})}">
|
})}">
|
||||||
${translate("welcomepage.wcchange2")}
|
${translate("welcomepage.wcchange2")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -240,9 +240,9 @@ class ChatWelcomePage extends LitElement {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<mwc-button ?disabled="${this.isLoading}" slot="primaryAction" @click=${() => {
|
<mwc-button ?disabled="${this.isLoading}" slot="primaryAction" @click=${() => {
|
||||||
this._sendMessage();
|
this._sendMessage();
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
${translate("welcomepage.wcchange6")}</mwc-button>
|
${translate("welcomepage.wcchange6")}</mwc-button>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
?disabled="${this.isLoading}"
|
?disabled="${this.isLoading}"
|
||||||
@ -304,11 +304,7 @@ class ChatWelcomePage extends LitElement {
|
|||||||
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
||||||
this.selectedAddress = selectedAddress
|
this.selectedAddress = selectedAddress
|
||||||
})
|
})
|
||||||
parentEpml.request('apiCall', {
|
|
||||||
url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}`
|
|
||||||
}).then(res => {
|
|
||||||
this.balance = res
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
parentEpml.imReady()
|
parentEpml.imReady()
|
||||||
|
@ -1,126 +1,133 @@
|
|||||||
import { LitElement, html, css } from 'lit'
|
import { LitElement, html, css } from 'lit';
|
||||||
import { render } from 'lit/html.js'
|
import { render } from 'lit/html.js';
|
||||||
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
|
import {
|
||||||
|
use,
|
||||||
|
get,
|
||||||
|
translate,
|
||||||
|
translateUnsafeHTML,
|
||||||
|
registerTranslateConfig,
|
||||||
|
} from 'lit-translate';
|
||||||
|
|
||||||
export class ImageComponent extends LitElement {
|
export class ImageComponent extends LitElement {
|
||||||
|
static get properties() {
|
||||||
static get properties() {
|
return {
|
||||||
return {
|
class: { type: String },
|
||||||
class: { type: String },
|
gif: { type: Object },
|
||||||
gif: { type: Object },
|
alt: { type: String },
|
||||||
alt: { type: String },
|
attempts: { type: Number },
|
||||||
attempts: { type: Number },
|
maxAttempts: { type: Number },
|
||||||
maxAttempts: { type: Number },
|
error: { type: Boolean },
|
||||||
error: { type: Boolean },
|
sendMessage: { attribute: false },
|
||||||
sendMessage: { attribute: false },
|
setOpenGifModal: { attribute: false },
|
||||||
setOpenGifModal: { attribute: false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
.gif-error-msg {
|
|
||||||
margin: 0;
|
|
||||||
font-family: Roboto, sans-serif;
|
|
||||||
font-size: 17px;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
color: var(--chat-bubble-msg-color);
|
|
||||||
font-weight: 300;
|
|
||||||
padding: 10px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-image {
|
|
||||||
border-radius: 15px;
|
|
||||||
background-color: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
height: 150px;
|
|
||||||
object-fit: cover;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
transition: all 0.2s cubic-bezier(0, 0.55, 0.45, 1);
|
|
||||||
box-shadow: rgb(50 50 93 / 25%) 0px 6px 12px -2px, rgb(0 0 0 / 30%) 0px 3px 7px -3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-image:hover {
|
|
||||||
border: 1px solid var(--mdc-theme-primary );
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.attempts = 0
|
|
||||||
this.maxAttempts = 5
|
|
||||||
}
|
|
||||||
getApiKey() {
|
|
||||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
|
||||||
let apiKey = myNode.apiKey
|
|
||||||
return apiKey
|
|
||||||
}
|
|
||||||
|
|
||||||
async _fetchImage() {
|
|
||||||
this.attempts++;
|
|
||||||
if (this.attempts > this.maxAttempts) return
|
|
||||||
await new Promise((res) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
res()
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
try {
|
|
||||||
const response = await fetch(this.gif.url)
|
|
||||||
const data = await response.json()
|
|
||||||
if (data.ok) {
|
|
||||||
this.error = false
|
|
||||||
this.gif = {
|
|
||||||
...this.gif,
|
|
||||||
url: data.src
|
|
||||||
};
|
};
|
||||||
this.requestUpdate();
|
}
|
||||||
} else if (!data.ok || data.error) {
|
|
||||||
this.error = true
|
static get styles() {
|
||||||
} else {
|
return css`
|
||||||
this.error = false
|
.gif-error-msg {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
font-size: 17px;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
color: var(--chat-bubble-msg-color);
|
||||||
|
font-weight: 300;
|
||||||
|
padding: 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gif-image {
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
transition: all 0.2s cubic-bezier(0, 0.55, 0.45, 1);
|
||||||
|
box-shadow: rgb(50 50 93 / 25%) 0px 6px 12px -2px,
|
||||||
|
rgb(0 0 0 / 30%) 0px 3px 7px -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gif-image:hover {
|
||||||
|
border: 1px solid var(--mdc-theme-primary);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attempts = 0;
|
||||||
|
this.maxAttempts = 5;
|
||||||
|
}
|
||||||
|
getApiKey() {
|
||||||
|
const myNode =
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||||
|
];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _fetchImage() {
|
||||||
|
this.attempts++;
|
||||||
|
if (this.attempts > this.maxAttempts) return;
|
||||||
|
await new Promise((res) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
res();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.gif.url);
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.ok) {
|
||||||
|
this.error = false;
|
||||||
|
this.gif = {
|
||||||
|
...this.gif,
|
||||||
|
url: data.src,
|
||||||
|
};
|
||||||
|
this.requestUpdate();
|
||||||
|
} else if (!data.ok || data.error) {
|
||||||
|
this.error = true;
|
||||||
|
} else {
|
||||||
|
this.error = false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.error = true;
|
||||||
|
console.error(error);
|
||||||
|
this._fetchImage();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}
|
||||||
this.error = true
|
|
||||||
console.error(error)
|
render() {
|
||||||
this._fetchImage()
|
if (this.error && this.attempts <= this.maxAttempts) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this._fetchImage();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
return html` ${this.gif && !this.error
|
||||||
|
? html` <img
|
||||||
|
class=${this.class}
|
||||||
|
src=${this.gif.url}
|
||||||
|
alt=${this.alt}
|
||||||
|
@click=${() => {
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'gif',
|
||||||
|
identifier: this.gif.identifier,
|
||||||
|
name: this.gif.name,
|
||||||
|
filePath: this.gif.filePath,
|
||||||
|
service: 'GIF_REPOSITORY',
|
||||||
|
});
|
||||||
|
this.setOpenGifModal(false);
|
||||||
|
}}
|
||||||
|
@error=${this._fetchImage}
|
||||||
|
/>`
|
||||||
|
: this.error && this.attempts <= this.maxAttempts
|
||||||
|
? html`
|
||||||
|
<p class="gif-error-msg">${translate('gifs.gchange15')}</p>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<p class="gif-error-msg">${translate('gifs.gchange16')}</p>
|
||||||
|
`}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
customElements.define('image-component', ImageComponent);
|
||||||
if (this.error && this.attempts <= this.maxAttempts) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this._fetchImage()
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
return html`
|
|
||||||
${this.gif && !this.error
|
|
||||||
? html`
|
|
||||||
<img
|
|
||||||
class=${this.class}
|
|
||||||
src=${this.gif.url}
|
|
||||||
alt=${this.alt}
|
|
||||||
@click=${() => {
|
|
||||||
this.sendMessage({
|
|
||||||
type: 'gif',
|
|
||||||
identifier: this.gif.identifier,
|
|
||||||
name: this.gif.name,
|
|
||||||
filePath: this.gif.filePath,
|
|
||||||
service: "GIF_REPOSITORY"
|
|
||||||
})
|
|
||||||
this.setOpenGifModal(false);
|
|
||||||
}}
|
|
||||||
@error=${this._fetchImage}
|
|
||||||
/>`
|
|
||||||
: this.error && this.attempts <= this.maxAttempts ? html`
|
|
||||||
<p class='gif-error-msg'>${translate('gifs.gchange15')}</p>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<p class='gif-error-msg'>${translate('gifs.gchange16')}</p>
|
|
||||||
`
|
|
||||||
}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('image-component', ImageComponent)
|
|
||||||
|
@ -4,14 +4,17 @@ import { Epml } from '../../../epml.js'
|
|||||||
import snackbar from './snackbar.js'
|
import snackbar from './snackbar.js'
|
||||||
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
|
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
|
||||||
import '@polymer/paper-tooltip/paper-tooltip.js'
|
import '@polymer/paper-tooltip/paper-tooltip.js'
|
||||||
|
import { RequestQueue } from '../../utils/queue.js'
|
||||||
|
|
||||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
|
const queue = new RequestQueue(3);
|
||||||
|
|
||||||
|
|
||||||
class LevelFounder extends LitElement {
|
class LevelFounder extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
checkleveladdress: { type: String },
|
checkleveladdress: { type: String },
|
||||||
selectedAddress: { type: String },
|
|
||||||
config: { type: Object },
|
config: { type: Object },
|
||||||
memberInfo: { type: Array }
|
memberInfo: { type: Array }
|
||||||
}
|
}
|
||||||
@ -39,7 +42,7 @@ class LevelFounder extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h2, h3, h4, h5 {
|
h2, h3, h4, h5 {
|
||||||
color:# var(--black);
|
color: var(--black);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +91,6 @@ class LevelFounder extends LitElement {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.memberInfo = []
|
this.memberInfo = []
|
||||||
this.selectedAddress = window.parent.reduxStore.getState().app.selectedAddress.address
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -101,42 +103,37 @@ class LevelFounder extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
this.checkAddressInfo()
|
queue.push(() => this.checkAddressInfo());
|
||||||
|
|
||||||
parentEpml.ready().then(() => {
|
|
||||||
parentEpml.subscribe('selected_address', async selectedAddress => {
|
|
||||||
this.selectedAddress = {}
|
|
||||||
selectedAddress = JSON.parse(selectedAddress)
|
|
||||||
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
|
||||||
this.selectedAddress = selectedAddress
|
|
||||||
})
|
|
||||||
})
|
|
||||||
parentEpml.imReady()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkAddressInfo() {
|
async checkAddressInfo() {
|
||||||
let toCheck = this.checkleveladdress
|
try {
|
||||||
const memberInfo = await parentEpml.request('apiCall', {
|
let toCheck = this.checkleveladdress
|
||||||
url: `/addresses/${toCheck}`
|
const memberInfo = await parentEpml.request('apiCall', {
|
||||||
})
|
url: `/addresses/${toCheck}`
|
||||||
this.memberInfo = memberInfo
|
})
|
||||||
|
this.memberInfo = memberInfo
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFounder() {
|
renderFounder() {
|
||||||
let adressfounder = this.memberInfo.flags
|
let adressfounder = this.memberInfo.flags
|
||||||
if (adressfounder === 1) {
|
if (adressfounder === 1) {
|
||||||
return html `
|
return html`
|
||||||
<span id="founderTooltip" class="badge">F</span>
|
<span id="founderTooltip" class="badge">F</span>
|
||||||
<paper-tooltip class="custom" for="founderTooltip" position="top">FOUNDER</paper-tooltip>
|
<paper-tooltip class="custom" for="founderTooltip" position="top">FOUNDER</paper-tooltip>
|
||||||
`
|
`
|
||||||
} else {
|
} else {
|
||||||
return html ``
|
return html``
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLevel() {
|
renderLevel() {
|
||||||
let adresslevel = this.memberInfo.level
|
let adresslevel = this.memberInfo.level
|
||||||
return adresslevel ? html `
|
return adresslevel ? html`
|
||||||
<img id="level-img" src=${`/img/badges/level-${adresslevel}.png`} alt=${`badge-${adresslevel}`} class="message-data-level" />
|
<img id="level-img" src=${`/img/badges/level-${adresslevel}.png`} alt=${`badge-${adresslevel}`} class="message-data-level" />
|
||||||
<paper-tooltip class="level-img-tooltip" for="level-img" position="top" >
|
<paper-tooltip class="level-img-tooltip" for="level-img" position="top" >
|
||||||
${translate("mintingpage.mchange27")} ${adresslevel}
|
${translate("mintingpage.mchange27")} ${adresslevel}
|
||||||
|
@ -228,9 +228,9 @@ class NameMenu extends LitElement {
|
|||||||
<textarea class="textarea" @keydown=${(e) => this._textArea(e)} ?disabled=${this.isLoading} id="messageBox" placeholder="${translate("welcomepage.wcchange5")}" rows="1"></textarea>
|
<textarea class="textarea" @keydown=${(e) => this._textArea(e)} ?disabled=${this.isLoading} id="messageBox" placeholder="${translate("welcomepage.wcchange5")}" rows="1"></textarea>
|
||||||
</p>
|
</p>
|
||||||
<mwc-button ?disabled="${this.isLoading}" slot="primaryAction" @click=${() => {
|
<mwc-button ?disabled="${this.isLoading}" slot="primaryAction" @click=${() => {
|
||||||
this._sendMessage();
|
this._sendMessage();
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
${translate("welcomepage.wcchange6")}</mwc-button>
|
${translate("welcomepage.wcchange6")}</mwc-button>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
?disabled="${this.isLoading}"
|
?disabled="${this.isLoading}"
|
||||||
@ -247,11 +247,11 @@ class NameMenu extends LitElement {
|
|||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
this.getChatBlockedAdresses()
|
this.getChatBlockedAdresses()
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.getChatBlockedAdresses()
|
this.getChatBlockedAdresses()
|
||||||
}, 60000)
|
}, 60000)
|
||||||
|
|
||||||
window.onclick = function(event) {
|
window.onclick = function (event) {
|
||||||
if (!event.target.matches('.block')) {
|
if (!event.target.matches('.block')) {
|
||||||
var dropdowns = document.getElementsByClassName('dropdown-content');
|
var dropdowns = document.getElementsByClassName('dropdown-content');
|
||||||
var i;
|
var i;
|
||||||
@ -290,11 +290,7 @@ class NameMenu extends LitElement {
|
|||||||
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
||||||
this.selectedAddress = selectedAddress
|
this.selectedAddress = selectedAddress
|
||||||
})
|
})
|
||||||
parentEpml.request('apiCall', {
|
|
||||||
url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}`
|
|
||||||
}).then(res => {
|
|
||||||
this.balance = res
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
parentEpml.imReady()
|
parentEpml.imReady()
|
||||||
}
|
}
|
||||||
@ -333,7 +329,7 @@ class NameMenu extends LitElement {
|
|||||||
|
|
||||||
relMessages() {
|
relMessages() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = window.location.href.split( '#' )[0]
|
window.location.href = window.location.href.split('#')[0]
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,8 +403,8 @@ class NameMenu extends LitElement {
|
|||||||
fetch(`${nodeUrl}/names/address/${item}?limit=0&reverse=true`).then(res => {
|
fetch(`${nodeUrl}/names/address/${item}?limit=0&reverse=true`).then(res => {
|
||||||
return res.json()
|
return res.json()
|
||||||
}).then(jsonRes => {
|
}).then(jsonRes => {
|
||||||
if(jsonRes.length) {
|
if (jsonRes.length) {
|
||||||
jsonRes.map (item => {
|
jsonRes.map(item => {
|
||||||
obj.push(item)
|
obj.push(item)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
332
plugins/plugins/core/components/ReusableImage.js
Normal file
332
plugins/plugins/core/components/ReusableImage.js
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
import { LitElement, html, css } from 'lit';
|
||||||
|
import {
|
||||||
|
translate,
|
||||||
|
} from 'lit-translate';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { RequestQueueWithPromise } from '../../utils/queue';
|
||||||
|
import '@material/mwc-menu';
|
||||||
|
import '@material/mwc-list/mwc-list-item.js';
|
||||||
|
import '@material/mwc-dialog'
|
||||||
|
|
||||||
|
const requestQueue = new RequestQueueWithPromise(5);
|
||||||
|
const requestQueue2 = new RequestQueueWithPromise(5);
|
||||||
|
|
||||||
|
|
||||||
|
export class ResuableImage extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
resource: { type: Object },
|
||||||
|
isReady: { type: Boolean },
|
||||||
|
status: { type: Object },
|
||||||
|
missingData: {type: Boolean},
|
||||||
|
openDialogImage: { type: Boolean },
|
||||||
|
onLoad: {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;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.smallLoading,
|
||||||
|
.smallLoading:after {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 2px;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallLoading {
|
||||||
|
border-width: 0.8em;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2)
|
||||||
|
rgba(3, 169, 244, 0.2) rgb(3, 169, 244);
|
||||||
|
font-size: 30px;
|
||||||
|
position: relative;
|
||||||
|
text-indent: -9999em;
|
||||||
|
transform: translateZ(0px);
|
||||||
|
animation: 1.1s linear 0s infinite normal none running
|
||||||
|
loadingAnimation;
|
||||||
|
}
|
||||||
|
.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.resource = {
|
||||||
|
identifier: '',
|
||||||
|
name: '',
|
||||||
|
service: '',
|
||||||
|
};
|
||||||
|
this.status = {
|
||||||
|
status: '',
|
||||||
|
};
|
||||||
|
this.url = '';
|
||||||
|
this.isReady = false;
|
||||||
|
this.nodeUrl = this.getNodeUrl();
|
||||||
|
this.myNode = this.getMyNode();
|
||||||
|
this.hasCalledWhenDownloaded = false;
|
||||||
|
this.isFetching = false;
|
||||||
|
this.missingData = false
|
||||||
|
this.openDialogImage = false
|
||||||
|
|
||||||
|
this.observer = new IntersectionObserver((entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.isIntersecting && this.status.status !== 'READY') {
|
||||||
|
this._fetchImage();
|
||||||
|
// Stop observing after the image has started loading
|
||||||
|
this.observer.unobserve(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getNodeUrl() {
|
||||||
|
const myNode =
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||||
|
];
|
||||||
|
|
||||||
|
const nodeUrl =
|
||||||
|
myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||||
|
return nodeUrl;
|
||||||
|
}
|
||||||
|
getMyNode() {
|
||||||
|
const myNode =
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||||
|
];
|
||||||
|
|
||||||
|
return myNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode =
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||||
|
];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchResource() {
|
||||||
|
try {
|
||||||
|
if (this.isFetching) return;
|
||||||
|
this.isFetching = true;
|
||||||
|
|
||||||
|
await requestQueue2.enqueue(() => {
|
||||||
|
return axios.get(
|
||||||
|
`${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
this.isFetching = false;
|
||||||
|
} catch (error) {
|
||||||
|
this.isFetching = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchVideoUrl() {
|
||||||
|
this.fetchResource();
|
||||||
|
this.url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?async=true&apiKey=${this.myNode.apiKey}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchStatus() {
|
||||||
|
let isCalling = false;
|
||||||
|
let percentLoaded = 0;
|
||||||
|
let timer = 24;
|
||||||
|
const response = await axios.get(
|
||||||
|
`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||||
|
);
|
||||||
|
if (response && response.data && response.data.status === 'READY') {
|
||||||
|
this.status = response.data;
|
||||||
|
this.onLoad()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (isCalling) return;
|
||||||
|
isCalling = true;
|
||||||
|
|
||||||
|
const data = await requestQueue.enqueue(() => {
|
||||||
|
return axios.get(
|
||||||
|
`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const res = data.data;
|
||||||
|
|
||||||
|
isCalling = false;
|
||||||
|
if (res.localChunkCount) {
|
||||||
|
if (res.percentLoaded) {
|
||||||
|
if (
|
||||||
|
res.percentLoaded === percentLoaded &&
|
||||||
|
res.percentLoaded !== 100
|
||||||
|
) {
|
||||||
|
timer = timer - 5;
|
||||||
|
} else {
|
||||||
|
timer = 24;
|
||||||
|
}
|
||||||
|
if (timer < 0) {
|
||||||
|
timer = 24;
|
||||||
|
isCalling = true;
|
||||||
|
this.status = {
|
||||||
|
...res,
|
||||||
|
status: 'REFETCHING',
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
isCalling = false;
|
||||||
|
this.fetchResource();
|
||||||
|
}, 25000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
percentLoaded = res.percentLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = res;
|
||||||
|
if (this.status.status === 'DOWNLOADED') {
|
||||||
|
this.fetchResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if progress is 100% and clear interval if true
|
||||||
|
if (res.status === 'READY') {
|
||||||
|
this.onLoad()
|
||||||
|
clearInterval(intervalId);
|
||||||
|
this.status = res;
|
||||||
|
this.isReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res.status === 'MISSING_DATA'){
|
||||||
|
this.status = res
|
||||||
|
this.missingData = true
|
||||||
|
clearInterval(intervalId)
|
||||||
|
}
|
||||||
|
}, 5000); // 1 second interval
|
||||||
|
}
|
||||||
|
|
||||||
|
async _fetchImage() {
|
||||||
|
try {
|
||||||
|
this.fetchVideoUrl({
|
||||||
|
name: this.resource.name,
|
||||||
|
service: this.resource.service,
|
||||||
|
identifier: this.resource.identifier,
|
||||||
|
});
|
||||||
|
this.fetchStatus();
|
||||||
|
} catch (error) { /* empty */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.observer.observe(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
showContextMenu(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const contextMenu = this.shadowRoot.getElementById('contextMenu');
|
||||||
|
const containerRect = e.currentTarget.getBoundingClientRect();
|
||||||
|
|
||||||
|
// Adjusting the positions
|
||||||
|
const adjustedX = e.clientX - containerRect.left;
|
||||||
|
const adjustedY = e.clientY - containerRect.top;
|
||||||
|
|
||||||
|
contextMenu.style.top = `${adjustedY}px`;
|
||||||
|
contextMenu.style.left = `${adjustedX}px`;
|
||||||
|
|
||||||
|
contextMenu.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
${this.status.status !== 'READY'
|
||||||
|
? html`
|
||||||
|
<div
|
||||||
|
style="display:flex;flex-direction:column;width:100%;height:100%;justify-content:center;align-items:center;"
|
||||||
|
>
|
||||||
|
<div class=${`smallLoading`}></div>
|
||||||
|
<p style="color: var(--black)">
|
||||||
|
${`${Math.round(
|
||||||
|
this.status.percentLoaded || 0
|
||||||
|
).toFixed(0)}% `}${translate(
|
||||||
|
'chatpage.cchange94'
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''}
|
||||||
|
${this.status.status === 'READY'
|
||||||
|
? html`
|
||||||
|
<div style="position:relative; cursor:pointer" @click=${()=> {
|
||||||
|
this.openDialogImage = true;
|
||||||
|
}}>
|
||||||
|
<img crossorigin="anonymous" src=${this.url} />
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mwc-dialog
|
||||||
|
id="showDialogPublicKey"
|
||||||
|
?open=${this.openDialogImage}
|
||||||
|
@closed=${() => {
|
||||||
|
this.openDialogImage = false;
|
||||||
|
}}>
|
||||||
|
<div class="dialog-header"></div>
|
||||||
|
<div class="dialog-container imageContainer">
|
||||||
|
${this.openDialogImage ? html`
|
||||||
|
<img src=${this.url} style="height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;"/>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
dialogAction="cancel"
|
||||||
|
class="red"
|
||||||
|
@click=${() => {
|
||||||
|
this.openDialogImage = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${translate('general.close')}
|
||||||
|
</mwc-button>
|
||||||
|
</mwc-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('reusable-image', ResuableImage);
|
@ -24,7 +24,7 @@ export class TipUser extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.sendMoneyLoading = false
|
this.sendMoneyLoading = false
|
||||||
this.btnDisable = false
|
this.btnDisable = false
|
||||||
this.errorMessage = ""
|
this.errorMessage = ""
|
||||||
@ -95,167 +95,140 @@ export class TipUser extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchWalletDetails() {
|
async fetchWalletDetails() {
|
||||||
await parentEpml.request('apiCall', {
|
|
||||||
url: `/addresses/balance/${this.myAddress.address}?apiKey=${this.getApiKey()}`,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (isNaN(Number(res))) {
|
|
||||||
let snack4string = get("chatpage.cchange48")
|
|
||||||
parentEpml.request('showSnackBar', `${snack4string}`)
|
|
||||||
} else {
|
|
||||||
this.walletBalance = Number(res).toFixed(8)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendQort() {
|
async sendQort() {
|
||||||
const amount = this.shadowRoot.getElementById("amountInput").value
|
const amount = this.shadowRoot.getElementById("amountInput").value;
|
||||||
let recipient = this.userName
|
const recipient = this.userName;
|
||||||
this.sendMoneyLoading = true
|
|
||||||
this.btnDisable = true
|
this.sendMoneyLoading = true;
|
||||||
|
this.btnDisable = true;
|
||||||
if (parseFloat(amount) + parseFloat(0.011) > parseFloat(this.walletBalance)) {
|
|
||||||
this.sendMoneyLoading = false
|
// Helper function to reset loading and button state
|
||||||
this.btnDisable = false
|
const resetState = () => {
|
||||||
let snack1string = get("chatpage.cchange51")
|
this.sendMoneyLoading = false;
|
||||||
parentEpml.request('showSnackBar', `${snack1string}`)
|
this.btnDisable = false;
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parseFloat(amount) <= 0) {
|
|
||||||
this.sendMoneyLoading = false
|
|
||||||
this.btnDisable = false
|
|
||||||
let snack2string = get("chatpage.cchange52")
|
|
||||||
parentEpml.request('showSnackBar', `${snack2string}`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recipient.length === 0) {
|
|
||||||
this.sendMoneyLoading = false
|
|
||||||
this.btnDisable = false
|
|
||||||
let snack3string = get("chatpage.cchange53")
|
|
||||||
parentEpml.request('showSnackBar', `${snack3string}`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateName = async (receiverName) => {
|
|
||||||
let myRes
|
|
||||||
let myNameRes = await parentEpml.request('apiCall', {
|
|
||||||
type: 'api',
|
|
||||||
url: `/names/${receiverName}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (myNameRes.error === 401) {
|
|
||||||
myRes = false
|
|
||||||
} else {
|
|
||||||
myRes = myNameRes
|
|
||||||
}
|
}
|
||||||
return myRes
|
|
||||||
}
|
if (parseFloat(amount) + parseFloat(0.011) > parseFloat(this.walletBalance)) {
|
||||||
|
resetState();
|
||||||
const validateAddress = async (receiverAddress) => {
|
const snack1string = get("chatpage.cchange51");
|
||||||
let myAddress = await window.parent.validateAddress(receiverAddress)
|
parentEpml.request('showSnackBar', `${snack1string}`);
|
||||||
return myAddress
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
const validateReceiver = async (recipient) => {
|
|
||||||
let lastRef = await this.getLastRef()
|
|
||||||
let theFee = await this.getSendQortFee()
|
|
||||||
let isAddress
|
|
||||||
|
|
||||||
try {
|
|
||||||
isAddress = await validateAddress(recipient)
|
|
||||||
} catch (err) {
|
|
||||||
isAddress = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAddress) {
|
if (parseFloat(amount) <= 0) {
|
||||||
let myTransaction = await makeTransactionRequest(recipient, lastRef, theFee)
|
resetState();
|
||||||
getTxnRequestResponse(myTransaction)
|
const snack2string = get("chatpage.cchange52");
|
||||||
} else {
|
parentEpml.request('showSnackBar', `${snack2string}`);
|
||||||
let myNameRes = await validateName(recipient)
|
return false;
|
||||||
if (myNameRes !== false) {
|
|
||||||
let myNameAddress = myNameRes.owner
|
|
||||||
let myTransaction = await makeTransactionRequest(myNameAddress, lastRef, theFee)
|
|
||||||
getTxnRequestResponse(myTransaction)
|
|
||||||
} else {
|
|
||||||
console.error(this.renderReceiverText())
|
|
||||||
this.errorMessage = this.renderReceiverText()
|
|
||||||
this.sendMoneyLoading = false
|
|
||||||
this.btnDisable = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (recipient.length === 0) {
|
||||||
const getName = async (recipient)=> {
|
resetState();
|
||||||
try {
|
const snack3string = get("chatpage.cchange53");
|
||||||
const getNames = await parentEpml.request("apiCall", {
|
parentEpml.request('showSnackBar', `${snack3string}`);
|
||||||
type: "api",
|
return false;
|
||||||
url: `/names/address/${recipient}`,
|
}
|
||||||
|
|
||||||
|
const validateName = async (receiverName) => {
|
||||||
|
const myNameRes = await parentEpml.request('apiCall', {
|
||||||
|
type: 'api',
|
||||||
|
url: `/names/${receiverName}`
|
||||||
});
|
});
|
||||||
|
return myNameRes.error === 401 ? false : myNameRes;
|
||||||
if (getNames?.length > 0 ) {
|
};
|
||||||
return getNames[0].name
|
|
||||||
} else {
|
const validateAddress = async (receiverAddress) => {
|
||||||
return ''
|
return await window.parent.validateAddress(receiverAddress);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getName = async (recipient) => {
|
||||||
|
try {
|
||||||
|
const getNames = await parentEpml.request("apiCall", {
|
||||||
|
type: "api",
|
||||||
|
url: `/names/address/${recipient}`
|
||||||
|
});
|
||||||
|
return getNames?.length > 0 ? getNames[0].name : '';
|
||||||
|
} catch (error) {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
} catch (error) {
|
};
|
||||||
return ""
|
|
||||||
}
|
const makeTransactionRequest = async (receiver, lastRef) => {
|
||||||
}
|
const dialogAmount = get("transactions.amount");
|
||||||
|
const dialogAddress = get("login.address");
|
||||||
const makeTransactionRequest = async (receiver, lastRef, theFee) => {
|
const dialogName = get("login.name");
|
||||||
let myReceiver = receiver
|
const dialogTo = get("transactions.to");
|
||||||
let mylastRef = lastRef
|
const recipientName = await getName(receiver);
|
||||||
let myFee = theFee
|
|
||||||
let dialogamount = get("transactions.amount")
|
return await parentEpml.request('transaction', {
|
||||||
let dialogAddress = get("login.address")
|
type: 2,
|
||||||
let dialogName = get("login.name")
|
nonce: this.myAddress.nonce,
|
||||||
let dialogto = get("transactions.to")
|
params: {
|
||||||
let recipientName = await getName(myReceiver)
|
recipient: receiver,
|
||||||
let myTxnrequest = await parentEpml.request('transaction', {
|
recipientName: recipientName,
|
||||||
type: 2,
|
amount: amount,
|
||||||
nonce: this.myAddress.nonce,
|
lastReference: lastRef,
|
||||||
params: {
|
fee: this.qortPaymentFee,
|
||||||
recipient: myReceiver,
|
dialogAmount,
|
||||||
recipientName: recipientName,
|
dialogTo,
|
||||||
amount: amount,
|
dialogAddress,
|
||||||
lastReference: mylastRef,
|
dialogName
|
||||||
fee: myFee,
|
}
|
||||||
dialogamount: dialogamount,
|
});
|
||||||
dialogto: dialogto,
|
};
|
||||||
dialogAddress,
|
|
||||||
dialogName
|
const getTxnRequestResponse = (txnResponse) => {
|
||||||
},
|
if (txnResponse.success === false && txnResponse.message) {
|
||||||
})
|
this.errorMessage = txnResponse.message;
|
||||||
return myTxnrequest
|
resetState();
|
||||||
}
|
throw new Error(txnResponse);
|
||||||
|
} else if (txnResponse.success === true && !txnResponse.data.error) {
|
||||||
const getTxnRequestResponse = (txnResponse) => {
|
this.shadowRoot.getElementById('amountInput').value = '';
|
||||||
if (txnResponse.success === false && txnResponse.message) {
|
this.errorMessage = '';
|
||||||
this.errorMessage = txnResponse.message
|
this.successMessage = this.renderSuccessText();
|
||||||
this.sendMoneyLoading = false
|
resetState();
|
||||||
this.btnDisable = false
|
setTimeout(() => {
|
||||||
throw new Error(txnResponse)
|
this.setOpenTipUser(false);
|
||||||
} else if (txnResponse.success === true && !txnResponse.data.error) {
|
this.successMessage = "";
|
||||||
this.shadowRoot.getElementById('amountInput').value = ''
|
}, 3000);
|
||||||
this.errorMessage = ''
|
} else {
|
||||||
this.successMessage = this.renderSuccessText()
|
this.errorMessage = txnResponse.data.message;
|
||||||
this.sendMoneyLoading = false
|
resetState();
|
||||||
this.btnDisable = false
|
throw new Error(txnResponse);
|
||||||
setTimeout(() => {
|
}
|
||||||
this.setOpenTipUser(false)
|
};
|
||||||
this.successMessage = ""
|
|
||||||
}, 3000)
|
const validateReceiver = async (recipient) => {
|
||||||
} else {
|
let lastRef = await this.getLastRef();
|
||||||
this.errorMessage = txnResponse.data.message
|
let isAddress;
|
||||||
this.sendMoneyLoading = false
|
|
||||||
this.btnDisable = false
|
try {
|
||||||
throw new Error(txnResponse)
|
isAddress = await validateAddress(recipient);
|
||||||
}
|
} catch (err) {
|
||||||
}
|
isAddress = false;
|
||||||
validateReceiver(recipient)
|
}
|
||||||
|
|
||||||
|
if (isAddress) {
|
||||||
|
const myTransaction = await makeTransactionRequest(recipient, lastRef);
|
||||||
|
getTxnRequestResponse(myTransaction);
|
||||||
|
} else {
|
||||||
|
const myNameRes = await validateName(recipient);
|
||||||
|
if (myNameRes !== false) {
|
||||||
|
const myTransaction = await makeTransactionRequest(myNameRes.owner, lastRef);
|
||||||
|
getTxnRequestResponse(myTransaction);
|
||||||
|
} else {
|
||||||
|
this.errorMessage = this.renderReceiverText();
|
||||||
|
resetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await validateReceiver(recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
@ -270,8 +243,8 @@ export class TipUser extends LitElement {
|
|||||||
${this.sendMoneyLoading ?
|
${this.sendMoneyLoading ?
|
||||||
html`
|
html`
|
||||||
<paper-progress indeterminate style="width: 100%; margin: 4px;">
|
<paper-progress indeterminate style="width: 100%; margin: 4px;">
|
||||||
</paper-progress>`
|
</paper-progress>`
|
||||||
: html`
|
: html`
|
||||||
<div style=${"text-align: center;"}>
|
<div style=${"text-align: center;"}>
|
||||||
<vaadin-button
|
<vaadin-button
|
||||||
?disabled=${this.btnDisable}
|
?disabled=${this.btnDisable}
|
||||||
@ -284,21 +257,21 @@ export class TipUser extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
|
||||||
${this.successMessage ?
|
${this.successMessage ?
|
||||||
html`
|
html`
|
||||||
<p class="success-msg">
|
<p class="success-msg">
|
||||||
${this.successMessage}
|
${this.successMessage}
|
||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
: this.errorMessage ?
|
: this.errorMessage ?
|
||||||
html`
|
html`
|
||||||
<p class="error-msg">
|
<p class="error-msg">
|
||||||
${this.errorMessage}
|
${this.errorMessage}
|
||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define('tip-user', TipUser)
|
customElements.define('tip-user', TipUser)
|
||||||
|
2871
plugins/plugins/core/components/webworkerDecodeMessages.js
Normal file
2871
plugins/plugins/core/components/webworkerDecodeMessages.js
Normal file
File diff suppressed because it is too large
Load Diff
15
plugins/plugins/core/components/webworkerSortMessages.js
Normal file
15
plugins/plugins/core/components/webworkerSortMessages.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.addEventListener('message', async e => {
|
||||||
|
const response = e.data.list.sort(function (a, b) {
|
||||||
|
return a.timestamp
|
||||||
|
- b.timestamp
|
||||||
|
})
|
||||||
|
postMessage(response)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1743,6 +1743,12 @@ class GroupManagement extends LitElement {
|
|||||||
})
|
})
|
||||||
return joinedG
|
return joinedG
|
||||||
}
|
}
|
||||||
|
const getGroupInfo = async (groupId) => {
|
||||||
|
let joinedG = await parentEpml.request('apiCall', {
|
||||||
|
url: `/groups/${groupId}`
|
||||||
|
})
|
||||||
|
return joinedG
|
||||||
|
}
|
||||||
|
|
||||||
const getGroupInvites = async () => {
|
const getGroupInvites = async () => {
|
||||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||||
@ -1839,6 +1845,25 @@ class GroupManagement extends LitElement {
|
|||||||
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
||||||
this.selectedAddress = selectedAddress
|
this.selectedAddress = selectedAddress
|
||||||
})
|
})
|
||||||
|
parentEpml.subscribe('side_effect_action', async sideEffectActionParam => {
|
||||||
|
const sideEffectAction = JSON.parse(sideEffectActionParam)
|
||||||
|
|
||||||
|
if(sideEffectAction && sideEffectAction.type === 'openJoinGroupModal'){
|
||||||
|
const res = await getGroupInfo(sideEffectAction.data)
|
||||||
|
if(res && res.groupId){
|
||||||
|
if(res.isOpen){
|
||||||
|
this.joinGroup(res)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let snackbarstring = get("managegroup.mg45")
|
||||||
|
parentEpml.request('showSnackBar', `${snackbarstring}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.parent.reduxStore.dispatch(
|
||||||
|
window.parent.reduxAction.setSideEffectAction(null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
parentEpml.subscribe('config', c => {
|
parentEpml.subscribe('config', c => {
|
||||||
if (!configLoaded) {
|
if (!configLoaded) {
|
||||||
setTimeout(getOpen_JoinedGroups, 1)
|
setTimeout(getOpen_JoinedGroups, 1)
|
||||||
@ -2849,6 +2874,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) {
|
async _joinGroup(groupId, groupName) {
|
||||||
this.resetDefaultSettings()
|
this.resetDefaultSettings()
|
||||||
const joinFeeInput = this.joinFee
|
const joinFeeInput = this.joinFee
|
||||||
@ -2885,7 +2921,8 @@ class GroupManagement extends LitElement {
|
|||||||
lastReference: lastRef,
|
lastReference: lastRef,
|
||||||
groupdialog1: groupdialog1,
|
groupdialog1: groupdialog1,
|
||||||
groupdialog2: groupdialog2
|
groupdialog2: groupdialog2
|
||||||
}
|
},
|
||||||
|
apiVersion: 2
|
||||||
})
|
})
|
||||||
return myTxnrequest
|
return myTxnrequest
|
||||||
}
|
}
|
||||||
@ -2897,6 +2934,12 @@ class GroupManagement extends LitElement {
|
|||||||
throw new Error(txnResponse)
|
throw new Error(txnResponse)
|
||||||
} else if (txnResponse.success === true && !txnResponse.data.error) {
|
} else if (txnResponse.success === true && !txnResponse.data.error) {
|
||||||
this.message = this.renderErr8Text()
|
this.message = this.renderErr8Text()
|
||||||
|
this.setTxNotification({
|
||||||
|
groupName,
|
||||||
|
groupId,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
...(txnResponse.data || {})
|
||||||
|
})
|
||||||
this.error = false
|
this.error = false
|
||||||
} else {
|
} else {
|
||||||
this.error = true
|
this.error = true
|
||||||
|
File diff suppressed because one or more lines are too long
@ -157,6 +157,10 @@ export const qchatStyles = css`
|
|||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
@ -172,13 +176,13 @@ export const qchatStyles = css`
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: none;
|
border: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 14px;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: var(--tradehead);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
flex: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.people-list .create-chat:hover {
|
.people-list .create-chat:hover {
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { LitElement, html, css } from 'lit'
|
import { LitElement, html } from 'lit'
|
||||||
import { render } from 'lit/html.js'
|
import { render } from 'lit/html.js'
|
||||||
import { passiveSupport } from 'passive-events-support/src/utils'
|
import { passiveSupport } from 'passive-events-support/src/utils'
|
||||||
import { Epml } from '../../../../epml.js'
|
import { Epml } from '../../../../epml.js'
|
||||||
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
|
import { get, translate } from 'lit-translate'
|
||||||
import { qchatStyles } from './q-chat-css.src.js'
|
import { qchatStyles } from './q-chat-css.src.js'
|
||||||
import { repeat } from 'lit/directives/repeat.js'
|
|
||||||
import { Editor, Extension } from '@tiptap/core'
|
import { Editor, Extension } from '@tiptap/core'
|
||||||
import isElectron from 'is-electron'
|
import isElectron from 'is-electron'
|
||||||
import WebWorker from 'web-worker:./computePowWorker.src.js'
|
import WebWorker from 'web-worker:./computePowWorker.src.js'
|
||||||
@ -13,19 +12,22 @@ import Underline from '@tiptap/extension-underline';
|
|||||||
import Placeholder from '@tiptap/extension-placeholder'
|
import Placeholder from '@tiptap/extension-placeholder'
|
||||||
import Highlight from '@tiptap/extension-highlight'
|
import Highlight from '@tiptap/extension-highlight'
|
||||||
import snackbar from '../../components/snackbar.js'
|
import snackbar from '../../components/snackbar.js'
|
||||||
|
import ShortUniqueId from 'short-unique-id';
|
||||||
|
|
||||||
import '../../components/ChatWelcomePage.js'
|
import '../../components/ChatWelcomePage.js'
|
||||||
import '../../components/ChatHead.js'
|
import '../../components/ChatHead.js'
|
||||||
import '../../components/ChatPage.js'
|
import '../../components/ChatPage.js'
|
||||||
import '../../components/WrapperModal.js'
|
import '../../components/WrapperModal.js'
|
||||||
import '../../components/ChatSearchResults.js'
|
import '../../components/ChatSearchResults.js'
|
||||||
|
import '../../components/ChatGroupsModal.js'
|
||||||
import '@material/mwc-button'
|
import '@material/mwc-button'
|
||||||
import '@material/mwc-dialog'
|
import '@material/mwc-dialog'
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
import '@material/mwc-snackbar'
|
import '@material/mwc-snackbar'
|
||||||
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||||
import '@vaadin/grid'
|
import '@vaadin/grid'
|
||||||
|
import '@vaadin/tooltip';
|
||||||
|
|
||||||
|
|
||||||
passiveSupport({ events: ['touchstart'] })
|
passiveSupport({ events: ['touchstart'] })
|
||||||
|
|
||||||
@ -53,15 +55,20 @@ class Chat extends LitElement {
|
|||||||
userFoundModalOpen: { type: Boolean },
|
userFoundModalOpen: { type: Boolean },
|
||||||
userSelected: { type: Object },
|
userSelected: { type: Object },
|
||||||
editor: {type: Object},
|
editor: {type: Object},
|
||||||
groupInvites: { type: Array }
|
groupInvites: { type: Array },
|
||||||
|
loggedInUserName: {type: String},
|
||||||
|
loggedInUserAddress: {type: String},
|
||||||
|
openDialogGroupsModal: {type: Boolean}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = [qchatStyles]
|
static get styles() {
|
||||||
|
return [qchatStyles];
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.selectedAddress = {}
|
this.selectedAddress = window.parent.reduxStore.getState().app.selectedAddress
|
||||||
this.config = {
|
this.config = {
|
||||||
user: {
|
user: {
|
||||||
node: {
|
node: {
|
||||||
@ -92,12 +99,15 @@ class Chat extends LitElement {
|
|||||||
this.userFoundModalOpen = false
|
this.userFoundModalOpen = false
|
||||||
this.userSelected = {}
|
this.userSelected = {}
|
||||||
this.groupInvites = []
|
this.groupInvites = []
|
||||||
|
this.loggedInUserName = ""
|
||||||
|
this.openDialogGroupsModal = false
|
||||||
|
this.uid = new ShortUniqueId();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setActiveChatHeadUrl(url) {
|
async setActiveChatHeadUrl(url) {
|
||||||
this.activeChatHeadUrl = ''
|
|
||||||
await this.updateComplete;
|
|
||||||
this.activeChatHeadUrl = url
|
this.activeChatHeadUrl = url
|
||||||
|
this.requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
resetChatEditor(){
|
resetChatEditor(){
|
||||||
@ -146,6 +156,8 @@ class Chat extends LitElement {
|
|||||||
|
|
||||||
this.unsubscribeStore = window.parent.reduxStore.subscribe(() => {
|
this.unsubscribeStore = window.parent.reduxStore.subscribe(() => {
|
||||||
try {
|
try {
|
||||||
|
const currentState = window.parent.reduxStore.getState();
|
||||||
|
|
||||||
if(window.parent.location && window.parent.location.search) {
|
if(window.parent.location && window.parent.location.search) {
|
||||||
const queryString = window.parent.location.search
|
const queryString = window.parent.location.search
|
||||||
const params = new URLSearchParams(queryString)
|
const params = new URLSearchParams(queryString)
|
||||||
@ -157,8 +169,13 @@ class Chat extends LitElement {
|
|||||||
this.setActiveChatHeadUrl(chat)
|
this.setActiveChatHeadUrl(chat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
if(currentState.app.accountInfo && currentState.app.accountInfo.names && currentState.app.accountInfo.names.length > 0 && this.loggedInUserName !== currentState.app.accountInfo.names[0].name){
|
||||||
}
|
this.loggedInUserName = currentState.app.accountInfo.names[0].name
|
||||||
|
}
|
||||||
|
if(currentState.app.accountInfo && currentState.app.accountInfo.addressInfo && currentState.app.accountInfo.addressInfo.address && this.loggedInUserAddress !== currentState.app.accountInfo.addressInfo.address){
|
||||||
|
this.loggedInUserAddress = currentState.app.accountInfo.addressInfo.address
|
||||||
|
}
|
||||||
|
} catch (error) { /* empty */ }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,44 +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() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="container clearfix">
|
<div class="container clearfix">
|
||||||
<div class="people-list" id="people-list">
|
<div class="people-list" id="people-list">
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<div class="create-chat" @click=${() => { this.openPrivateMessage = true }}>
|
<div id="openPrivateMessage" class="create-chat" @click=${() => { this.openPrivateMessage = true }}>
|
||||||
${translate("chatpage.cchange1")}
|
<mwc-icon style="color: var(--black);">edit_square</mwc-icon>
|
||||||
|
<vaadin-tooltip
|
||||||
|
for="openPrivateMessage"
|
||||||
|
position="top"
|
||||||
|
hover-delay=${200}
|
||||||
|
hide-delay=${1}
|
||||||
|
text=${get('chatpage.cchange1')}>
|
||||||
|
</vaadin-tooltip>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex; align-items:center;gap:10px">
|
||||||
|
|
||||||
|
<div id="goToGroup" class="create-chat" @click=${() => { this.openTabToGroupManagement() }}>
|
||||||
|
<mwc-icon style="color: var(--black);">group_add</mwc-icon>
|
||||||
|
<vaadin-tooltip
|
||||||
|
for="goToGroup"
|
||||||
|
position="top"
|
||||||
|
hover-delay=${200}
|
||||||
|
hide-delay=${1}
|
||||||
|
text=${get('chatpage.cchange96')}>
|
||||||
|
</vaadin-tooltip>
|
||||||
|
</div>
|
||||||
|
<div id="blockedUsers" class="create-chat" @click=${() => { this.shadowRoot.querySelector('#blockedUserDialog').show() }}>
|
||||||
|
<mwc-icon style="color: var(--black);">person_off</mwc-icon>
|
||||||
|
<vaadin-tooltip
|
||||||
|
for="blockedUsers"
|
||||||
|
position="top"
|
||||||
|
hover-delay=${200}
|
||||||
|
hide-delay=${1}
|
||||||
|
text=${get('chatpage.cchange3')}>
|
||||||
|
</vaadin-tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
${this.isEmptyArray(this.chatHeads) ? this.renderLoadingText() : this.renderChatHead(this.chatHeads)}
|
${this.isEmptyArray(this.chatHeads) ? this.renderLoadingText() : this.renderChatHead(this.chatHeads)}
|
||||||
</ul>
|
</ul>
|
||||||
<div class="blockedusers">
|
|
||||||
<!-- <div class="groups-button-container">
|
|
||||||
<button
|
|
||||||
@click=${() => { this.redirectToGroups() }}
|
|
||||||
class="groups-button">
|
|
||||||
<mwc-icon>groups</mwc-icon>
|
|
||||||
${translate("sidemenu.groupmanagement")}
|
|
||||||
</button>
|
|
||||||
${this.groupInvites.length > 0 ? (
|
|
||||||
html`
|
|
||||||
<div class="groups-button-notif">
|
|
||||||
${this.groupInvites.length}
|
|
||||||
</div>
|
|
||||||
<div class="groups-button-notif-number">
|
|
||||||
${this.groupInvites.length} ${translate("chatpage.cchange60")}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
) : null}
|
|
||||||
</div> -->
|
|
||||||
<mwc-button
|
|
||||||
raised
|
|
||||||
label="${translate("chatpage.cchange3")}"
|
|
||||||
icon="person_off"
|
|
||||||
@click=${() => this.shadowRoot.querySelector('#blockedUserDialog').show()}>
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="chat">
|
<div class="chat">
|
||||||
<div id="newMessageBar" class="new-message-bar hide-new-message-bar clearfix" @click=${() => this.scrollToBottom()}>
|
<div id="newMessageBar" class="new-message-bar hide-new-message-bar clearfix" @click=${() => this.scrollToBottom()}>
|
||||||
@ -370,7 +417,7 @@ class Chat extends LitElement {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
</mwc-dialog>
|
</mwc-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,23 +479,32 @@ class Chat extends LitElement {
|
|||||||
document.querySelector('html').setAttribute('theme', this.theme)
|
document.querySelector('html').setAttribute('theme', this.theme)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!isElectron()) {
|
if (!isElectron()) { /* empty */ } else {
|
||||||
} else {
|
|
||||||
window.addEventListener('contextmenu', (event) => {
|
window.addEventListener('contextmenu', (event) => {
|
||||||
event.preventDefault()
|
// Check if the clicked element has the class
|
||||||
window.parent.electronAPI.showMyMenu()
|
let target = event.target;
|
||||||
})
|
while (target !== null) {
|
||||||
|
if (target.classList && target.classList.contains('customContextMenuDiv')) {
|
||||||
|
// Your custom context menu logic
|
||||||
|
this.showContextMenu(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = target.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it doesn't, show the default Electron context menu
|
||||||
|
event.preventDefault();
|
||||||
|
window.parent.electronAPI.showMyMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let configLoaded = false
|
let configLoaded = false
|
||||||
|
|
||||||
parentEpml.ready().then(() => {
|
parentEpml.ready().then(() => {
|
||||||
parentEpml.subscribe('selected_address', async selectedAddress => {
|
|
||||||
this.selectedAddress = {}
|
|
||||||
selectedAddress = JSON.parse(selectedAddress)
|
|
||||||
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
|
|
||||||
this.selectedAddress = selectedAddress
|
|
||||||
})
|
|
||||||
parentEpml.subscribe('config', c => {
|
parentEpml.subscribe('config', c => {
|
||||||
if (!configLoaded) {
|
if (!configLoaded) {
|
||||||
setTimeout(getBlockedUsers, 1)
|
setTimeout(getBlockedUsers, 1)
|
||||||
@ -464,6 +520,7 @@ class Chat extends LitElement {
|
|||||||
url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}`
|
url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}`
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
this.balance = res
|
this.balance = res
|
||||||
|
this.requestUpdate()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
parentEpml.imReady()
|
parentEpml.imReady()
|
||||||
@ -473,9 +530,10 @@ class Chat extends LitElement {
|
|||||||
}, 60000)
|
}, 60000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
clearConsole() {
|
clearConsole() {
|
||||||
if (!isElectron()) {
|
if (!isElectron()) { /* empty */ } else {
|
||||||
} else {
|
|
||||||
console.clear()
|
console.clear()
|
||||||
window.parent.electronAPI.clearCache()
|
window.parent.electronAPI.clearCache()
|
||||||
}
|
}
|
||||||
@ -565,7 +623,7 @@ class Chat extends LitElement {
|
|||||||
recipient = _recipient
|
recipient = _recipient
|
||||||
} else {
|
} else {
|
||||||
recipient = myNameRes.owner
|
recipient = myNameRes.owner
|
||||||
};
|
}
|
||||||
|
|
||||||
const getAddressPublicKey = async () => {
|
const getAddressPublicKey = async () => {
|
||||||
let isEncrypted;
|
let isEncrypted;
|
||||||
@ -620,7 +678,7 @@ class Chat extends LitElement {
|
|||||||
const worker = new WebWorker()
|
const worker = new WebWorker()
|
||||||
let nonce = null
|
let nonce = null
|
||||||
let chatBytesArray = null;
|
let chatBytesArray = null;
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res) => {
|
||||||
worker.postMessage({chatBytes, path, difficulty})
|
worker.postMessage({chatBytes, path, difficulty})
|
||||||
worker.onmessage = e => {
|
worker.onmessage = e => {
|
||||||
worker.terminate()
|
worker.terminate()
|
||||||
@ -669,7 +727,7 @@ class Chat extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingText() {
|
renderLoadingText() {
|
||||||
return html`${translate("chatpage.cchange2")}`
|
return html`<div style="width:100%;display:flex;justify-content:center"> <paper-spinner-lite active></paper-spinner-lite></div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSendText() {
|
renderSendText() {
|
||||||
@ -833,6 +891,9 @@ class Chat extends LitElement {
|
|||||||
chatId=${this.activeChatHeadUrl}
|
chatId=${this.activeChatHeadUrl}
|
||||||
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
|
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
|
||||||
.setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}
|
.setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}
|
||||||
|
balance=${this.balance}
|
||||||
|
loggedInUserName=${this.loggedInUserName}
|
||||||
|
loggedInUserAddress=${this.loggedInUserAddress}
|
||||||
>
|
>
|
||||||
</chat-page>
|
</chat-page>
|
||||||
`
|
`
|
||||||
@ -897,7 +958,15 @@ class Chat extends LitElement {
|
|||||||
|
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
const viewElement = this.shadowRoot.querySelector('chat-page').shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('viewElement')
|
const viewElement = this.shadowRoot.querySelector('chat-page').shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('viewElement')
|
||||||
viewElement.scroll({ top: viewElement.scrollHeight, left: 0, behavior: 'smooth' })
|
|
||||||
|
const chatScrollerElement = this.shadowRoot.querySelector('chat-page').shadowRoot.querySelector('chat-scroller')
|
||||||
|
if (chatScrollerElement && chatScrollerElement.disableAddingNewMessages) {
|
||||||
|
const chatPageElement = this.shadowRoot.querySelector('chat-page')
|
||||||
|
if(chatPageElement && chatPageElement.getLastestMessages)
|
||||||
|
chatPageElement.getLastestMessages()
|
||||||
|
} else {
|
||||||
|
viewElement.scroll({ top: viewElement.scrollHeight, left: 0, behavior: 'smooth' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showNewMessageBar() {
|
showNewMessageBar() {
|
||||||
|
@ -21,6 +21,7 @@ import '@vaadin/icon'
|
|||||||
import '@vaadin/icons'
|
import '@vaadin/icons'
|
||||||
import '@vaadin/grid'
|
import '@vaadin/grid'
|
||||||
import '@vaadin/text-field'
|
import '@vaadin/text-field'
|
||||||
|
import { warningModal } from '../../utils/warning-modal.js'
|
||||||
|
|
||||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
@ -753,7 +754,16 @@ class NameRegistration extends LitElement {
|
|||||||
return html`<mwc-button class="warning" @click=${() => this.openUpdateNameDialog(nameObj)}><mwc-icon>update</mwc-icon> ${translate("publishpage.pchange2")} ${translate("login.name")}</mwc-button>`
|
return html`<mwc-button class="warning" @click=${() => this.openUpdateNameDialog(nameObj)}><mwc-icon>update</mwc-icon> ${translate("publishpage.pchange2")} ${translate("login.name")}</mwc-button>`
|
||||||
}
|
}
|
||||||
|
|
||||||
openUpdateNameDialog(nameObj) {
|
async openUpdateNameDialog(nameObj) {
|
||||||
|
const res = await warningModal.showModalAndWaitPublish(
|
||||||
|
{
|
||||||
|
message: get('registernamepage.nchange48')
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.action !== 'accept'){
|
||||||
|
this.closeUpdateNameDialog()
|
||||||
|
return
|
||||||
|
}
|
||||||
this.toUpdateName = ''
|
this.toUpdateName = ''
|
||||||
this.shadowRoot.getElementById("oldNameInput").value = ''
|
this.shadowRoot.getElementById("oldNameInput").value = ''
|
||||||
this.shadowRoot.getElementById("newNameInput").value = ''
|
this.shadowRoot.getElementById("newNameInput").value = ''
|
||||||
|
@ -4,6 +4,7 @@ import { Epml } from '../../../epml.js'
|
|||||||
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
|
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
|
||||||
import { columnBodyRenderer, gridRowDetailsRenderer } from '@vaadin/grid/lit.js'
|
import { columnBodyRenderer, gridRowDetailsRenderer } from '@vaadin/grid/lit.js'
|
||||||
import isElectron from 'is-electron'
|
import isElectron from 'is-electron'
|
||||||
|
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||||
|
|
||||||
registerTranslateConfig({
|
registerTranslateConfig({
|
||||||
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
||||||
@ -42,7 +43,9 @@ class QApps extends LitElement {
|
|||||||
blockedResources: { type: Array },
|
blockedResources: { type: Array },
|
||||||
textStatus: { type: String },
|
textStatus: { type: String },
|
||||||
textProgress: { type: String },
|
textProgress: { type: String },
|
||||||
theme: { type: String, reflect: true }
|
theme: { type: String, reflect: true },
|
||||||
|
hasInitiallyFetched: {type:Boolean},
|
||||||
|
isLoading: {type: Boolean}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,6 +372,7 @@ class QApps extends LitElement {
|
|||||||
this.blockedNames = []
|
this.blockedNames = []
|
||||||
this.relayMode = null
|
this.relayMode = null
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
|
this.hasInitiallyFetched = false
|
||||||
this.btnDisabled = false
|
this.btnDisabled = false
|
||||||
this.searchName = ''
|
this.searchName = ''
|
||||||
this.searchResources = []
|
this.searchResources = []
|
||||||
@ -421,13 +425,14 @@ class QApps extends LitElement {
|
|||||||
</vaadin-grid-column>
|
</vaadin-grid-column>
|
||||||
</vaadin-grid>
|
</vaadin-grid>
|
||||||
<div id="pages"></div>
|
<div id="pages"></div>
|
||||||
${this.pageRes == null ? html`
|
${this.isLoading ? html`
|
||||||
Loading...
|
<div style="width:100%;display:flex;justify-content:center;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%)"> <paper-spinner-lite active></paper-spinner-lite></div>
|
||||||
` : ''}
|
` : ''}
|
||||||
${this.isEmptyArray(this.pageRes) ? html`
|
${this.isEmptyArray(this.pageRes) && this.hasInitiallyFetched ? html`
|
||||||
<span style="color: var(--black);">${translate("appspage.schange10")}</span>
|
<span style="color: var(--black);">${translate("appspage.schange10")}</span>
|
||||||
` : ''}
|
` : ''}
|
||||||
${this.renderRelayModeText()}<br>
|
${this.renderRelayModeText()}<br>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div id="tab-followed-content">
|
<div id="tab-followed-content">
|
||||||
<div style="min-height:48px; display: flex; padding-bottom: 6px; margin: 2px;">
|
<div style="min-height:48px; display: flex; padding-bottom: 6px; margin: 2px;">
|
||||||
@ -738,11 +743,16 @@ class QApps extends LitElement {
|
|||||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
let jsonOffsetUrl = `${nodeUrl}/arbitrary/resources?service=APP&default=true&limit=20&offset=${offset}&reverse=false&includestatus=true&includemetadata=true&excludeblocked=true`
|
let jsonOffsetUrl = `${nodeUrl}/arbitrary/resources?service=APP&default=true&limit=20&offset=${offset}&reverse=false&includestatus=true&includemetadata=true&excludeblocked=true`
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
const jsonOffsetRes = await fetch(jsonOffsetUrl)
|
const jsonOffsetRes = await fetch(jsonOffsetUrl)
|
||||||
const jsonOffsetData = await jsonOffsetRes.json()
|
const jsonOffsetData = await jsonOffsetRes.json()
|
||||||
|
|
||||||
this.pageRes = jsonOffsetData
|
this.pageRes = jsonOffsetData
|
||||||
|
if(!this.hasInitiallyFetched){
|
||||||
|
this.hasInitiallyFetched = true
|
||||||
|
}
|
||||||
|
this.isLoading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateItemsFromPage(page) {
|
async updateItemsFromPage(page) {
|
||||||
|
@ -276,7 +276,7 @@ class PublishData extends LitElement {
|
|||||||
<p>
|
<p>
|
||||||
<mwc-select id="category" label="${translate("publishpage.pchange7")}" index="0" style="min-width: 130px; max-width:100%; width:100%;">
|
<mwc-select id="category" label="${translate("publishpage.pchange7")}" index="0" style="min-width: 130px; max-width:100%; width:100%;">
|
||||||
${this.categories.map((c, index) => html`
|
${this.categories.map((c, index) => html`
|
||||||
<mwc-list-item value="${c.id}">${c.name}</mwc-list-item>
|
<mwc-list-item style="color:var(--black)" value="${c.id}">${c.name}</mwc-list-item>
|
||||||
`)}
|
`)}
|
||||||
</mwc-select>
|
</mwc-select>
|
||||||
</p>
|
</p>
|
||||||
@ -300,7 +300,9 @@ class PublishData extends LitElement {
|
|||||||
<p style="color: green; word-break: break-word;">${this.successMessage}</p>
|
<p style="color: green; word-break: break-word;">${this.successMessage}</p>
|
||||||
${this.loading ? html` <paper-progress indeterminate style="width:100%; margin:4px;"></paper-progress> ` : ''}
|
${this.loading ? html` <paper-progress indeterminate style="width:100%; margin:4px;"></paper-progress> ` : ''}
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<mwc-button ?disabled=${this.btnDisable} style="width:49%;" raised icon="science" @click=${(e) => this.shadowRoot.querySelector('#publishWithFeeDialog').close()}> ${translate("appspage.schange40")}</mwc-button>
|
<mwc-button ?disabled=${this.btnDisable} style="width:49%;" raised icon="science" @click=${(e) => {
|
||||||
|
this.doPublish(e, true, false)}
|
||||||
|
}> ${translate("appspage.schange40")}</mwc-button>
|
||||||
<mwc-button ?disabled=${this.btnDisable} style="width:49%;" raised icon="send" @click=${(e) => {
|
<mwc-button ?disabled=${this.btnDisable} style="width:49%;" raised icon="send" @click=${(e) => {
|
||||||
this.doPublish(e, false, true)
|
this.doPublish(e, false, true)
|
||||||
}}> ${translate("publishpage.pchange11")}</mwc-button>
|
}}> ${translate("publishpage.pchange11")}</mwc-button>
|
||||||
|
14
plugins/plugins/utils/id-generation.js
Normal file
14
plugins/plugins/utils/id-generation.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export function simpleHash(str) {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
hash = (hash << 5) - hash + str.charCodeAt(i);
|
||||||
|
hash = hash & hash; // Convert to 32bit integer
|
||||||
|
}
|
||||||
|
return hash.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateIdFromAddresses(address1, address2) {
|
||||||
|
// Sort addresses lexicographically and concatenate
|
||||||
|
const sortedAddresses = [address1, address2].sort().join('');
|
||||||
|
return simpleHash(sortedAddresses);
|
||||||
|
}
|
71
plugins/plugins/utils/queue.js
Normal file
71
plugins/plugins/utils/queue.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
export class RequestQueue {
|
||||||
|
constructor(maxConcurrent = 5) {
|
||||||
|
this.queue = [];
|
||||||
|
this.maxConcurrent = maxConcurrent;
|
||||||
|
this.currentConcurrent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
push(request) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.queue.push({
|
||||||
|
request,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
});
|
||||||
|
this.checkQueue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkQueue() {
|
||||||
|
if (this.queue.length === 0 || this.currentConcurrent >= this.maxConcurrent) return;
|
||||||
|
|
||||||
|
const { request, resolve, reject } = this.queue.shift();
|
||||||
|
this.currentConcurrent++;
|
||||||
|
|
||||||
|
request()
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject)
|
||||||
|
.finally(() => {
|
||||||
|
this.currentConcurrent--;
|
||||||
|
this.checkQueue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RequestQueueWithPromise {
|
||||||
|
constructor(maxConcurrent = 5) {
|
||||||
|
this.queue = [];
|
||||||
|
this.maxConcurrent = maxConcurrent;
|
||||||
|
this.currentlyProcessing = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a request to the queue and return a promise
|
||||||
|
enqueue(request) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Push the request and its resolve and reject callbacks to the queue
|
||||||
|
this.queue.push({ request, resolve, reject });
|
||||||
|
this.process();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process requests in the queue
|
||||||
|
async process() {
|
||||||
|
while (this.queue.length > 0 && this.currentlyProcessing < this.maxConcurrent) {
|
||||||
|
this.currentlyProcessing++;
|
||||||
|
const { request, resolve, reject } = this.queue.shift();
|
||||||
|
try {
|
||||||
|
const response = await request();
|
||||||
|
resolve(response);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
} finally {
|
||||||
|
this.currentlyProcessing--;
|
||||||
|
this.process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,117 +1,251 @@
|
|||||||
export const replaceMessagesEdited = async ({
|
// export const replaceMessagesEdited = async ({
|
||||||
decodedMessages,
|
// decodedMessages,
|
||||||
parentEpml,
|
// parentEpml,
|
||||||
isReceipient,
|
// isReceipient,
|
||||||
decodeMessageFunc,
|
// decodeMessageFunc,
|
||||||
_publicKey
|
// _publicKey,
|
||||||
}) => {
|
// addToUpdateMessageHashmap
|
||||||
const findNewMessages = decodedMessages.map(async (msg) => {
|
// }) => {
|
||||||
let msgItem = msg
|
// const findNewMessages = decodedMessages.map(async (msg) => {
|
||||||
try {
|
// let msgItem = null
|
||||||
let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`
|
// try {
|
||||||
if (!isReceipient) {
|
// let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`
|
||||||
msgQuery = `&txGroupId=${msg.txGroupId}`
|
// if (!isReceipient) {
|
||||||
}
|
// msgQuery = `&txGroupId=${msg.txGroupId}`
|
||||||
const response = await parentEpml.request("apiCall", {
|
// }
|
||||||
type: "api",
|
// const response = await parentEpml.request("apiCall", {
|
||||||
url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}&encoding=BASE64`,
|
// type: "api",
|
||||||
})
|
// url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}&encoding=BASE64`,
|
||||||
|
// })
|
||||||
|
|
||||||
if (response && Array.isArray(response) && response.length !== 0) {
|
// if (response && Array.isArray(response) && response.length !== 0) {
|
||||||
let responseItem = { ...response[0] }
|
// let responseItem = { ...response[0] }
|
||||||
const decodeResponseItem = decodeMessageFunc(responseItem, isReceipient, _publicKey)
|
// const decodeResponseItem = decodeMessageFunc(responseItem, isReceipient, _publicKey)
|
||||||
delete decodeResponseItem.timestamp
|
// delete decodeResponseItem.timestamp
|
||||||
|
|
||||||
msgItem = {
|
// msgItem = {
|
||||||
...msg,
|
// ...msg,
|
||||||
...decodeResponseItem,
|
// ...decodeResponseItem,
|
||||||
senderName: msg.senderName,
|
// senderName: msg.senderName,
|
||||||
sender: msg.sender,
|
// sender: msg.sender,
|
||||||
editedTimestamp: response[0].timestamp,
|
// editedTimestamp: response[0].timestamp,
|
||||||
}
|
// originalSignature: msg.signature
|
||||||
}
|
// }
|
||||||
} catch (error) {
|
// }
|
||||||
}
|
// } catch (error) {
|
||||||
|
// }
|
||||||
|
|
||||||
return msgItem
|
// return msgItem
|
||||||
})
|
// })
|
||||||
const updateMessages = await Promise.all(findNewMessages)
|
// const updateMessages = await Promise.all(findNewMessages)
|
||||||
const findNewMessages2 = updateMessages.map(async (msg) => {
|
// const filterOutNull = updateMessages.filter((item)=> item !== 'null' && item !== null)
|
||||||
let parsedMessageObj = msg
|
|
||||||
try {
|
// const findNewMessages2 = filterOutNull.map(async (msg) => {
|
||||||
parsedMessageObj = JSON.parse(msg.decodedMessage)
|
// let parsedMessageObj = msg
|
||||||
} catch (error) {
|
// try {
|
||||||
return msg
|
// parsedMessageObj = JSON.parse(msg.decodedMessage)
|
||||||
}
|
// } catch (error) {
|
||||||
let msgItem = msg
|
// return msg
|
||||||
try {
|
// }
|
||||||
let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`
|
// let msgItem = msg
|
||||||
if (!isReceipient) {
|
// try {
|
||||||
msgQuery = `&txGroupId=${msg.txGroupId}`
|
// let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`
|
||||||
}
|
// if (!isReceipient) {
|
||||||
if (parsedMessageObj.repliedTo) {
|
// msgQuery = `&txGroupId=${msg.txGroupId}`
|
||||||
let originalReply
|
// }
|
||||||
if(+parsedMessageObj.version > 2){
|
// if (parsedMessageObj.repliedTo) {
|
||||||
originalReply = await parentEpml.request("apiCall", {
|
// let originalReply
|
||||||
type: "api",
|
// if(+parsedMessageObj.version > 2){
|
||||||
url: `/chat/message/${parsedMessageObj.repliedTo}?encoding=BASE64`,
|
// originalReply = await parentEpml.request("apiCall", {
|
||||||
})
|
// type: "api",
|
||||||
}
|
// url: `/chat/message/${parsedMessageObj.repliedTo}?encoding=BASE64`,
|
||||||
if(+parsedMessageObj.version < 3){
|
// })
|
||||||
originalReply = await parentEpml.request("apiCall", {
|
// }
|
||||||
type: "api",
|
// if(+parsedMessageObj.version < 3){
|
||||||
url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&encoding=BASE64`,
|
// originalReply = await parentEpml.request("apiCall", {
|
||||||
})
|
// type: "api",
|
||||||
}
|
// url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&encoding=BASE64`,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const originalReplyMessage = originalReply.timestamp ? originalReply : originalReply.length !== 0 ? originalReply[0] : null
|
// const originalReplyMessage = originalReply.timestamp ? originalReply : originalReply.length !== 0 ? originalReply[0] : null
|
||||||
|
|
||||||
const response = await parentEpml.request("apiCall", {
|
// const response = await parentEpml.request("apiCall", {
|
||||||
|
// type: "api",
|
||||||
|
// url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}&encoding=BASE64`,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// originalReplyMessage &&
|
||||||
|
// response &&
|
||||||
|
// Array.isArray(response) &&
|
||||||
|
// response.length !== 0
|
||||||
|
// ) {
|
||||||
|
// const decodeOriginalReply = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey)
|
||||||
|
|
||||||
|
// const decodeUpdatedReply = decodeMessageFunc(response[0], isReceipient, _publicKey)
|
||||||
|
// const formattedRepliedToData = {
|
||||||
|
// ...decodeUpdatedReply,
|
||||||
|
// senderName: decodeOriginalReply.senderName,
|
||||||
|
// sender: decodeOriginalReply.sender,
|
||||||
|
// }
|
||||||
|
// msgItem = {
|
||||||
|
// ...msg,
|
||||||
|
// repliedToData: formattedRepliedToData,
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// originalReplyMessage
|
||||||
|
// ) {
|
||||||
|
|
||||||
|
// msgItem = {
|
||||||
|
// ...msg,
|
||||||
|
// repliedToData: decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return msgItem
|
||||||
|
// })
|
||||||
|
// const updateMessages2 = await Promise.all(findNewMessages2)
|
||||||
|
// console.log({updateMessages2})
|
||||||
|
// updateMessages2.forEach((item)=> {
|
||||||
|
// addToUpdateMessageHashmap(item.originalSignature, item)
|
||||||
|
// })
|
||||||
|
// return updateMessages2
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
export const replaceMessagesEdited = async ({
|
||||||
|
decodedMessages,
|
||||||
|
parentEpml,
|
||||||
|
isReceipient,
|
||||||
|
decodeMessageFunc,
|
||||||
|
_publicKey,
|
||||||
|
addToUpdateMessageHashmap
|
||||||
|
}) => {
|
||||||
|
const MAX_CONCURRENT_REQUESTS = 5; // Maximum number of concurrent requests
|
||||||
|
|
||||||
|
const executeWithConcurrencyLimit = async (array, asyncFn) => {
|
||||||
|
const results = [];
|
||||||
|
const concurrencyPool = [];
|
||||||
|
|
||||||
|
for (const item of array) {
|
||||||
|
const promise = asyncFn(item);
|
||||||
|
concurrencyPool.push(promise);
|
||||||
|
|
||||||
|
if (concurrencyPool.length >= MAX_CONCURRENT_REQUESTS) {
|
||||||
|
results.push(...await Promise.all(concurrencyPool));
|
||||||
|
concurrencyPool.length = 0; // Clear the concurrency pool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (concurrencyPool.length > 0) {
|
||||||
|
results.push(...await Promise.all(concurrencyPool));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findUpdatedMessage = async (msg) => {
|
||||||
|
let msgItem = { ...msg };
|
||||||
|
|
||||||
|
try {
|
||||||
|
let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`;
|
||||||
|
if (!isReceipient) {
|
||||||
|
msgQuery = `&txGroupId=${msg.txGroupId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find new messages first
|
||||||
|
const newMsgResponse = await parentEpml.request("apiCall", {
|
||||||
|
type: "api",
|
||||||
|
url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}&encoding=BASE64`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Array.isArray(newMsgResponse) && newMsgResponse.length > 0) {
|
||||||
|
const decodeResponseItem = decodeMessageFunc(newMsgResponse[0], isReceipient, _publicKey);
|
||||||
|
delete decodeResponseItem.timestamp;
|
||||||
|
|
||||||
|
msgItem = {
|
||||||
|
...msgItem,
|
||||||
|
...decodeResponseItem,
|
||||||
|
senderName: msg.senderName,
|
||||||
|
sender: msg.sender,
|
||||||
|
editedTimestamp: newMsgResponse[0].timestamp,
|
||||||
|
originalSignature: msg.signature
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then check and find replies in the same iteration
|
||||||
|
let parsedMessageObj;
|
||||||
|
try {
|
||||||
|
parsedMessageObj = JSON.parse(msg.decodedMessage);
|
||||||
|
} catch (error) {
|
||||||
|
// If parsing fails, return the msgItem as is
|
||||||
|
return msgItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedMessageObj.repliedTo) {
|
||||||
|
let originalReply;
|
||||||
|
if(+parsedMessageObj.version > 2){
|
||||||
|
originalReply = await parentEpml.request("apiCall", {
|
||||||
|
type: "api",
|
||||||
|
url: `/chat/message/${parsedMessageObj.repliedTo}?encoding=BASE64`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
originalReply = await parentEpml.request("apiCall", {
|
||||||
|
type: "api",
|
||||||
|
url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&encoding=BASE64`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalReplyMessage = originalReply.timestamp ? originalReply : originalReply.length > 0 ? originalReply[0] : null;
|
||||||
|
|
||||||
|
const replyResponse = await parentEpml.request("apiCall", {
|
||||||
type: "api",
|
type: "api",
|
||||||
url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}&encoding=BASE64`,
|
url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}&encoding=BASE64`,
|
||||||
})
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
originalReplyMessage &&
|
originalReplyMessage &&
|
||||||
response &&
|
Array.isArray(replyResponse) &&
|
||||||
Array.isArray(response) &&
|
replyResponse.length !== 0
|
||||||
response.length !== 0
|
|
||||||
) {
|
) {
|
||||||
const decodeOriginalReply = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey)
|
const decodeOriginalReply = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey);
|
||||||
|
const decodeUpdatedReply = decodeMessageFunc(replyResponse[0], isReceipient, _publicKey);
|
||||||
const decodeUpdatedReply = decodeMessageFunc(response[0], isReceipient, _publicKey)
|
|
||||||
const formattedRepliedToData = {
|
msgItem.repliedToData = {
|
||||||
...decodeUpdatedReply,
|
...decodeUpdatedReply,
|
||||||
senderName: decodeOriginalReply.senderName,
|
senderName: decodeOriginalReply.senderName,
|
||||||
sender: decodeOriginalReply.sender,
|
sender: decodeOriginalReply.sender,
|
||||||
}
|
};
|
||||||
msgItem = {
|
} else if (originalReplyMessage) {
|
||||||
...msg,
|
msgItem.repliedToData = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey);
|
||||||
repliedToData: formattedRepliedToData,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
|
|
||||||
if (
|
|
||||||
originalReplyMessage
|
|
||||||
) {
|
|
||||||
|
|
||||||
msgItem = {
|
|
||||||
...msg,
|
|
||||||
repliedToData: decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// Handle or log the error gracefully
|
||||||
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return msgItem
|
return msgItem;
|
||||||
})
|
};
|
||||||
const updateMessages2 = await Promise.all(findNewMessages2)
|
|
||||||
|
const sortedMessages = decodedMessages.sort((a, b) => b.timestamp - a.timestamp);
|
||||||
return updateMessages2
|
|
||||||
}
|
// Execute the functions with concurrency limit
|
||||||
|
const updatedMessages = await executeWithConcurrencyLimit(sortedMessages, findUpdatedMessage);
|
||||||
|
addToUpdateMessageHashmap(updatedMessages);
|
||||||
|
|
||||||
|
return updatedMessages;
|
||||||
|
|
||||||
|
};
|
||||||
|
252
plugins/plugins/utils/warning-modal.js
Normal file
252
plugins/plugins/utils/warning-modal.js
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import { get } from 'lit-translate';
|
||||||
|
|
||||||
|
export class WarningModal {
|
||||||
|
constructor() {
|
||||||
|
this.initializeStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async showModalAndWaitPublish(data) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const modal = this.createModal(data);
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
this.addModalEventListeners(modal, resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createModal(data) {
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.id = "backdrop";
|
||||||
|
modal.classList.add("backdrop");
|
||||||
|
modal.innerHTML = `
|
||||||
|
<div class="modal my-modal-class">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="modal-subcontainer">
|
||||||
|
<div class="checkbox-row">
|
||||||
|
<p style="font-size: 16px;overflow-wrap: anywhere;" class="modal-paragraph">${data.message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-buttons">
|
||||||
|
<button id="cancel-button">${get("general.close")}</button>
|
||||||
|
<button id="ok-button">${get("general.continue")}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return modal;
|
||||||
|
}
|
||||||
|
|
||||||
|
addModalEventListeners(modal, resolve) {
|
||||||
|
// Event listener for the 'OK' button
|
||||||
|
const okButton = modal.querySelector('#ok-button');
|
||||||
|
okButton.addEventListener('click', () => {
|
||||||
|
const userData = { isWithFee: true };
|
||||||
|
if (modal.parentNode === document.body) {
|
||||||
|
document.body.removeChild(modal);
|
||||||
|
}
|
||||||
|
resolve({ action: 'accept', userData });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prevent modal content from closing the modal
|
||||||
|
const modalContent = modal.querySelector('.modal-content');
|
||||||
|
modalContent.addEventListener('click', e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Event listeners for backdrop and 'Cancel' button
|
||||||
|
const backdropClick = document.getElementById('backdrop');
|
||||||
|
backdropClick.addEventListener('click', () => {
|
||||||
|
if (modal.parentNode === document.body) {
|
||||||
|
document.body.removeChild(modal);
|
||||||
|
}
|
||||||
|
resolve({ action: 'reject' });
|
||||||
|
});
|
||||||
|
|
||||||
|
const cancelButton = modal.querySelector('#cancel-button');
|
||||||
|
cancelButton.addEventListener('click', () => {
|
||||||
|
if (modal.parentNode === document.body) {
|
||||||
|
document.body.removeChild(modal);
|
||||||
|
}
|
||||||
|
resolve({ action: 'reject' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeStyles() {
|
||||||
|
const styles = `
|
||||||
|
* {
|
||||||
|
--mdc-theme-primary: rgb(3, 169, 244);
|
||||||
|
--mdc-theme-secondary: var(--mdc-theme-primary);
|
||||||
|
--paper-input-container-focus-color: var(--mdc-theme-primary);
|
||||||
|
--mdc-checkbox-unchecked-color: var(--black);
|
||||||
|
--mdc-theme-on-surface: var(--black);
|
||||||
|
--mdc-checkbox-disabled-color: var(--black);
|
||||||
|
--mdc-checkbox-ink-color: var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.backdrop {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgb(186 186 186 / 26%);
|
||||||
|
overflow: hidden;
|
||||||
|
animation: backdrop_blur cubic-bezier(0.22, 1, 0.36, 1) 0.1s forwards;
|
||||||
|
z-index: 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes backdrop_blur {
|
||||||
|
0% {
|
||||||
|
backdrop-filter: blur(0px);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
background: rgb(186 186 186 / 26%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modal_transition {
|
||||||
|
0% {
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
animation: 0.1s cubic-bezier(0.22, 1, 0.36, 1) 0s 1 normal forwards running modal_transition;
|
||||||
|
z-index: 1000001;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modal_transition {
|
||||||
|
0% {
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: var(--white);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
||||||
|
max-width: 650px;
|
||||||
|
min-width: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-subcontainer {
|
||||||
|
color: var(--black);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-subcontainer-error {
|
||||||
|
color: var(--black);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-paragraph-error {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--black);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-paragraph {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
font-size: 18px;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
font-weight: 300;
|
||||||
|
color: var(--black);
|
||||||
|
margin: 0;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.capitalize-first {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-family: Montserrat, sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-buttons button {
|
||||||
|
background-color: #4caf50;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-buttons button:hover {
|
||||||
|
background-color: #3e8e41;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cancel-button {
|
||||||
|
background-color: #f44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cancel-button:hover {
|
||||||
|
background-color: #d32f2f;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const styleSheet = new CSSStyleSheet();
|
||||||
|
styleSheet.replaceSync(styles);
|
||||||
|
|
||||||
|
document.adoptedStyleSheets = [styleSheet];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static getInstance() {
|
||||||
|
if (!WarningModal.instance) {
|
||||||
|
WarningModal.instance = new WarningModal();
|
||||||
|
}
|
||||||
|
return WarningModal.instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const warningModal = WarningModal.getInstance();
|
Loading…
x
Reference in New Issue
Block a user