Merge remote-tracking branch 'justin/justin-groups-features' into feature/group-features

This commit is contained in:
Phillip 2023-01-10 15:50:54 -05:00
commit c2eaa62d4a
13 changed files with 1296 additions and 736 deletions

2
.gitignore vendored
View File

@ -5,7 +5,7 @@ yarn.lock
qortal-ui-plugins/plugins/core/**/*.js qortal-ui-plugins/plugins/core/**/*.js
!*.src.js !*.src.js
qortal-ui-core/src/redux/app/version.js qortal-ui-core/src/redux/app/version.js
!qortal-ui-plugins/plugins/core/components/*.js !qortal-ui-plugins/plugins/core/components/**/*.js
# Node modules # Node modules
node_modules/ node_modules/

View File

@ -21,22 +21,22 @@
"korean": "Korean" "korean": "Korean"
}, },
"sidemenu": { "sidemenu": {
"minting":"MINTING", "minting": "MINTING",
"mintingdetails":"MINTING DETAILS", "mintingdetails": "MINTING DETAILS",
"becomeAMinter":"BECOME A MINTER", "becomeAMinter": "BECOME A MINTER",
"wallets":"WALLETS", "wallets": "WALLETS",
"tradeportal":"TRADE PORTAL", "tradeportal": "TRADE PORTAL",
"rewardshare":"REWARD SHARE", "rewardshare": "REWARD SHARE",
"nameregistration":"NAME REGISTRATION", "nameregistration": "NAME REGISTRATION",
"websites":"WEBSITES", "websites": "WEBSITES",
"management":"MANAGEMENT", "management": "MANAGEMENT",
"datamanagement":"DATA MANAGEMENT", "datamanagement": "DATA MANAGEMENT",
"qchat":"Q-CHAT", "qchat": "Q-CHAT",
"groupmanagement":"GROUP MANAGEMENT", "groupmanagement": "GROUP MANAGEMENT",
"puzzles":"PUZZLES", "puzzles": "PUZZLES",
"nodemanagement":"NODE MANAGEMENT", "nodemanagement": "NODE MANAGEMENT",
"trading":"TRADING", "trading": "TRADING",
"groups":"GROUPS" "groups": "GROUPS"
}, },
"login": { "login": {
"login": "Login", "login": "Login",
@ -134,33 +134,33 @@
"exp3":"Export", "exp3":"Export",
"exp4":"Please choose a wallet to backup the private master key." "exp4":"Please choose a wallet to backup the private master key."
}, },
"appinfo":{ "appinfo": {
"blockheight":"Block Height", "blockheight": "Block Height",
"uiversion":"UI Version", "uiversion": "UI Version",
"coreversion":"Core Version", "coreversion": "Core Version",
"minting":"(Minting)", "minting": "(Minting)",
"synchronizing":"Synchronizing", "synchronizing": "Synchronizing",
"peers":"Connected Peers" "peers": "Connected Peers"
}, },
"walletprofile": { "walletprofile": {
"minterlevel": "Minter Level", "minterlevel": "Minter Level",
"blocksminted": "Blocks Minted" "blocksminted": "Blocks Minted"
}, },
"general":{ "general": {
"yes":"Yes", "yes": "Yes",
"no":"No", "no": "No",
"confirm":"Confirm", "confirm": "Confirm",
"decline":"Decline", "decline": "Decline",
"open":"Open", "open": "Open",
"close":"Close", "close": "Close",
"back":"Back", "back": "Back",
"next":"Next", "next": "Next",
"create":"Create", "create": "Create",
"continue":"Continue", "continue": "Continue",
"save":"Save", "save": "Save",
"balance":"Balance", "balance": "Balance",
"balances":"YOUR WALLET BALANCES", "balances": "YOUR WALLET BALANCES",
"update":"UPDATE WALLET BALANCES" "update": "UPDATE WALLET BALANCES"
}, },
"startminting": { "startminting": {
"smchange1": "Cannot fetch minting accounts", "smchange1": "Cannot fetch minting accounts",
@ -360,47 +360,47 @@
"rchange21": "Reward Share Successful!", "rchange21": "Reward Share Successful!",
"rchange22": "Reward Share Removed Successfully!" "rchange22": "Reward Share Removed Successfully!"
}, },
"registernamepage":{ "registernamepage": {
"nchange1":"Name Registration", "nchange1": "Name Registration",
"nchange2":"Register Name", "nchange2": "Register Name",
"nchange3":"Registered Names", "nchange3": "Registered Names",
"nchange4":"Avatar", "nchange4": "Avatar",
"nchange5":"Name", "nchange5": "Name",
"nchange6":"Owner", "nchange6": "Owner",
"nchange7":"Action", "nchange7": "Action",
"nchange8":"No names registered by this account!", "nchange8": "No names registered by this account!",
"nchange9":"Register a Name!", "nchange9": "Register a Name!",
"nchange10":"Description (optional)", "nchange10": "Description (optional)",
"nchange11":"Doing something delicious", "nchange11": "Doing something delicious",
"nchange12":"Registering Name", "nchange12": "Registering Name",
"nchange13":"The current name registration fee is", "nchange13": "The current name registration fee is",
"nchange14":"Register", "nchange14": "Register",
"nchange15":"Set Avatar", "nchange15": "Set Avatar",
"nchange16":"Need Core Update", "nchange16": "Need Core Update",
"nchange17":"Name Already Exists!", "nchange17": "Name Already Exists!",
"nchange18":"Name Registration Successful!", "nchange18": "Name Registration Successful!",
"nchange19":"Sell Name", "nchange19": "Sell Name",
"nchange20":"Cancel Sell", "nchange20": "Cancel Sell",
"nchange21":"Buy Name", "nchange21": "Buy Name",
"nchange22":"Open Market Names To Sell", "nchange22": "Open Market Names To Sell",
"nchange23":"Sell Price", "nchange23": "Sell Price",
"nchange24":"No Names To Sell", "nchange24": "No Names To Sell",
"nchange25":"Name To Sell", "nchange25": "Name To Sell",
"nchange26":"Are you sure to sell this name ?", "nchange26": "Are you sure to sell this name ?",
"nchange27":"For this price in QORT", "nchange27": "For this price in QORT",
"nchange28":"On pressing confirm, the sell name request will be sent!", "nchange28": "On pressing confirm, the sell name request will be sent!",
"nchange29":"Name To Cancel", "nchange29": "Name To Cancel",
"nchange30":"Are you sure to cancel the sell for this name ?", "nchange30": "Are you sure to cancel the sell for this name ?",
"nchange31":"On pressing confirm, the cancel sell name request will be sent!", "nchange31": "On pressing confirm, the cancel sell name request will be sent!",
"nchange32":"Sell Name Request Successful!", "nchange32": "Sell Name Request Successful!",
"nchange33":"Cancel Sell Name Request Successful!", "nchange33": "Cancel Sell Name Request Successful!",
"nchange34":"Buy Name Request Successful!", "nchange34": "Buy Name Request Successful!",
"nchange35":"YOU HAVE A NAME!", "nchange35": "YOU HAVE A NAME!",
"nchange36":"Only accounts with no registered name can buy a name.", "nchange36": "Only accounts with no registered name can buy a name.",
"nchange37":"ATTENTION!", "nchange37": "ATTENTION!",
"nchange38":"You not have enough qort to buy this name.", "nchange38": "You not have enough qort to buy this name.",
"nchange39":"Are you sure to buy this name ?", "nchange39": "Are you sure to buy this name ?",
"nchange40":"On pressing confirm, the buy name request will be sent!" "nchange40": "On pressing confirm, the buy name request will be sent!"
}, },
"websitespage": { "websitespage": {
"schange1": "Browse Websites", "schange1": "Browse Websites",
@ -547,9 +547,25 @@
"cchange38": "User Verified", "cchange38": "User Verified",
"cchange39": "Cannot send an encrypted message to this user since they do not have their publickey on chain.", "cchange39": "Cannot send an encrypted message to this user since they do not have their publickey on chain.",
"cchange40": "IMAGE (click to view)", "cchange40": "IMAGE (click to view)",
"cchange41":"Your Balance Is Under 4.20 QORT", "cchange41": "Your Balance Is Under 4.20 QORT",
"cchange42":"Out of the need to combat spam, accounts with under 4.20 Qort balance will take a long time to SEND messages in Q-Chat. If you wish to immediately increase the send speed for Q-Chat messages, obtain over 4.20 QORT to your address. This can be done with trades in the Trade Portal, or by way of another Qortian giving you the QORT. Once you have over 4.20 QORT in your account, Q-Chat messages will be instant and this dialog will no more show. Thank you for your understanding of this necessary spam prevention method, and we hope you enjoy Qortal!" "cchange42": "Out of the need to combat spam, accounts with under 4.20 Qort balance will take a long time to SEND messages in Q-Chat. If you wish to immediately increase the send speed for Q-Chat messages, obtain over 4.20 QORT to your address. This can be done with trades in the Trade Portal, or by way of another Qortian giving you the QORT. Once you have over 4.20 QORT in your account, Q-Chat messages will be instant and this dialog will no more show. Thank you for your understanding of this necessary spam prevention method, and we hope you enjoy Qortal!",
"cchange43": "Tip QORT to",
"cchange44": "SEND MESSAGE",
"cchange45": "TIP USER",
"cchange46": "Tip Amount",
"cchange47": "Available Balance",
"cchange48": "Failed to Fetch QORT Balance. Try again!",
"cchange49": "Current static fee",
"cchange50": "Send",
"cchange51": "Insufficient Funds!",
"cchange52": "Invalid Amount!",
"cchange53": "Receiver cannot be empty!",
"cchange54": "Invalid Receiver!",
"cchange55": "Transaction Successful!",
"cchange56": "Transaction Failed!",
"cchange57": "User Info",
"cchange58": "SEND MESSAGE",
"cchange59": "TIP USER"
}, },
"welcomepage": { "welcomepage": {
"wcchange1": "Welcome to Q-Chat", "wcchange1": "Welcome to Q-Chat",
@ -579,67 +595,68 @@
"bcchange14": "Forward", "bcchange14": "Forward",
"bcchange15": "Message Forwarded", "bcchange15": "Message Forwarded",
"bcchange16": "Choose Recipient or Search for One Below", "bcchange16": "Choose Recipient or Search for One Below",
"bcchange17": "FORWARDED" "bcchange17": "FORWARDED",
"bcchange18": "Tip User"
}, },
"grouppage":{ "grouppage": {
"gchange1":"Qortal Groups", "gchange1": "Qortal Groups",
"gchange2":"Create Group", "gchange2": "Create Group",
"gchange3":"Your Joined Groups", "gchange3": "Your Joined Groups",
"gchange4":"Group Name", "gchange4": "Group Name",
"gchange5":"Description", "gchange5": "Description",
"gchange6":"Role", "gchange6": "Role",
"gchange7":"Action", "gchange7": "Action",
"gchange8":"Not a member of any group!", "gchange8": "Not a member of any group!",
"gchange9":"Public Groups", "gchange9": "Public Groups",
"gchange10":"Owner", "gchange10": "Owner",
"gchange11":"No Open Public Groups available!", "gchange11": "No Open Public Groups available!",
"gchange12":"Create a New Group", "gchange12": "Create a New Group",
"gchange13":"Group Type", "gchange13": "Group Type",
"gchange14":"This Field is Required", "gchange14": "This Field is Required",
"gchange15":"Select an option", "gchange15": "Select an option",
"gchange16":"Public", "gchange16": "Public",
"gchange17":"Private", "gchange17": "Private",
"gchange18":"Group Approval Threshold (number / percentage of Admins that must approve a transaction):", "gchange18": "Group Approval Threshold (number / percentage of Admins that must approve a transaction):",
"gchange19":"NONE", "gchange19": "NONE",
"gchange20":"ONE", "gchange20": "ONE",
"gchange21":"Minimum Block delay for Group Transaction Approvals:", "gchange21": "Minimum Block delay for Group Transaction Approvals:",
"gchange22":"minutes", "gchange22": "minutes",
"gchange23":"hour", "gchange23": "hour",
"gchange24":"hours", "gchange24": "hours",
"gchange25":"day", "gchange25": "day",
"gchange26":"days", "gchange26": "days",
"gchange27":"Maximum Block delay for Group Transaction Approvals:", "gchange27": "Maximum Block delay for Group Transaction Approvals:",
"gchange28":"Creating Group", "gchange28": "Creating Group",
"gchange29":"Create Group", "gchange29": "Create Group",
"gchange30":"Join Group Request", "gchange30": "Join Group Request",
"gchange31":"Date Created", "gchange31": "Date Created",
"gchange32":"Date Updated", "gchange32": "Date Updated",
"gchange33":"Joining", "gchange33": "Joining",
"gchange34":"Join Group", "gchange34": "Join Group",
"gchange35":"Leave Group Request", "gchange35": "Leave Group Request",
"gchange36":"Leaving", "gchange36": "Leaving",
"gchange37":"Leave Group", "gchange37": "Leave Group",
"gchange38":"Manage Group Owner:", "gchange38": "Manage Group Owner:",
"gchange39":"Manage Group Admin:", "gchange39": "Manage Group Admin:",
"gchange40":"Manage Group", "gchange40": "Manage Group",
"gchange41":"Group Creation Successful!", "gchange41": "Group Creation Successful!",
"gchange42":"Invalid Group Name", "gchange42": "Invalid Group Name",
"gchange43":"Invalid Group Description", "gchange43": "Invalid Group Description",
"gchange44":"Select a Group Typ", "gchange44": "Select a Group Typ",
"gchange45":"Select a Group Approval Threshold", "gchange45": "Select a Group Approval Threshold",
"gchange46":"Select a Minimum Block delay for Group Transaction Approvals", "gchange46": "Select a Minimum Block delay for Group Transaction Approvals",
"gchange47":"Select a Maximum Block delay for Group Transaction Approvals", "gchange47": "Select a Maximum Block delay for Group Transaction Approvals",
"gchange48":"Join Group Request Sent Successfully!", "gchange48": "Join Group Request Sent Successfully!",
"gchange49":"Leave Group Request Sent Successfully!", "gchange49": "Leave Group Request Sent Successfully!",
"gchange50":"Leave", "gchange50": "Leave",
"gchange51":"Join", "gchange51": "Join",
"gchange52":"Admin", "gchange52": "Admin",
"gchange53":"Member", "gchange53": "Member",
"gchange54":"Members", "gchange54": "Members",
"gchange55":"Search Private Group", "gchange55": "Search Private Group",
"gchange56":"Group Name To Search", "gchange56": "Group Name To Search",
"gchange57":"Private Group Name Not Found", "gchange57": "Private Group Name Not Found",
"gchange58":"Note that group name must exact match." "gchange58": "Note that group name must exact match."
}, },
"puzzlepage": { "puzzlepage": {
"pchange1": "Puzzles", "pchange1": "Puzzles",
@ -729,51 +746,51 @@
"rewarddialog5": "You are removing a reward share transaction associated with account:", "rewarddialog5": "You are removing a reward share transaction associated with account:",
"rewarddialog6": "On pressing confirm, the rewardshare will be removed and the minting key will become invalid." "rewarddialog6": "On pressing confirm, the rewardshare will be removed and the minting key will become invalid."
}, },
"sponsorshipspage":{ "sponsorshipspage": {
"schange1":"Active Sponsorships", "schange1": "Active Sponsorships",
"schange2":"Account Address", "schange2": "Account Address",
"schange3":"Total Sponsorships active", "schange3": "Total Sponsorships active",
"schange4":"Next sponsorship ending in", "schange4": "Next sponsorship ending in",
"schange5":"Sponsor New Minter", "schange5": "Sponsor New Minter",
"schange6":"Finished Sponsorships", "schange6": "Finished Sponsorships",
"schange7":"Completed", "schange7": "Completed",
"schange8":"Addresses", "schange8": "Addresses",
"schange9":"You currently have no active sponsorships", "schange9": "You currently have no active sponsorships",
"schange10":"Public Key Lookup", "schange10": "Public Key Lookup",
"schange11":"Copy", "schange11": "Copy",
"schange12":"Address to Public Key Converter", "schange12": "Address to Public Key Converter",
"schange13":"Enter address", "schange13": "Enter address",
"schange14":"In progress", "schange14": "In progress",
"schange15":"Finishing up", "schange15": "Finishing up",
"schange16":"Copy the key below and share it with your sponsored person.", "schange16": "Copy the key below and share it with your sponsored person.",
"schange17":"Copied to clipboard", "schange17": "Copied to clipboard",
"schange18":"Warning: do not leave this plugin or close the Qortal UI until completion!", "schange18": "Warning: do not leave this plugin or close the Qortal UI until completion!",
"schange19":"Copy Sponsorship Key", "schange19": "Copy Sponsorship Key",
"schange20":"Creating relationship", "schange20": "Creating relationship",
"schange21":"Remove Sponsorship Key" "schange21": "Remove Sponsorship Key"
}, },
"explorerpage":{ "explorerpage": {
"exp1":"Address or name to search", "exp1": "Address or name to search",
"exp2":"Account Balance", "exp2": "Account Balance",
"exp3":"More Info", "exp3": "More Info",
"exp4":"Address or Name not found !", "exp4": "Address or Name not found !",
"exp5":"Note that registered names are case-sensitive.", "exp5": "Note that registered names are case-sensitive.",
"exp6":"Founder", "exp6": "Founder",
"exp7":"Info", "exp7": "Info",
"exp8":"Show all buy trades", "exp8": "Show all buy trades",
"exp9":"Show all sell trades", "exp9": "Show all sell trades",
"exp10":"BUY HISTORY", "exp10": "BUY HISTORY",
"exp11":"SELL HISTORY", "exp11": "SELL HISTORY",
"exp12":"No buy trades made yet.", "exp12": "No buy trades made yet.",
"exp13":"No sell trades made yet.", "exp13": "No sell trades made yet.",
"exp14":"Show complete info", "exp14": "Show complete info",
"exp15":"Minting Since", "exp15": "Minting Since",
"exp16":"Not Minting", "exp16": "Not Minting",
"exp17":"ALL PAYMENTS", "exp17": "ALL PAYMENTS",
"exp18":"Payments", "exp18": "Payments",
"exp19":"Sent", "exp19": "Sent",
"exp20":"Received", "exp20": "Received",
"exp21":"Trades" "exp21": "Trades"
}, },
"managegroup":{ "managegroup":{
"mg1":"Group Members", "mg1":"Group Members",

View File

@ -142,8 +142,6 @@ class AppView extends connect(store)(LitElement) {
app-drawer { app-drawer {
box-shadow: var(--shadow-2); box-shadow: var(--shadow-2);
background: var(--sidetopbar);
--app-drawer-scrim-background: rgba(0,0,0,0);
} }
app-header { app-header {
@ -185,24 +183,26 @@ class AppView extends connect(store)(LitElement) {
background: var(--sidetopbar); background: var(--sidetopbar);
} }
.sideBarMenu{ .sideBarMenu {
overflow-y: auto; overflow-y: auto;
flex: 1 1; flex: 1 1;
} }
#sideBar::-webkit-scrollbar { .sideBarMenu::-webkit-scrollbar-track {
width: 7px; background-color: whitesmoke;
background-color: transparent; border-radius: 7px;
} }
#sideBar::-webkit-scrollbar-track { .sideBarMenu::-webkit-scrollbar {
background-color: transparent; width: 6px;
border-radius: 7px;
background-color: whitesmoke;
} }
#sideBar::-webkit-scrollbar-thumb { .sideBarMenu::-webkit-scrollbar-thumb {
background-color: #333; background-color: rgb(180, 176, 176);
border-radius: 6px; border-radius: 7px;
border: 3px solid #333; transition: all 0.3s ease-in-out;
} }
#balanceheader { #balanceheader {
@ -325,6 +325,11 @@ class AppView extends connect(store)(LitElement) {
0%,100% { opacity: 0; } 0%,100% { opacity: 0; }
50% { opacity: 10; } 50% { opacity: 10; }
} }
.sideBarMenu::-webkit-scrollbar-thumb:hover {
background-color: rgb(148, 146, 146);
cursor: pointer;
}
` `
] ]
} }

