diff --git a/qortal-ui-crypto/api/transactions/groups/AddGroupAdminTransaction.js b/qortal-ui-crypto/api/transactions/groups/AddGroupAdminTransaction.js index e69de29b..25aa482f 100644 --- a/qortal-ui-crypto/api/transactions/groups/AddGroupAdminTransaction.js +++ b/qortal-ui-crypto/api/transactions/groups/AddGroupAdminTransaction.js @@ -0,0 +1,46 @@ +'use strict'; +import TransactionBase from '../TransactionBase.js' +import Base58 from '../../deps/Base58.js' +import { store } from '../../../api.js' +import { QORT_DECIMALS } from "../../constants.js" + +export default class AddGroupAdminTransaction extends TransactionBase { + constructor() { + super() + this.type = 24 + } + + render(html) { + const conf = store.getState().config + return html` + Are you sure to update this group ? +
+ +
+ On pressing confirm, the group details will be updated! + ` + } + + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + set member(member) { + this._member = member instanceof Uint8Array ? member : this.constructor.Base58.decode(member) + } + + set _groupId(_groupId){ + this._groupBytes = this.constructor.utils.int32ToBytes(_groupId) + } + get params() { + const params = super.params + params.push( + this._groupBytes, + this._member, + this._feeBytes + ) + console.log('verify params', params) + return params + } +} \ No newline at end of file diff --git a/qortal-ui-crypto/api/transactions/groups/LeaveGroupTransaction.js b/qortal-ui-crypto/api/transactions/groups/LeaveGroupTransaction.js index 0a3939f0..f15ce71c 100644 --- a/qortal-ui-crypto/api/transactions/groups/LeaveGroupTransaction.js +++ b/qortal-ui-crypto/api/transactions/groups/LeaveGroupTransaction.js @@ -58,6 +58,7 @@ export default class LeaveGroupTransaction extends TransactionBase { this._rGroupIdBytes, this._feeBytes ) + console.log('check exec params2', params) return params; } } diff --git a/qortal-ui-crypto/api/transactions/groups/RemoveGroupAdminTransaction.js b/qortal-ui-crypto/api/transactions/groups/RemoveGroupAdminTransaction.js index e69de29b..72cce399 100644 --- a/qortal-ui-crypto/api/transactions/groups/RemoveGroupAdminTransaction.js +++ b/qortal-ui-crypto/api/transactions/groups/RemoveGroupAdminTransaction.js @@ -0,0 +1,46 @@ +'use strict'; +import TransactionBase from '../TransactionBase.js' +import Base58 from '../../deps/Base58.js' +import { store } from '../../../api.js' +import { QORT_DECIMALS } from "../../constants.js" + +export default class RemoveGroupAdminTransaction extends TransactionBase { + constructor() { + super() + this.type = 25 + } + + render(html) { + const conf = store.getState().config + return html` + Are you sure to update this group ? +
+ +
+ On pressing confirm, the group details will be updated + ` + } + + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + set member(member) { + this._member = member instanceof Uint8Array ? member : this.constructor.Base58.decode(member) + } + + set _groupId(_groupId){ + this._groupBytes = this.constructor.utils.int32ToBytes(_groupId) + } + get params() { + const params = super.params + params.push( + this._groupBytes, + this._member, + this._feeBytes + ) + console.log('verify params', params) + return params + } +} \ No newline at end of file diff --git a/qortal-ui-crypto/api/transactions/groups/UpdateGroupTransaction.js b/qortal-ui-crypto/api/transactions/groups/UpdateGroupTransaction.js index e69de29b..8709f778 100644 --- a/qortal-ui-crypto/api/transactions/groups/UpdateGroupTransaction.js +++ b/qortal-ui-crypto/api/transactions/groups/UpdateGroupTransaction.js @@ -0,0 +1,72 @@ +'use strict'; +import TransactionBase from '../TransactionBase.js' +import Base58 from '../../deps/Base58.js' +import { store } from '../../../api.js' +import { QORT_DECIMALS } from "../../constants.js" + +export default class UpdateGroupTransaction extends TransactionBase { + constructor() { + super() + this.type = 23 + } + + render(html) { + const conf = store.getState().config + return html` + Are you sure to update this group ? +
+ +
+ On pressing confirm, the group details will be updated! + ` + } + + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + set newOwner(newOwner) { + this._newOwner = newOwner instanceof Uint8Array ? newOwner : this.constructor.Base58.decode(newOwner) + } + set newIsOpen(newIsOpen) { + + this._rGroupType = new Uint8Array(1) + this._rGroupType[0] = newIsOpen + } + set newDescription(newDescription) { + this._rGroupDescBytes = this.constructor.utils.stringtoUTF8Array(newDescription.toLocaleLowerCase()) + this._rGroupDescLength = this.constructor.utils.int32ToBytes(this._rGroupDescBytes.length) + } + set newApprovalThreshold(newApprovalThreshold) { + this._rGroupApprovalThreshold = new Uint8Array(1) + this._rGroupApprovalThreshold[0] = newApprovalThreshold; + } + set newMinimumBlockDelay(newMinimumBlockDelay) { + this._rGroupMinimumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMinimumBlockDelay) + } + set newMaximumBlockDelay(newMaximumBlockDelay) { + + this._rGroupMaximumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMaximumBlockDelay) + } + + set _groupId(_groupId){ + this._groupBytes = this.constructor.utils.int32ToBytes(_groupId) + } + get params() { + const params = super.params + params.push( + this._groupBytes, + this._newOwner, + this._rGroupDescLength, + this._rGroupDescBytes, + this._rGroupType, + this._rGroupApprovalThreshold, + this._rGroupMinimumBlockDelayBytes, + this._rGroupMaximumBlockDelayBytes, + this._feeBytes + ) + console.log('verify params', params) + return params + } +} \ No newline at end of file diff --git a/qortal-ui-crypto/api/transactions/transactions.js b/qortal-ui-crypto/api/transactions/transactions.js index c15cbd25..f19ce1fd 100644 --- a/qortal-ui-crypto/api/transactions/transactions.js +++ b/qortal-ui-crypto/api/transactions/transactions.js @@ -7,8 +7,11 @@ import RewardShareTransaction from './reward-share/RewardShareTransaction.js' import RemoveRewardShareTransaction from './reward-share/RemoveRewardShareTransaction.js' import CreateGroupTransaction from './groups/CreateGroupTransaction.js'; import JoinGroupTransaction from './groups/JoinGroupTransaction.js' +import UpdateGroupTransaction from './groups/UpdateGroupTransaction.js' import LeaveGroupTransaction from './groups/LeaveGroupTransaction.js' import PublicizeTransaction from './PublicizeTransaction.js' +import AddGroupAdminTransaction from './groups/AddGroupAdminTransaction.js' +import RemoveGroupAdminTransaction from './groups/RemoveGroupAdminTransaction.js' export const transactionTypes = { 2: PaymentTransaction, @@ -18,6 +21,9 @@ export const transactionTypes = { 181: GroupChatTransaction, 19: PublicizeTransaction, 22: CreateGroupTransaction, + 23: UpdateGroupTransaction, + 24: AddGroupAdminTransaction, + 25: RemoveGroupAdminTransaction, 31: JoinGroupTransaction, 32: LeaveGroupTransaction, 38: RewardShareTransaction, diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json index e7b41de8..59282688 100644 --- a/qortal-ui-plugins/package.json +++ b/qortal-ui-plugins/package.json @@ -54,6 +54,9 @@ "@vaadin/button": "23.2.5", "@vaadin/grid": "23.2.5", "@vaadin/icons": "23.2.5", + "@vaadin/tabs": "23.2.5", + "@vaadin/avatar": "23.2.5", + "@vaadin/horizontal-layout": "23.2.5", "epml": "0.3.3", "file-saver": "2.0.5", "html-escaper": "3.0.3", diff --git a/qortal-ui-plugins/plugins/core/components/ChatGroupInvites.js b/qortal-ui-plugins/plugins/core/components/ChatGroupInvites.js new file mode 100644 index 00000000..27cc0a26 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/ChatGroupInvites.js @@ -0,0 +1,335 @@ +import { LitElement, html, css } from "lit" +import { render } from "lit/html.js" +import { get, translate } from "lit-translate" +import { Epml } from "../../../epml" +import snackbar from "./snackbar.js" +import "@material/mwc-button" +import "@material/mwc-dialog" +import "@polymer/paper-spinner/paper-spinner-lite.js" +import "@material/mwc-icon" +import "./WrapperModal" + +const parentEpml = new Epml({ type: "WINDOW", source: window.parent }) + +class ChatGroupInvites extends LitElement { + static get properties() { + return { + isLoading: { type: Boolean }, + isOpenLeaveModal: { type: Boolean }, + leaveGroupObj: { type: Object }, + error: { type: Boolean }, + message: { type: String }, + chatHeads: { type: Array }, + groupAdmin: { attribute: false }, + groupMembers: { attribute: false }, + selectedHead: { type: Object }, + } + } + + constructor() { + super() + this.isLoading = false + this.isOpenLeaveModal = false + this.leaveGroupObj = {} + this.leaveFee = 0.001 + this.error = false + this.message = "" + this.chatHeads = [] + this.groupAdmin = [] + this.groupMembers = [] + } + + 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; + } + ` + } + + firstUpdated() {} + + timeIsoString(timestamp) { + let myTimestamp = timestamp === undefined ? 1587560082346 : timestamp + let time = new Date(myTimestamp) + return time.toISOString() + } + + resetDefaultSettings() { + this.error = false + this.message = "" + this.isLoading = false + } + + renderErr9Text() { + return html`${translate("grouppage.gchange49")}` + } + + async confirmRelationship(reference) { + let interval = null + let stop = false + const getAnswer = async () => { + + + if (!stop) { + stop = true + try { + let myRef = await parentEpml.request("apiCall", { + type: "api", + url: `/transactions/reference/${reference}`, + }) + if (myRef && myRef.type) { + clearInterval(interval) + this.isLoading = false + this.isOpenLeaveModal = false + } + } catch (error) {} + stop = false + } + } + interval = setInterval(getAnswer, 5000) + } + + async getLastRef() { + let myRef = await parentEpml.request("apiCall", { + type: "api", + url: `/addresses/lastreference/${this.selectedAddress.address}`, + }) + return myRef + } + + getTxnRequestResponse(txnResponse, reference) { + if (txnResponse === true) { + this.message = this.renderErr9Text() + this.error = false + this.confirmRelationship(reference) + } else { + this.error = true + this.message = "" + throw new Error(txnResponse) + } + } + + async convertBytesForSigning(transactionBytesBase58) { + let convertedBytes = await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/transactions/convert`, + body: `${transactionBytesBase58}`, + }) + return convertedBytes + } + + async signTx(body){ + return await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/transactions/sign`, + body: body, + headers: { + 'Content-Type': 'application/json' + } + }) + } + + async process(body){ + return await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/transactions/process`, + body: body, + }) + } + async _addAdmin(groupId) { + // Reset Default Settings... + this.resetDefaultSettings() + const leaveFeeInput = this.leaveFee + + this.isLoading = true + + // Get Last Ref + + const validateReceiver = async () => { + let lastRef = await this.getLastRef() + let myTransaction = await makeTransactionRequest(lastRef) + this.getTxnRequestResponse(myTransaction, lastRef ) + } + + // Make Transaction Request + const makeTransactionRequest = async (lastRef) => { + const body = { + timestamp: Date.now(), + reference: lastRef, + fee: leaveFeeInput, + ownerPublicKey: window.parent.Base58.encode( + window.parent.reduxStore.getState().app.selectedAddress + .keyPair.publicKey + ), + groupId: groupId, + member: this.selectedHead.address, + } + const bodyToString = JSON.stringify(body) + let transactionBytes = await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/groups/addadmin`, + body: bodyToString, + headers: { + "Content-Type": "application/json", + }, + }) + const readforsign = await this.convertBytesForSigning( + transactionBytes + ) + const body2 = { + privateKey: window.parent.Base58.encode( + window.parent.reduxStore.getState().app.selectedAddress + .keyPair.privateKey + ), + transactionBytes: readforsign, + } + const bodyToString2 = JSON.stringify(body2) + let signTransaction = await this.signTx(bodyToString2) + let processTransaction = await this.process(signTransaction) + return processTransaction + } + + validateReceiver() + } + + async _removeAdmin(groupId) { + // Reset Default Settings... + this.resetDefaultSettings() + const leaveFeeInput = this.leaveFee + + this.isLoading = true + + // Get Last Ref + + const validateReceiver = async () => { + let lastRef = await this.getLastRef() + let myTransaction = await makeTransactionRequest(lastRef) + this.getTxnRequestResponse(myTransaction, lastRef) + } + + // Make Transaction Request + const makeTransactionRequest = async (lastRef) => { + const body = { + timestamp: Date.now(), + reference: lastRef, + fee: leaveFeeInput, + ownerPublicKey: window.parent.Base58.encode( + window.parent.reduxStore.getState().app.selectedAddress + .keyPair.publicKey + ), + groupId: groupId, + admin: this.selectedHead.address, + } + const bodyToString = JSON.stringify(body) + let transactionBytes = await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/groups/removeadmin`, + body: bodyToString, + headers: { + "Content-Type": "application/json", + }, + }) + const readforsign = await this.convertBytesForSigning( + transactionBytes + ) + const body2 = { + privateKey: window.parent.Base58.encode( + window.parent.reduxStore.getState().app.selectedAddress + .keyPair.privateKey + ), + transactionBytes: readforsign, + } + const bodyToString2 = JSON.stringify(body2) + let signTransaction = await this.signTx(bodyToString2) + let processTransaction = await this.process(signTransaction) + return processTransaction + } + + validateReceiver() + } + + render() { + console.log("leaveGroupObj", this.leaveGroupObj) + return html` + { + this.isOpenLeaveModal = true + }} class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:users" slot="icon"> + + { + if (this.isLoading) return + this.isOpenLeaveModal = false + }} + style=${ + this.isOpenLeaveModal ? "display: block" : "display: none" + }> +
+

${translate("grouppage.gchange35")}

+
+
+ + + +
+ + + ${translate("grouppage.gchange36")}   + + + + + ${this.message} + +
+ + + +
+ ` + } +} + +customElements.define("chat-right-panel", ChatGroupInvites) diff --git a/qortal-ui-plugins/plugins/core/components/ChatGroupSettings.js b/qortal-ui-plugins/plugins/core/components/ChatGroupSettings.js new file mode 100644 index 00000000..da5f951d --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/ChatGroupSettings.js @@ -0,0 +1,281 @@ +import { LitElement, html, css } from 'lit'; +import { render } from 'lit/html.js'; +import { get, translate } from 'lit-translate'; +import { Epml } from '../../../epml'; +import snackbar from './snackbar.js' +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import '@polymer/paper-spinner/paper-spinner-lite.js' +import '@material/mwc-icon'; +import './WrapperModal'; + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class ChatGroupSettings extends LitElement { + static get properties() { + return { + isLoading: { type: Boolean }, + isOpenLeaveModal: {type: Boolean}, + leaveGroupObj: { type: Object }, + error: {type: Boolean}, + message: {type: String}, + chatHeads: {type: Array}, + setActiveChatHeadUrl: {attribute: false} + } + } + + constructor() { + super(); + this.isLoading = false; + this.isOpenLeaveModal = false + this.leaveGroupObj = {} + this.leaveFee = 0.001 + this.error = false + this.message = '' + this.chatHeads = [] + } + + static get styles() { + return css` + .top-bar-icon { + cursor: pointer; + height: 18px; + width: 18px; + transition: .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; + } + ` + } + + firstUpdated() { + + } + + timeIsoString(timestamp) { + let myTimestamp = timestamp === undefined ? 1587560082346 : timestamp + let time = new Date(myTimestamp) + return time.toISOString() + } + + resetDefaultSettings() { + this.error = false + this.message = '' + this.isLoading = false + } + + renderErr9Text() { + return html`${translate("grouppage.gchange49")}` + } + + async confirmRelationship() { + + + let interval = null + let stop = false + const getAnswer = async () => { + const currentChats = this.chatHeads + + if (!stop) { + stop = true; + try { + const findGroup = currentChats.find((item)=> item.groupId === this.leaveGroupObj.groupId) + if (!findGroup) { + clearInterval(interval) + this.isLoading = false + this.isOpenLeaveModal= false + this.setActiveChatHeadUrl('') + } + + } catch (error) { + } + stop = false + } + }; + interval = setInterval(getAnswer, 5000); + } + + async _convertToPrivate(groupId) { + // Reset Default Settings... + this.resetDefaultSettings() + const leaveFeeInput = this.leaveFee + + this.isLoading = true + + // Get Last Ref + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + }; + + const validateReceiver = async () => { + let lastRef = await getLastRef(); + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + + } + const convertBytesForSigning = async (transactionBytesBase58) => { + let convertedBytes = await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/transactions/convert`, + body: `${transactionBytesBase58}`, + }) + return convertedBytes + } + + + // Make Transaction Request + const makeTransactionRequest = async (lastRef) => { + let groupdialog3 = get("transactions.groupdialog3") + let groupdialog4 = get("transactions.groupdialog4") + + const body = { + "timestamp": Date.now(), + "reference": lastRef, + "fee": leaveFeeInput, + "ownerPublicKey": window.parent.Base58.encode(window.parent.reduxStore.getState().app.selectedAddress.keyPair.publicKey), + "groupId": groupId, + "newOwner": "QdR4bQ1fJFnSZgswtW27eE8ToXwHqUQyaU", + "newIsOpen": false, + "newDescription": "my group for accounts I like", + "newApprovalThreshold": "NONE", + "newMinimumBlockDelay": 5, + "newMaximumBlockDelay": 60 + } + console.log('STRING3') + // const bodyToString = JSON.stringify(body) + // let transactionBytes = await parentEpml.request("apiCall", { + // type: "api", + // method: "POST", + // url: `/groups/update`, + // body: bodyToString, + // headers: { + // 'Content-Type': 'application/json' + // } + // }) + // console.log({transactionBytes}) + // const readforsign = await convertBytesForSigning(transactionBytes) + // // const res = await signAndProcess(transactionBytes) + // const body2 = { + // "privateKey": window.parent.Base58.encode(window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey), + // "transactionBytes": readforsign + // } + // const bodyToString2 = JSON.stringify(body2) + // let signTransaction = await parentEpml.request("apiCall", { + // type: "api", + // method: "POST", + // url: `/transactions/sign`, + // body: bodyToString2, + // headers: { + // 'Content-Type': 'application/json' + // } + // }) + // let processTransaction = await parentEpml.request("apiCall", { + // type: "api", + // method: "POST", + // url: `/transactions/process`, + // body: signTransaction, + // }) + // return processTransaction + console.log('this.selectedAddress.nonce', this.selectedAddress.nonce) + let myTxnrequest = await parentEpml.request('transaction', { + type: 23, + nonce: this.selectedAddress.nonce, + params: { + _groupId: groupId, + lastReference: lastRef, + fee: leaveFeeInput, + "newOwner": "QdR4bQ1fJFnSZgswtW27eE8ToXwHqUQyaU", + "newIsOpen": false, + "newDescription": "my group for accounts I like", + "newApprovalThreshold": "NONE", + "newMinimumBlockDelay": 5, + "newMaximumBlockDelay": 60 + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + + if (txnResponse === true) { + this.message = this.renderErr9Text() + this.error = false + this.confirmRelationship() + } else { + this.error = true + this.message = "" + throw new Error(txnResponse) + } + } + validateReceiver() + } + + render() { + console.log('leaveGroupObj', this.leaveGroupObj) + return html` + { + this.isOpenLeaveModal = true + }} class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:cog" slot="icon"> + + { + if(this.isLoading) return + this.isOpenLeaveModal = false + } } + style=${(this.isOpenLeaveModal) ? "display: block" : "display: none"}> +
+

