start group chat components

This commit is contained in:
Phillip Lang Martinez 2022-12-23 17:01:30 -05:00
parent 7639c8b211
commit b986f1ae58
19 changed files with 1662 additions and 40 deletions

View File

@ -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 ?
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
</div>
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
}
}

View File

@ -58,6 +58,7 @@ export default class LeaveGroupTransaction extends TransactionBase {
this._rGroupIdBytes,
this._feeBytes
)
console.log('check exec params2', params)
return params;
}
}

View File

@ -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 ?
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
</div>
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
}
}

View File

@ -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 ?
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
</div>
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
}
}

View File

@ -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,

View File

@ -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",

View File

@ -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`
<vaadin-icon @click=${()=> {
this.isOpenLeaveModal = true
}} class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:users" slot="icon"></vaadin-icon>
<wrapper-modal
.removeImage=${() => {
if (this.isLoading) return
this.isOpenLeaveModal = false
}}
style=${
this.isOpenLeaveModal ? "display: block" : "display: none"
}>
<div style="text-align:center">
<h1>${translate("grouppage.gchange35")}</h1>
<hr>
</div>
<button @click=${() =>
this._addAdmin(
this.leaveGroupObj.groupId
)}>Promote to Admin</button>
<button @click=${() =>
this._removeAdmin(
this.leaveGroupObj.groupId
)}>Remove as Admin</button>
<div style="text-align:right; height:36px;">
<span ?hidden="${!this.isLoading}">
<!-- loading message -->
${translate("grouppage.gchange36")} &nbsp;
<paper-spinner-lite
style="margin-top:12px;"
?active="${this.isLoading}"
alt="Leaving"
>
</paper-spinner-lite>
</span>
<span ?hidden=${this.message === ""} style="${
this.error ? "color:red;" : ""
}">
${this.message}
</span>
</div>
<button
@click=${() => {
this.isOpenLeaveModal = false
}}
class="modal-button"
?disabled="${this.isLoading}"
>
${translate("general.close")}
</button>
</wrapper-modal >
`
}
}
customElements.define("chat-right-panel", ChatGroupInvites)

View File

@ -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`
<vaadin-icon @click=${()=> {
this.isOpenLeaveModal = true
}} class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:cog" slot="icon"></vaadin-icon>
<!-- Leave Group Dialog -->
<wrapper-modal
.removeImage=${() => {
if(this.isLoading) return
this.isOpenLeaveModal = false
} }
style=${(this.isOpenLeaveModal) ? "display: block" : "display: none"}>
<div style="text-align:center">
<h1>${translate("grouppage.gchange35")}</h1>
<hr>
</div>
<
<button @click=${() => this._convertToPrivate(this.leaveGroupObj.groupId, this.leaveGroupObj.groupName)}> Convert a public group to private</button>
<div style="text-align:right; height:36px;">
<span ?hidden="${!this.isLoading}">
<!-- loading message -->
${translate("grouppage.gchange36")} &nbsp;
<paper-spinner-lite
style="margin-top:12px;"
?active="${this.isLoading}"
alt="Leaving"
>
</paper-spinner-lite>
</span>
<span ?hidden=${this.message === ''} style="${this.error ? 'color:red;' : ''}">
${this.message}
</span>
</div>
<button
@click=${() => {
this.isOpenLeaveModal= false
}}
class="modal-button"
?disabled="${this.isLoading}"
>
${translate("general.close")}
</button>
</wrapper-modal >
`;
}
}
customElements.define('chat-group-settings', ChatGroupSettings);

View File

@ -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`
<vaadin-horizontal-layout style="align-items: center;display:flex" theme="spacing">
<vaadin-avatar style="margin-right:5px" img="${person.pictureUrl}" .name="${person.displayName}"></vaadin-avatar>
<span> ${person.displayName} </span>
</vaadin-horizontal-layout>
`;
};
render() {
return html`
<vaadin-icon @click=${()=> {
this.isOpenLeaveModal = true
}} class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:exit" slot="icon"></vaadin-icon>
<!-- Leave Group Dialog -->
<wrapper-modal
.removeImage=${() => {
if(this.isLoading) return
this.isOpenLeaveModal = false
} }
customStyle=${"width: 90%; max-width: 900px; height: 90%"}
style=${(this.isOpenLeaveModal) ? "display: block" : "display: none"}>
<div style="width: 100%;height: 100%;display: flex; flex-direction: column;background:var(--mdc-theme-surface)">
<div style="height: 50px;display: flex; flex:0">
<vaadin-tabs id="tabs" selected="${this.currentTab}" @selected-changed="${this._tabChanged}" style="width: 100%">
<vaadin-tab>Groups</vaadin-tab>
<vaadin-tab>Group Join Requests</vaadin-tab>
<vaadin-tab>Invites</vaadin-tab>
<vaadin-tab>Blocked Users</vaadin-tab>
</vaadin-tabs>
</div>
<div style="width: 100%;display: flex; flex-direction: column; flex-grow: 1; overflow:auto;background:var(--mdc-theme-surface)">
${this.currentTab === 0 ? html`
<div>
<!-- Groups tab -->
<!-- Search groups and be able to join -->
<p>Search groups</p>
<!-- Click group and it goes to that group and open right panel and settings -->
<p>Current groups as owner</p>
<p>Current groups as member</p>
</div>
` : ''}
</div>
<div style="width: 100%;height: 50;display: flex; flex: 0">
<button
class="modal-button"
?disabled="${this.isLoading}"
@click=${() => this._leaveGroup(this.leaveGroupObj.groupId, this.leaveGroupObj.groupName)}
>
${translate("grouppage.gchange37")}
</button>
<button
@click=${() => {
this.isOpenLeaveModal= false
}}
class="modal-button"
?disabled="${this.isLoading}"
>
${translate("general.close")}
</button>
</div>
</div>
</wrapper-modal >
`;
}
}
customElements.define('chat-groups-management', ChatGroupsManagement);