View File

@ -107,6 +107,7 @@ class ChatPage extends LitElement {
flex-direction: column; flex-direction: column;
height: 50vh; height: 50vh;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden;
width: 100%; width: 100%;
} }
@ -289,7 +290,6 @@ class ChatPage extends LitElement {
.chat-container { .chat-container {
display: grid; display: grid;
grid-template-rows: minmax(6%, 92vh) minmax(40px, auto);
max-height: 100%; max-height: 100%;
} }
@ -598,13 +598,6 @@ class ChatPage extends LitElement {
object-fit: contain; object-fit: contain;
} }
.chat-container {
display: grid;
grid-template-rows: minmax(40px, auto) minmax(6%, 92vh) minmax(40px, auto);
max-height: 100%;
flex: 3;
}
.chat-right-panel { .chat-right-panel {
flex: 0; flex: 0;
border-left: 3px solid rgb(221, 221, 221); border-left: 3px solid rgb(221, 221, 221);
@ -869,21 +862,24 @@ class ChatPage extends LitElement {
render() { render() {
return html` return html`
<div class="main-container"> <div class="main-container">
<div class="chat-container"> <div
${(!this.isReceipient && +this._chatId !== 0) ? html` class="chat-container"
style=${(!this.isReceipient && +this._chatId !== 0) ? "grid-template-rows: minmax(40px, auto) minmax(6%, 92vh) minmax(40px, auto); flex: 3;" : "grid-template-rows: minmax(6%, 92vh) minmax(40px, auto); flex: 2;"}>
${(!this.isReceipient && +this._chatId !== 0) ?
html`
<div class="group-nav-container"> <div class="group-nav-container">
<div @click=${this._toggle} style="height: 100%;display: flex;align-items: center;flex-grow: 1;cursor: pointer;cursor:pointer;user-select:none"> <div @click=${this._toggle} style="height: 100%; display: flex; align-items: center;flex-grow: 1; cursor: pointer; cursor: pointer; user-select: none">
<p class="group-name">${this.groupInfo && this.groupInfo.groupName}</p> <p class="group-name">${this.groupInfo && this.groupInfo.groupName}</p>
</div> </div>
<div style="display:flex;height:100%;align-items:center"> <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" @click=${this._toggle} style="margin: 0px 10px" icon="vaadin:info" slot="icon"></vaadin-icon>
<!-- <chat-group-settings .chatHeads=${this.chatHeads} .selectedAddress=${this.selectedAddress} .leaveGroupObj=${this.groupInfo} .setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}></chat-group-settings> --> <!-- <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:search" slot="icon"></vaadin-icon> -->
<!-- <vaadin-icon class="top-bar-icon" style="margin: 0px 20px" icon="vaadin:exit" 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> --> <!-- <chat-leave-group .chatHeads=${this.chatHeads} .selectedAddress=${this.selectedAddress} .leaveGroupObj=${this.groupInfo} .setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}></chat-leave-group> -->
</div> </div>
</div> </div>
` : html`<div></div>`} ` : null}
<div> <div>
${this.isLoadingMessages ? ${this.isLoadingMessages ?
@ -1176,12 +1172,20 @@ class ChatPage extends LitElement {
</div> </div>
</wrapper-modal> </wrapper-modal>
</div> </div>
<div class="chat-right-panel ${this.shifted ? "movedin" : "movedout"}" ${animate()}> <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> <chat-right-panel
.getMoreMembers=${(val)=> this.getMoreMembers(val)}
</div> .toggle=${(val)=> this._toggle(val)}
</div> .selectedAddress=${this.selectedAddress}
` .groupMembers=${this.groupMembers}
.groupAdmin=${this.groupAdmin}
.leaveGroupObj=${this.groupInfo}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
.chatEditor=${this.chatEditor}>
</chat-right-panel>
</div>
</div>
`
} }
async getMoreMembers(groupId){ async getMoreMembers(groupId){
@ -1458,12 +1462,10 @@ class ChatPage extends LitElement {
async updated(changedProperties) { async updated(changedProperties) {
if (changedProperties && changedProperties.has('userLanguage')) { if (changedProperties && changedProperties.has('userLanguage')) {
const userLang = changedProperties.get('userLanguage') const userLang = changedProperties.get('userLanguage')
if (userLang) {
if(userLang){
await new Promise(r => setTimeout(r, 100)); await new Promise(r => setTimeout(r, 100));
this.chatEditorPlaceholder = this.isReceipient === true ? `Message ${this._chatId}` : `${get("chatpage.cchange8")}`; this.chatEditorPlaceholder = this.isReceipient === true ? `Message ${this._chatId}` : `${get("chatpage.cchange8")}`;
} }
} }
if (changedProperties && changedProperties.has('chatId') && changedProperties.get('chatId')) { if (changedProperties && changedProperties.has('chatId') && changedProperties.get('chatId')) {
@ -1475,7 +1477,6 @@ class ChatPage extends LitElement {
this.chatEditor.disable(); this.chatEditor.disable();
} }
} }
} }
async getName (recipient) { async getName (recipient) {
@ -1529,6 +1530,7 @@ async getName (recipient) {
chatId=${this.chatId} chatId=${this.chatId}
.messages=${this.messagesRendered} .messages=${this.messagesRendered}
.escapeHTML=${escape} .escapeHTML=${escape}
.chatEditor=${this.chatEditor}
.getOldMessage=${this.getOldMessage} .getOldMessage=${this.getOldMessage}
.setRepliedToMessageObj=${(val) => this.setRepliedToMessageObj(val)} .setRepliedToMessageObj=${(val) => this.setRepliedToMessageObj(val)}
.setEditedMessageObj=${(val) => this.setEditedMessageObj(val)} .setEditedMessageObj=${(val) => this.setEditedMessageObj(val)}

View File

@ -1,178 +1,195 @@
import { LitElement, html, css } from "lit" import { LitElement, html, css } from "lit";
import { render } from "lit/html.js" import { render } from "lit/html.js";
import { get, translate } from "lit-translate" import { get, translate } from "lit-translate";
import { Epml } from "../../../epml" import { Epml } from "../../../epml";
import snackbar from "./snackbar.js" import { getUserNameFromAddress } from "../../utils/getUserNameFromAddress";
import "@material/mwc-button" import snackbar from "./snackbar.js";
import "@material/mwc-dialog" import "@material/mwc-button";
import "@polymer/paper-spinner/paper-spinner-lite.js" import "@material/mwc-dialog";
import "@material/mwc-icon" import "@polymer/paper-spinner/paper-spinner-lite.js";
import "./WrapperModal" import '@polymer/paper-progress/paper-progress.js';
import "@material/mwc-icon";
const parentEpml = new Epml({ type: "WINDOW", source: window.parent }) import '@vaadin/button';
import "./WrapperModal";
import "./TipUser"
import "./UserInfo/UserInfo";
class ChatRightPanel extends LitElement { class ChatRightPanel extends LitElement {
static get properties() { static get properties() {
return { return {
isLoading: { type: Boolean }, openUserInfo: { type: Boolean },
isOpenLeaveModal: { type: Boolean },
leaveGroupObj: { type: Object }, leaveGroupObj: { type: Object },
error: { type: Boolean }, error: { type: Boolean },
message: { type: String },
chatHeads: { type: Array }, chatHeads: { type: Array },
groupAdmin: { attribute: false }, groupAdmin: { attribute: false },
groupMembers: { attribute: false }, groupMembers: { attribute: false },
selectedHead: { type: Object }, selectedHead: { type: Object },
toggle: { attribute: false }, toggle: { attribute: false },
getMoreMembers:{ attribute: false } getMoreMembers:{ attribute: false },
setOpenPrivateMessage: { attribute: false },
openTipUser: { type: Boolean },
userName: { type: String },
chatEditor: { type: Object },
walletBalance: { type: Number },
sendMoneyLoading: { type: Boolean },
btnDisable: { type: Boolean },
errorMessage: { type: String },
successMessage: { type: String }
} }
} }
constructor() { constructor() {
super() super()
this.isLoading = false this.openUserInfo = false
this.isOpenLeaveModal = false
this.leaveGroupObj = {} this.leaveGroupObj = {}
this.leaveFee = 0.001 this.leaveFee = 0.001
this.error = false this.error = false
this.message = ""
this.chatHeads = [] this.chatHeads = []
this.groupAdmin = [] this.groupAdmin = []
this.groupMembers = [] this.groupMembers = []
this.observerHandler = this.observerHandler.bind(this) this.observerHandler = this.observerHandler.bind(this)
this.viewElement = '' this.viewElement = ''
this.downObserverElement = '' this.downObserverElement = ''
this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address
this.openTipUser = false
this.userName = ""
this.sendMoneyLoading = false
this.btnDisable = false
this.errorMessage = ""
this.successMessage = ""
this.setOpenTipUser = this.setOpenTipUser.bind(this);
this.setOpenUserInfo = this.setOpenUserInfo.bind(this);
} }
static get styles() { static get styles() {
return css` return css`
.top-bar-icon { .top-bar-icon {
cursor: pointer; cursor: pointer;
height: 18px; height: 18px;
width: 18px; width: 18px;
transition: 0.2s all; transition: 0.2s all;
} }
.top-bar-icon:hover { .top-bar-icon:hover {
color: var(--black); color: var(--black);
} }
.modal-button { .modal-button {
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
font-size: 16px; font-size: 16px;
color: var(--mdc-theme-primary); color: var(--mdc-theme-primary);
background-color: transparent; background-color: transparent;
padding: 8px 10px; padding: 8px 10px;
border-radius: 5px; border-radius: 5px;
border: none; border: none;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
.close-row { .close-row {
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
height: 50px; height: 50px;
flex:0 flex:0
} }
.container-body { .container-body {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 1; flex-grow: 1;
overflow:auto; overflow:auto;
margin-top: 5px; margin-top: 5px;
padding: 0px 6px; padding: 0px 6px;
box-sizing: border-box; box-sizing: border-box;
} }
.container-body::-webkit-scrollbar-track { .container-body::-webkit-scrollbar-track {
background-color: whitesmoke; background-color: whitesmoke;
border-radius: 7px; border-radius: 7px;
} }
.container-body::-webkit-scrollbar { .container-body::-webkit-scrollbar {
width: 6px; width: 6px;
border-radius: 7px; border-radius: 7px;
background-color: whitesmoke; background-color: whitesmoke;
} }
.container-body::-webkit-scrollbar-thumb { .container-body::-webkit-scrollbar-thumb {
background-color: rgb(180, 176, 176); background-color: rgb(180, 176, 176);
border-radius: 7px; border-radius: 7px;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
.container-body::-webkit-scrollbar-thumb:hover { .container-body::-webkit-scrollbar-thumb:hover {
background-color: rgb(148, 146, 146); background-color: rgb(148, 146, 146);
cursor: pointer; cursor: pointer;
} }
p { p {
color: var(--black); color: var(--black);
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
word-break: break-all; word-break: break-all;
} }
.container { .container {
display: flex; display: flex;
width: 100%; width: 100%;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
.chat-right-panel-label { .chat-right-panel-label {
font-family: Montserrat, sans-serif; font-family: Montserrat, sans-serif;
color: var(--group-header); color: var(--group-header);
padding: 5px; padding: 5px;
font-size: 13px; font-size: 13px;
user-select: none; user-select: none;
} }
.group-info { .group-info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
gap: 10px; gap: 10px;
} }
.group-name { .group-name {
font-family: Raleway, sans-serif; font-family: Raleway, sans-serif;
font-size: 20px; font-size: 20px;
color: var(--chat-bubble-msg-color); color: var(--chat-bubble-msg-color);
text-align: center; text-align: center;
user-select: none; user-select: none;
} }
.group-description { .group-description {
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
color: var(--chat-bubble-msg-color); color: var(--chat-bubble-msg-color);
letter-spacing: 0.3px; letter-spacing: 0.3px;
font-weight: 300; font-weight: 300;
font-size: 14px; font-size: 14px;
margin-top: 15px; margin-top: 15px;
word-break: break-word; word-break: break-word;
user-select: none; user-select: none;
} }
.group-subheader { .group-subheader {
font-family: Montserrat, sans-serif; font-family: Montserrat, sans-serif;
font-size: 14px; font-size: 14px;
color: var(--chat-bubble-msg-color); color: var(--chat-bubble-msg-color);
} }
.group-data { .group-data {
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
letter-spacing: 0.3px; letter-spacing: 0.3px;
font-weight: 300; font-weight: 300;
font-size: 14px; font-size: 14px;
color: var(--chat-bubble-msg-color); color: var(--chat-bubble-msg-color);
}
`
} }
`
}
firstUpdated() { firstUpdated() {
this.viewElement = this.shadowRoot.getElementById('viewElement'); this.viewElement = this.shadowRoot.getElementById('viewElement');
@ -180,235 +197,14 @@ return css`
this.elementObserver(); this.elementObserver();
} }
timeIsoString(timestamp) { async updated(changedProperties) {
let myTimestamp = timestamp === undefined ? 1587560082346 : timestamp if (changedProperties && changedProperties.has('selectedHead')) {
let time = new Date(myTimestamp) if (this.selectedHead !== {}) {
return time.toISOString() const userName = await getUserNameFromAddress(this.selectedHead.address);
} this.userName = userName;
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() { elementObserver() {
const options = { const options = {
@ -423,120 +219,120 @@ return css`
// call `observe()` on that MutationObserver instance, // call `observe()` on that MutationObserver instance,
// passing it the element to observe, and the options object // passing it the element to observe, and the options object
observer.observe(elementToObserve); observer.observe(elementToObserve);
} }
observerHandler(entries) {
if (!entries[0].isIntersecting) { observerHandler(entries) {
if (!entries[0].isIntersecting) {
return
} else {
if(this.groupMembers.length < 20){
return return
} else {
if(this.groupMembers.length < 20){
return
}
console.log('this.leaveGroupObjp', this.leaveGroupObj)
this.getMoreMembers(this.leaveGroupObj.groupId)
} }
console.log('this.leaveGroupObjp', this.leaveGroupObj)
this.getMoreMembers(this.leaveGroupObj.groupId)
} }
}
setOpenTipUser(props) {
this.openTipUser = props
}
setOpenUserInfo(props) {
this.openUserInfo = props
}
render() { render() {
console.log('this.groupMembers', this.groupMembers);
console.log(5, "Chat Right Panel Here");
const owner = this.groupAdmin.filter((admin)=> admin.address === this.leaveGroupObj.owner) const owner = this.groupAdmin.filter((admin)=> admin.address === this.leaveGroupObj.owner)
return html` return html`
<div class="container"> <div class="container">
<div class="close-row" style="margin-top: 15px"> <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> <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 class="group-name">${this.leaveGroupObj && this.leaveGroupObj.groupName}</p>
<div class="group-info">
<p class="group-description">${this.leaveGroupObj && this.leaveGroupObj.description}</p>
<p class="group-subheader">Members: <span class="group-data">${this.leaveGroupObj && this.leaveGroupObj.memberCount}</span></p>
<p class="group-subheader">Date created : <span class="group-data">${new Date(this.leaveGroupObj.created).toLocaleDateString("en-US")}</span></p>
</div> </div>
<br /> <div id="viewElement" class="container-body">
<p class="chat-right-panel-label">GROUP OWNER</p> <p class="group-name">${this.leaveGroupObj && this.leaveGroupObj.groupName}</p>
${owner.map((item) => { <div class="group-info">
return html`<chat-side-nav-heads <p class="group-description">${this.leaveGroupObj && this.leaveGroupObj.description}</p>
activeChatHeadUrl="" <p class="group-subheader">Members: <span class="group-data">${this.leaveGroupObj && this.leaveGroupObj.memberCount}</span></p>
.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 <p class="group-subheader">Date created : <span class="group-data">${new Date(this.leaveGroupObj.created).toLocaleDateString("en-US")}</span></p>
.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> </div>
<br />
<p class="chat-right-panel-label">GROUP OWNER</p>
${owner.map((item) => {
return html`<chat-side-nav-heads
activeChatHeadUrl=""
.setActiveChatHeadUrl=${(val) => {
if (val.address === this.myAddress) return;
console.log({ val });
this.selectedHead = val;
this.openUserInfo = true;
}}
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) => {
if (val.address === this.myAddress) return;
console.log({ val });
this.selectedHead = val;
this.openUserInfo = true;
}}
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) => {
if (val.address === this.myAddress) return;
console.log({ val });
this.selectedHead = val;
this.openUserInfo = true;
}}
chatInfo=${JSON.stringify(item)}
></chat-side-nav-heads>`
})}
<div id='downObserver'></div>
</div>
<wrapper-modal
.onClickFunc=${() => {
this.openUserInfo = false;
this.userName = "";
this.shadowRoot.querySelector("tip-user").shadowRoot.getElementById('amountInput').value = "";
}}
style=${
this.openUserInfo ? "display: block" : "display: none"
}>
<user-info
.setOpenUserInfo=${(val) => this.setOpenUserInfo(val)}
.setOpenTipUser=${(val) => this.setOpenTipUser(val)}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
.chatEditor=${this.chatEditor}
.userName=${this.userName}
.selectedHead=${this.selectedHead}
></user-info>
</wrapper-modal>
<wrapper-modal
.onClickFunc=${() => {
this.openTipUser = false;
this.chatEditor.enable();
}}
style=${this.openTipUser ? "display: block" : "display: none"}>
<tip-user
.closeTipUser=${this.openUserInfo}
.chatEditor=${this.chatEditor}
.userName=${this.userName}
.setOpenTipUser=${(val) => this.setOpenTipUser(val)}
>
</tip-user>
</wrapper-modal>
</div>
</div>
` `
} }
} }

View File

@ -63,12 +63,19 @@ export const chatStyles = css`
margin-bottom: 5px; margin-bottom: 5px;
} }
.forwarded-text {
user-select: none;
color: #03a9f4;
margin-bottom: 5px;
}
.message-data-forward { .message-data-forward {
user-select: none; user-select: none;
color: var(--mainmenutext); color: var(--mainmenutext);
margin-bottom: 5px; margin-bottom: 5px;
font-size: 12px; font-size: 12px;
} }
.message-data-my-name { .message-data-my-name {
color: #cf21e8; color: #cf21e8;
text-shadow: 0 0 3px #cf21e8; text-shadow: 0 0 3px #cf21e8;

View File

@ -5,16 +5,19 @@ import { translate, get } from 'lit-translate';
import {unsafeHTML} from 'lit/directives/unsafe-html.js'; import {unsafeHTML} from 'lit/directives/unsafe-html.js';
import { chatStyles } from './ChatScroller-css.js' import { chatStyles } from './ChatScroller-css.js'
import { Epml } from "../../../epml"; import { Epml } from "../../../epml";
import { EmojiPicker } from 'emoji-picker-js';
import { cropAddress } from "../../utils/cropAddress";
import './LevelFounder.js'; import './LevelFounder.js';
import './NameMenu.js'; import './NameMenu.js';
import './ChatModals.js'; import './ChatModals.js';
import './WrapperModal';
import './TipUser'
import "./UserInfo/UserInfo";
import '@vaadin/icons'; import '@vaadin/icons';
import '@vaadin/icon'; import '@vaadin/icon';
import '@material/mwc-button'; import '@material/mwc-button';
import '@material/mwc-dialog'; import '@material/mwc-dialog';
import '@material/mwc-icon'; import '@material/mwc-icon';
import { EmojiPicker } from 'emoji-picker-js';
import { cropAddress } from "../../utils/cropAddress";
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
let toggledMessage = {} let toggledMessage = {}
@ -26,18 +29,23 @@ class ChatScroller extends LitElement {
escapeHTML: { attribute: false }, escapeHTML: { attribute: false },
messages: { type: Array }, messages: { type: Array },
hideMessages: { type: Array }, hideMessages: { type: Array },
setRepliedToMessageObj: {attribute: false}, setRepliedToMessageObj: { attribute: false },
setEditedMessageObj: {attribute: false}, setEditedMessageObj: { attribute: false },
focusChatEditor: {attribute: false}, focusChatEditor: { attribute: false },
sendMessage: {attribute: false}, sendMessage: { attribute: false },
sendMessageForward: {attribute: false}, sendMessageForward: { attribute: false },
showLastMessageRefScroller: { type: Function }, showLastMessageRefScroller: { attribute: false },
emojiPicker: { attribute: false }, emojiPicker: { attribute: false },
isLoadingMessages: { type: Boolean}, isLoadingMessages: { type: Boolean},
setIsLoadingMessages: {attribute: false}, setIsLoadingMessages: { attribute: false },
chatId: { type: String }, chatId: { type: String },
chatEditor: { type: Object },
setForwardProperties: { attribute: false }, setForwardProperties: { attribute: false },
setOpenPrivateMessage: { attribute: false }, setOpenPrivateMessage: { attribute: false },
openTipUser: { type: Boolean },
openUserInfo: { type: Boolean },
userName: { type: String },
selectedHead: { type: Object }
} }
} }
@ -48,12 +56,18 @@ class ChatScroller extends LitElement {
this.messages = [] this.messages = []
this._upObserverhandler = this._upObserverhandler.bind(this) this._upObserverhandler = this._upObserverhandler.bind(this)
this._downObserverHandler = this._downObserverHandler.bind(this) this._downObserverHandler = this._downObserverHandler.bind(this)
this.setOpenTipUser = this.setOpenTipUser.bind(this)
this.setOpenUserInfo = this.setOpenUserInfo.bind(this)
this.setUserName = this.setUserName.bind(this)
this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address
this.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]") this.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]")
this.openTipUser = false;
this.openUserInfo = false;
this.userName = "";
} }
render() { render() {
console.log(9, "chat scroller here");
let formattedMessages = this.messages.reduce((messageArray, message, index) => { let formattedMessages = this.messages.reduce((messageArray, message, index) => {
const lastGroupedMessage = messageArray[messageArray.length - 1]; const lastGroupedMessage = messageArray[messageArray.length - 1];
let timestamp; let timestamp;
@ -117,12 +131,47 @@ class ChatScroller extends LitElement {
.setToggledMessage=${this.setToggledMessage} .setToggledMessage=${this.setToggledMessage}
.setForwardProperties=${this.setForwardProperties} .setForwardProperties=${this.setForwardProperties}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)} .setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
> .setOpenTipUser=${(val) => this.setOpenTipUser(val)}
.setOpenUserInfo=${(val) => this.setOpenUserInfo(val)}
.setUserName=${(val) => this.setUserName(val)}>
</message-template>` </message-template>`
) )
})} })}
<div id='downObserver'></div> <div id='downObserver'></div>
</ul> </ul>
<wrapper-modal
.onClickFunc=${() => {
this.openTipUser = false;
this.chatEditor.enable();
}}
style=${this.openTipUser ? "display: block;" : "display: none;"}>
<tip-user
.closeTipUser=${!this.openTipUser}
.chatEditor=${this.chatEditor}
.focusChatEditor=${this.focusChatEditor}
.userName=${this.userName}
.setOpenTipUser=${(val) => this.setOpenTipUser(val)}>
</tip-user>
</wrapper-modal>
<wrapper-modal
.onClickFunc=${() => {
this.openUserInfo = false;
this.chatEditor.enable();
this.userName = "";
this.selectedHead = {};
}}
style=${
this.openUserInfo ? "display: block" : "display: none"
}>
<user-info
.setOpenUserInfo=${(val) => this.setOpenUserInfo(val)}
.setOpenTipUser=${(val) => this.setOpenTipUser(val)}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
.chatEditor=${this.chatEditor}
.userName=${this.userName}
.selectedHead=${this.selectedHead}
></user-info>
</wrapper-modal>
` `
} }
@ -133,6 +182,12 @@ class ChatScroller extends LitElement {
if(changedProperties.has('chatId') && changedProperties.get('chatId')){ if(changedProperties.has('chatId') && changedProperties.get('chatId')){
return true return true
} }
if(changedProperties.has('openTipUser')){
return true
}
if(changedProperties.has('openUserInfo')){
return true
}
// Only update element if prop1 changed. // Only update element if prop1 changed.
return changedProperties.has('messages'); return changedProperties.has('messages');
} }
@ -148,6 +203,25 @@ class ChatScroller extends LitElement {
toggledMessage = message; toggledMessage = message;
} }
setOpenTipUser(props) {
this.openTipUser = props;
this.chatEditor.disable();
}
setOpenUserInfo(props) {
this.openUserInfo = props;
this.chatEditor.disable();
}
setUserName(props) {
this.userName = props.senderName ? props.senderName : props.sender;
this.selectedHead = {
...this.selectedHead,
address: props.sender,
name: props.senderName,
};
}
async firstUpdated() { async firstUpdated() {
this.emojiPicker.on('emoji', selection => { this.emojiPicker.on('emoji', selection => {
@ -240,10 +314,13 @@ class MessageTemplate extends LitElement {
isFirstMessage: { type: Boolean }, isFirstMessage: { type: Boolean },
isSingleMessageInGroup: { type: Boolean }, isSingleMessageInGroup: { type: Boolean },
isLastMessageInGroup: { type: Boolean }, isLastMessageInGroup: { type: Boolean },
setToggledMessage: {attribute: false}, setToggledMessage: { attribute: false },
setForwardProperties: {attribute: false}, setForwardProperties: { attribute: false },
viewImage: {type: Boolean}, viewImage: { type: Boolean },
setOpenPrivateMessage : { attribute: false } setOpenPrivateMessage : { attribute: false },
setOpenTipUser: { attribute: false },
setOpenUserInfo: { attribute: false },
setUserName: { attribute: false }
} }
} }
@ -413,7 +490,13 @@ class MessageTemplate extends LitElement {
(this.isSingleMessageInGroup === true && this.isLastMessageInGroup === true)) (this.isSingleMessageInGroup === true && this.isLastMessageInGroup === true))
? ( ? (
html` html`
<div class="message-data-avatar"> <div
style=${this.myAddress === this.messageObj.sender ? "cursor: auto;" : "cursor: pointer;"}
@click=${() => {
if (this.myAddress === this.messageObj.sender) return;
this.setOpenUserInfo(true);
this.setUserName(this.messageObj);
}} class="message-data-avatar">
${avatarImg} ${avatarImg}
</div> </div>
` `
@ -440,7 +523,14 @@ class MessageTemplate extends LitElement {
<div class="message-user-info"> <div class="message-user-info">
${this.isFirstMessage ? ${this.isFirstMessage ?
html` html`
<span class="message-data-name"> <span
style=${this.myAddress === this.messageObj.sender ? "cursor: auto;" : "cursor: pointer;"}
@click=${() => {
if (this.myAddress === this.messageObj.sender) return;
this.setOpenUserInfo(true);
this.setUserName(this.messageObj);
}}
class="message-data-name">
${nameMenu} ${nameMenu}
</span> </span>
` `
@ -448,7 +538,7 @@ class MessageTemplate extends LitElement {
} }
${isForwarded ? ${isForwarded ?
html` html`
<span class="message-data-name"> <span class="forwarded-text">
${forwarded} ${forwarded}
</span> </span>
` `
@ -462,8 +552,15 @@ class MessageTemplate extends LitElement {
</div> </div>
${repliedToData && html` ${repliedToData && html`
<div class="original-message"> <div class="original-message">
<p class="original-message-sender"> <p
${repliedToData.senderName ?? cropAddress(repliedToData.sender)} style=${this.myAddress === repliedToData.sender ? "cursor: auto;" : "cursor: pointer;"}
@click=${() => {
if (this.myAddress === repliedToData.sender) return;
this.setOpenUserInfo(true);
this.setUserName(repliedToData)
}}
class="original-message-sender">
${repliedToData.senderName ?? cropAddress(repliedToData.sender)}
</p> </p>
<p class="replied-message"> <p class="replied-message">
${repliedToData.decodedMessage.messageText} ${repliedToData.decodedMessage.messageText}
@ -538,6 +635,8 @@ class MessageTemplate extends LitElement {
.setForwardProperties=${this.setForwardProperties} .setForwardProperties=${this.setForwardProperties}
?firstMessageInChat=${this.messageObj.firstMessageInChat} ?firstMessageInChat=${this.messageObj.firstMessageInChat}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)} .setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
.setOpenTipUser=${(val) => this.setOpenTipUser(val)}
.setUserName=${(val) => this.setUserName(val)}
> >
</chat-menu> </chat-menu>
</div> </div>
@ -643,7 +742,9 @@ class ChatMenu extends LitElement {
sendMessageForward: { attribute: false }, sendMessageForward: { attribute: false },
setForwardProperties: { attribute: false }, setForwardProperties: { attribute: false },
firstMessageInChat: { type: Boolean }, firstMessageInChat: { type: Boolean },
setOpenPrivateMessage: { attribute: false } setOpenPrivateMessage: { attribute: false },
setOpenTipUser: { attribute: false },
setUserName: { attribute: false },
} }
} }
@ -789,6 +890,19 @@ class ChatMenu extends LitElement {
</div> </div>
` `
) : html`<div></div>`} ) : html`<div></div>`}
${this.myAddress !== this.originalMessage.sender ? (
html`
<div
class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`}
data-text="${translate("blockpage.bcchange18")}"
@click=${() => {
this.setOpenTipUser(true);
this.setUserName(this.originalMessage);
}}>
<vaadin-icon icon="vaadin:dollar" slot="icon"></vaadin-icon>
</div>
`
) : html`<div></div>`}
<div class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`} data-text="${translate("blockpage.bcchange10")}" @click="${() => this.showBlockIconFunc(true)}"> <div class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`} data-text="${translate("blockpage.bcchange10")}" @click="${() => this.showBlockIconFunc(true)}">
<vaadin-icon icon="vaadin:ellipsis-dots-h" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:ellipsis-dots-h" slot="icon"></vaadin-icon>
</div> </div>

View File

@ -101,36 +101,63 @@ class ChatSideNavHeads extends LitElement {
imageHTMLRes.src = imageUrl; imageHTMLRes.src = imageUrl;
}, 500); }, 500);
} else { } else {
this.isImageLoaded = false
this.isImageLoaded = false
} }
}; };
return imageHTMLRes; return imageHTMLRes;
} }
render() { render() {
let avatarImg = ''; let avatarImg = ""
let backupAvatarImg = '' if (this.chatInfo.name) {
if(this.chatInfo.name){
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`; const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`;
avatarImg= this.createImage(avatarUrl) avatarImg = this.createImage(avatarUrl)
} }
return html` return html`
<li @click=${() => this.getUrl(this.chatInfo)} class="clearfix"> <li @click=${() => this.getUrl(this.chatInfo)} class="clearfix">
${this.isImageLoaded ? html`${avatarImg}` : html`` } ${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 && !this.chatInfo.groupName
${!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>`: ''} ? html`<mwc-icon class="img-icon">account_circle</mwc-icon>`
${!this.isImageLoaded && this.chatInfo.groupName ? 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.groupName.charAt(0)}</div>`: ''} : html``}
<div> ${!this.isImageLoaded && this.chatInfo.name
<div class="name"><span style="float:left; padding-left: 8px; color: var(--chat-group);">${this.chatInfo.groupName ? this.chatInfo.groupName : this.chatInfo.name !== undefined ? this.chatInfo.name : this.chatInfo.address.substr(0, 15)} </span> </div> ? html`<div
</div> style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url
</li> ? "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>`
: ""}
${!this.isImageLoaded && this.chatInfo.groupName
? 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.groupName.charAt(0)}
</div>`
: ""}
<div>
<div class="name">
<span style="float:left; padding-left: 8px; color: var(--chat-group);">
${this.chatInfo.groupName
? this.chatInfo.groupName
: this.chatInfo.name !== undefined
? this.chatInfo.name
: this.chatInfo.address.substr(0, 15)}
</span>
</div>
</div>
</li>
`
} }
firstUpdated() { firstUpdated() {
@ -149,9 +176,7 @@ class ChatSideNavHeads extends LitElement {
this.config = JSON.parse(c) this.config = JSON.parse(c)
}) })
}) })
parentEpml.imReady() parentEpml.imReady();
} }
shouldUpdate(changedProperties) { shouldUpdate(changedProperties) {
@ -161,6 +186,9 @@ class ChatSideNavHeads extends LitElement {
if(changedProperties.has('chatInfo')){ if(changedProperties.has('chatInfo')){
return true return true
} }
if(changedProperties.has('isImageLoaded')){
return true
}
return false return false
} }

View File

@ -0,0 +1,85 @@
import { css } from 'lit'
export const tipUserStyles = css`
.tip-user-header {
display: flex;
justify-content: center;
align-items: center;
padding: 12px;
border-bottom: 1px solid whitesmoke;
gap: 25px;
user-select: none;
}
.tip-user-header-font {
font-family: Montserrat, sans-serif;
font-size: 20px;
color: var(--chat-bubble-msg-color);
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tip-user-body {
display: flex;
justify-content: center;
align-items: center;
padding: 20px 10px;
flex-direction: column;
gap: 25px;
}
.tip-input {
width: 300px;
margin-bottom: 15px;
outline: 0;
border-width: 0 0 2px;
border-color: var(--mdc-theme-primary);
background-color: transparent;
padding: 10px;
font-family: Roboto, sans-serif;
font-size: 15px;
color: var(--chat-bubble-msg-color);
}
.tip-input::selection {
background-color: var(--mdc-theme-primary);
color: white;
}
.tip-input::placeholder {
opacity: 0.9;
color: var(--black);
}
.tip-available {
font-family: Roboto, sans-serif;
font-size: 17px;
color: var(--chat-bubble-msg-color);
font-weight: 300;
letter-spacing: 0.3px;
margin: 0;
user-select: none;
}
.success-msg {
font-family: Roboto, sans-serif;
font-size: 18px;
font-weight: 400;
letter-spacing: 0.3px;
margin: 0;
user-select: none;
color: #10880b;
}
.error-msg {
font-family: Roboto, sans-serif;
font-size: 18px;
font-weight: 400;
letter-spacing: 0.3px;
margin: 0;
user-select: none;
color: #f30000;
}
`

View File

@ -0,0 +1,282 @@
import { LitElement, html } from 'lit';
import { render } from 'lit/html.js';
import { get, translate } from 'lit-translate';
import { tipUserStyles } from './TipUser-css.js';
import { Epml } from '../../../epml';
import '@vaadin/button';
import '@polymer/paper-progress/paper-progress.js';
const parentEpml = new Epml({ type: "WINDOW", source: window.parent });
export class TipUser extends LitElement {
static get properties() {
return {
userName: { type: String },
chatEditor: { type: Object },
walletBalance: { type: Number },
sendMoneyLoading: { type: Boolean },
closeTipUser: { type: Boolean },
btnDisable: { type: Boolean },
errorMessage: { type: String },
successMessage: { type: String },
setOpenTipUser: { attribute: false },
focusChatEditor: { attribute: false }
}
}
constructor() {
super()
this.sendMoneyLoading = false
this.btnDisable = false
this.errorMessage = ""
this.successMessage = ""
this.myAddress = window.parent.reduxStore.getState().app.selectedAddress
}
static styles = [tipUserStyles]
async firstUpdated() {
await this.fetchWalletDetails();
}
updated(changedProperties) {
if (changedProperties && changedProperties.has("closeTipUser")) {
if (this.closeTipUser) {
this.shadowRoot.getElementById("amountInput").value = "";
this.errorMessage = "";
this.successMessage = "";
}
}
}
async getLastRef() {
let myRef = await parentEpml.request("apiCall", {
type: "api",
url: `/addresses/lastreference/${this.myAddress.address}`,
})
return myRef;
}
renderSuccessText() {
return html`${translate("chatpage.cchange55")}`
}
renderReceiverText() {
return html`${translate("chatpage.cchange54")}`
}
getApiKey() {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
let apiKey = myNode.apiKey;
return apiKey;
}
async fetchWalletDetails() {
await parentEpml.request('apiCall', {
url: `/addresses/balance/${this.myAddress.address}?apiKey=${this.getApiKey()}`,
})
.then((res) => {
if (isNaN(Number(res))) {
let snack4string = get("chatpage.cchange48")
parentEpml.request('showSnackBar', `${snack4string}`)
} else {
this.walletBalance = Number(res).toFixed(8);
}
})
}
async sendQort() {
const amount = this.shadowRoot.getElementById("amountInput").value;
let recipient = this.userName;
this.sendMoneyLoading = true;
this.btnDisable = true;
if (parseFloat(amount) + parseFloat(0.001) > parseFloat(this.walletBalance)) {
this.sendMoneyLoading = false;
this.btnDisable = false;
let snack1string = get("chatpage.cchange51");
parentEpml.request('showSnackBar', `${snack1string}`);
return false;
}
if (parseFloat(amount) <= 0) {
this.sendMoneyLoading = false;
this.btnDisable = false;
let snack2string = get("chatpage.cchange52");
parentEpml.request('showSnackBar', `${snack2string}`);
return false;
}
if (recipient.length === 0) {
this.sendMoneyLoading = false;
this.btnDisable = false;
let snack3string = get("chatpage.cchange53");
parentEpml.request('showSnackBar', `${snack3string}`);
return false;
}
const validateName = async (receiverName) => {
let myRes;
let myNameRes = await parentEpml.request('apiCall', {
type: 'api',
url: `/names/${receiverName}`,
})
if (myNameRes.error === 401) {
myRes = false;
} else {
myRes = myNameRes;
}
return myRes;
}
const validateAddress = async (receiverAddress) => {
let myAddress = await window.parent.validateAddress(receiverAddress);
return myAddress;
}
const validateReceiver = async (recipient) => {
let lastRef = await this.getLastRef();
let isAddress;
try {
isAddress = await validateAddress(recipient);
} catch (err) {
isAddress = false;
}
if (isAddress) {
let myTransaction = await makeTransactionRequest(recipient, lastRef);
getTxnRequestResponse(myTransaction);
} else {
let myNameRes = await validateName(recipient);
if (myNameRes !== false) {
let myNameAddress = myNameRes.owner
let myTransaction = await makeTransactionRequest(myNameAddress, lastRef)
getTxnRequestResponse(myTransaction)
} else {
console.error(this.renderReceiverText())
this.errorMessage = this.renderReceiverText();
this.sendMoneyLoading = false;
this.btnDisable = false;
}
}
}
const getName = async (recipient)=> {
try {
const getNames = await parentEpml.request("apiCall", {
type: "api",
url: `/names/address/${recipient}`,
});
if (getNames?.length > 0 ) {
return getNames[0].name;
} else {
return '';
}
} catch (error) {
return "";
}
}
const makeTransactionRequest = async (receiver, lastRef) => {
let myReceiver = receiver;
let mylastRef = lastRef;
let dialogamount = get("transactions.amount");
let dialogAddress = get("login.address");
let dialogName = get("login.name");
let dialogto = get("transactions.to");
let recipientName = await getName(myReceiver);
let myTxnrequest = await parentEpml.request('transaction', {
type: 2,
nonce: this.myAddress.nonce,
params: {
recipient: myReceiver,
recipientName: recipientName,
amount: amount,
lastReference: mylastRef,
fee: 0.001,
dialogamount: dialogamount,
dialogto: dialogto,
dialogAddress,
dialogName
},
})
return myTxnrequest;
}
const getTxnRequestResponse = (txnResponse) => {
if (txnResponse.success === false && txnResponse.message) {
this.errorMessage = txnResponse.message;
this.sendMoneyLoading = false;
this.btnDisable = false;
throw new Error(txnResponse);
} else if (txnResponse.success === true && !txnResponse.data.error) {
this.shadowRoot.getElementById('amountInput').value = '';
this.userName = '';
this.errorMessage = '';
this.successMessage = this.renderSuccessText();
this.sendMoneyLoading = false;
this.btnDisable = false;
setTimeout(() => {
this.setOpenTipUser(false);
this.chatEditor.enable();
this.focusChatEditor();
this.successMessage = "";
}, 3000);
} else {
this.errorMessage = txnResponse.data.message;
this.sendMoneyLoading = false;
this.btnDisable = false;
throw new Error(txnResponse);
}
}
validateReceiver(recipient);
}
render() {
return html`
<div class="tip-user-header">
<img src="/img/qort.png" width="32" height="32">
<p class="tip-user-header-font">${translate("chatpage.cchange43")} ${this.userName}</p>
</div>
<div class="tip-user-body">
<p class="tip-available">${translate("chatpage.cchange47")}: ${this.walletBalance} QORT</p>
<input id="amountInput" class="tip-input" type="number" placeholder="${translate("chatpage.cchange46")}" />
<p class="tip-available">${translate("chatpage.cchange49")}: 0.001 QORT</p>
${this.sendMoneyLoading ?
html`
<paper-progress indeterminate style="width: 100%; margin: 4px;">
</paper-progress>`
: html`
<div style=${"text-align: center;"}>
<vaadin-button
?disabled=${this.btnDisable}
theme="primary medium"
style="width: 100%; cursor: pointer"
@click=${() => this.sendQort()}>
<vaadin-icon icon="vaadin:arrow-forward" slot="prefix"></vaadin-icon>
${translate("chatpage.cchange50")} QORT
</vaadin-button>
</div>
`}
${this.successMessage ?
html`
<p class="success-msg">
${this.successMessage}
</p>
`
: this.errorMessage ?
html`
<p class="error-msg">
${this.errorMessage}
</p>
`
: null}
</div>
`;
}
}
customElements.define('tip-user', TipUser);

View File

@ -0,0 +1,69 @@
import { css } from 'lit'
export const userInfoStyles = css`
.user-info-header {
font-family: Montserrat, sans-serif;
text-align: center;
font-size: 28px;
color: var(--chat-bubble-msg-color);
margin-bottom: 10px;
padding: 10px 0;
user-select: none;
}
.avatar-container {
display: flex;
justify-content: center;
}
.user-info-avatar {
width: 100px;
height: 100px;
border-radius: 50%;
margin: 10px 0;
}
.user-info-no-avatar {
display: flex;
justify-content: center;
align-items: center;
text-transform: capitalize;
font-size: 50px;
font-family: Roboto, sans-serif;
width: 100px;
height: 100px;
border-radius:50%;
background: var(--chatHeadBg);
color: var(--chatHeadText);
}
.send-message-button {
font-family: Roboto, sans-serif;
letter-spacing: 0.3px;
font-weight: 300;
padding: 8px 5px;
border-radius: 3px;
text-align: center;
color: var(--mdc-theme-primary);
transition: all 0.3s ease-in-out;
}
.send-message-button:hover {
cursor: pointer;
background-color: #03a8f485;
}
.close-icon {
position: absolute;
top: 3px;
right: 5px;
color: #676b71;
width: 14px;
transition: all 0.1s ease-in-out;
}
.close-icon:hover {
cursor: pointer;
color: #494c50;
}
`

View File

@ -0,0 +1,134 @@
import { LitElement, html } from 'lit';
import { render } from 'lit/html.js';
import { translate } from 'lit-translate';
import { userInfoStyles } from './UserInfo-css.js';
import { Epml } from '../../../../epml';
import '@vaadin/button';
import '@polymer/paper-progress/paper-progress.js';
import { cropAddress } from '../../../utils/cropAddress.js';
// const parentEpml = new Epml({ type: "WINDOW", source: window.parent });
export class UserInfo extends LitElement {
static get properties() {
return {
setOpenUserInfo: { attribute: false },
setOpenTipUser: { attribute: false },
setOpenPrivateMessage: { attribute: false },
chatEditor: { type: Object },
userName: { type: String },
selectedHead: { type: Object },
isImageLoaded: { type: Boolean }
}
}
constructor() {
super()
this.isImageLoaded = false
this.selectedHead = {}
this.imageFetches = 0
}
static styles = [userInfoStyles]
createImage(imageUrl) {
const imageHTMLRes = new Image();
imageHTMLRes.src = imageUrl;
imageHTMLRes.classList.add("user-info-avatar");
// imageHTMLRes.style= "width:30px; height:30px; float: left; border-radius:50%; font-size:14px";
imageHTMLRes.onload = () => {
this.isImageLoaded = true;
}
imageHTMLRes.onerror = () => {
if (this.imageFetches < 4) {
setTimeout(() => {
this.imageFetches = this.imageFetches + 1;
imageHTMLRes.src = imageUrl;
}, 500);
} else {
this.isImageLoaded = false
}
};
return imageHTMLRes;
}
updated(changedProperties) {
if (changedProperties && changedProperties.has('selectedHead')) {
if (this.selectedHead) {
console.log(this.selectedHead, "selected head")
}
}
}
render() {
let avatarImg = "";
if (this.selectedHead.name) {
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 avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.selectedHead.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`;
avatarImg = this.createImage(avatarUrl);
}
return html`
<div style=${"position: relative;"}>
<vaadin-icon
class="close-icon"
icon="vaadin:close-big"
slot="icon"
@click=${() => {
this.setOpenUserInfo(false)
this.chatEditor.enable();
}}>
</vaadin-icon>
${this.isImageLoaded ?
html`
<div class="avatar-container">
${avatarImg}
</div>` :
html``}
${!this.isImageLoaded && this.selectedHead.name ?
html`
<div class="avatar-container">
<div class="user-info-no-avatar">
${this.selectedHead.name.charAt(0)}
</div>
</div>
`
: ""}
${!this.isImageLoaded && !this.selectedHead.name ?
html`
<div
class="avatar-container"
>
<img src="/img/qortal-chat-logo.png" alt="avatar" />
</div>`
: ""}
<div class="user-info-header">
${this.selectedHead.name ? this.selectedHead.name : cropAddress(this.selectedHead.address)}
</div>
<div
class="send-message-button"
@click="${() => {
this.setOpenPrivateMessage({
name: this.userName,
open: true
})
this.setOpenUserInfo(false);
}
}">
${translate("chatpage.cchange58")}
</div>
<div
style=${"margin-top: 5px;"}
class="send-message-button"
@click=${() => {
this.setOpenTipUser(true);
this.setOpenUserInfo(false);
this.chatEditor.disable();
}}>
${translate("chatpage.cchange59")}
</div>
</div>
`
}
}
customElements.define('user-info', UserInfo);

View File

@ -0,0 +1,21 @@
import { Epml } from '../../epml.js';
import { cropAddress } from './cropAddress.js';
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
export const getUserNameFromAddress = async (address) => {
try {
const getNames = await parentEpml.request("apiCall", {
type: "api",
url: `/names/address/${address}`,
});
if (Array.isArray(getNames) && getNames.length > 0 ) {
return getNames[0].name;
} else {
return address;
}
} catch (error) {
console.error(error);
}
}