${translate("grouppage.gchange35")}

+
+
+ < + + +
+ + + ${translate("grouppage.gchange36")}   + + + + + ${this.message} + +
+ + + +
+ `; + } +} + +customElements.define('chat-group-settings', ChatGroupSettings); \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/components/ChatGroupsManagement.js b/qortal-ui-plugins/plugins/core/components/ChatGroupsManagement.js new file mode 100644 index 00000000..029a31b7 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/ChatGroupsManagement.js @@ -0,0 +1,296 @@ +import { LitElement, html, css } from 'lit'; +import { render } from 'lit/html.js'; +import { get, translate } from 'lit-translate'; +import { Epml } from '../../../epml'; +import snackbar from './snackbar.js' +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import '@polymer/paper-spinner/paper-spinner-lite.js' +import '@material/mwc-icon'; +import './WrapperModal'; +import '@vaadin/tabs' +import '@vaadin/tabs/theme/material/vaadin-tabs.js'; +import '@vaadin/avatar'; +import '@vaadin/grid'; +import '@vaadin/grid/vaadin-grid-filter-column.js'; +import { columnBodyRenderer } from '@vaadin/grid/lit.js'; + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class ChatGroupsManagement extends LitElement { + static get properties() { + return { + isLoading: { type: Boolean }, + isOpenLeaveModal: {type: Boolean}, + leaveGroupObj: { type: Object }, + error: {type: Boolean}, + message: {type: String}, + chatHeads: {type: Array}, + setActiveChatHeadUrl: {attribute: false}, + selectedAddress: {attribute: Object}, + currentTab: {type: Number}, + groups: {type: Array} + } + } + + constructor() { + super(); + this.isLoading = false; + this.isOpenLeaveModal = false + this.leaveGroupObj = {} + this.fee = null + this.error = false + this.message = '' + this.chatHeads = [] + this.currentTab = 0 + this.groups = [] + } + + static get styles() { + return css` + .top-bar-icon { + cursor: pointer; + height: 18px; + width: 18px; + transition: .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; + } + ` + } + + async getJoinedGroups(){ + let joinedG = await parentEpml.request('apiCall', { + url: `/groups/member/${this.selectedAddress.address}` + }) + return joinedG +} + + async firstUpdated() { + + try { + let _joinedGroups = await this.getJoinedGroups() + this.joinedGroups = _joinedGroups + } catch (error) { + + } + + } + + _tabChanged(e) { + this.currentTab = e.detail.value + } + + async unitFee() { + 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 url = `${nodeUrl}/transactions/unitfee?txType=LEAVE_GROUP` + let fee = null + + try { + const res = await fetch(url) + const data = await res.json() + fee = (Number(data) / 1e8).toFixed(3) + } catch (error) { + fee = null + } + + return fee + } + + timeIsoString(timestamp) { + let myTimestamp = timestamp === undefined ? 1587560082346 : timestamp + let time = new Date(myTimestamp) + return time.toISOString() + } + + resetDefaultSettings() { + this.error = false + this.message = '' + this.isLoading = false + } + + renderErr9Text() { + return html`${translate("grouppage.gchange49")}` + } + + async confirmRelationship() { + + + let interval = null + let stop = false + const getAnswer = async () => { + const currentChats = this.chatHeads + + if (!stop) { + stop = true; + try { + const findGroup = currentChats.find((item)=> item.groupId === this.leaveGroupObj.groupId) + if (!findGroup) { + clearInterval(interval) + this.isLoading = false + this.isOpenLeaveModal= false + this.setActiveChatHeadUrl('') + } + + } catch (error) { + } + stop = false + } + }; + interval = setInterval(getAnswer, 5000); + } + + async _leaveGroup(groupId, groupName) { + // Reset Default Settings... + this.resetDefaultSettings() + + const leaveFeeInput = await this.unitFee() + if(!leaveFeeInput){ + throw Error() + } + this.isLoading = true + + // Get Last Ref + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + }; + + const validateReceiver = async () => { + let lastRef = await getLastRef(); + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + + } + + // Make Transaction Request + const makeTransactionRequest = async (lastRef) => { + let groupdialog3 = get("transactions.groupdialog3") + let groupdialog4 = get("transactions.groupdialog4") + let myTxnrequest = await parentEpml.request('transaction', { + type: 32, + nonce: this.selectedAddress.nonce, + params: { + fee: leaveFeeInput, + registrantAddress: this.selectedAddress.address, + rGroupName: groupName, + rGroupId: groupId, + lastReference: lastRef, + groupdialog3: groupdialog3, + groupdialog4: groupdialog4, + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + + if (txnResponse.success === false && txnResponse.message) { + this.error = true + this.message = txnResponse.message + throw new Error(txnResponse) + } else if (txnResponse.success === true && !txnResponse.data.error) { + this.message = this.renderErr9Text() + this.error = false + this.confirmRelationship() + } else { + this.error = true + this.message = txnResponse.data.message + throw new Error(txnResponse) + } + } + validateReceiver() + } + + nameRenderer(person){ + console.log({person}) + return html` + + + ${person.displayName} + + `; + }; + + render() { + return html` + { + this.isOpenLeaveModal = true + }} class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:exit" slot="icon"> + + { + if(this.isLoading) return + this.isOpenLeaveModal = false + } } + customStyle=${"width: 90%; max-width: 900px; height: 90%"} + style=${(this.isOpenLeaveModal) ? "display: block" : "display: none"}> +
+
+ + + Groups + Group Join Requests + Invites + Blocked Users + +
+ +
+ + ${this.currentTab === 0 ? html` +
+ + + + +

Search groups

+ +

Current groups as owner

+

Current groups as member

+
+ ` : ''} + + +
+
+ + +
+
+
+ `; + } +} + +customElements.define('chat-groups-management', ChatGroupsManagement); \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/components/ChatHead.js b/qortal-ui-plugins/plugins/core/components/ChatHead.js index 4e9c170c..28771bd8 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatHead.js +++ b/qortal-ui-plugins/plugins/core/components/ChatHead.js @@ -21,7 +21,9 @@ class ChatHead extends LitElement { static get styles() { return css` li { - padding: 10px 2px 20px 5px; + + width: 100%; + padding: 7px 5px 7px 5px; cursor: pointer; width: 100%; } diff --git a/qortal-ui-plugins/plugins/core/components/ChatLeaveGroup.js b/qortal-ui-plugins/plugins/core/components/ChatLeaveGroup.js index 85bdb53b..be249818 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatLeaveGroup.js +++ b/qortal-ui-plugins/plugins/core/components/ChatLeaveGroup.js @@ -19,7 +19,8 @@ class ChatLeaveGroup extends LitElement { error: {type: Boolean}, message: {type: String}, chatHeads: {type: Array}, - setActiveChatHeadUrl: {attribute: false} + setActiveChatHeadUrl: {attribute: false}, + selectedAddress: {attribute: Object} } } @@ -28,7 +29,7 @@ class ChatLeaveGroup extends LitElement { this.isLoading = false; this.isOpenLeaveModal = false this.leaveGroupObj = {} - this.leaveFee = 0.001 + this.fee = null this.error = false this.message = '' this.chatHeads = [] @@ -59,7 +60,24 @@ class ChatLeaveGroup extends LitElement { } firstUpdated() { + + } + async unitFee() { + 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 url = `${nodeUrl}/transactions/unitfee?txType=LEAVE_GROUP` + let fee = null + + try { + const res = await fetch(url) + const data = await res.json() + fee = (Number(data) / 1e8).toFixed(3) + } catch (error) { + fee = null + } + + return fee } timeIsoString(timestamp) { @@ -108,8 +126,11 @@ class ChatLeaveGroup extends LitElement { async _leaveGroup(groupId, groupName) { // Reset Default Settings... this.resetDefaultSettings() - const leaveFeeInput = this.leaveFee - + + const leaveFeeInput = await this.unitFee() + if(!leaveFeeInput){ + throw Error() + } this.isLoading = true // Get Last Ref diff --git a/qortal-ui-plugins/plugins/core/components/ChatPage.js b/qortal-ui-plugins/plugins/core/components/ChatPage.js index 22cbd82e..25fae5b4 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatPage.js +++ b/qortal-ui-plugins/plugins/core/components/ChatPage.js @@ -3,6 +3,8 @@ import { render } from 'lit/html.js'; import {animate} from '@lit-labs/motion'; import { Epml } from '../../../epml.js'; import { use, get, translate, registerTranslateConfig } from 'lit-translate'; +import { chatStyles } from './ChatScroller-css.js' + // import localForage from "localforage"; registerTranslateConfig({ loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) @@ -20,6 +22,8 @@ import './WrapperModal'; import './ChatSelect.js' import './ChatSideNavHeads.js' import './ChatLeaveGroup.js' +import './ChatGroupSettings.js' +import './ChatRightPanel.js' import '@polymer/paper-spinner/paper-spinner-lite.js'; import '@material/mwc-button'; import '@material/mwc-dialog'; @@ -615,29 +619,35 @@ class ChatPage extends LitElement { this.groupMembers = [] this.shifted = false this.groupInfo = {} + this.pageNumber = 1 } - _toggle() { - this.shifted = !this.shifted; + _toggle(value) { + console.log('toggel', value, this.shifted) + this.shifted = value === (false || true) ? value : !this.shifted; + this.requestUpdate() } render() { - console.log('this.chatHeads', this.chatHeads) + console.log('this._chatId', this._chatId) return html`
+ ${(!this.isReceipient && +this._chatId !== 0) ? html`
-
-

Name

+
+

${this.groupInfo && this.groupInfo.groupName}

- - + this.setActiveChatHeadUrl(val)}> + this.setActiveChatHeadUrl(val)}>
+ ` : html`
`} +
${this.isLoadingMessages ? html` @@ -829,36 +839,44 @@ class ChatPage extends LitElement {
-
-

Exit Icon

- Group Avatar

Group Name

-

Group owner

-

100 Members

-

Description

-

date created

-

private / public

-

approvalThreshold

-

"minimumBlockDelay": 0, "maximumBlockDelay": 0

-

Admins

- ${this.groupAdmin.map((item)=> { - return html` { - - }} chatInfo=${JSON.stringify(item)}>` - })} - -

Members

- ${this.groupAdmin.map((item)=> { - return html` { - - }} chatInfo=${JSON.stringify(item)}>` - })} -
- +
+ this.getMoreMembers(val)} .toggle=${(val)=> this._toggle(val)} .selectedAddress=${this.selectedAddress} .groupMembers=${this.groupMembers} .groupAdmin=${this.groupAdmin} .leaveGroupObj=${this.groupInfo}>
+
` } + async getMoreMembers(groupId){ + console.log('getMoreMembers', groupId) + try { + const getMembers = await parentEpml.request("apiCall", { + type: "api", + url: `/groups/members/${groupId}?onlyAdmins=false&limit=20&offset=${this.pageNumber * 20}`, + }); + + const getMembersWithName = (getMembers.members || []).map(async (member) => { + let memberItem = member + try { + const name = await this.getName(member.member) + memberItem = { + address: member.member, + name: name ? name : undefined + } + } catch (error) { + console.log(error) + } + + return memberItem + }) + const membersWithName = await Promise.all(getMembersWithName) + this.groupMembers = membersWithName + this.pageNumber = this.pageNumber + 1 + } catch (error) { + + } + } + setForwardProperties(forwardedMessage){ this.openForwardOpen = true this.forwardedMessage = forwardedMessage @@ -957,6 +975,7 @@ class ChatPage extends LitElement { this.webSocket.close() this.webSocket= '' } + this.pageNumber = 1 const getAddressPublicKey = () => { parentEpml.request('apiCall', { @@ -1006,7 +1025,7 @@ class ChatPage extends LitElement { try { const getMembers = await parentEpml.request("apiCall", { type: "api", - url: `/groups/members/${groupId}?onlyAdmins=false&limit=20`, + url: `/groups/members/${groupId}?onlyAdmins=false&limit=20&offset=0`, }); const getMembersAdmins = await parentEpml.request("apiCall", { type: "api", @@ -1404,6 +1423,7 @@ async getName (recipient) { let decodedMessageObj = {}; if (isReceipientVar === true) { + console.log('encoded', encodedMessageObj.isEncrypted, _publicKeyVar.hasPubKey,encodedMessageObj.data) // direct chat if (encodedMessageObj.isEncrypted === true && _publicKeyVar.hasPubKey === true && encodedMessageObj.data) { let decodedMessage = window.parent.decryptChatMessage(encodedMessageObj.data, window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey, _publicKeyVar.key, encodedMessageObj.reference); diff --git a/qortal-ui-plugins/plugins/core/components/ChatRightPanel.js b/qortal-ui-plugins/plugins/core/components/ChatRightPanel.js new file mode 100644 index 00000000..402fe2cf --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/ChatRightPanel.js @@ -0,0 +1,486 @@ +import { LitElement, html, css } from "lit" +import { render } from "lit/html.js" +import { get, translate } from "lit-translate" +import { Epml } from "../../../epml" +import snackbar from "./snackbar.js" +import "@material/mwc-button" +import "@material/mwc-dialog" +import "@polymer/paper-spinner/paper-spinner-lite.js" +import "@material/mwc-icon" +import "./WrapperModal" + +const parentEpml = new Epml({ type: "WINDOW", source: window.parent }) + +class ChatRightPanel extends LitElement { + static get properties() { + return { + isLoading: { type: Boolean }, + isOpenLeaveModal: { type: Boolean }, + leaveGroupObj: { type: Object }, + error: { type: Boolean }, + message: { type: String }, + chatHeads: { type: Array }, + groupAdmin: { attribute: false }, + groupMembers: { attribute: false }, + selectedHead: { type: Object }, + toggle: {attribute: false}, + getMoreMembers:{attribute: false} + } + } + + constructor() { + super() + this.isLoading = false + this.isOpenLeaveModal = false + this.leaveGroupObj = {} + this.leaveFee = 0.001 + this.error = false + this.message = "" + this.chatHeads = [] + this.groupAdmin = [] + this.groupMembers = [] + this.observerHandler = this.observerHandler.bind(this) + this.viewElement = '' + this.downObserverElement = '' + } + + 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 + + } + .container-body { + width: 100%; + display: flex; + flex-direction: column; + flex-grow: 1; + overflow:auto; + margin-top: 15px; + padding: 0px 5px; + 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%; + } + ` + } + + firstUpdated() { + this.viewElement = this.shadowRoot.getElementById('viewElement'); + this.downObserverElement = this.shadowRoot.getElementById('downObserver'); + this.elementObserver(); + } + + timeIsoString(timestamp) { + let myTimestamp = timestamp === undefined ? 1587560082346 : timestamp + let time = new Date(myTimestamp) + return time.toISOString() + } + + resetDefaultSettings() { + this.error = false + this.message = "" + this.isLoading = false + } + + renderErr9Text() { + return html`${translate("grouppage.gchange49")}` + } + + async confirmRelationship(reference) { + let interval = null + let stop = false + const getAnswer = async () => { + + + if (!stop) { + stop = true + try { + let myRef = await parentEpml.request("apiCall", { + type: "api", + url: `/transactions/reference/${reference}`, + }) + if (myRef && myRef.type) { + clearInterval(interval) + this.isLoading = false + this.isOpenLeaveModal = false + } + } catch (error) {} + stop = false + } + } + interval = setInterval(getAnswer, 5000) + } + + async unitFee(txType) { + 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 url = `${nodeUrl}/transactions/unitfee?txType=${txType}` + let fee = null + + try { + const res = await fetch(url) + const data = await res.json() + fee = (Number(data) / 1e8).toFixed(3) + } catch (error) { + fee = null + } + + return fee + } + + async getLastRef() { + let myRef = await parentEpml.request("apiCall", { + type: "api", + url: `/addresses/lastreference/${this.selectedAddress.address}`, + }) + return myRef + } + + getTxnRequestResponse(txnResponse, reference) { + if (txnResponse === true) { + this.message = this.renderErr9Text() + this.error = false + this.confirmRelationship(reference) + } else { + this.error = true + this.message = "" + throw new Error(txnResponse) + } + } + + async convertBytesForSigning(transactionBytesBase58) { + let convertedBytes = await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/transactions/convert`, + body: `${transactionBytesBase58}`, + }) + return convertedBytes + } + + async signTx(body){ + return await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/transactions/sign`, + body: body, + headers: { + 'Content-Type': 'application/json' + } + }) + } + + async process(body){ + return await parentEpml.request("apiCall", { + type: "api", + method: "POST", + url: `/transactions/process`, + body: body, + }) + } + async _addAdmin(groupId) { + this.resetDefaultSettings() + + const leaveFeeInput = await this.unitFee('ADD_GROUP_ADMIN') + if(!leaveFeeInput){ + throw Error() + } + this.isLoading = true + + // Get Last Ref + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + }; + + const validateReceiver = async () => { + let lastRef = await getLastRef(); + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + + } + + // Make Transaction Request + const makeTransactionRequest = async (lastRef) => { + let groupdialog3 = get("transactions.groupdialog3") + let groupdialog4 = get("transactions.groupdialog4") + let myTxnrequest = await parentEpml.request('transaction', { + type: 24, + nonce: this.selectedAddress.nonce, + params: { + _groupId: groupId, + fee: leaveFeeInput, + member: this.selectedHead.address, + lastReference: lastRef + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + + if (txnResponse.success === false && txnResponse.message) { + this.error = true + this.message = txnResponse.message + throw new Error(txnResponse) + } else if (txnResponse.success === true && !txnResponse.data.error) { + this.message = this.renderErr9Text() + this.error = false + this.confirmRelationship() + } else { + this.error = true + this.message = txnResponse.data.message + throw new Error(txnResponse) + } + } + validateReceiver() + } + + async _removeAdmin(groupId) { + this.resetDefaultSettings() + + const leaveFeeInput = await this.unitFee('REMOVE_GROUP_ADMIN') + if(!leaveFeeInput){ + throw Error() + } + this.isLoading = true + + // Get Last Ref + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + }; + + const validateReceiver = async () => { + let lastRef = await getLastRef(); + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + + } + + // Make Transaction Request + const makeTransactionRequest = async (lastRef) => { + let groupdialog3 = get("transactions.groupdialog3") + let groupdialog4 = get("transactions.groupdialog4") + let myTxnrequest = await parentEpml.request('transaction', { + type: 25, + nonce: this.selectedAddress.nonce, + params: { + _groupId: groupId, + fee: leaveFeeInput, + member: this.selectedHead.address, + lastReference: lastRef + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + + if (txnResponse.success === false && txnResponse.message) { + this.error = true + this.message = txnResponse.message + throw new Error(txnResponse) + } else if (txnResponse.success === true && !txnResponse.data.error) { + this.message = this.renderErr9Text() + this.error = false + this.confirmRelationship() + } else { + this.error = true + this.message = txnResponse.data.message + throw new Error(txnResponse) + } + } + validateReceiver() + } + + 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.groupMembers.length < 20){ + return + } + console.log('this.leaveGroupObjp', this.leaveGroupObj) + this.getMoreMembers(this.leaveGroupObj.groupId) + } + } + render() { + console.log('this.groupMembers', this.groupMembers) + const owner = this.groupAdmin.filter((admin)=> admin.address === this.leaveGroupObj.owner) + return html` +
+
+ this.toggle(false)} style="margin: 0px 10px" icon="vaadin:close" slot="icon"> +
+
+

${this.leaveGroupObj && this.leaveGroupObj.groupName}

+

${this.leaveGroupObj && this.leaveGroupObj.description}

+

Members: ${this.leaveGroupObj && this.leaveGroupObj.memberCount}

+ +

Date created : ${new Date(this.leaveGroupObj.created).toLocaleDateString("en-US")}

+
+

Group Owner

+ ${owner.map((item) => { + return html` {}} + chatInfo=${JSON.stringify(item)} + >` + })} +

Admins

+ ${this.groupAdmin.map((item) => { + return html` {}} + chatInfo=${JSON.stringify(item)} + >` + })} +

Members

+ ${this.groupMembers.map((item) => { + return html` { + console.log({ val }) + this.selectedHead = val + this.isOpenLeaveModal = true + }} + chatInfo=${JSON.stringify(item)} + >` + })} +
+
+ + { + if (this.isLoading) return + this.isOpenLeaveModal = false + }} + style=${ + this.isOpenLeaveModal ? "display: block" : "display: none" + }> +
+

${translate("grouppage.gchange35")}

+
+
+ + + +
+ + + ${translate("grouppage.gchange36")}   + + + + + ${this.message} + +
+ + + +
+
+
+ ` + } +} + +customElements.define("chat-right-panel", ChatRightPanel) diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js index 204b302d..ad87b308 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js @@ -662,6 +662,7 @@ class ChatMenu extends LitElement { } render() { + console.log(this.messages, 'this.messages') return html`
this.getUrl(this.chatInfo.url)} class="clearfix"> +
  • this.getUrl(this.chatInfo)} class="clearfix"> ${this.isImageLoaded ? html`${avatarImg}` : html`` } ${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`account_circle` : html`` } ${!this.isImageLoaded && this.chatInfo.name ? html`
    ${this.chatInfo.name.charAt(0)}
    `: ''} diff --git a/qortal-ui-plugins/plugins/core/components/WrapperModal-css.js b/qortal-ui-plugins/plugins/core/components/WrapperModal-css.js index 4dbdce66..50b0534a 100644 --- a/qortal-ui-plugins/plugins/core/components/WrapperModal-css.js +++ b/qortal-ui-plugins/plugins/core/components/WrapperModal-css.js @@ -10,6 +10,7 @@ export const wrapperModalStyles = css` background: rgb(186 186 186 / 26%); overflow: hidden; animation: backdrop_blur cubic-bezier(0.22, 1, 0.36, 1) 1s forwards; + z-index: 50 } .modal-body { @@ -29,6 +30,7 @@ export const wrapperModalStyles = css` overflow-y: auto; animation: 1s cubic-bezier(0.22, 1, 0.36, 1) 0s 1 normal forwards running modal_transition; max-height: 80%; + z-index: 51 } @keyframes backdrop_blur { diff --git a/qortal-ui-plugins/plugins/core/components/WrapperModal.js b/qortal-ui-plugins/plugins/core/components/WrapperModal.js index 23b6c040..785f7aad 100644 --- a/qortal-ui-plugins/plugins/core/components/WrapperModal.js +++ b/qortal-ui-plugins/plugins/core/components/WrapperModal.js @@ -6,6 +6,7 @@ export class WrapperModal extends LitElement { static get properties() { return { removeImage: { type: Function }, + customStyle: {type: String} } } @@ -17,7 +18,7 @@ export class WrapperModal extends LitElement {
    { this.removeImage() }}>
    - diff --git a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js index ee4eedc3..be56a9bf 100644 --- a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js +++ b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js @@ -523,6 +523,7 @@ class GroupManagement extends LitElement { setTimeout(getOpen_JoinedGroups, 1) configLoaded = true } + console.log('parse', JSON.parse(c)) this.config = JSON.parse(c) }) parentEpml.subscribe('copy_menu_switch', async value => { diff --git a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js index 5f5cbfb4..01697455 100644 --- a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -10,6 +10,7 @@ registerTranslateConfig({ import '../../components/ChatWelcomePage.js' import '../../components/ChatHead.js' import '../../components/ChatPage.js' +import '../../components/ChatGroupsManagement.js' import snackbar from '../../components/snackbar.js' import '@polymer/paper-spinner/paper-spinner-lite.js' import '@material/mwc-button' @@ -342,6 +343,7 @@ class Chat extends LitElement {
      ${this.isEmptyArray(this.chatHeads) ? this.renderLoadingText() : this.renderChatHead(this.chatHeads)}