View File

@ -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%;
}

View File

@ -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

View File

@ -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`
<div class="main-container">
<div class="chat-container">
${(!this.isReceipient && +this._chatId !== 0) ? html`
<div style="display:flex; height:40px; padding:3px; margin:0px;background-color: var(--chat-bubble-bg); box-sizing: border-box; align-items: center;justify-content: space-between">
<div>
<p style="color: var(--black);margin:0px;padding:0px">Name</p>
<div @click=${this._toggle} style="height: 100%;display: flex;align-items: center;flex-grow: 1;cursor: pointer;cursor:pointer;user-select:none">
<p style="color: var(--black);margin:0px;padding:0px">${this.groupInfo && this.groupInfo.groupName}</p>
</div>
<div style="display:flex;height:100%;align-items:center">
<vaadin-icon class="top-bar-icon" @click=${this._toggle} style="margin: 0px 10px" icon="vaadin:info" slot="icon"></vaadin-icon>
<vaadin-icon class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:cog" slot="icon"></vaadin-icon>
<vaadin-icon class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:search" slot="icon"></vaadin-icon>
<chat-group-settings .chatHeads=${this.chatHeads} .selectedAddress=${this.selectedAddress} .leaveGroupObj=${this.groupInfo} .setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}></chat-group-settings>
<!-- <vaadin-icon class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:search" slot="icon"></vaadin-icon> -->
<!-- <vaadin-icon class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:exit" slot="icon"></vaadin-icon> -->
<chat-leave-group .chatHeads=${this.chatHeads} .selectedAddress=${this.selectedAddress} .leaveGroupObj=${this.groupInfo} .setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}></chat-leave-group>
</div>
</div>
` : html`<div></div>`}
<div>
${this.isLoadingMessages ?
html`
@ -829,36 +839,44 @@ class ChatPage extends LitElement {
</div>
</wrapper-modal>
</div>
<div class="chat-right-panel ${this.shifted ? "movedin" : "movedout"}" ${animate()}>
<p>Exit Icon</p>
<span>Group Avatar</span> <p>Group Name</p>
<p>Group owner</p>
<p>100 Members</p>
<p>Description</p>
<p>date created</p>
<p>private / public</p>
<p>approvalThreshold</p>
<p>"minimumBlockDelay": 0, "maximumBlockDelay": 0</p>
<p class="chat-right-panel-label">Admins</p>
${this.groupAdmin.map((item)=> {
return html`<chat-side-nav-heads activeChatHeadUrl="" setActiveChatHeadUrl=${(val)=> {
}} chatInfo=${JSON.stringify(item)}></chat-side-nav-heads>`
})}
<p class="chat-right-panel-label">Members</p>
${this.groupAdmin.map((item)=> {
return html`<chat-side-nav-heads activeChatHeadUrl="" setActiveChatHeadUrl=${(val)=> {
}} chatInfo=${JSON.stringify(item)}></chat-side-nav-heads>`
})}
</div>
<div class="chat-right-panel ${this.shifted ? "movedin" : "movedout"}" ${animate()}>
<chat-right-panel .getMoreMembers=${(val)=> this.getMoreMembers(val)} .toggle=${(val)=> this._toggle(val)} .selectedAddress=${this.selectedAddress} .groupMembers=${this.groupMembers} .groupAdmin=${this.groupAdmin} .leaveGroupObj=${this.groupInfo}></chat-right-panel>
</div>
</div>
`
}
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);

View File

@ -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`
<div class="container">
<div class="close-row" style="margin-top: 15px">
<vaadin-icon class="top-bar-icon" @click=${()=> this.toggle(false)} style="margin: 0px 10px" icon="vaadin:close" slot="icon"></vaadin-icon>
</div>
<div id="viewElement" class="container-body">
<p style="font-size: 20px;">${this.leaveGroupObj && this.leaveGroupObj.groupName}</p>
<p style="font-size: 14px;margin-top: 5px">${this.leaveGroupObj && this.leaveGroupObj.description}</p>
<p style="font-size: 14px;margin-top: 10px">Members: ${this.leaveGroupObj && this.leaveGroupObj.memberCount}</p>
<p style="font-size: 14px;margin-top: 5px">Date created : ${new Date(this.leaveGroupObj.created).toLocaleDateString("en-US")}</p>
<br />
<p class="chat-right-panel-label">Group Owner</p>
${owner.map((item) => {
return html`<chat-side-nav-heads
activeChatHeadUrl=""
.setActiveChatHeadUrl=${(val) => {}}
chatInfo=${JSON.stringify(item)}
></chat-side-nav-heads>`
})}
<p class="chat-right-panel-label">Admins</p>
${this.groupAdmin.map((item) => {
return html`<chat-side-nav-heads
activeChatHeadUrl=""
.setActiveChatHeadUrl=${(val) => {}}
chatInfo=${JSON.stringify(item)}
></chat-side-nav-heads>`
})}
<p class="chat-right-panel-label">Members</p>
${this.groupMembers.map((item) => {
return html`<chat-side-nav-heads
activeChatHeadUrl=""
.setActiveChatHeadUrl=${(val) => {
console.log({ val })
this.selectedHead = val
this.isOpenLeaveModal = true
}}
chatInfo=${JSON.stringify(item)}
></chat-side-nav-heads>`
})}
<div id='downObserver'></div>
</div>
<wrapper-modal
.removeImage=${() => {
if (this.isLoading) return
this.isOpenLeaveModal = false
}}
style=${
this.isOpenLeaveModal ? "display: block" : "display: none"
}>
<div style="text-align:center">
<h1>${translate("grouppage.gchange35")}</h1>
<hr>
</div>
<button @click=${() =>
this._addAdmin(
this.leaveGroupObj.groupId
)}>Promote to Admin</button>
<button @click=${() =>
this._removeAdmin(
this.leaveGroupObj.groupId
)}>Remove as Admin</button>
<div style="text-align:right; height:36px;">
<span ?hidden="${!this.isLoading}">
<!-- loading message -->
${translate("grouppage.gchange36")} &nbsp;
<paper-spinner-lite
style="margin-top:12px;"
?active="${this.isLoading}"
alt="Leaving"
>
</paper-spinner-lite>
</span>
<span ?hidden=${this.message === ""} style="${
this.error ? "color:red;" : ""
}">
${this.message}
</span>
</div>
<button
@click=${() => {
this.isOpenLeaveModal = false
}}
class="modal-button"
?disabled="${this.isLoading}"
>
${translate("general.close")}
</button>
</wrapper-modal >
</div>
</div>
`
}
}
customElements.define("chat-right-panel", ChatRightPanel)

View File

@ -662,6 +662,7 @@ class ChatMenu extends LitElement {
}
render() {
console.log(this.messages, 'this.messages')
return html`
<div class="container">
<div

View File

@ -124,7 +124,7 @@ class ChatSideNavHeads extends LitElement {
}
return html`
<li @click=${() => this.getUrl(this.chatInfo.url)} class="clearfix">
<li @click=${() => this.getUrl(this.chatInfo)} class="clearfix">
${this.isImageLoaded ? html`${avatarImg}` : html`` }
${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`<mwc-icon class="img-icon">account_circle</mwc-icon>` : html`` }
${!this.isImageLoaded && this.chatInfo.name ? html`<div style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadBgActive)' : 'var(--chatHeadBg)' }; color: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadTextActive)' : 'var(--chatHeadText)' }; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize">${this.chatInfo.name.charAt(0)}</div>`: ''}

View File

@ -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 {

View File

@ -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 {
<div class="backdrop" @click=${() => {
this.removeImage()
}}></div>
<div class="modal-body">
<div class="modal-body" style=${this.customStyle ? this.customStyle : ""}>
<slot></slot>
</div>
</div>

View File

@ -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 => {

View File

@ -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 {
<div class="people-list" id="people-list">
<div class="search">
<div class="create-chat" @click=${() => this.shadowRoot.querySelector('#startChatDialog').show()}>${translate("chatpage.cchange1")}</div>
<chat-groups-management></chat-groups-management>
</div>
<ul class="list">
${this.isEmptyArray(this.chatHeads) ? this.renderLoadingText() : this.renderChatHead(this.chatHeads)}