- ${(!this.isReceipient && +this._chatId !== 0) ?
- html`
+ style="grid-template-rows: minmax(40px, auto) minmax(6%, 92vh) minmax(40px, auto); flex: 3;">
+
${this.imageFile && html`
-
![dialog-img](${URL.createObjectURL(this.imageFile)})
+
![dialog-img](${this.imageFile.identifier)
`}
@@ -1642,18 +657,18 @@ class ChatPage extends LitElement {
@@ -1845,6 +860,25 @@ class ChatPage extends LitElement {
>
+
+ this.getMoreMembers(val)}
+ .toggle=${(val) => this._toggleResources(val)}
+ .selectedAddress=${this.selectedAddress}
+ .groupMembers=${this.groupMembers}
+ .groupAdmin=${this.groupAdmin}
+ .leaveGroupObj=${this.groupInfo}
+ .setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
+ .setOpenTipUser=${(val) => this.setOpenTipUser(val)}
+ .setOpenUserInfo=${(val) => this.setOpenUserInfo(val)}
+ .setUserName=${(val) => this.setUserName(val)}
+ _chatId=${ifDefined(this._chatId)}
+ chatId=${this.chatId}
+ ?isreceipient=${this.isReceipient}
+ .repost=${this.insertFile}
+ >
+
+
`
}
@@ -1864,23 +898,32 @@ class ChatPage extends LitElement {
address: member.member,
name: name ? name : undefined
}
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
return memberItem
})
const membersWithName = await Promise.all(getMembersWithName)
this.groupMembers = [...this.groupMembers, ...membersWithName]
this.pageNumber = this.pageNumber + 1
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
}
async connectedCallback() {
super.connectedCallback()
await this.initUpdate()
- this.webWorker = new WebWorker()
- this.webWorkerFile = new WebWorkerFile()
+ if(!this.webWorker){
+ this.webWorker = new WebWorker()
+ }
+ if(!this.webWorkerFile){
+ this.webWorkerFile = new WebWorkerFile()
+ }
+ if(!this.webWorkerSortMessages){
+ this.webWorkerSortMessages = new WebWorkerSortMessages()
+
+ }
+ if(!this.webWorkerDecodeMessages){
+ this.webWorkerDecodeMessages = new WebWorkerDecodeMessages()
+ }
await this.getUpdateCompleteTextEditor()
const elementChatId = this.shadowRoot.getElementById('_chatEditorDOM').shadowRoot.getElementById('_chatEditorDOM')
@@ -1984,14 +1027,7 @@ class ChatPage extends LitElement {
document.addEventListener('keydown', this.initialChat)
document.addEventListener('paste', this.pasteImage)
- if (this.chatId) {
- window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({
- key: this.chatId,
- timestamp: Date.now()
- }))
- }
-
- let callback = (entries, observer) => {
+ let callback = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
@@ -2034,6 +1070,9 @@ class ChatPage extends LitElement {
if (this.webWorkerFile) {
this.webWorkerFile.terminate()
}
+ if(this.webWorkerSortMessages){
+ this.webWorkerSortMessages.terminate()
+ }
if (this.editor) {
this.editor.destroy()
}
@@ -2046,20 +1085,12 @@ class ChatPage extends LitElement {
document.removeEventListener('keydown', this.initialChat)
document.removeEventListener('paste', this.pasteImage)
-
- if (this.chatId) {
- window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({
- key: this.chatId,
- timestamp: Date.now()
- }))
- }
}
initialChat(e) {
if (this.editor && !this.editor.isFocused && this.currentEditor === '_chatEditorDOM' && !this.openForwardOpen && !this.openTipUser && !this.openGifModal) {
// WARNING: Deprecated methods from KeyBoard Event
- if (e.code === "Space" || e.keyCode === 32 || e.which === 32) {
- } else if (inputKeyCodes.includes(e.keyCode)) {
+ if (e.code === "Space" || e.keyCode === 32 || e.which === 32) { /* empty */ } else if (inputKeyCodes.includes(e.keyCode)) {
this.editor.commands.insertContent(e.key)
this.editor.commands.focus('end')
} else {
@@ -2067,6 +1098,7 @@ class ChatPage extends LitElement {
}
}
}
+
async pasteImage(e) {
const event = e
@@ -2075,13 +1107,13 @@ class ChatPage extends LitElement {
const [firstItem] = dataTransfer.items
const blob = firstItem.getAsFile()
return blob
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
}
if (event.clipboardData) {
const blobFound = handleTransferIntoURL(event.clipboardData)
if (blobFound) {
- this.insertImage(blobFound)
+ this.insertFile(blobFound)
+ e.preventDefault();
return
} else {
const item_list = await navigator.clipboard.read()
@@ -2100,7 +1132,8 @@ class ChatPage extends LitElement {
let file = new File([blob], "name", {
type: image_type
})
- this.insertImage(file)
+ this.insertFile(file)
+ e.preventDefault();
} catch (error) {
console.error(error)
let errorMsg = get("chatpage.cchange81")
@@ -2118,7 +1151,7 @@ class ChatPage extends LitElement {
const findMessage = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById(message.signature)
if (findMessage) {
- findMessage.scrollIntoView({ behavior: 'smooth', block: 'center' })
+ findMessage.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
const findElement = findMessage.shadowRoot.querySelector('.message-parent')
if (findElement) {
findElement.classList.add('blink-bg')
@@ -2126,16 +1159,14 @@ class ChatPage extends LitElement {
findElement.classList.remove('blink-bg')
}, 2000)
}
+ const chatScrollerElement = this.shadowRoot.querySelector('chat-scroller');
+ if (chatScrollerElement && chatScrollerElement.disableFetching) {
+ chatScrollerElement.disableFetching = false
+ }
return
}
- if ((message.timestamp - this.messagesRendered[0].timestamp) > 86400000) {
- let errorMsg = get("chatpage.cchange66")
- parentEpml.request('showSnackBar', `${errorMsg}`)
- return
- }
-
- if ((message.timestamp - this.messagesRendered[0].timestamp) < 86400000) {
+
const findOriginalMessage = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById(clickedOnMessage.signature)
if (findOriginalMessage) {
const messageClientRect = findOriginalMessage.getBoundingClientRect()
@@ -2146,16 +1177,21 @@ class ChatPage extends LitElement {
top: messageClientRect.top,
offsetHeight: findOriginalMessage.offsetHeight
}
+
+ await this.getOldMessageDynamic(0, clickedOnMessage.timestamp, message)
+ await this.getUpdateComplete()
+
+ const marginElements = Array.from(this.shadowRoot.querySelector('chat-scroller').shadowRoot.querySelectorAll('message-template'))
+ const findMessage2 = marginElements.find((item) => item.messageObj.signature === message.signature) || marginElements.find((item) => item.messageObj.originalSignature === message.signature) || marginElements.find((item) => item.messageObj.signature === message.originalSignature) || marginElements.find((item) => item.messageObj.originalSignature === message.originalSignature)
+ if (findMessage2) {
+ findMessage2.scrollIntoView({ block: 'center' })
}
- await this.getOldMessageDynamic(0, this.messagesRendered[0].timestamp, message.timestamp - 7200000)
- const findMessage = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById(message.signature)
- if (findMessage) {
+ if (findMessage2) {
this.isLoadingGoToRepliedMessage = {
...this.isLoadingGoToRepliedMessage,
loading: false
}
- findMessage.scrollIntoView({ block: 'center' })
- const findElement = findMessage.shadowRoot.querySelector('.message-parent')
+ const findElement = findMessage2.shadowRoot.querySelector('.message-parent')
if (findElement) {
findElement.classList.add('blink-bg')
setTimeout(() => {
@@ -2226,9 +1262,8 @@ class ChatPage extends LitElement {
}
delete message.reactions
const stringifyMessageObject = JSON.stringify(message)
- this.sendMessage(stringifyMessageObject, undefined, '', true)
- } catch (error) {
- }
+ this.sendMessage({messageText: stringifyMessageObject, chatReference: undefined, isForward: true})
+ } catch (error) { /* empty */ }
}
showLastMessageRefScroller(props) {
@@ -2236,6 +1271,11 @@ class ChatPage extends LitElement {
}
insertFile(file) {
+ if(file.identifier){
+ this.imageFile = file
+ this.currentEditor = 'newChat'
+ return
+ }else
if (file.type.includes('image')) {
this.imageFile = file
this.currentEditor = 'newChat'
@@ -2265,6 +1305,10 @@ class ChatPage extends LitElement {
}
async initUpdate() {
+ if (this.webSocket) {
+ this.webSocket.close(1000, 'switch chat')
+ this.webSocket = ''
+ }
this.pageNumber = 1
const getAddressPublicKey = () => {
@@ -2336,8 +1380,7 @@ class ChatPage extends LitElement {
address: member.member,
name: name ? name : undefined
}
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
return memberItem
})
@@ -2351,22 +1394,22 @@ class ChatPage extends LitElement {
address: member.member,
name: name ? name : undefined
}
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
return memberItem
})
const membersWithName = await Promise.all(getMembersWithName)
this.groupAdmin = membersAdminsWithName
this.groupMembers = membersWithName
this.groupInfo = getGroupInfo
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
}
}
async firstUpdated() {
this.changeTheme()
-
+
+ // this.processQueue();
+
window.addEventListener('storage', () => {
const checkLanguage = localStorage.getItem('qortalLanguage')
const checkTheme = localStorage.getItem('qortalTheme')
@@ -2381,19 +1424,9 @@ class ChatPage extends LitElement {
document.querySelector('html').setAttribute('theme', this.theme)
})
- parentEpml.ready().then(() => {
- parentEpml.subscribe('selected_address', async selectedAddress => {
- this.selectedAddress = {}
- selectedAddress = JSON.parse(selectedAddress)
- if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
- this.selectedAddress = selectedAddress
- })
- parentEpml.request('apiCall', {
- url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}`
- }).then(res => {
- this.balance = res
- })
- })
+
+ this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatId) || 0
+
parentEpml.imReady()
const isEnabledChatEnter = localStorage.getItem('isEnabledChatEnter')
@@ -2431,6 +1464,36 @@ class ChatPage extends LitElement {
this.editor.setEditable(true)
}
}
+ if(changedProperties && changedProperties.has('chatId') && this.webSocket){
+ const previousChatId = changedProperties.get('chatId');
+
+ this.isLoadingMessages = true
+ this.initUpdate()
+
+
+ if (previousChatId) {
+ window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({
+ key: previousChatId,
+ timestamp: Date.now()
+ }))
+ }
+ }
+ }
+
+
+ shouldUpdate(changedProperties) {
+ if(changedProperties.has('chatId')){
+ return true
+ }
+ if (changedProperties.has('setActiveChatHeadUrl')) {
+ return false
+ }
+ if (changedProperties.has('setOpenPrivateMessage')) {
+ return false
+ }
+
+ return true
+
}
async getName(recipient) {
@@ -2483,8 +1546,10 @@ class ChatPage extends LitElement {
this.setRepliedToMessageObj(val)}
.setEditedMessageObj=${(val) => this.setEditedMessageObj(val)}
.sendMessage=${(val) => this._sendMessage(val)}
@@ -2502,7 +1567,11 @@ class ChatPage extends LitElement {
?openTipUser=${this.openTipUser}
.selectedHead=${this.selectedHead}
.goToRepliedMessage=${(val, val2) => this.goToRepliedMessage(val, val2)}
- .getOldMessageAfter=${(val) => this.getOldMessageAfter(val)}
+ .updateMessageHash=${this.updateMessageHash}
+ .clearUpdateMessageHashmap=${this.clearUpdateMessageHashmap}
+ .messageQueue=${this.messageQueue}
+ loggedInUserName=${this.loggedInUserName}
+ loggedInUserAddress=${this.loggedInUserAddress}
>
`
@@ -2519,6 +1588,13 @@ class ChatPage extends LitElement {
return true
}
+ async getUpdateCompleteMessages() {
+ await super.getUpdateComplete()
+ const marginElements = Array.from(this.shadowRoot.querySelector('chat-scroller').shadowRoot.querySelectorAll('message-template'))
+ await Promise.all(marginElements.map(el => el.updateComplete))
+ return true
+ }
+
async getUpdateCompleteTextEditor() {
await super.getUpdateComplete()
const marginElements = Array.from(this.shadowRoot.querySelectorAll('chat-text-editor'))
@@ -2537,94 +1613,179 @@ class ChatPage extends LitElement {
})
}
- async getOldMessageDynamic(limit, before, after) {
-
+ async getOldMessageDynamic(limit, timestampClickedOnMessage, messageToGoTo) {
+ const findMsg = await parentEpml.request("apiCall", {
+ type: "api",
+ url: `/chat/message/${messageToGoTo.originalSignature || messageToGoTo.signature}?encoding=BASE64`,
+ })
+ if(!findMsg) return null
if (this.isReceipient) {
- const getInitialMessages = await parentEpml.request('apiCall', {
+ const getInitialMessagesBefore = await parentEpml.request('apiCall', {
type: 'api',
- url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${limit}&reverse=true&before=${before}&after=${after}&haschatreference=false&encoding=BASE64`
+ url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${20}&reverse=true&before=${findMsg.timestamp}&haschatreference=false&encoding=BASE64`
})
-
- const decodeMsgs = getInitialMessages.map((eachMessage) => {
- return this.decodeMessage(eachMessage)
+ const getInitialMessagesAfter = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${20}&reverse=false&after=${findMsg.timestamp - 1000}&haschatreference=false&encoding=BASE64`
})
+ const getInitialMessages = [...getInitialMessagesBefore, ...getInitialMessagesAfter]
+ let decodeMsgs = []
+ await new Promise((res, rej) => {
+ this.webWorkerDecodeMessages.postMessage({messages: getInitialMessages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey });
+
+ this.webWorkerDecodeMessages.onmessage = e => {
+ decodeMsgs = e.data
+ res()
+
+ }
+ this.webWorkerDecodeMessages.onerror = () => {
+ rej()
+
+ }
+ })
- const replacedMessages = await replaceMessagesEdited({
+
+ queue.push(() => replaceMessagesEdited({
decodedMessages: decodeMsgs,
parentEpml,
isReceipient: this.isReceipient,
decodeMessageFunc: this.decodeMessage,
- _publicKey: this._publicKey
- })
+ _publicKey: this._publicKey,
+ addToUpdateMessageHashmap: this.addToUpdateMessageHashmap
+ }));
- this.messagesRendered = [...replacedMessages, ...this.messagesRendered].sort(function (a, b) {
- return a.timestamp
- - b.timestamp
- })
+
+ let list = [...decodeMsgs]
+
+ await new Promise((res) => {
+
+ this.webWorkerSortMessages.postMessage({list});
+
+ this.webWorkerSortMessages.onmessage = e => {
+
+ list = e.data
+ res()
+
+ }
+ })
+
+ this.messagesRendered = {
+ messages: list,
+ type: 'inBetween',
+ message: messageToGoTo
+ }
this.isLoadingOldMessages = false
- await this.getUpdateComplete()
- const viewElement = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('viewElement')
-
- if (viewElement) {
- viewElement.scrollTop = 200
- }
+
} else {
- const getInitialMessages = await parentEpml.request('apiCall', {
+ const getInitialMessagesBefore = await parentEpml.request('apiCall', {
type: 'api',
- url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=${limit}&reverse=true&before=${before}&after=${after}&haschatreference=false&encoding=BASE64`
+ url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=${20}&reverse=true&before=${findMsg.timestamp}&haschatreference=false&encoding=BASE64`
})
-
- const decodeMsgs = getInitialMessages.map((eachMessage) => {
- return this.decodeMessage(eachMessage)
+ const getInitialMessagesAfter = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=${20}&reverse=false&after=${findMsg.timestamp - 1000}&haschatreference=false&encoding=BASE64`
})
+ const getInitialMessages = [...getInitialMessagesBefore, ...getInitialMessagesAfter]
- const replacedMessages = await replaceMessagesEdited({
+ let decodeMsgs = []
+ await new Promise((res, rej) => {
+ this.webWorkerDecodeMessages.postMessage({messages: getInitialMessages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey });
+
+ this.webWorkerDecodeMessages.onmessage = e => {
+ decodeMsgs = e.data
+ res()
+
+ }
+ this.webWorkerDecodeMessages.onerror = () => {
+ rej()
+
+ }
+ })
+
+ queue.push(() => replaceMessagesEdited({
decodedMessages: decodeMsgs,
parentEpml,
isReceipient: this.isReceipient,
decodeMessageFunc: this.decodeMessage,
- _publicKey: this._publicKey
- })
+ _publicKey: this._publicKey,
+ addToUpdateMessageHashmap: this.addToUpdateMessageHashmap
+ }));
+
+
+ let list = [...decodeMsgs]
+
+ await new Promise((res) => {
+
+ this.webWorkerSortMessages.postMessage({list});
+
+ this.webWorkerSortMessages.onmessage = e => {
+
+ list = e.data
+ res()
+
+ }
+ })
+
+ this.messagesRendered = {
+ messages: list,
+ type: 'inBetween',
+ signature: messageToGoTo.signature
+ }
- this.messagesRendered = [...replacedMessages, ...this.messagesRendered].sort(function (a, b) {
- return a.timestamp
- - b.timestamp
- })
this.isLoadingOldMessages = false
- await this.getUpdateComplete()
- const viewElement = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('viewElement')
-
- if (viewElement) {
- viewElement.scrollTop = 200
- }
+
}
}
async getOldMessage(scrollElement) {
+ if(!scrollElement || !scrollElement.messageObj || !scrollElement.messageObj.timestamp){
+ this.messagesRendered = {
+ messages: [],
+ type: 'old',
+ el: scrollElement
+ }
+ return
+ }
if (this.isReceipient) {
const getInitialMessages = await parentEpml.request('apiCall', {
type: 'api',
- url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`
+ url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${chatLimit}&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`
})
-
- const decodeMsgs = getInitialMessages.map((eachMessage) => {
- return this.decodeMessage(eachMessage)
- })
-
- const replacedMessages = await replaceMessagesEdited({
+ let decodeMsgs = []
+ await new Promise((res, rej) => {
+ this.webWorkerDecodeMessages.postMessage({messages: getInitialMessages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey });
+
+ this.webWorkerDecodeMessages.onmessage = e => {
+ decodeMsgs = e.data
+ res()
+
+ }
+ this.webWorkerDecodeMessages.onerror = () => {
+ rej()
+
+ }
+ })
+
+ queue.push(() => replaceMessagesEdited({
decodedMessages: decodeMsgs,
parentEpml,
isReceipient: this.isReceipient,
decodeMessageFunc: this.decodeMessage,
- _publicKey: this._publicKey
- })
-
- this.messagesRendered = [...replacedMessages, ...this.messagesRendered].sort(function (a, b) {
- return a.timestamp
- - b.timestamp
- })
+ _publicKey: this._publicKey,
+ addToUpdateMessageHashmap: this.addToUpdateMessageHashmap
+ }));
+
+ let list = [...decodeMsgs]
+
+
+
+ this.messagesRendered = {
+ messages: list,
+ type: 'old',
+ el: scrollElement
+ }
this.isLoadingOldMessages = false
await this.getUpdateComplete()
@@ -2638,27 +1799,45 @@ class ChatPage extends LitElement {
} else {
const getInitialMessages = await parentEpml.request('apiCall', {
type: 'api',
- url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=20&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`
+ url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=${chatLimit}&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`
})
- const decodeMsgs = getInitialMessages.map((eachMessage) => {
- return this.decodeMessage(eachMessage)
- })
+ let decodeMsgs = []
- const replacedMessages = await replaceMessagesEdited({
+
+ await new Promise((res, rej) => {
+ this.webWorkerDecodeMessages.postMessage({messages: getInitialMessages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey });
+
+ this.webWorkerDecodeMessages.onmessage = e => {
+ decodeMsgs = e.data
+ res()
+
+ }
+ this.webWorkerDecodeMessages.onerror = () => {
+ rej()
+
+ }
+ })
+
+
+ queue.push(() => replaceMessagesEdited({
decodedMessages: decodeMsgs,
parentEpml,
isReceipient: this.isReceipient,
decodeMessageFunc: this.decodeMessage,
- _publicKey: this._publicKey
- })
-
- this.messagesRendered = [...replacedMessages, ...this.messagesRendered].sort(function (a, b) {
- return a.timestamp
- - b.timestamp
- })
-
- this.isLoadingOldMessages = false
+ _publicKey: this._publicKey,
+ addToUpdateMessageHashmap: this.addToUpdateMessageHashmap
+ }));
+ let list = [...decodeMsgs]
+
+
+
+ this.messagesRendered = {
+ messages: list,
+ type: 'old',
+ el: scrollElement
+ }
+ // this.isLoadingOldMessages = false
await this.getUpdateComplete()
const marginElements = Array.from(this.shadowRoot.querySelector('chat-scroller').shadowRoot.querySelectorAll('message-template'))
const findElement = marginElements.find((item) => item.messageObj.signature === scrollElement.messageObj.signature)
@@ -2668,30 +1847,66 @@ class ChatPage extends LitElement {
}
}
}
-
- async getOldMessageAfter(scrollElement) {
+ async getAfterMessages(scrollElement) {
+ if(!scrollElement || !scrollElement.messageObj || !scrollElement.messageObj.timestamp){
+ this.messagesRendered = {
+ messages: [],
+ type: 'new',
+ }
+ return
+ }
+ const timestamp = scrollElement.messageObj.timestamp
+
if (this.isReceipient) {
const getInitialMessages = await parentEpml.request('apiCall', {
type: 'api',
- url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=true&afer=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`
+ url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${chatLimit}&reverse=false&after=${timestamp}&haschatreference=false&encoding=BASE64`
})
- const decodeMsgs = getInitialMessages.map((eachMessage) => {
- return this.decodeMessage(eachMessage)
- })
+ let decodeMsgs = []
+ await new Promise((res, rej) => {
+ this.webWorkerDecodeMessages.postMessage({messages: getInitialMessages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey });
+
+ this.webWorkerDecodeMessages.onmessage = e => {
+ decodeMsgs = e.data
+ res()
+
+ }
+ this.webWorkerDecodeMessages.onerror = () => {
+ rej()
+
+ }
+ })
- const replacedMessages = await replaceMessagesEdited({
+
+ queue.push(() => replaceMessagesEdited({
decodedMessages: decodeMsgs,
parentEpml,
isReceipient: this.isReceipient,
decodeMessageFunc: this.decodeMessage,
- _publicKey: this._publicKey
- })
-
- this.messagesRendered = [...this.messagesRendered, ...replacedMessages].sort(function (a, b) {
- return a.timestamp
- - b.timestamp
- })
+ _publicKey: this._publicKey,
+ addToUpdateMessageHashmap: this.addToUpdateMessageHashmap
+ }));
+
+ let list = [ ...decodeMsgs]
+
+ await new Promise((res) => {
+
+ this.webWorkerSortMessages.postMessage({list});
+
+ this.webWorkerSortMessages.onmessage = e => {
+
+ list = e.data
+ res()
+
+ }
+ })
+
+ this.messagesRendered = {
+ messages: list,
+ type: 'new'
+ }
+
this.isLoadingOldMessages = false
await this.getUpdateComplete()
@@ -2705,82 +1920,209 @@ class ChatPage extends LitElement {
} else {
const getInitialMessages = await parentEpml.request('apiCall', {
type: 'api',
- url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=20&reverse=true&after=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`
+ url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=${chatLimit}&reverse=false&after=${timestamp}&haschatreference=false&encoding=BASE64`
})
+ let decodeMsgs = []
+ await new Promise((res, rej) => {
+ this.webWorkerDecodeMessages.postMessage({messages: getInitialMessages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey });
+
+ this.webWorkerDecodeMessages.onmessage = e => {
+ decodeMsgs = e.data
+ res()
+
+ }
+ this.webWorkerDecodeMessages.onerror = () => {
+ rej()
+
+ }
+ })
- const decodeMsgs = getInitialMessages.map((eachMessage) => {
- return this.decodeMessage(eachMessage)
- })
-
- const replacedMessages = await replaceMessagesEdited({
+
+ queue.push(() => replaceMessagesEdited({
decodedMessages: decodeMsgs,
parentEpml,
isReceipient: this.isReceipient,
decodeMessageFunc: this.decodeMessage,
- _publicKey: this._publicKey
- })
+ _publicKey: this._publicKey,
+ addToUpdateMessageHashmap: this.addToUpdateMessageHashmap
+ }));
+
+
+
+ let list = [...decodeMsgs]
+
+ await new Promise((res) => {
+
+ this.webWorkerSortMessages.postMessage({list});
+
+ this.webWorkerSortMessages.onmessage = e => {
+
+ list = e.data
+ res()
+
+ }
+ })
+
+ this.messagesRendered = {
+ messages: list,
+ type: 'new'
+ }
- this.messagesRendered = [...this.messagesRendered, ...replacedMessages].sort(function (a, b) {
- return a.timestamp
- - b.timestamp
- })
this.isLoadingOldMessages = false
await this.getUpdateComplete()
const marginElements = Array.from(this.shadowRoot.querySelector('chat-scroller').shadowRoot.querySelectorAll('message-template'))
const findElement = marginElements.find((item) => item.messageObj.signature === scrollElement.messageObj.signature)
-
if (findElement) {
findElement.scrollIntoView({ behavior: 'auto', block: 'center' })
}
}
}
- async processMessages(messages, isInitial) {
+ async addToUpdateMessageHashmap(array){
+
+
+
+ const newObj = {}
+
+ array.forEach((item)=> {
+ const signature = item.originalSignature || item.signature
+ newObj[signature] = item
+ })
+ this.updateMessageHash = {
+ ...this.updateMessageHash,
+ ...newObj
+ }
+ this.requestUpdate()
+ await this.getUpdateComplete()
+
+ }
+
+ async clearUpdateMessageHashmap(){
+ this.updateMessageHash = {}
+ this.requestUpdate()
+ }
+
+ findContent(identifier, data) {
+ const [type, id] = identifier.split('/');
+
+ if (type === 'group') {
+ for (let group of data.groups) {
+ if (group.groupId === parseInt(id, 10)) {
+ return group;
+ }
+ }
+ } else if (type === 'direct') {
+ for (let direct of data.direct) {
+ if (direct.address === id) {
+ return direct;
+ }
+ }
+ }
+ return null;
+ }
+
+
+
+ async processMessages(messages, isInitial, isUnread, count) {
const isReceipient = this.chatId.includes('direct')
- const decodedMessages = messages.map((eachMessage) => {
-
- if (eachMessage.isText === true) {
- this.messageSignature = eachMessage.signature
- let _eachMessage = this.decodeMessage(eachMessage)
- return _eachMessage
- } else {
- this.messageSignature = eachMessage.signature
- let _eachMessage = this.decodeMessage(eachMessage)
- return _eachMessage
+ let decodedMessages = []
+ if(!this.webWorkerDecodeMessages){
+ this.webWorkerDecodeMessages = new WebWorkerDecodeMessages()
+ }
+ if(!this.webWorkerSortMessages){
+ this.webWorkerSortMessages = new WebWorkerSortMessages()
+ }
+ await new Promise((res, rej) => {
+ this.webWorkerDecodeMessages.postMessage({messages: messages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey });
+
+ this.webWorkerDecodeMessages.onmessage = e => {
+ decodedMessages = e.data
+ res()
+
}
- })
+ this.webWorkerDecodeMessages.onerror = () => {
+ rej()
+
+ }
+ })
if (isInitial) {
this.chatEditorPlaceholder = await this.renderPlaceholder()
- const replacedMessages = await replaceMessagesEdited({
- decodedMessages: decodedMessages,
- parentEpml,
- isReceipient: isReceipient,
- decodeMessageFunc: this.decodeMessage,
- _publicKey: this._publicKey
- })
+
- this._messages = replacedMessages.sort(function (a, b) {
- return a.timestamp
- - b.timestamp
- })
+ try {
+ queue.push(() => replaceMessagesEdited({
+ decodedMessages: decodedMessages,
+ parentEpml,
+ isReceipient: isReceipient,
+ decodeMessageFunc: this.decodeMessage,
+ _publicKey: this._publicKey,
+ addToUpdateMessageHashmap: this.addToUpdateMessageHashmap
+ }));
+ } catch (error) {
+ console.log({error})
+ }
+
+
+
+
+ let list = decodedMessages
+
+ await new Promise((res) => {
+
+ this.webWorkerSortMessages.postMessage({list});
+
+ this.webWorkerSortMessages.onmessage = e => {
+
+ list = e.data
+ res()
+
+ }
+ })
+
+ this._messages = list
// TODO: Determine number of initial messages by screen height...
- this.messagesRendered = this._messages
+ // this.messagesRendered = this._messages
+ const lastReadMessageTimestamp = this.lastReadMessageTimestamp
+
+
+ if(isUnread){
+
+ this.messagesRendered = {
+ messages: this._messages,
+ type: 'initialLastSeen',
+ lastReadMessageTimestamp,
+ count
+ }
+
+ window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({
+ key: this.chatId,
+ timestamp: Date.now()
+ }))
+ } else {
+ this.messagesRendered = {
+ messages: this._messages,
+ type: 'initial'
+ }
+ }
+
this.isLoadingMessages = false
setTimeout(() => this.downElementObserver(), 500)
} else {
- const replacedMessages = await replaceMessagesEdited({
+
+ queue.push(() => replaceMessagesEdited({
decodedMessages: decodedMessages,
parentEpml,
isReceipient: isReceipient,
decodeMessageFunc: this.decodeMessage,
_publicKey: this._publicKey,
- isNotInitial: true
- })
+ isNotInitial: true,
+ addToUpdateMessageHashmap: this.addToUpdateMessageHashmap
+ }));
- const renderEachMessage = replacedMessages.map(async (msg) => {
+ const renderEachMessage = decodedMessages.map(async (msg) => {
await this.renderNewMessage(msg)
})
@@ -2793,11 +2135,7 @@ class ChatPage extends LitElement {
}))
}
- // this.newMessages = this.newMessages.concat(_newMessages)
- this.messagesRendered = [...this.messagesRendered].sort(function (a, b) {
- return a.timestamp
- - b.timestamp
- })
+
}
}
@@ -2839,37 +2177,49 @@ class ChatPage extends LitElement {
async renderNewMessage(newMessage) {
if (newMessage.chatReference) {
- const findOriginalMessageIndex = this.messagesRendered.findIndex(msg => msg.signature === newMessage.chatReference || (msg.chatReference && msg.chatReference === newMessage.chatReference))
- if (findOriginalMessageIndex !== -1 && this.messagesRendered[findOriginalMessageIndex].sender === newMessage.sender) {
- const newMessagesRendered = [...this.messagesRendered]
- newMessagesRendered[findOriginalMessageIndex] = {
- ...newMessage, timestamp: newMessagesRendered[findOriginalMessageIndex].timestamp, senderName: newMessagesRendered[findOriginalMessageIndex].senderName,
- sender: newMessagesRendered[findOriginalMessageIndex].sender, editedTimestamp: newMessage.timestamp
- }
- this.messagesRendered = newMessagesRendered
- await this.getUpdateComplete()
+ this.messagesRendered = {
+ messages: [newMessage],
+ type: 'update',
}
return
}
- const viewElement = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('viewElement')
+ let viewElement = this.shadowRoot.querySelector('chat-scroller')
+ if(viewElement){
+ viewElement = viewElement.shadowRoot.getElementById('viewElement')
+ } else {
+ viewElement = null
+ }
if (newMessage.sender === this.selectedAddress.address) {
- this.messagesRendered = [...this.messagesRendered, newMessage]
+
+ this.messagesRendered = {
+ messages: [newMessage],
+ type: 'newComingInAuto',
+ }
await this.getUpdateComplete()
- viewElement.scrollTop = viewElement.scrollHeight
+ // viewElement.scrollTop = viewElement.scrollHeight
} else if (this.isUserDown) {
+ this.messagesRendered = {
+ messages: [newMessage],
+ type: 'newComingInAuto',
+ }
// Append the message and scroll to the bottom if user is down the page
- this.messagesRendered = [...this.messagesRendered, newMessage]
+ // this.messagesRendered = [...this.messagesRendered, newMessage]
await this.getUpdateComplete()
-
- viewElement.scrollTop = viewElement.scrollHeight
+ if(viewElement){
+ viewElement.scrollTop = viewElement.scrollHeight
+ }
+
} else {
- this.messagesRendered = [...this.messagesRendered, newMessage]
+ this.messagesRendered = {
+ messages: [newMessage],
+ type: 'newComingInAuto',
+ }
await this.getUpdateComplete()
this.showNewMessageBar()
@@ -2931,7 +2281,9 @@ class ChatPage extends LitElement {
} else {
// Fallback to http
directSocketLink = `ws://${nodeUrl}/websockets/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&encoding=BASE64&limit=1`
- }
+ }
+
+
this.webSocket = new WebSocket(directSocketLink)
// Open Connection
@@ -2946,25 +2298,52 @@ class ChatPage extends LitElement {
directSocketTimeout = setTimeout(pingDirectSocket, 45000)
return
}
+
if (initial === 0) {
+ this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatId) || 0
if (noInitial) return
- const cachedData = null
let getInitialMessages = []
- if (cachedData && cachedData.length !== 0) {
- const lastMessage = cachedData[cachedData.length - 1]
- const newMessages = await parentEpml.request('apiCall', {
+ let count = 0
+ let isUnread = false
+
+ const chatId = this.chatId
+ const findContent = this.chatHeads.find((item)=> item.url === chatId)
+ const chatInfoTimestamp = findContent.timestamp || 0
+ const lastReadMessageTimestamp = this.lastReadMessageTimestamp
+
+
+ if(lastReadMessageTimestamp && chatInfoTimestamp){
+ if(lastReadMessageTimestamp < chatInfoTimestamp){
+ isUnread = true
+ }
+ }
+ if(isUnread){
+ const getInitialMessagesBefore = await parentEpml.request('apiCall', {
type: 'api',
- url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64`
+ url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=${chatLimitHalf}&reverse=true&before=${lastReadMessageTimestamp}&haschatreference=false&encoding=BASE64`
})
- getInitialMessages = [...cachedData, ...newMessages].slice(-20)
+ const getInitialMessagesAfter = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=${chatLimitHalf}&reverse=false&after=${lastReadMessageTimestamp - 1000}&haschatreference=false&encoding=BASE64`
+ })
+ getInitialMessages = [...getInitialMessagesBefore, ...getInitialMessagesAfter]
+ const lastMessage = getInitialMessagesAfter.at(-1)
+ if(lastMessage){
+ count = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/chat/messages/count?after=${lastMessage.timestamp}&involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=false`
+ })
+ }
} else {
getInitialMessages = await parentEpml.request('apiCall', {
type: 'api',
- url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=true&haschatreference=false&encoding=BASE64`
+ url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=${chatLimit}&reverse=true&haschatreference=false&encoding=BASE64`
})
}
+
+
- this.processMessages(getInitialMessages, true)
+ this.processMessages(getInitialMessages, true, isUnread, count)
initial = initial + 1
@@ -2973,15 +2352,16 @@ class ChatPage extends LitElement {
if (e.data) {
this.processMessages(JSON.parse(e.data), false)
}
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
}
}
// Closed Event
this.webSocket.onclose = (e) => {
clearTimeout(directSocketTimeout)
+
if (e.reason === 'switch chat') return
+
restartDirectWebSocket()
}
@@ -3015,7 +2395,7 @@ class ChatPage extends LitElement {
let groupId = Number(gId)
let initial = 0
-
+ let count = 0
let groupSocketTimeout
let myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
@@ -3029,7 +2409,7 @@ class ChatPage extends LitElement {
// Fallback to http
groupSocketLink = `ws://${nodeUrl}/websockets/chat/messages?txGroupId=${groupId}&encoding=BASE64&limit=1`
}
-
+
this.webSocket = new WebSocket(groupSocketLink)
// Open Connection
@@ -3045,26 +2425,53 @@ class ChatPage extends LitElement {
return
}
if (initial === 0) {
+ this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatId) || 0
if (noInitial) return
- const cachedData = null
let getInitialMessages = []
- if (cachedData && cachedData.length !== 0) {
+ const lastReadMessageTimestamp = this.lastReadMessageTimestamp
- const lastMessage = cachedData[cachedData.length - 1]
+ let isUnread = false
+
+ const chatId = this.chatId
+ const findContent = this.chatHeads.find((item)=> item.url === chatId)
+ const chatInfoTimestamp = findContent.timestamp || 0
- const newMessages = await parentEpml.request('apiCall', {
- type: 'api',
- url: `/chat/messages?txGroupId=${groupId}&limit=20&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64`
- })
- getInitialMessages = [...cachedData, ...newMessages].slice(-20)
- } else {
- getInitialMessages = await parentEpml.request('apiCall', {
- type: 'api',
- url: `/chat/messages?txGroupId=${groupId}&limit=20&reverse=true&haschatreference=false&encoding=BASE64`
- })
- }
+ if(lastReadMessageTimestamp && chatInfoTimestamp){
+ if(lastReadMessageTimestamp < chatInfoTimestamp){
+ isUnread = true
+ }
+ }
+ if(isUnread){
+
- this.processMessages(getInitialMessages, true)
+ const getInitialMessagesBefore = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/chat/messages?txGroupId=${groupId}&limit=${chatLimitHalf}&reverse=true&before=${lastReadMessageTimestamp}&haschatreference=false&encoding=BASE64`
+ })
+ const getInitialMessagesAfter = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/chat/messages?txGroupId=${groupId}&limit=${chatLimitHalf}&reverse=false&after=${lastReadMessageTimestamp - 1000}&haschatreference=false&encoding=BASE64`
+ })
+ getInitialMessages = [...getInitialMessagesBefore, ...getInitialMessagesAfter]
+ const lastMessage = getInitialMessagesAfter.at(-1)
+ if(lastMessage){
+ count = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/chat/messages/count?after=${lastMessage.timestamp}&txGroupId=${groupId}&limit=20&reverse=false`
+ })
+ }
+
+ } else {
+ getInitialMessages = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/chat/messages?txGroupId=${groupId}&limit=${chatLimit}&reverse=true&haschatreference=false&encoding=BASE64`
+ })
+ }
+
+
+
+
+ this.processMessages(getInitialMessages, true, isUnread, count)
initial = initial + 1
} else {
@@ -3072,8 +2479,7 @@ class ChatPage extends LitElement {
if (e.data) {
this.processMessages(JSON.parse(e.data), false)
}
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
}
}
@@ -3124,11 +2530,16 @@ class ChatPage extends LitElement {
}
}
- async _sendMessage(outSideMsg, msg) {
+ async _sendMessage(outSideMsg, msg, messageQueue) {
+ const _chatId= this._chatId
+ const isReceipient= this.isReceipient
+ let _publicKey= this._publicKey
+ const attachment= this.attachment
+
try {
if (this.isReceipient) {
let hasPublicKey = true
- if (!this._publicKey.hasPubKey) {
+ if (!_publicKey.hasPubKey) {
hasPublicKey = false
try {
const res = await parentEpml.request('apiCall', {
@@ -3136,20 +2547,19 @@ class ChatPage extends LitElement {
url: `/addresses/publickey/${this.selectedAddress.address}`
})
if (res.error === 102) {
- this._publicKey.key = ''
- this._publicKey.hasPubKey = false
+ _publicKey.key = ''
+ _publicKey.hasPubKey = false
} else if (res !== false) {
- this._publicKey.key = res
- this._publicKey.hasPubKey = true
+ _publicKey.key = res
+ _publicKey.hasPubKey = true
hasPublicKey = true
} else {
- this._publicKey.key = ''
- this._publicKey.hasPubKey = false
+ _publicKey.key = ''
+ _publicKey.hasPubKey = false
}
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
- if (!hasPublicKey || !this._publicKey.hasPubKey) {
+ if (!hasPublicKey || !_publicKey.hasPubKey) {
let err4string = get("chatpage.cchange39")
parentEpml.request('showSnackBar', `${err4string}`)
return
@@ -3164,7 +2574,7 @@ class ChatPage extends LitElement {
// create new var called repliedToData and use that to modify the UI
// find specific object property in local
let typeMessage = 'regular'
- this.isLoading = true
+ // this.isLoading = true
const trimmedMessage = msg
const getName = async (recipient) => {
@@ -3231,7 +2641,7 @@ class ChatPage extends LitElement {
compressedFile = file
resolve()
},
- error(err) {
+ error() {
},
})
})
@@ -3280,11 +2690,11 @@ class ChatPage extends LitElement {
isImageDeleted: true
}
const stringifyMessageObject = JSON.stringify(messageObject)
- this.sendMessage(stringifyMessageObject, typeMessage, chatReference)
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
} else if (outSideMsg && outSideMsg.type === 'deleteAttachment') {
this.isDeletingAttachment = true
let compressedFile = ''
- var str = "iVBORw0KGgoAAAANSUhEUgAAAsAAAAGMAQMAAADuk4YmAAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAADlJREFUeF7twDEBAAAAwiD7p7bGDlgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAGJrAABgPqdWQAAAABJRU5ErkJggg=="
+ const str = "iVBORw0KGgoAAAANSUhEUgAAAsAAAAGMAQMAAADuk4YmAAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAADlJREFUeF7twDEBAAAAwiD7p7bGDlgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAGJrAABgPqdWQAAAABJRU5ErkJggg=="
const userName = outSideMsg.name
const identifier = outSideMsg.identifier
@@ -3328,7 +2738,7 @@ class ChatPage extends LitElement {
compressedFile = file
resolve()
},
- error(err) {
+ error() {
},
})
})
@@ -3378,9 +2788,11 @@ class ChatPage extends LitElement {
isAttachmentDeleted: true
}
const stringifyMessageObject = JSON.stringify(messageObject)
- this.sendMessage(stringifyMessageObject, typeMessage, chatReference)
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
} else if (outSideMsg && outSideMsg.type === 'image') {
- this.isUploadingImage = true
+ if(!this.imageFile.identifier){
+ this.isUploadingImage = true
+ }
const userName = await getName(this.selectedAddress.address)
if (!userName) {
parentEpml.request('showSnackBar', get("chatpage.cchange27"))
@@ -3389,84 +2801,103 @@ class ChatPage extends LitElement {
this.imageFile = null
return
}
- const arbitraryFeeData = await modalHelper.getArbitraryFee()
- const res = await modalHelper.showModalAndWaitPublish(
- {
- feeAmount: arbitraryFeeData.feeToShow
+
+
+ let service = "QCHAT_IMAGE"
+ let name = userName
+ let identifier
+ if(this.imageFile.identifier){
+ identifier = this.imageFile.identifier
+ name = this.imageFile.name
+ service = this.imageFile.service
+ } else {
+ const arbitraryFeeData = await modalHelper.getArbitraryFee()
+ const res = await modalHelper.showModalAndWaitPublish(
+ {
+ feeAmount: arbitraryFeeData.feeToShow
+ }
+ );
+ if (res.action !== 'accept') throw new Error('User declined publish')
+
+ if (this.webWorkerFile) {
+ this.webWorkerFile.terminate()
+ this.webWorkerFile = null
}
- );
- if (res.action !== 'accept') throw new Error('User declined publish')
-
- if (this.webWorkerFile) {
- this.webWorkerFile.terminate()
- this.webWorkerFile = null
- }
-
- this.webWorkerFile = new WebWorkerFile()
-
- const image = this.imageFile
- const id = this.uid.rnd()
- const identifier = `qchat_${id}`
- let compressedFile = ''
- await new Promise(resolve => {
- new Compressor(image, {
- quality: .6,
- maxWidth: 1200,
- success(result) {
- const file = new File([result], "name", {
- type: image.type
- })
- compressedFile = file
- resolve()
- },
- error(err) {
- },
+
+ this.webWorkerFile = new WebWorkerFile()
+ const image = this.imageFile
+ const id = this.uid.rnd()
+ let groupPart
+ if(this.isReceipient){
+ groupPart = `direct_${generateIdFromAddresses(this._chatId, this.selectedAddress.address)}`
+ } else {
+ groupPart = `group_${this._chatId}`
+ }
+ identifier = `qchat_${groupPart}_${id}`
+ let compressedFile = ''
+ await new Promise(resolve => {
+ new Compressor(image, {
+ quality: .6,
+ maxWidth: 1200,
+ mimeType: 'image/webp',
+ success(result) {
+ const file = new File([result], "name", {
+ type: 'image/webp'
+ })
+ compressedFile = file
+ resolve()
+ },
+ error() {
+ },
+ })
})
- })
- const fileSize = compressedFile.size
- if (fileSize > 500000) {
- parentEpml.request('showSnackBar', get("chatpage.cchange26"))
- this.isLoading = false
- this.isUploadingImage = false
- return
+ const fileSize = compressedFile.size
+ if (fileSize > 500000) {
+ parentEpml.request('showSnackBar', get("chatpage.cchange26"))
+ this.isLoading = false
+ this.isUploadingImage = false
+ return
+ }
+
+ try {
+
+ await publishData({
+ registeredName: userName,
+ file: compressedFile,
+ service: 'QCHAT_IMAGE',
+ identifier: identifier,
+ parentEpml,
+ metaData: undefined,
+ uploadType: 'file',
+ selectedAddress: this.selectedAddress,
+ worker: this.webWorkerFile,
+ withFee: true,
+ feeAmount: arbitraryFeeData.fee
+ })
+ this.isUploadingImage = false
+ this.removeImage()
+ } catch (error) {
+ this.isLoading = false
+ this.isUploadingImage = false
+ return
+ }
+
}
-
- try {
-
- await publishData({
- registeredName: userName,
- file: compressedFile,
- service: 'QCHAT_IMAGE',
- identifier: identifier,
- parentEpml,
- metaData: undefined,
- uploadType: 'file',
- selectedAddress: this.selectedAddress,
- worker: this.webWorkerFile,
- withFee: true,
- feeAmount: arbitraryFeeData.fee
- })
- this.isUploadingImage = false
- this.removeImage()
- } catch (error) {
- this.isLoading = false
- this.isUploadingImage = false
- return
- }
-
+
const messageObject = {
messageText: trimmedMessage,
images: [{
- service: "QCHAT_IMAGE",
- name: userName,
- identifier: identifier
+ service: service,
+ name: name,
+ identifier: identifier,
}],
isImageDeleted: false,
repliedTo: '',
version: 3
}
const stringifyMessageObject = JSON.stringify(messageObject)
- this.sendMessage(stringifyMessageObject, typeMessage)
+ this.removeImage()
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference: undefined, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
} else if (outSideMsg && outSideMsg.type === 'gif') {
const userName = await getName(this.selectedAddress.address)
if (!userName) {
@@ -3487,7 +2918,7 @@ class ChatPage extends LitElement {
version: 3
}
const stringifyMessageObject = JSON.stringify(messageObject)
- this.sendMessage(stringifyMessageObject, typeMessage)
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference: undefined, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
} else if (outSideMsg && outSideMsg.type === 'attachment') {
this.isUploadingAttachment = true
const userName = await getName(this.selectedAddress.address)
@@ -3504,8 +2935,8 @@ class ChatPage extends LitElement {
this.webWorkerFile = new WebWorkerFile()
- const attachment = this.attachment
- const id = this.uid.rnd()
+ // const attachment = attachment
+ const id = this.uid()
const identifier = `qchat_${id}`
const fileSize = attachment.size
if (fileSize > 1000000) {
@@ -3556,7 +2987,7 @@ class ChatPage extends LitElement {
version: 3
}
const stringifyMessageObject = JSON.stringify(messageObject)
- this.sendMessage(stringifyMessageObject, typeMessage)
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference: undefined, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
} else if (outSideMsg && outSideMsg.type === 'reaction') {
const userName = await getName(this.selectedAddress.address)
typeMessage = 'edit'
@@ -3611,7 +3042,7 @@ class ChatPage extends LitElement {
reactions
}
const stringifyMessageObject = JSON.stringify(messageObject)
- this.sendMessage(stringifyMessageObject, typeMessage, chatReference)
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
} else if (/^\s*$/.test(trimmedMessage)) {
this.isLoading = false
} else if (this.repliedToMessageObj) {
@@ -3627,7 +3058,7 @@ class ChatPage extends LitElement {
version: 3
}
const stringifyMessageObject = JSON.stringify(messageObject)
- this.sendMessage(stringifyMessageObject, typeMessage)
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference: undefined, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
} else if (this.editedMessageObj) {
typeMessage = 'edit'
let chatReference = this.editedMessageObj.signature
@@ -3650,7 +3081,7 @@ class ChatPage extends LitElement {
isEdited: true
}
const stringifyMessageObject = JSON.stringify(messageObject)
- this.sendMessage(stringifyMessageObject, typeMessage, chatReference)
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
} else {
const messageObject = {
messageText: trimmedMessage,
@@ -3665,7 +3096,7 @@ class ChatPage extends LitElement {
this.myTrimmedMeassage = stringifyMessageObject
this.shadowRoot.getElementById('confirmDialog').open()
} else {
- this.sendMessage(stringifyMessageObject, typeMessage)
+ return this.sendMessage({messageText: stringifyMessageObject, typeMessage, chatReference: undefined, isForward: false, isReceipient, _chatId, _publicKey, messageQueue})
}
}
} catch (error) {
@@ -3676,21 +3107,31 @@ class ChatPage extends LitElement {
}
- async sendMessage(messageText, typeMessage, chatReference, isForward) {
- this.isLoading = true
+ async sendMessage({messageText, typeMessage, chatReference, isForward,isReceipient, _chatId, _publicKey, messageQueue}) {
+ if(messageQueue){
+ this.addToQueue({messageText, typeMessage, chatReference, isForward, isReceipient, _chatId, _publicKey}, messageQueue);
+ this.resetChatEditor()
+ this.closeEditMessageContainer()
+ this.closeRepliedToContainer()
+ return
+ }
+ if(isForward){
+ this.isLoading = true
+ }
+
let _reference = new Uint8Array(64)
window.crypto.getRandomValues(_reference)
let reference = window.parent.Base58.encode(_reference)
const sendMessageRequest = async () => {
- if (this.isReceipient === true) {
+ if (isReceipient === true) {
let chatResponse = await parentEpml.request('chat', {
type: 18,
nonce: this.selectedAddress.nonce,
params: {
timestamp: Date.now(),
- recipient: this._chatId,
- recipientPublicKey: this._publicKey.key,
+ recipient: _chatId,
+ recipientPublicKey: _publicKey.key,
hasChatReference: typeMessage === 'edit' ? 1 : 0,
chatReference: chatReference,
message: messageText,
@@ -3700,14 +3141,14 @@ class ChatPage extends LitElement {
isText: 1
}
})
- _computePow(chatResponse)
+ return _computePow(chatResponse)
} else {
let groupResponse = await parentEpml.request('chat', {
type: 181,
nonce: this.selectedAddress.nonce,
params: {
timestamp: Date.now(),
- groupID: Number(this._chatId),
+ groupID: Number(_chatId),
hasReceipient: 0,
hasChatReference: typeMessage === 'edit' ? 1 : 0,
chatReference: chatReference,
@@ -3718,7 +3159,7 @@ class ChatPage extends LitElement {
isText: 1
}
})
- _computePow(groupResponse)
+ return _computePow(groupResponse)
}
}
@@ -3753,8 +3194,7 @@ class ChatPage extends LitElement {
publicKey.key = ''
publicKey.hasPubKey = false
}
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
}
if (!this.forwardActiveChatHeadUrl.selected && this.shadowRoot.getElementById("sendTo").value !== "") {
@@ -3822,8 +3262,7 @@ class ChatPage extends LitElement {
publicKey.key = ''
publicKey.hasPubKey = false
}
- } catch (error) {
- }
+ } catch (error) { /* empty */ }
}
const isRecipient = this.forwardActiveChatHeadUrl.url.includes('direct') === true ? true : false
@@ -3907,29 +3346,40 @@ class ChatPage extends LitElement {
})
getSendChatResponse(_response, isForward)
+ return _response
}
- const getSendChatResponse = (response, isForward, customErrorMessage) => {
+ const getSendChatResponse = (response, isForward) => {
if (response === true) {
- this.resetChatEditor()
+ // this.resetChatEditor()
if (isForward) {
let successString = get("blockpage.bcchange15")
parentEpml.request('showSnackBar', `${successString}`)
+ this.resetChatEditor()
+ this.closeEditMessageContainer()
+ this.closeRepliedToContainer()
+ this.openForwardOpen = false
+ this.forwardActiveChatHeadUrl = {
+ url: "",
+ name: "",
+ selected: false
+ }
+ this.isLoading = false
}
- this.closeEditMessageContainer()
- this.closeRepliedToContainer()
- this.openForwardOpen = false
- this.forwardActiveChatHeadUrl = {
- url: "",
- name: "",
- selected: false
- }
+ // this.closeEditMessageContainer()
+ // this.closeRepliedToContainer()
+ // this.openForwardOpen = false
+ // this.forwardActiveChatHeadUrl = {
+ // url: "",
+ // name: "",
+ // selected: false
+ // }
} else if (response.error) {
- parentEpml.request('showSnackBar', response.message)
+ // parentEpml.request('showSnackBar', response.message)
} else {
- let err2string = get("chatpage.cchange21")
- parentEpml.request('showSnackBar', `${customErrorMessage || err2string}`)
+ // let err2string = get("chatpage.cchange21")
+ // parentEpml.request('showSnackBar', `${customErrorMessage || err2string}`)
}
if (isForward && response !== true) {
this.isLoading = false
@@ -3943,7 +3393,7 @@ class ChatPage extends LitElement {
sendForwardRequest()
return
}
- sendMessageRequest()
+ return sendMessageRequest()
}
/**
@@ -3966,6 +3416,8 @@ class ChatPage extends LitElement {
}
downElementObserver() {
+ const chatscrollerEl = this.shadowRoot.querySelector('chat-scroller')
+ if(!chatscrollerEl) return
const downObserver = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('downObserver')
const options = {
diff --git a/plugins/plugins/core/components/ChatRightPanelResources.js b/plugins/plugins/core/components/ChatRightPanelResources.js
new file mode 100644
index 00000000..23686aac
--- /dev/null
+++ b/plugins/plugins/core/components/ChatRightPanelResources.js
@@ -0,0 +1,670 @@
+import { LitElement, html, css } from 'lit';
+import { Epml } from '../../../epml';
+import '@material/mwc-button';
+import '@material/mwc-dialog';
+import '@polymer/paper-spinner/paper-spinner-lite.js';
+import '@polymer/paper-progress/paper-progress.js';
+import '@material/mwc-icon';
+import '@vaadin/button';
+import './WrapperModal';
+import './TipUser';
+import './UserInfo/UserInfo';
+import './ChatImage';
+import './ReusableImage';
+import {
+ get,
+ translate,
+} from 'lit-translate';
+import { generateIdFromAddresses } from '../../utils/id-generation';
+
+const parentEpml = new Epml({ type: 'WINDOW', source: window.parent });
+
+class ChatRightPanelResources extends LitElement {
+ static get properties() {
+ return {
+ leaveGroupObj: { type: Object },
+ error: { type: Boolean },
+ chatHeads: { type: Array },
+ groupAdmin: { attribute: false },
+ groupMembers: { attribute: false },
+ selectedHead: { type: Object },
+ toggle: { attribute: false },
+ getMoreMembers: { attribute: false },
+ setOpenPrivateMessage: { attribute: false },
+ userName: { type: String },
+ walletBalance: { type: Number },
+ sendMoneyLoading: { type: Boolean },
+ btnDisable: { type: Boolean },
+ errorMessage: { type: String },
+ successMessage: { type: String },
+ setOpenTipUser: { attribute: false },
+ setOpenUserInfo: { attribute: false },
+ setUserName: { attribute: false },
+ chatId: { type: String },
+ _chatId: { type: String },
+ isReceipient: { type: Boolean },
+ images: { type: Array },
+ viewImage: { type: Boolean },
+ autoView: {type: Boolean},
+ onlyMyImages: {type: Boolean},
+ repost: {attribute: false}
+ };
+ }
+
+ constructor() {
+ super();
+ this.leaveGroupObj = {};
+ this.leaveFee = 0.001;
+ this.error = false;
+ this.chatHeads = [];
+ this.groupAdmin = [];
+ this.groupMembers = [];
+ this.observerHandler = this.observerHandler.bind(this);
+ this.getMoreImages = this.getMoreImages.bind(this);
+ this.viewElement = '';
+ this.downObserverElement = '';
+
+ this.sendMoneyLoading = false;
+ this.btnDisable = false;
+ this.errorMessage = '';
+ this.successMessage = '';
+
+ this.images = [];
+ this.viewImage = false;
+ this.myName =
+ window.parent.reduxStore.getState().app.accountInfo.names[0].name;
+ this.myAddress =
+ window.parent.reduxStore.getState().app.selectedAddress.address;
+ this.autoView =false
+ this.onlyMyImages = true
+ }
+
+ static get styles() {
+ return css`
+ .top-bar-icon {
+ cursor: pointer;
+ height: 18px;
+ width: 18px;
+ transition: 0.2s all;
+ }
+
+ .top-bar-icon:hover {
+ color: var(--black);
+ }
+
+ .modal-button {
+ font-family: Roboto, sans-serif;
+ font-size: 16px;
+ color: var(--mdc-theme-primary);
+ background-color: transparent;
+ padding: 8px 10px;
+ border-radius: 5px;
+ border: none;
+ transition: all 0.3s ease-in-out;
+ }
+
+ .close-row {
+ width: 100%;
+ display: flex;
+ justify-content: flex-end;
+ height: 50px;
+ flex: 0;
+ align-items: center;
+ }
+
+ .container-body {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ overflow: auto;
+ margin-top: 5px;
+ padding: 0px 6px;
+ box-sizing: border-box;
+ }
+
+ .container-body::-webkit-scrollbar-track {
+ background-color: whitesmoke;
+ border-radius: 7px;
+ }
+
+ .container-body::-webkit-scrollbar {
+ width: 6px;
+ border-radius: 7px;
+ background-color: whitesmoke;
+ }
+
+ .container-body::-webkit-scrollbar-thumb {
+ background-color: rgb(180, 176, 176);
+ border-radius: 7px;
+ transition: all 0.3s ease-in-out;
+ }
+
+ .container-body::-webkit-scrollbar-thumb:hover {
+ background-color: rgb(148, 146, 146);
+ cursor: pointer;
+ }
+
+ p {
+ color: var(--black);
+ margin: 0px;
+ padding: 0px;
+ word-break: break-all;
+ }
+
+ .container {
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+ height: 100%;
+ }
+
+ .chat-right-panel-label {
+ font-family: Montserrat, sans-serif;
+ color: var(--group-header);
+ padding: 5px;
+ font-size: 13px;
+ user-select: none;
+ }
+
+ .group-info {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ gap: 10px;
+ }
+
+ .group-name {
+ font-family: Raleway, sans-serif;
+ font-size: 20px;
+ color: var(--chat-bubble-msg-color);
+ text-align: center;
+ user-select: none;
+ }
+
+ .group-description {
+ font-family: Roboto, sans-serif;
+ color: var(--chat-bubble-msg-color);
+ letter-spacing: 0.3px;
+ font-weight: 300;
+ font-size: 14px;
+ margin-top: 15px;
+ word-break: break-word;
+ user-select: none;
+ }
+
+ .group-subheader {
+ font-family: Montserrat, sans-serif;
+ font-size: 14px;
+ color: var(--chat-bubble-msg-color);
+ }
+
+ .group-data {
+ font-family: Roboto, sans-serif;
+ letter-spacing: 0.3px;
+ font-weight: 300;
+ font-size: 14px;
+ color: var(--chat-bubble-msg-color);
+ }
+ .message-myBg {
+ background-color: var(--chat-bubble-myBg) !important;
+ margin-bottom: 15px;
+ border-radius: 5px;
+ padding: 5px;
+ }
+ .message-data-name {
+ user-select: none;
+ color: #03a9f4;
+ margin-bottom: 5px;
+ }
+ .message-user-info {
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ gap: 10px;
+ }
+
+ .hideImg {
+ visibility: hidden;
+ }
+ .checkbox-row {
+ position: relative;
+ display: flex;
+ align-items: center;
+ align-content: center;
+ font-family: Montserrat, sans-serif;
+ font-weight: 600;
+ color: var(--black);
+ padding-left: 5px;
+ }
+ `;
+ }
+
+ async getMoreImages(reset) {
+ try {
+ if(reset){
+ this.images = []
+ }
+ const groupPart = this.isReceipient
+ ? `direct_${generateIdFromAddresses(this._chatId, this.myAddress)}`
+ : `group_${this._chatId}`;
+
+ let offset = reset ? 0 : this.images.length;
+ let endpoint = `/arbitrary/resources/search?service=QCHAT_IMAGE&identifier=qchat_${groupPart}&reverse=true&limit=20&reverse=true&offset=${offset}`
+ if(this.onlyMyImages){
+ endpoint = endpoint + `&name=${this.myName}`
+ }
+ const qchatImages = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: endpoint,
+ });
+
+ let list = []
+ if(reset){
+ list = qchatImages
+ } else {
+ list = [...this.images, ...qchatImages]
+ }
+
+ this.images = list
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ firstUpdated() {
+ this.viewElement = this.shadowRoot.getElementById('viewElement');
+ this.downObserverElement =
+ this.shadowRoot.getElementById('downObserver');
+ this.elementObserver();
+ }
+
+ async updated(changedProperties) {
+ if (changedProperties && changedProperties.has('_chatId')) {
+ this.images = [];
+ this.getMoreImages(true);
+ }
+
+ if (changedProperties && changedProperties.has('onlyMyImages')) {
+ this.getMoreImages(true)
+ }
+ }
+
+ elementObserver() {
+ const options = {
+ root: this.viewElement,
+ rootMargin: '0px',
+ threshold: 1,
+ };
+ // identify an element to observe
+ const elementToObserve = this.downObserverElement;
+ // passing it a callback function
+ const observer = new IntersectionObserver(
+ this.observerHandler,
+ options
+ );
+ // call `observe()` on that MutationObserver instance,
+ // passing it the element to observe, and the options object
+ observer.observe(elementToObserve);
+ }
+
+ observerHandler(entries) {
+ if (!entries[0].isIntersecting) {
+ return;
+ } else {
+ if (this.images.length < 20) {
+ return;
+ }
+ this.getMoreImages();
+ }
+ }
+
+ selectAuto(e) {
+ if (e.target.checked) {
+ this.autoView = false
+ } else {
+ this.autoView = true
+ }
+ }
+
+ selectMyImages(e) {
+ if (e.target.checked) {
+ this.onlyMyImages = false
+ } else {
+ this.onlyMyImages = true
+ }
+ }
+
+ render() {
+ return html`
+
+
+
+ {
+ this.getMoreImages(true)
+ }} style="color: var(--black); cursor:pointer;">refresh
+
+ this.toggle(
+ false
+ )} style="margin: 0px 10px" icon="vaadin:close" slot="icon">
+
+
+
+ this.selectAuto(e)} ?checked=${this.autoView}>
+
+
+
+ this.selectMyImages(e)} ?checked=${this.onlyMyImages}>
+
+
+
+ ${this.images.map((image) => {
+ return html`
`;
+ })}
+
+
+
+
+ `;
+ }
+}
+
+customElements.define('chat-right-panel-resources', ChatRightPanelResources);
+
+class ImageParent extends LitElement {
+ static get properties() {
+ return {
+ leaveGroupObj: { type: Object },
+ error: { type: Boolean },
+ chatHeads: { type: Array },
+ groupAdmin: { attribute: false },
+ groupMembers: { attribute: false },
+ selectedHead: { type: Object },
+ toggle: { attribute: false },
+ getMoreMembers: { attribute: false },
+ setOpenPrivateMessage: { attribute: false },
+ userName: { type: String },
+ walletBalance: { type: Number },
+ sendMoneyLoading: { type: Boolean },
+ btnDisable: { type: Boolean },
+ errorMessage: { type: String },
+ successMessage: { type: String },
+ setOpenTipUser: { attribute: false },
+ setOpenUserInfo: { attribute: false },
+ setUserName: { attribute: false },
+ chatId: { type: String },
+ _chatId: { type: String },
+ isReceipient: { type: Boolean },
+ images: { type: Array },
+ viewImage: { type: Boolean },
+ image: { type: Object },
+ autoView: {type: Boolean},
+ repost: {attribute: false},
+ isImgLoaded: {type: Boolean}
+ };
+ }
+
+ constructor() {
+ super();
+ this.leaveGroupObj = {};
+ this.leaveFee = 0.001;
+ this.error = false;
+ this.chatHeads = [];
+ this.groupAdmin = [];
+ this.groupMembers = [];
+ this.viewElement = '';
+
+ this.sendMoneyLoading = false;
+ this.btnDisable = false;
+ this.errorMessage = '';
+ this.successMessage = '';
+ this.isImgLoaded = false
+ this.images = [];
+ this.viewImage = false;
+ this.myName =
+ window.parent.reduxStore.getState().app.accountInfo.names[0].name;
+ }
+
+ static get styles() {
+ return css`
+ .top-bar-icon {
+ cursor: pointer;
+ height: 18px;
+ width: 18px;
+ transition: 0.2s all;
+ }
+
+ .top-bar-icon:hover {
+ color: var(--black);
+ }
+
+ .modal-button {
+ font-family: Roboto, sans-serif;
+ font-size: 16px;
+ color: var(--mdc-theme-primary);
+ background-color: transparent;
+ padding: 8px 10px;
+ border-radius: 5px;
+ border: none;
+ transition: all 0.3s ease-in-out;
+ }
+
+ .close-row {
+ width: 100%;
+ display: flex;
+ justify-content: flex-end;
+ height: 50px;
+ flex: 0;
+ gap: 20px;
+ align-items: center;
+ }
+
+ .container-body {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ overflow: auto;
+ margin-top: 5px;
+ padding: 0px 6px;
+ box-sizing: border-box;
+ }
+
+ .container-body::-webkit-scrollbar-track {
+ background-color: whitesmoke;
+ border-radius: 7px;
+ }
+
+ .container-body::-webkit-scrollbar {
+ width: 6px;
+ border-radius: 7px;
+ background-color: whitesmoke;
+ }
+
+ .container-body::-webkit-scrollbar-thumb {
+ background-color: rgb(180, 176, 176);
+ border-radius: 7px;
+ transition: all 0.3s ease-in-out;
+ }
+
+ .container-body::-webkit-scrollbar-thumb:hover {
+ background-color: rgb(148, 146, 146);
+ cursor: pointer;
+ }
+
+ p {
+ color: var(--black);
+ margin: 0px;
+ padding: 0px;
+ word-break: break-all;
+ }
+
+ .container {
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+ height: 100%;
+ }
+
+ .chat-right-panel-label {
+ font-family: Montserrat, sans-serif;
+ color: var(--group-header);
+ padding: 5px;
+ font-size: 13px;
+ user-select: none;
+ }
+
+ .group-info {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ gap: 10px;
+ }
+
+ .group-name {
+ font-family: Raleway, sans-serif;
+ font-size: 20px;
+ color: var(--chat-bubble-msg-color);
+ text-align: center;
+ user-select: none;
+ }
+
+ .group-description {
+ font-family: Roboto, sans-serif;
+ color: var(--chat-bubble-msg-color);
+ letter-spacing: 0.3px;
+ font-weight: 300;
+ font-size: 14px;
+ margin-top: 15px;
+ word-break: break-word;
+ user-select: none;
+ }
+
+ .group-subheader {
+ font-family: Montserrat, sans-serif;
+ font-size: 14px;
+ color: var(--chat-bubble-msg-color);
+ }
+
+ .group-data {
+ font-family: Roboto, sans-serif;
+ letter-spacing: 0.3px;
+ font-weight: 300;
+ font-size: 14px;
+ color: var(--chat-bubble-msg-color);
+ }
+ .message-myBg {
+ background-color: var(--chat-bubble-myBg) !important;
+ margin-bottom: 15px;
+ border-radius: 5px;
+ padding: 5px;
+ }
+ .message-data-name {
+ user-select: none;
+ color: #03a9f4;
+ margin-bottom: 5px;
+ }
+ .message-user-info {
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ gap: 10px;
+ }
+
+ .hideImg {
+ visibility: hidden;
+ }
+ .image-container {
+ display: flex;
+ }
+ .repost-btn {
+ margin-top: 4px;
+ max-height: 28px;
+ padding: 5px 5px;
+ font-size: 14px;
+ background-color: #03a9f4;
+ color: white;
+ border: 1px solid transparent;
+ border-radius: 3px;
+ cursor: pointer;
+ }
+ `;
+ }
+
+ firstUpdated() {}
+
+ async updated(changedProperties) {
+ if (changedProperties && changedProperties.has('chatId')) {
+ // const autoSeeChatList =
+ // window.parent.reduxStore.getState().app.autoLoadImageChats;
+ // if (autoSeeChatList.includes(this.chatId)) {
+ // this.viewImage = true;
+ // }
+ }
+ }
+
+ onLoad(){
+ this.isImgLoaded = true
+ this.requestUpdate()
+ }
+
+ render() {
+ return html`
+ ${!this.autoView && !this.viewImage && this.myName !== this.image.name
+ ? html`
+
+
+
+ ${this.image.name}
+
+
+
{
+ this.viewImage = true;
+ }}
+ class=${[`image-container`].join(' ')}
+ style="height: 200px"
+ >
+
+ ${translate('chatpage.cchange40')}
+
+
+
+ `
+ : html``}
+ ${this.autoView || this.viewImage || this.myName === this.image.name
+ ? html`
+
+
+
+ ${this.image.name}
+
+
+
this.onLoad()}
+ >
+ ${this.isImgLoaded ? html`
+
+
+
+ ` : ''}
+
+
+ `
+ : ''}
+ `;
+ }
+}
+
+customElements.define('image-parent', ImageParent);
diff --git a/plugins/plugins/core/components/ChatScroller-css.js b/plugins/plugins/core/components/ChatScroller-css.js
index 028b0a79..2aad505d 100644
--- a/plugins/plugins/core/components/ChatScroller-css.js
+++ b/plugins/plugins/core/components/ChatScroller-css.js
@@ -43,6 +43,10 @@ export const chatStyles = css`
margin: 0;
padding: 20px 17px;
}
+ .message-sending {
+ opacity: 0.5;
+ cursor: progress;
+}
.chat-list {
overflow-y: auto;
@@ -256,7 +260,6 @@ export const chatStyles = css`
.message-parent {
padding: 3px;
background: rgba(245, 245, 245, 0);
- transition: all 0.1s ease-in-out;
}
.message-parent:hover {
@@ -364,7 +367,6 @@ export const chatStyles = css`
background:#fff;
color: #000;
text-align: center;
- box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
font-size: 12px;
z-index: 5;
white-space: nowrap;
@@ -410,7 +412,6 @@ export const chatStyles = css`
width: 150px;
height: 32px;
padding: 3px 8px;
- box-shadow: rgba(77, 77, 82, 0.2) 0px 7px 29px 0px;
}
.block-user:hover {
@@ -753,6 +754,17 @@ export const chatStyles = css`
visibility: visible;
}
+ .unread-divider {
+ width: 100%;
+ padding: 5px;
+ color: var(--black);
+ border-bottom: 1px solid var(--black);
+ display: flex;
+ justify-content: center;
+ border-radius: 2px;
+ margin-top: 5px;
+ }
+
.blink-bg{
border-radius: 8px;
animation: blinkingBackground 3s;
diff --git a/plugins/plugins/core/components/ChatScroller.js b/plugins/plugins/core/components/ChatScroller.js
index 8421df12..9da8a1b2 100644
--- a/plugins/plugins/core/components/ChatScroller.js
+++ b/plugins/plugins/core/components/ChatScroller.js
@@ -1,1129 +1,2179 @@
-import { LitElement, html, css } from 'lit'
-import { render } from 'lit/html.js'
-import { repeat } from 'lit/directives/repeat.js'
-import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
-import { unsafeHTML } from 'lit/directives/unsafe-html.js'
-import { chatStyles } from './ChatScroller-css.js'
-import { Epml } from '../../../epml'
-import { cropAddress } from "../../utils/cropAddress"
-import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js'
-import { EmojiPicker } from 'emoji-picker-js'
-import { generateHTML } from '@tiptap/core'
-import isElectron from 'is-electron'
+import { LitElement, html, } from 'lit';
+import { repeat } from 'lit/directives/repeat.js';
+import {
+ get,
+ translate,
+} from 'lit-translate';
+import { unsafeHTML } from 'lit/directives/unsafe-html.js';
+import { chatStyles } from './ChatScroller-css.js';
+import { Epml } from '../../../epml';
+import { cropAddress } from '../../utils/cropAddress';
+import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js';
+import { generateHTML } from '@tiptap/core';
+import isElectron from 'is-electron';
-import axios from 'axios'
-import Highlight from '@tiptap/extension-highlight'
-import ShortUniqueId from 'short-unique-id'
-import StarterKit from '@tiptap/starter-kit'
-import Underline from '@tiptap/extension-underline'
+import axios from 'axios';
+import Highlight from '@tiptap/extension-highlight';
+import ShortUniqueId from 'short-unique-id';
+import StarterKit from '@tiptap/starter-kit';
+import Underline from '@tiptap/extension-underline';
-import './ChatModals.js'
-import './LevelFounder.js'
-import './NameMenu.js'
-import './UserInfo/UserInfo.js'
-import './WrapperModal'
+import './ChatModals.js';
+import './LevelFounder.js';
+import './NameMenu.js';
+import './UserInfo/UserInfo.js';
+import './WrapperModal';
+import './ChatImage';
-import '@material/mwc-button'
-import '@material/mwc-dialog'
-import '@material/mwc-icon'
-import '@vaadin/icon'
-import '@vaadin/icons'
-import '@vaadin/tooltip'
+import '@material/mwc-button';
+import '@material/mwc-dialog';
+import '@material/mwc-icon';
+import '@vaadin/icon';
+import '@vaadin/icons';
+import '@vaadin/tooltip';
+import { chatLimit, totalMsgCount } from './ChatPage.js';
-const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
+const parentEpml = new Epml({ type: 'WINDOW', source: window.parent });
+let toggledMessage = {};
-let toggledMessage = {}
-
-const uid = new ShortUniqueId()
+const uid = new ShortUniqueId();
const getApiKey = () => {
- const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
- let apiKey = myNode.apiKey
- return apiKey
-}
+ const myNode =
+ window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
+ window.parent.reduxStore.getState().app.nodeConfig.node
+ ];
+ let apiKey = myNode.apiKey;
+ return apiKey;
+};
const extractComponents = async (url) => {
- if (!url.startsWith("qortal://")) {
- return null
- }
+ if (!url.startsWith('qortal://')) {
+ return null;
+ }
- url = url.replace(/^(qortal\:\/\/)/, "")
- if (url.includes("/")) {
- let parts = url.split("/")
- const service = parts[0].toUpperCase()
- parts.shift()
- const name = parts[0]
- parts.shift()
- let identifier
+ url = url.replace(/^(qortal:\/\/)/, '');
+ if (url.startsWith('use-')) {
+ // Handle the new 'use' format
+ let parts = url.split('/');
+ const type = parts[0].split('-')[1]; // e.g., 'group' from 'use-group'
+ parts.shift();
+ const action = parts.length > 0 ? parts[0].split('-')[1] : null; // e.g., 'invite' from 'action-invite'
+ parts.shift();
+ const idPrefix = parts.length > 0 ? parts[0].split('-')[0] : null; // e.g., 'groupid' from 'groupid-321'
+ const id = parts.length > 0 ? parts[0].split('-')[1] : null; // e.g., '321' from 'groupid-321'
+ return {
+ type: type,
+ action: action,
+ [idPrefix]: id
+ }
+ } else if (url.includes('/')) {
+ let parts = url.split('/');
+ const service = parts[0].toUpperCase();
+ parts.shift();
+ const name = parts[0];
+ parts.shift();
+ let identifier;
- if (parts.length > 0) {
- identifier = parts[0] // Do not shift yet
- // Check if a resource exists with this service, name and identifier combination
- let responseObj = await parentEpml.request('apiCall', {
- url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${getApiKey()}`
- })
+ if (parts.length > 0) {
+ identifier = parts[0]; // Do not shift yet
+ // Check if a resource exists with this service, name and identifier combination
+ let responseObj = await parentEpml.request('apiCall', {
+ url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${getApiKey()}`,
+ });
- if (responseObj.totalChunkCount > 0) {
- // Identifier exists, so don't include it in the path
- parts.shift()
- }
- else {
- identifier = null
- }
- }
+ if (responseObj.totalChunkCount > 0) {
+ // Identifier exists, so don't include it in the path
+ parts.shift();
+ } else {
+ identifier = null;
+ }
+ }
- const path = parts.join("/")
+ const path = parts.join('/');
- const components = {}
- components["service"] = service
- components["name"] = name
- components["identifier"] = identifier
- components["path"] = path
- return components
- }
+ const components = {};
+ components['service'] = service;
+ components['name'] = name;
+ components['identifier'] = identifier;
+ components['path'] = path;
+ return components;
+ }
- return null
-}
+ return null;
+};
function processText(input) {
- const linkRegex = /(qortal:\/\/\S+)/g
+ const linkRegex = /(qortal:\/\/\S+)/g;
- function processNode(node) {
- if (node.nodeType === Node.TEXT_NODE) {
- const parts = node.textContent.split(linkRegex)
+ function processNode(node) {
+ if (node.nodeType === Node.TEXT_NODE) {
+ const parts = node.textContent.split(linkRegex);
- if (parts.length > 1) {
- const fragment = document.createDocumentFragment()
+ if (parts.length > 1) {
+ const fragment = document.createDocumentFragment();
- parts.forEach((part) => {
- if (part.startsWith('qortal://')) {
- const link = document.createElement('span')
- // Store the URL in a data attribute
- link.setAttribute('data-url', part)
- link.textContent = part
- link.style.color = 'var(--code-block-text-color)'
- link.style.textDecoration = 'underline'
- link.style.cursor = 'pointer'
+ parts.forEach((part) => {
+ if (part.startsWith('qortal://')) {
+ const link = document.createElement('span');
+ // Store the URL in a data attribute
+ link.setAttribute('data-url', part);
+ link.textContent = part;
+ link.style.color = 'var(--code-block-text-color)';
+ link.style.textDecoration = 'underline';
+ link.style.cursor = 'pointer';
- link.addEventListener('click', async (e) => {
- e.preventDefault()
- try {
- const res = await extractComponents(part)
- if (!res) return
- const { service, name, identifier, path } = res
- let query = `?service=${service}`
- if (name) {
- query = query + `&name=${name}`
- }
- if (identifier) {
- query = query + `&identifier=${identifier}`
- }
- if (path) {
- query = query + `&path=${path}`
- }
- window.parent.reduxStore.dispatch(window.parent.reduxAction.setNewTab({
- url: `qdn/browser/index.html${query}`,
- id: uid.rnd(),
- myPlugObj: {
- "url": "myapp",
- "domain": "core",
- "page": `qdn/browser/index.html${query}`,
- "title": name,
- "icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser',
- "mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser',
- "menus": [],
- "parent": false
- }
- }))
+ link.addEventListener('click', async (e) => {
+ e.preventDefault();
+ try {
+ const res = await extractComponents(part);
+ if (!res) return;
+ if(res.type && res.groupid && res.action === 'join'){
+ window.parent.reduxStore.dispatch(
+ window.parent.reduxAction.setNewTab({
+ url: `group-management`,
+ id: uid.rnd(),
+ myPlugObj: {
+ "url": "group-management",
+ "domain": "core",
+ "page": "group-management/index.html",
+ "title": "Group Management",
+ "icon": "vaadin:group",
+ "mwcicon": "group",
+ "pluginNumber": "plugin-fJZNpyLGTl",
+ "menus": [],
+ "parent": false
+ },
+ openExisting: true
+ })
+ );
+ window.parent.reduxStore.dispatch(
+ window.parent.reduxAction.setSideEffectAction({
+ type: 'openJoinGroupModal',
+ data: +res.groupid
+ })
+ );
+ return
+ }
+ const { service, name, identifier, path } = res;
+ let query = `?service=${service}`;
+ if (name) {
+ query = query + `&name=${name}`;
+ }
+ if (identifier) {
+ query = query + `&identifier=${identifier}`;
+ }
+ if (path) {
+ query = query + `&path=${path}`;
+ }
+ window.parent.reduxStore.dispatch(
+ window.parent.reduxAction.setNewTab({
+ url: `qdn/browser/index.html${query}`,
+ id: uid.rnd(),
+ myPlugObj: {
+ url: 'myapp',
+ domain: 'core',
+ page: `qdn/browser/index.html${query}`,
+ title: name,
+ icon:
+ service === 'WEBSITE'
+ ? 'vaadin:desktop'
+ : 'vaadin:external-browser',
+ mwcicon:
+ service === 'WEBSITE'
+ ? 'desktop_mac'
+ : 'open_in_browser',
+ menus: [],
+ parent: false,
+ },
+ })
+ );
+ } catch (error) {
+ console.log({ error });
+ }
+ });
- } catch (error) {
- console.log({ error })
- }
+ fragment.appendChild(link);
+ } else {
+ const textNode = document.createTextNode(part);
+ fragment.appendChild(textNode);
+ }
+ });
- })
+ node.replaceWith(fragment);
+ }
+ } else {
+ for (const childNode of Array.from(node.childNodes)) {
+ processNode(childNode);
+ }
+ }
+ }
- fragment.appendChild(link)
- } else {
- const textNode = document.createTextNode(part)
- fragment.appendChild(textNode)
- }
- })
+ const wrapper = document.createElement('div');
+ wrapper.innerHTML = input;
- node.replaceWith(fragment)
- }
- } else {
- for (const childNode of Array.from(node.childNodes)) {
- processNode(childNode)
- }
- }
- }
+ processNode(wrapper);
- const wrapper = document.createElement('div')
- wrapper.innerHTML = input
-
- processNode(wrapper)
-
- return wrapper
+ return wrapper;
}
+
+
class ChatScroller extends LitElement {
- static get properties() {
- return {
- theme: { type: String, reflect: true },
- getNewMessage: { attribute: false },
- getOldMessage: { attribute: false },
- escapeHTML: { attribute: false },
- messages: { type: Array },
- hideMessages: { type: Array },
- setRepliedToMessageObj: { attribute: false },
- setEditedMessageObj: { attribute: false },
- sendMessage: { attribute: false },
- sendMessageForward: { attribute: false },
- showLastMessageRefScroller: { attribute: false },
- emojiPicker: { attribute: false },
- isLoadingMessages: { type: Boolean },
- setIsLoadingMessages: { attribute: false },
- chatId: { type: String },
- setForwardProperties: { attribute: false },
- setOpenPrivateMessage: { attribute: false },
- setOpenUserInfo: { attribute: false },
- setOpenTipUser: { attribute: false },
- setUserName: { attribute: false },
- setSelectedHead: { attribute: false },
- openTipUser: { type: Boolean },
- openUserInfo: { type: Boolean },
- userName: { type: String },
- selectedHead: { type: Object },
- goToRepliedMessage: { attribute: false },
- getOldMessageAfter: { attribute: false },
- listSeenMessages: { type: Array }
- }
- }
+ static get properties() {
+ return {
+ theme: { type: String, reflect: true },
+ getNewMessage: { attribute: false },
+ getOldMessage: { attribute: false },
+ getAfterMessages: { attribute: false },
+ escapeHTML: { attribute: false },
+ messages: { type: Object },
+ hideMessages: { type: Array },
+ setRepliedToMessageObj: { attribute: false },
+ setEditedMessageObj: { attribute: false },
+ sendMessage: { attribute: false },
+ sendMessageForward: { attribute: false },
+ showLastMessageRefScroller: { attribute: false },
+ emojiPicker: { attribute: false },
+ isLoadingMessages: { type: Boolean },
+ setIsLoadingMessages: { attribute: false },
+ chatId: { type: String },
+ setForwardProperties: { attribute: false },
+ setOpenPrivateMessage: { attribute: false },
+ setOpenUserInfo: { attribute: false },
+ setOpenTipUser: { attribute: false },
+ setUserName: { attribute: false },
+ setSelectedHead: { attribute: false },
+ openTipUser: { type: Boolean },
+ openUserInfo: { type: Boolean },
+ userName: { type: String },
+ selectedHead: { type: Object },
+ goToRepliedMessage: { attribute: false },
+ listSeenMessages: { type: Array },
+ updateMessageHash: { type: Object },
+ messagesToRender: { type: Array },
+ oldMessages: { type: Array },
+ clearUpdateMessageHashmap: { attribute: false },
+ disableFetching: { type: Boolean },
+ isLoadingBefore: { type: Boolean },
+ isLoadingAfter: { type: Boolean },
+ messageQueue: { type: Array },
+ loggedInUserName: { type: String },
+ loggedInUserAddress: { type: String },
+ };
+ }
- static get styles() {
- return [chatStyles]
- }
+ static get styles() {
+ return [chatStyles];
+ }
- constructor() {
- super()
- this.messages = []
- this._upObserverhandler = this._upObserverhandler.bind(this)
- this._downObserverHandler = this._downObserverHandler.bind(this)
- this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address
- this.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]")
- this.openTipUser = false
- this.openUserInfo = false
- this.listSeenMessages = []
- this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
- }
+ constructor() {
+ super();
+ this.messages = {
+ messages: [],
+ type: '',
+ };
+ this.oldMessages = [];
+ this._upObserverhandler = this._upObserverhandler.bind(this);
+ this.newListMessages = this.newListMessages.bind(this);
+ this.newListMessagesUnreadMessages =
+ this.newListMessagesUnreadMessages.bind(this);
+ this._downObserverHandler = this._downObserverHandler.bind(this);
+ this.isLastMessageBeforeUnread =
+ this.isLastMessageBeforeUnread.bind(this);
+ this.replaceMessagesWithUpdate =
+ this.replaceMessagesWithUpdate.bind(this);
+ this.__bottomObserverForFetchingMessagesHandler =
+ this.__bottomObserverForFetchingMessagesHandler.bind(this);
+ this.myAddress =
+ window.parent.reduxStore.getState().app.selectedAddress.address;
+ this.hideMessages = JSON.parse(
+ localStorage.getItem('MessageBlockedAddresses') || '[]'
+ );
+ this.openTipUser = false;
+ this.openUserInfo = false;
+ this.listSeenMessages = [];
+ this.theme = localStorage.getItem('qortalTheme')
+ ? localStorage.getItem('qortalTheme')
+ : 'light';
+ this.messagesToRender = [];
+ this.disableFetching = false;
+ this.isLoadingBefore = false;
+ this.isLoadingAfter = false;
+ this.disableAddingNewMessages = false;
+ this.lastReadMessageTimestamp = null;
+ this.messageQueue = [];
+ }
- addSeenMessage(val) {
- this.listSeenMessages.push(val)
- }
+ addSeenMessage(val) {
+ this.listSeenMessages.push(val);
+ }
+ goToRepliedMessageFunc(val, val2) {
+ this.disableFetching = true;
+ this.goToRepliedMessage(val, val2);
+ }
- render() {
- let formattedMessages = this.messages.reduce((messageArray, message, index) => {
- const lastGroupedMessage = messageArray[messageArray.length - 1]
- let timestamp
- let sender
- let repliedToData
- let firstMessageInChat
+ shouldGroupWithLastMessage(newMessage, lastGroupedMessage) {
+ if (!lastGroupedMessage) return false;
- if (index === 0) {
- firstMessageInChat = true
- } else {
- firstMessageInChat = false
- }
+ return (
+ Math.abs(lastGroupedMessage.timestamp - newMessage.timestamp) <
+ 600000 &&
+ lastGroupedMessage.sender === newMessage.sender &&
+ !lastGroupedMessage.repliedToData
+ );
+ }
- message = { ...message, firstMessageInChat }
+ clearLoaders() {
+ this.isLoadingBefore = false;
+ this.isLoadingAfter = false;
+ this.disableFetching = false;
+ }
+ addNewMessage(newMessage) {
+ const lastGroupedMessage =
+ this.messagesToRender[this.messagesToRender.length - 1];
- if (lastGroupedMessage) {
- timestamp = lastGroupedMessage.timestamp
- sender = lastGroupedMessage.sender
- repliedToData = lastGroupedMessage.repliedToData
- }
- const isSameGroup = Math.abs(timestamp - message.timestamp) < 600000 && sender === message.sender && !repliedToData
+ if (this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)) {
+ lastGroupedMessage.messages.push(newMessage);
+ } else {
+ this.messagesToRender.push({
+ messages: [newMessage],
+ ...newMessage,
+ });
+ }
+ this.clearLoaders();
+ this.requestUpdate();
+ }
- if (isSameGroup) {
- messageArray[messageArray.length - 1].messages = [
- ...(messageArray[messageArray.length - 1].messages || []),
- message
- ]
- } else {
- messageArray.push({
- messages: [message],
- ...message
- })
- }
- return messageArray
- }, [])
+ async newListMessages(newMessages) {
+ let data = [];
+ const copy = [...newMessages];
+ copy.forEach((newMessage) => {
+ const lastGroupedMessage = data[data.length - 1];
- return html`
- ${this.isLoadingMessages ? html`
-
- ` : ''}
-
-
- ${formattedMessages.map((formattedMessage) => {
- return repeat(
- formattedMessage.messages,
- (message) => message.signature,
- (message, indexMessage) => html`
- 1}
- ?isLastMessageInGroup=${indexMessage === formattedMessage.messages.length - 1}
- .setToggledMessage=${this.setToggledMessage}
- .setForwardProperties=${this.setForwardProperties}
- .setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
- .setOpenTipUser=${(val) => this.setOpenTipUser(val)}
- .setOpenUserInfo=${(val) => this.setOpenUserInfo(val)}
- .setUserName=${(val) => this.setUserName(val)}
- id=${message.signature}
- .goToRepliedMessage=${this.goToRepliedMessage}
- .addSeenMessage=${(val) => this.addSeenMessage(val)}
- .listSeenMessages=${this.listSeenMessages}
- chatId=${this.chatId}
- >
- `
- )
- })}
-
-
- `
- }
+ if (
+ this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)
+ ) {
+ lastGroupedMessage.messages.push(newMessage);
+ } else {
+ data.push({
+ messages: [newMessage],
+ ...newMessage,
+ });
+ }
+ });
- shouldUpdate(changedProperties) {
- if (changedProperties.has('isLoadingMessages')) {
- return true
- }
- if (changedProperties.has('chatId') && changedProperties.get('chatId')) {
- return true
- }
- if (changedProperties.has('openTipUser')) {
- return true
- }
- if (changedProperties.has('openUserInfo')) {
- return true
- }
- if (changedProperties.has('userName')) {
- return true
- }
- // Only update element if prop1 changed.
- return changedProperties.has('messages')
- }
+ // const getCount = await parentEpml.request('apiCall', {
+ // type: 'api',
+ // url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${chatLimit}&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`
+ // })
+ this.messagesToRender = data;
+ this.clearLoaders();
+ this.requestUpdate();
+ await this.updateComplete;
+ }
- async getUpdateComplete() {
- await super.getUpdateComplete()
- const marginElements = Array.from(this.shadowRoot.querySelectorAll('message-template'))
- await Promise.all(marginElements.map(el => el.updateComplete))
- return true
- }
+ async newListMessagesUnreadMessages(
+ newMessages,
+ message,
+ lastReadMessageTimestamp,
+ count
+ ) {
+ let data = [];
+ const copy = [...newMessages];
- setToggledMessage(message) {
- toggledMessage = message
- }
+ let dividerPlaced = false; // To ensure the divider is added only once
- async firstUpdated() {
- this.changeTheme()
+ // Start from the end of the list (newest messages)
+ for (let i = copy.length - 1; i >= 0; i--) {
+ let newMessage = copy[i];
- window.addEventListener('storage', () => {
- const checkTheme = localStorage.getItem('qortalTheme')
+ // Initialize a property for the divider
+ newMessage.isDivider = false;
- if (checkTheme === 'dark') {
- this.theme = 'dark'
- } else {
- this.theme = 'light'
- }
- document.querySelector('html').setAttribute('theme', this.theme)
- })
+ // Check if this is the message before which the divider should be placed
+ if (
+ !dividerPlaced &&
+ newMessage.timestamp <= lastReadMessageTimestamp
+ ) {
+ newMessage.isDivider = true;
+ dividerPlaced = true; // Ensure the divider is only added once
+ break; // Exit once the divider is placed
+ }
+ }
- this.emojiPicker.on('emoji', selection => {
- this.sendMessage({
- type: 'reaction',
- editedMessageObj: toggledMessage,
- reaction: selection.emoji,
- })
- })
- this.viewElement = this.shadowRoot.getElementById('viewElement')
- this.upObserverElement = this.shadowRoot.getElementById('upObserver')
- this.downObserverElement = this.shadowRoot.getElementById('downObserver')
- // Intialize Observers
- this.upElementObserver()
- this.downElementObserver()
- await this.getUpdateComplete()
- this.viewElement.scrollTop = this.viewElement.scrollHeight + 50
+ copy.forEach((newMessage) => {
+ const lastGroupedMessage = data[data.length - 1];
- this.clearConsole()
- setInterval(() => {
- this.clearConsole()
- }, 60000)
- }
+ if (
+ this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)
+ ) {
+ lastGroupedMessage.messages.push(newMessage);
+ } else {
+ data.push({
+ messages: [newMessage],
+ ...newMessage,
+ });
+ }
+ });
+ if (count > 0) {
+ this.disableAddingNewMessages = true;
+ }
+ this.messagesToRender = data;
+ this.clearLoaders();
+ this.requestUpdate();
+ await this.updateComplete;
+ const findElement = this.shadowRoot.getElementById('unread-divider-id');
+ if (findElement) {
+ findElement.scrollIntoView({ behavior: 'auto', block: 'center' });
+ }
+ }
- clearConsole() {
- if (!isElectron()) {
- } else {
- console.clear()
- window.parent.electronAPI.clearCache()
- }
- }
+ async addNewMessages(newMessages, type) {
+ if (this.disableAddingNewMessages && type === 'newComingInAuto') return;
- changeTheme() {
- const checkTheme = localStorage.getItem('qortalTheme')
- if (checkTheme === 'dark') {
- this.theme = 'dark'
- } else {
- this.theme = 'light'
- }
- document.querySelector('html').setAttribute('theme', this.theme)
- }
+ const viewElement = this.shadowRoot.querySelector('#viewElement');
+ const copy = type === 'initial' ? [] : [...this.messagesToRender];
- _getOldMessage(_scrollElement) {
- this.getOldMessage(_scrollElement)
- }
+ for (const newMessage of newMessages) {
+ const lastGroupedMessage = copy[copy.length - 1];
- _getOldMessageAfter(_scrollElement) {
- this.getOldMessageAfter(_scrollElement)
- }
+ if (
+ this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)
+ ) {
+ lastGroupedMessage.messages.push(newMessage);
+ } else {
+ copy.push({
+ messages: [newMessage],
+ ...newMessage,
+ });
+ }
+ }
- _upObserverhandler(entries) {
- if (entries[0].isIntersecting) {
- if (this.messages.length < 20) {
- return
- }
- this.setIsLoadingMessages(true)
- let _scrollElement = entries[0].target.nextElementSibling
- this._getOldMessage(_scrollElement)
- }
- }
+ // Ensure that the total number of individual messages doesn't exceed totalMsgCount
+ let totalMessagesCount = copy.reduce(
+ (acc, group) => acc + group.messages.length,
+ 0
+ );
+ while (totalMessagesCount > totalMsgCount && copy.length) {
+ if (
+ newMessages.length < chatLimit &&
+ type !== 'newComingInAuto' &&
+ type !== 'initial'
+ ) {
+ this.disableAddingNewMessages = false;
+ }
+ const firstGroup = copy[0];
+ if (
+ firstGroup.messages.length <=
+ totalMessagesCount - totalMsgCount
+ ) {
+ // If removing the whole first group achieves the goal, remove it
+ totalMessagesCount -= firstGroup.messages.length;
+ copy.shift();
+ } else {
+ // Otherwise, trim individual messages from the first group
+ const messagesToRemove = totalMessagesCount - totalMsgCount;
+ firstGroup.messages.splice(0, messagesToRemove);
+ totalMessagesCount = totalMsgCount;
+ }
+ }
+ this.messagesToRender = copy;
+ this.requestUpdate();
+ await this.updateComplete;
- _downObserverHandler(entries) {
- if (!entries[0].isIntersecting) {
- let _scrollElement = entries[0].target.previousElementSibling
- // this._getOldMessageAfter(_scrollElement)
- this.showLastMessageRefScroller(true)
- } else {
- this.showLastMessageRefScroller(false)
- }
- }
+ if (type === 'initial') {
+ viewElement.scrollTop = viewElement.scrollHeight;
+ }
- upElementObserver() {
- const options = {
- root: this.viewElement,
- rootMargin: '0px',
- threshold: 1
- }
- const observer = new IntersectionObserver(this._upObserverhandler, options)
- observer.observe(this.upObserverElement)
- }
+ this.clearLoaders();
+ }
- downElementObserver() {
- 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._downObserverHandler, options)
- // call `observe()` on that MutationObserver instance,
- // passing it the element to observe, and the options object
- observer.observe(elementToObserve)
- }
+ async prependOldMessages(oldMessages) {
+ if (!this.messagesToRender) this.messagesToRender = []; // Ensure it's initialized
+
+ let currentMessageGroup = null;
+ let previousMessage = null;
+
+ for (const message of oldMessages) {
+ if (
+ !previousMessage ||
+ !this.shouldGroupWithLastMessage(message, previousMessage)
+ ) {
+
+ if (currentMessageGroup) {
+ this.messagesToRender.unshift(currentMessageGroup);
+ }
+ currentMessageGroup = {
+ id: message.signature,
+ messages: [message],
+ ...message,
+ };
+ } else {
+ // Add to the current group
+ currentMessageGroup.messages.unshift(message);
+ }
+ previousMessage = message;
+ }
+
+ // After processing all old messages, add the last group
+ if (currentMessageGroup) {
+ this.messagesToRender.unshift(currentMessageGroup);
+ }
+
+ // Ensure that the total number of individual messages doesn't exceed totalMsgCount
+ let totalMessagesCount = this.messagesToRender.reduce(
+ (acc, group) => acc + group.messages.length,
+ 0
+ );
+ while (
+ totalMessagesCount > totalMsgCount &&
+ this.messagesToRender.length
+ ) {
+ this.disableAddingNewMessages = true;
+ const lastGroup =
+ this.messagesToRender[this.messagesToRender.length - 1];
+ if (
+ lastGroup.messages.length <=
+ totalMessagesCount - totalMsgCount
+ ) {
+ // If removing the whole last group achieves the goal, remove it
+ totalMessagesCount -= lastGroup.messages.length;
+ this.messagesToRender.pop();
+ } else {
+ // Otherwise, trim individual messages from the last group
+ const messagesToRemove = totalMessagesCount - totalMsgCount;
+ lastGroup.messages.splice(-messagesToRemove, messagesToRemove);
+ totalMessagesCount = totalMsgCount;
+ }
+ }
+ this.clearLoaders();
+ this.requestUpdate();
+ }
+
+ async replaceMessagesWithUpdate(updatedMessages) {
+ const viewElement = this.shadowRoot.querySelector('#viewElement');
+ if (!viewElement) return; // Ensure the element exists
+ const isUserAtBottom =
+ viewElement.scrollTop + viewElement.clientHeight ===
+ viewElement.scrollHeight;
+
+
+ // Using map to return a new array, rather than mutating the old one
+ const newMessagesToRender = this.messagesToRender.map((group) => {
+ // For each message, return the updated message if it exists, otherwise return the original message
+ const updatedGroupMessages = group.messages.map((message) => {
+ return updatedMessages[message.signature]
+ ? { ...message, ...updatedMessages[message.signature] }
+ : message;
+ });
+
+ // Return a new group object with updated messages
+ return {
+ ...group,
+ messages: updatedGroupMessages,
+ };
+ });
+
+ this.messagesToRender = newMessagesToRender;
+ this.requestUpdate();
+ await this.updateComplete;
+
+ if (isUserAtBottom) {
+ viewElement.scrollTop =
+ viewElement.scrollHeight - viewElement.clientHeight;
+ } else {
+ // Adjust scroll position based on the difference in scroll heights
+ const newScrollHeight = viewElement.scrollHeight;
+ viewElement.scrollTop =
+ viewElement.scrollTop +
+ (newScrollHeight - viewElement.scrollHeight);
+ }
+
+ this.clearUpdateMessageHashmap();
+ this.clearLoaders();
+ }
+
+ async replaceMessagesWithUpdateByArray(updatedMessagesArray) {
+ let previousScrollTop;
+ let previousScrollHeight;
+
+ const viewElement = this.shadowRoot.querySelector('#viewElement');
+ previousScrollTop = viewElement.scrollTop;
+ previousScrollHeight = viewElement.scrollHeight;
+ for (let group of this.messagesToRender) {
+ for (let i = 0; i < group.messages.length; i++) {
+ const update = updatedMessagesArray.find(
+ (updatedMessage) =>
+ updatedMessage.chatReference ===
+ group.messages[i].signature ||
+ updatedMessage.chatReference ===
+ group.messages[i].originalSignature ||
+ updatedMessage.chatReference ===
+ group.messages[i].chatReference
+ );
+ if (update) {
+ Object.assign(group.messages[i], update);
+ }
+ }
+ }
+ this.requestUpdate();
+ const newScrollHeight = viewElement.scrollHeight;
+ viewElement.scrollTop =
+ previousScrollTop + (newScrollHeight - previousScrollHeight);
+ this.clearUpdateMessageHashmap();
+ this.clearLoaders();
+ }
+
+ async updated(changedProperties) {
+ if (changedProperties && changedProperties.has('messages')) {
+ if (this.messages.type === 'initial') {
+ this.addNewMessages(this.messages.messages, 'initial');
+ } else if (this.messages.type === 'initialLastSeen') {
+ this.newListMessagesUnreadMessages(
+ this.messages.messages,
+ 'initialLastSeen',
+ this.messages.lastReadMessageTimestamp,
+ this.messages.count
+ );
+ } else if (this.messages.type === 'new')
+ this.addNewMessages(this.messages.messages);
+ else if (this.messages.type === 'newComingInAuto')
+ this.addNewMessages(this.messages.messages, 'newComingInAuto');
+ else if (this.messages.type === 'old')
+ this.prependOldMessages(this.messages.messages);
+ else if (this.messages.type === 'inBetween')
+ this.newListMessages(
+ this.messages.messages,
+ this.messages.signature
+ );
+ else if (this.messages.type === 'update')
+ this.replaceMessagesWithUpdateByArray(this.messages.messages);
+ }
+ if (
+ changedProperties &&
+ changedProperties.has('updateMessageHash') &&
+ Object.keys(this.updateMessageHash).length > 0
+ ) {
+ this.replaceMessagesWithUpdate(this.updateMessageHash);
+ }
+ if (
+ changedProperties &&
+ changedProperties.has('messageQueue') &&
+ Object.keys(this.messageQueue).length > 0
+ ) {
+ if (!this.disableAddingNewMessages) {
+ await new Promise((res) => {
+ setTimeout(() => {
+ res();
+ }, 200);
+ });
+ const viewElement =
+ this.shadowRoot.querySelector('#viewElement');
+ viewElement.scrollTop = viewElement.scrollHeight + 200;
+ }
+ }
+ }
+
+ isLastMessageBeforeUnread(message, formattedMessages) {
+ // if the message is the last one in the older messages list and its timestamp is before the user's last seen timestamp
+ if (
+ message.timestamp < this.lastReadMessageTimestamp &&
+ formattedMessages.indexOf(message) === formattedMessages.length - 21
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ render() {
+
+ let formattedMessages = this.messagesToRender;
+
+ return html`
+ ${this.isLoadingBefore
+ ? html`
+
+ `
+ : ''}
+
+
+ ${repeat(
+ formattedMessages,
+ (formattedMessage) => formattedMessage.reference, // Use .id as the unique key for formattedMessage.
+ (formattedMessage) => html`
+ ${repeat(
+ formattedMessage.messages,
+ (message) => message.signature,
+ (message, indexMessage) => html`
+ 1}
+ ?isLastMessageInGroup=${indexMessage ===
+ formattedMessage.messages.length - 1}
+ .setToggledMessage=${this.setToggledMessage}
+ .setForwardProperties=${this
+ .setForwardProperties}
+ .setOpenPrivateMessage=${(val) =>
+ this.setOpenPrivateMessage(val)}
+ .setOpenTipUser=${(val) =>
+ this.setOpenTipUser(val)}
+ .setOpenUserInfo=${(val) =>
+ this.setOpenUserInfo(val)}
+ .setUserName=${(val) =>
+ this.setUserName(val)}
+ id=${message.signature}
+ .goToRepliedMessage=${(val, val2) =>
+ this.goToRepliedMessageFunc(val, val2)}
+ .addSeenMessage=${(val) =>
+ this.addSeenMessage(val)}
+ .listSeenMessages=${this.listSeenMessages}
+ chatId=${this.chatId}
+ >
+ ${message.isDivider
+ ? html`
+ ${translate('chatpage.cchange92')}
+
`
+ : null}
+ `
+ )}
+ `
+ )}
+ this.chatId.includes(item._chatId)).length > 0 ? 'height: 1px' : 'height: 1px'}
+ id="bottomObserverForFetchingMessages"
+ >
+
+
+
+ ${this.isLoadingAfter
+ ? html`
+
+ `
+ : ''}
+ ${!this.disableAddingNewMessages ? repeat(
+ this.messageQueue.filter((item) =>
+ this.chatId.includes(item._chatId)
+ ),
+ (message) => message.messageText,
+ (message) => html`
+
+ this.setOpenPrivateMessage(val)}
+ .setOpenTipUser=${(val) => this.setOpenTipUser(val)}
+ .setOpenUserInfo=${(val) =>
+ this.setOpenUserInfo(val)}
+ .setUserName=${(val) => this.setUserName(val)}
+ id=${message.signature}
+ .goToRepliedMessage=${(val, val2) =>
+ this.goToRepliedMessageFunc(val, val2)}
+ .addSeenMessage=${(val) => this.addSeenMessage(val)}
+ .listSeenMessages=${this.listSeenMessages}
+ chatId=${this.chatId}
+ ?isInProgress=${true}
+ >
+ `
+ ): ''}
+
+ `;
+ }
+
+ shouldUpdate(changedProperties) {
+ if (changedProperties.has('isLoadingMessages')) {
+ return true;
+ }
+ if (
+ changedProperties.has('chatId') &&
+ changedProperties.get('chatId')
+ ) {
+ return true;
+ }
+ if (changedProperties.has('openTipUser')) {
+ return true;
+ }
+ if (changedProperties.has('openUserInfo')) {
+ return true;
+ }
+ if (changedProperties.has('userName')) {
+ return true;
+ }
+ if (changedProperties.has('loggedInUserName')) {
+ return true;
+ }
+ if (changedProperties.has('updateMessageHash')) {
+ return true;
+ }
+ if (changedProperties.has('messagesToRender')) {
+ return true;
+ }
+ if (changedProperties.has('isLoadingBefore')) {
+ return true;
+ }
+ if (changedProperties.has('isLoadingAfter')) {
+ return true;
+ }
+ if (changedProperties.has('messageQueue')) {
+ return true;
+ }
+ // Only update element if prop1 changed.
+ return changedProperties.has('messages');
+ }
+
+ async getUpdateComplete() {
+ await super.getUpdateComplete();
+ const marginElements = Array.from(
+ this.shadowRoot.querySelectorAll('message-template')
+ );
+ await Promise.all(marginElements.map((el) => el.updateComplete));
+ return true;
+ }
+
+ setToggledMessage(message) {
+ toggledMessage = message;
+ }
+
+ async firstUpdated() {
+ this.changeTheme();
+ window.addEventListener('storage', () => {
+ const checkTheme = localStorage.getItem('qortalTheme');
+
+ if (checkTheme === 'dark') {
+ this.theme = 'dark';
+ } else {
+ this.theme = 'light';
+ }
+ document.querySelector('html').setAttribute('theme', this.theme);
+ });
+
+ this.emojiPicker.on('emoji', (selection) => {
+ this.sendMessage({
+ type: 'reaction',
+ editedMessageObj: toggledMessage,
+ reaction: selection.emoji,
+ });
+ });
+ this.viewElement = this.shadowRoot.getElementById('viewElement');
+ this.upObserverElement = this.shadowRoot.getElementById('upObserver');
+ this.downObserverElement =
+ this.shadowRoot.getElementById('downObserver');
+ this.bottomObserverForFetchingMessages = this.shadowRoot.getElementById(
+ 'bottomObserverForFetchingMessages'
+ );
+ // Intialize Observers
+ this.upElementObserver();
+ this.downElementObserver();
+ this.bottomObserver();
+
+ this.clearConsole();
+ setInterval(() => {
+ this.clearConsole();
+ }, 60000);
+ }
+
+ clearConsole() {
+ if (!isElectron()) { /* empty */ } else {
+ console.clear();
+ window.parent.electronAPI.clearCache();
+ }
+ }
+
+ changeTheme() {
+ const checkTheme = localStorage.getItem('qortalTheme');
+ if (checkTheme === 'dark') {
+ this.theme = 'dark';
+ } else {
+ this.theme = 'light';
+ }
+ document.querySelector('html').setAttribute('theme', this.theme);
+ }
+
+ _getOldMessage(_scrollElement) {
+ this.getOldMessage(_scrollElement);
+ }
+ _getAfterMessages(_scrollElement) {
+ this.getAfterMessages(_scrollElement);
+ }
+
+ _upObserverhandler(entries) {
+ if (!entries[0].target || !entries[0].target.nextElementSibling) return;
+ if (entries[0].isIntersecting) {
+ if (this.disableFetching) {
+ return;
+ }
+ this.disableFetching = true;
+ this.isLoadingBefore = true;
+ let _scrollElement = entries[0].target.nextElementSibling;
+ this._getOldMessage(_scrollElement);
+ }
+ }
+
+ _downObserverHandler(entries) {
+ if (!entries[0].isIntersecting) {
+ this.showLastMessageRefScroller(true);
+ } else {
+ this.showLastMessageRefScroller(false);
+ }
+ }
+
+ __bottomObserverForFetchingMessagesHandler(entries) {
+ if (this.messagesToRender.length === 0 || this.disableFetching) {
+ return;
+ }
+ if(!this.disableAddingNewMessages) return
+ if (
+ !entries[0].isIntersecting ||
+ !entries[0].target ||
+ !entries[0].target.previousElementSibling
+ ) { /* empty */ } else {
+ this.disableFetching = true;
+ this.isLoadingAfter = true;
+ let _scrollElement = entries[0].target.previousElementSibling;
+ this._getAfterMessages(_scrollElement);
+ }
+ }
+
+ upElementObserver() {
+ const options = {
+ root: this.viewElement,
+ rootMargin: '0px',
+ threshold: 1,
+ };
+ const observer = new IntersectionObserver(
+ this._upObserverhandler,
+ options
+ );
+ observer.observe(this.upObserverElement);
+ }
+
+ downElementObserver() {
+ const options = {};
+ // identify an element to observe
+ const elementToObserve = this.downObserverElement;
+ // passing it a callback function
+ const observer = new IntersectionObserver(
+ this._downObserverHandler,
+ options
+ );
+ // call `observe()` on that MutationObserver instance,
+ // passing it the element to observe, and the options object
+ observer.observe(elementToObserve);
+ }
+ bottomObserver() {
+ const options = {};
+ // identify an element to observe
+ const elementToObserve = this.bottomObserverForFetchingMessages;
+ // passing it a callback function
+ const observer = new IntersectionObserver(
+ this.__bottomObserverForFetchingMessagesHandler,
+ options
+ );
+ // call `observe()` on that MutationObserver instance,
+ // passing it the element to observe, and the options object
+ observer.observe(elementToObserve);
+ }
}
-window.customElements.define('chat-scroller', ChatScroller)
-
+window.customElements.define('chat-scroller', ChatScroller);
class MessageTemplate extends LitElement {
- static get properties() {
- return {
- messageObj: { type: Object },
- emojiPicker: { attribute: false },
- escapeHTML: { attribute: false },
- hideMessages: { type: Array },
- openDialogPrivateMessage: { type: Boolean },
- openDialogBlockUser: { type: Boolean },
- showBlockAddressIcon: { type: Boolean },
- setRepliedToMessageObj: { attribute: false },
- setEditedMessageObj: { attribute: false },
- sendMessage: { attribute: false },
- sendMessageForward: { attribute: false },
- openDialogImage: { type: Boolean },
- openDialogGif: { type: Boolean },
- openDeleteImage: { type: Boolean },
- openDeleteAttachment: { type: Boolean },
- isImageLoaded: { type: Boolean },
- isGifLoaded: { type: Boolean },
- isFirstMessage: { type: Boolean },
- isSingleMessageInGroup: { type: Boolean },
- isLastMessageInGroup: { type: Boolean },
- setToggledMessage: { attribute: false },
- setForwardProperties: { attribute: false },
- viewImage: { type: Boolean },
- setOpenPrivateMessage: { attribute: false },
- setOpenTipUser: { attribute: false },
- setOpenUserInfo: { attribute: false },
- setUserName: { attribute: false },
- openTipUser: { type: Boolean },
- goToRepliedMessage: { attribute: false },
- listSeenMessages: { type: Array },
- addSeenMessage: { attribute: false },
- chatId: { type: String }
- }
+ static get properties() {
+ return {
+ messageObj: { type: Object },
+ emojiPicker: { attribute: false },
+ escapeHTML: { attribute: false },
+ hideMessages: { type: Array },
+ openDialogPrivateMessage: { type: Boolean },
+ openDialogBlockUser: { type: Boolean },
+ showBlockAddressIcon: { type: Boolean },
+ setRepliedToMessageObj: { attribute: false },
+ setEditedMessageObj: { attribute: false },
+ sendMessage: { attribute: false },
+ sendMessageForward: { attribute: false },
+ openDialogImage: { type: Boolean },
+ openDialogGif: { type: Boolean },
+ openDeleteImage: { type: Boolean },
+ openDeleteAttachment: { type: Boolean },
+ isImageLoaded: { type: Boolean },
+ isGifLoaded: { type: Boolean },
+ isFirstMessage: { type: Boolean },
+ isSingleMessageInGroup: { type: Boolean },
+ isLastMessageInGroup: { type: Boolean },
+ setToggledMessage: { attribute: false },
+ setForwardProperties: { attribute: false },
+ viewImage: { type: Boolean },
+ setOpenPrivateMessage: { attribute: false },
+ setOpenTipUser: { attribute: false },
+ setOpenUserInfo: { attribute: false },
+ setUserName: { attribute: false },
+ openTipUser: { type: Boolean },
+ goToRepliedMessage: { attribute: false },
+ listSeenMessages: { type: Array },
+ addSeenMessage: { attribute: false },
+ chatId: { type: String },
+ isInProgress: { type: Boolean },
+ id: { type: String },
+ };
+ }
+
+ constructor() {
+ super();
+ this.messageObj = {};
+ this.openDialogPrivateMessage = false;
+ this.openDialogBlockUser = false;
+ this.showBlockAddressIcon = false;
+ this.myAddress =
+ window.parent.reduxStore.getState().app.selectedAddress.address;
+ this.imageFetches = 0;
+ this.gifFetches = 0;
+ this.openDialogImage = false;
+ this.openDialogGif = false;
+ this.isImageLoaded = false;
+ this.isGifLoaded = false;
+ this.isFirstMessage = false;
+ this.isSingleMessageInGroup = false;
+ this.isLastMessageInGroup = false;
+ this.viewImage = false;
+ this.isInProgress = false;
+ }
+
+ static get styles() {
+ return [chatStyles];
+ }
+
+ // Open & Close Private Message Chat Modal
+ showPrivateMessageModal() {
+ this.openDialogPrivateMessage = true;
+ }
+
+ hidePrivateMessageModal() {
+ this.openDialogPrivateMessage = false;
+ }
+
+ // Open & Close Block User Chat Modal
+ showBlockUserModal() {
+ this.openDialogBlockUser = true;
+ }
+
+ hideBlockUserModal() {
+ this.openDialogBlockUser = false;
+ }
+
+ showBlockIconFunc(bool) {
+ if (bool) {
+ this.showBlockAddressIcon = true;
+ } else {
+ this.showBlockAddressIcon = false;
+ }
+ }
+
+ async downloadAttachment(attachment) {
+ const myNode =
+ window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
+ window.parent.reduxStore.getState().app.nodeConfig.node
+ ];
+
+ const nodeUrl =
+ myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
+ try {
+ axios
+ .get(
+ `${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}?apiKey=${myNode.apiKey}`,
+ { responseType: 'blob' }
+ )
+ .then((response) => {
+ let filename = attachment.attachmentName;
+ let blob = new Blob([response.data], {
+ type: 'application/octet-stream',
+ });
+ this.saveFileToDisk(blob, filename);
+ });
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ async saveFileToDisk(blob, fileName) {
+ try {
+ const fileHandle = await self.showSaveFilePicker({
+ suggestedName: fileName,
+ types: [
+ {
+ description: 'File',
+ },
+ ],
+ });
+ const writeFile = async (fileHandle, contents) => {
+ const writable = await fileHandle.createWritable();
+ await writable.write(contents);
+ await writable.close();
+ };
+ writeFile(fileHandle, blob).then(() => console.log('FILE SAVED'));
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ setOpenDialogImage(val){
+ this.openDialogImage = val
}
- constructor() {
- super()
- this.messageObj = {}
- this.openDialogPrivateMessage = false
- this.openDialogBlockUser = false
- this.showBlockAddressIcon = false
- this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address
- this.imageFetches = 0
- this.gifFetches = 0
- this.openDialogImage = false
- this.openDialogGif = false
- this.isImageLoaded = false
- this.isGifLoaded = false
- this.isFirstMessage = false
- this.isSingleMessageInGroup = false
- this.isLastMessageInGroup = false
- this.viewImage = false
- }
+ firstUpdated() {
+ const autoSeeChatList =
+ window.parent.reduxStore.getState().app.autoLoadImageChats;
+ if (
+ autoSeeChatList.includes(this.chatId) ||
+ this.listSeenMessages.includes(this.messageObj.signature)
+ ) {
+ this.viewImage = true;
+ }
- static get styles() {
- return [chatStyles]
- }
+ const tooltips = this.shadowRoot.querySelectorAll('vaadin-tooltip');
+ tooltips.forEach((tooltip) => {
+ const overlay = tooltip.shadowRoot.querySelector(
+ 'vaadin-tooltip-overlay'
+ );
+ overlay.shadowRoot.getElementById('overlay').style.cssText =
+ 'background-color: transparent; box-shadow: rgb(50 50 93 / 25%) 0px 2px 5px -1px, rgb(0 0 0 / 30%) 0px 1px 3px -1px';
+ overlay.shadowRoot.getElementById('content').style.cssText =
+ 'background-color: var(--reactions-tooltip-bg); color: var(--chat-bubble-msg-color); text-align: center; padding: 20px 10px; border-radius: 8px; font-family: Roboto, sans-serif; letter-spacing: 0.3px; font-weight: 300; font-size: 13.5px; transition: all 0.3s ease-in-out;';
+ });
+ this.clearConsole();
+ setInterval(() => {
+ this.clearConsole();
+ }, 60000);
+ }
+
+ shouldUpdate(changedProperties) {
+ if (changedProperties.has('messageObj')) {
+ return true;
+ }
+ if (changedProperties.has('showBlockAddressIcon')) {
+ return true;
+ }
+ if (changedProperties.has('openDialogBlockUser')) {
+ return true;
+ }
+ if (changedProperties.has('viewImage')) {
+ return true;
+ }
+ if (changedProperties.has('isImageLoaded')) {
+ return true;
+ }
+ if (changedProperties.has('openDialogImage')) {
+ return true;
+ }
+ if (changedProperties.has('openDialogPrivateMessage')) {
+ return true;
+ }
+ if (changedProperties.has('openDialogGif')) {
+ return true;
+ }
+ if (changedProperties.has('isGifLoaded')) {
+ return true;
+ }
+ return false;
+ }
+
+ clearConsole() {
+ if (!isElectron()) { /* empty */ } else {
+ console.clear();
+ window.parent.electronAPI.clearCache();
+ }
+ }
+
+ render() {
+ const hidemsg = this.hideMessages;
+ let message = '';
+ let messageVersion2 = '';
+ let messageVersion2WithLink = null;
+ let reactions = [];
+ let repliedToData = null;
+ let image = null;
+ let gif = null;
+ let isImageDeleted = false;
+ let isAttachmentDeleted = false;
+ let version = 0;
+ let isForwarded = false;
+ let isEdited = false;
+ let attachment = null;
+ try {
+ const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage);
+ if (+parsedMessageObj.version > 1 && parsedMessageObj.messageText) {
+ messageVersion2 = generateHTML(parsedMessageObj.messageText, [
+ StarterKit,
+ Underline,
+ Highlight,
+ // other extensions …
+ ]);
+ messageVersion2WithLink = processText(messageVersion2);
+ }
+ message = parsedMessageObj.messageText;
+ repliedToData = this.messageObj.repliedToData;
+ isImageDeleted = parsedMessageObj.isImageDeleted;
+ isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted;
+ // reactions = parsedMessageObj.reactions || []
+ version = parsedMessageObj.version;
+ isForwarded = parsedMessageObj.type === 'forward';
+ isEdited = parsedMessageObj.isEdited && true;
+ if (
+ parsedMessageObj.attachments &&
+ Array.isArray(parsedMessageObj.attachments) &&
+ parsedMessageObj.attachments.length > 0
+ ) {
+ attachment = parsedMessageObj.attachments[0];
+ }
+ if (
+ parsedMessageObj.images &&
+ Array.isArray(parsedMessageObj.images) &&
+ parsedMessageObj.images.length > 0
+ ) {
+ image = parsedMessageObj.images[0];
+ }
+ if (
+ parsedMessageObj.gifs &&
+ Array.isArray(parsedMessageObj.gifs) &&
+ parsedMessageObj.gifs.length > 0
+ ) {
+ gif = parsedMessageObj.gifs[0];
+ }
+ } catch (error) {
+ message = this.messageObj.decodedMessage;
+ }
+ let avatarImg = '';
+ let imageHTML = '';
+ let imageUrl = '';
+ let gifHTML = '';
+ let gifHTMLDialog = '';
+ let gifUrl = '';
+ let nameMenu = '';
+ let levelFounder = '';
+ let hideit = hidemsg.includes(this.messageObj.sender);
+ let forwarded = '';
+ let edited = '';
+
+ levelFounder = html`
`;
+
+ if (this.messageObj.senderName) {
+ 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.messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`;
+ avatarImg = html`
![](${avatarUrl})
`;
+ } else {
+ avatarImg = html`
![](/img/incognito.png)
`;
+ }
- // Open & Close Private Message Chat Modal
- showPrivateMessageModal() {
- this.openDialogPrivateMessage = true
- }
+ const createGif = (gif) => {
+ const gifHTMLRes = new Image();
+ gifHTMLRes.src = gif;
+ gifHTMLRes.style =
+ 'max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer;';
+ gifHTMLRes.onclick = () => {
+ this.openDialogGif = true;
+ };
+ gifHTMLRes.onload = () => {
+ this.isGifLoaded = true;
+ };
+ gifHTMLRes.onerror = () => {
+ if (this.gifFetches < 4) {
+ setTimeout(() => {
+ this.gifFetches = this.gifFetches + 1;
+ gifHTMLRes.src = gif;
+ }, 10000);
+ } else {
+ gifHTMLRes.src = '/img/chain.png';
+ gifHTMLRes.style =
+ 'max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5);';
+ gifHTMLRes.onclick = () => {};
+ this.isGifLoaded = true;
+ }
+ };
+ return gifHTMLRes;
+ };
- hidePrivateMessageModal() {
- this.openDialogPrivateMessage = false
- }
+ if (image) {
+ const myNode =
+ window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
+ window.parent.reduxStore.getState().app.nodeConfig.node
+ ];
+ const nodeUrl =
+ myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
+ imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`;
- // Open & Close Block User Chat Modal
- showBlockUserModal() {
- this.openDialogBlockUser = true
- }
+ if (this.viewImage || this.myAddress === this.messageObj.sender) {
+ imageHTML = html`
this.setOpenDialogImage(val)}
+ >`;
+ // imageHTML = createImage(imageUrl)
+ // imageHTMLDialog = createImage(imageUrl)
+ // imageHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;"
+ }
+ }
- hideBlockUserModal() {
- this.openDialogBlockUser = false
- }
+ if (gif) {
+ const myNode =
+ window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
+ window.parent.reduxStore.getState().app.nodeConfig.node
+ ];
+ const nodeUrl =
+ myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
+ gifUrl = `${nodeUrl}/arbitrary/${gif.service}/${gif.name}/${gif.identifier}?filepath=${gif.filePath}&apiKey=${myNode.apiKey}`;
+ if (this.viewImage || this.myAddress === this.messageObj.sender) {
+ gifHTML = createGif(gifUrl);
+ gifHTMLDialog = createGif(gifUrl);
+ gifHTMLDialog.style =
+ 'height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;';
+ }
+ }
- showBlockIconFunc(bool) {
- if (bool) {
- this.showBlockAddressIcon = true
- } else {
- this.showBlockAddressIcon = false
- }
- }
+ nameMenu = html`
+
+ ${this.messageObj.senderName
+ ? this.messageObj.senderName
+ : cropAddress(this.messageObj.sender)}
+
+ `;
- async downloadAttachment(attachment) {
+ forwarded = html`
+
+ ${translate('blockpage.bcchange17')}
+
+ `;
- const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
+ edited = html`
+
+ ${translate('chatpage.cchange68')}
+
+ `;
- const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
- try {
- axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}?apiKey=${myNode.apiKey}`, { responseType: 'blob' })
- .then(response => {
- let filename = attachment.attachmentName
- let blob = new Blob([response.data], { type: "application/octet-stream" })
- this.saveFileToDisk(blob, filename)
- })
- } catch (error) {
- console.error(error)
- }
- }
+ if (repliedToData) {
+ try {
+ const parsedMsg = JSON.parse(repliedToData.decodedMessage);
+ repliedToData.decodedMessage = parsedMsg;
+ } catch (error) { /* empty */ }
+ }
- async saveFileToDisk(blob, fileName) {
- try {
- const fileHandle = await self.showSaveFilePicker({
- suggestedName: fileName,
- types: [{
- description: "File",
- }]
- })
- const writeFile = async (fileHandle, contents) => {
- const writable = await fileHandle.createWritable()
- await writable.write(contents)
- await writable.close()
- }
- writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
- } catch (error) {
- console.log(error)
- }
- }
+ let repliedToMessageText = '';
+ if (
+ repliedToData &&
+ repliedToData.decodedMessage &&
+ repliedToData.decodedMessage.messageText
+ ) {
+ try {
+ repliedToMessageText = generateHTML(
+ repliedToData.decodedMessage.messageText,
+ [
+ StarterKit,
+ Underline,
+ Highlight,
+ // other extensions …
+ ]
+ );
+ } catch (error) { /* empty */ }
+ }
- firstUpdated() {
- const autoSeeChatList = window.parent.reduxStore.getState().app.autoLoadImageChats
- if (autoSeeChatList.includes(this.chatId) || this.listSeenMessages.includes(this.messageObj.signature)) {
- this.viewImage = true
- }
+ let replacedMessage = '';
+ if (message && +version < 2) {
+ const escapedMessage = this.escapeHTML(message);
+ if (escapedMessage) {
+ replacedMessage = escapedMessage.replace(
+ // eslint-disable-next-line no-control-regex
+ new RegExp('\r?\n', 'g'),
+ '
'
+ );
+ }
+ }
- const tooltips = this.shadowRoot.querySelectorAll('vaadin-tooltip')
- tooltips.forEach(tooltip => {
- const overlay = tooltip.shadowRoot.querySelector('vaadin-tooltip-overlay')
- overlay.shadowRoot.getElementById("overlay").style.cssText = "background-color: transparent; box-shadow: rgb(50 50 93 / 25%) 0px 2px 5px -1px, rgb(0 0 0 / 30%) 0px 1px 3px -1px"
- overlay.shadowRoot.getElementById('content').style.cssText = "background-color: var(--reactions-tooltip-bg); color: var(--chat-bubble-msg-color); text-align: center; padding: 20px 10px; border-radius: 8px; font-family: Roboto, sans-serif; letter-spacing: 0.3px; font-weight: 300; font-size: 13.5px; transition: all 0.3s ease-in-out;"
- })
- this.clearConsole()
- setInterval(() => {
- this.clearConsole()
- }, 60000)
- }
-
- clearConsole() {
- if (!isElectron()) {
- } else {
- console.clear()
- window.parent.electronAPI.clearCache()
- }
- }
-
- render() {
- const hidemsg = this.hideMessages
- let message = ""
- let messageVersion2 = ""
- let messageVersion2WithLink = null
- let reactions = []
- let repliedToData = null
- let image = null
- let gif = null
- let isImageDeleted = false
- let isAttachmentDeleted = false
- let version = 0
- let isForwarded = false
- let isEdited = false
- let attachment = null
- try {
- const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage)
- if (+parsedMessageObj.version > 1 && parsedMessageObj.messageText) {
- messageVersion2 = generateHTML(parsedMessageObj.messageText, [
- StarterKit,
- Underline,
- Highlight
- // other extensions …
- ])
- messageVersion2WithLink = processText(messageVersion2)
-
- }
- message = parsedMessageObj.messageText
- repliedToData = this.messageObj.repliedToData
- isImageDeleted = parsedMessageObj.isImageDeleted
- isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted
- // reactions = parsedMessageObj.reactions || []
- version = parsedMessageObj.version
- isForwarded = parsedMessageObj.type === 'forward'
- isEdited = parsedMessageObj.isEdited && true
- if (parsedMessageObj.attachments && Array.isArray(parsedMessageObj.attachments) && parsedMessageObj.attachments.length > 0) {
- attachment = parsedMessageObj.attachments[0]
- }
- if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) {
- image = parsedMessageObj.images[0]
- }
- if (parsedMessageObj.gifs && Array.isArray(parsedMessageObj.gifs) && parsedMessageObj.gifs.length > 0) {
- gif = parsedMessageObj.gifs[0]
- }
- } catch (error) {
- message = this.messageObj.decodedMessage
- }
- let avatarImg = ''
- let imageHTML = ''
- let imageHTMLDialog = ''
- let imageUrl = ''
- let gifHTML = ''
- let gifHTMLDialog = ''
- let gifUrl = ''
- let nameMenu = ''
- let levelFounder = ''
- let hideit = hidemsg.includes(this.messageObj.sender)
- let forwarded = ''
- let edited = ''
-
- levelFounder = html`
`
-
- if (this.messageObj.senderName) {
- 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.messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`
- avatarImg = html`
![](${avatarUrl})
`
- } else {
- avatarImg = html`
![](/img/incognito.png)
`
- }
-
- const createImage = (imageUrl) => {
- const imageHTMLRes = new Image()
- imageHTMLRes.src = imageUrl
- imageHTMLRes.style = "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer;"
- imageHTMLRes.onclick = () => {
- this.openDialogImage = true
- }
- imageHTMLRes.onload = () => {
- this.isImageLoaded = true
- }
- imageHTMLRes.onerror = () => {
- if (this.imageFetches < 4) {
- setTimeout(() => {
- this.imageFetches = this.imageFetches + 1
- imageHTMLRes.src = imageUrl
- }, 10000)
- } else {
- setTimeout(() => {
- this.imageFetches = this.imageFetches + 1
- imageHTMLRes.src = imageUrl
- }, 15000)
- }
- }
- return imageHTMLRes
- }
-
- const createGif = (gif) => {
- const gifHTMLRes = new Image()
- gifHTMLRes.src = gif
- gifHTMLRes.style = "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer;"
- gifHTMLRes.onclick = () => {
- this.openDialogGif = true
- }
- gifHTMLRes.onload = () => {
- this.isGifLoaded = true
- }
- gifHTMLRes.onerror = () => {
- if (this.gifFetches < 4) {
- setTimeout(() => {
- this.gifFetches = this.gifFetches + 1
- gifHTMLRes.src = gif
- }, 10000)
- } else {
- gifHTMLRes.src = '/img/chain.png'
- gifHTMLRes.style = "max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5);"
- gifHTMLRes.onclick = () => { }
- this.isGifLoaded = true
- }
- }
- return gifHTMLRes
- }
-
- if (image) {
- const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
- const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
- imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`
-
- if (this.viewImage || this.myAddress === this.messageObj.sender) {
- imageHTML = createImage(imageUrl)
- imageHTMLDialog = createImage(imageUrl)
- imageHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;"
- }
- }
-
- if (gif) {
- const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
- const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
- gifUrl = `${nodeUrl}/arbitrary/${gif.service}/${gif.name}/${gif.identifier}?filepath=${gif.filePath}&apiKey=${myNode.apiKey}`
- if (this.viewImage || this.myAddress === this.messageObj.sender) {
- gifHTML = createGif(gifUrl)
- gifHTMLDialog = createGif(gifUrl)
- gifHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;"
- }
- }
-
- nameMenu = html`
-
- ${this.messageObj.senderName ? this.messageObj.senderName : cropAddress(this.messageObj.sender)}
-
- `
-
- forwarded = html`
-
- ${translate("blockpage.bcchange17")}
-
- `
-
- edited = html`
-
- ${translate("chatpage.cchange68")}
-
- `
-
- if (repliedToData) {
- try {
- const parsedMsg = JSON.parse(repliedToData.decodedMessage)
- repliedToData.decodedMessage = parsedMsg
- } catch (error) {
- }
-
- }
-
- let repliedToMessageText = ''
- if (repliedToData && repliedToData.decodedMessage && repliedToData.decodedMessage.messageText) {
- try {
- repliedToMessageText = generateHTML(repliedToData.decodedMessage.messageText, [
- StarterKit,
- Underline,
- Highlight
- // other extensions …
- ])
- } catch (error) {
-
- }
- }
-
- let replacedMessage = ''
- if (message && +version < 2) {
- const escapedMessage = this.escapeHTML(message)
- if (escapedMessage) {
- replacedMessage = escapedMessage.replace(new RegExp('\r?\n', 'g'), '
')
- }
-
- }
-
- return hideit ? html`
` : html`
+ return hideit
+ ? html`
`
+ : html`
-
- ${(this.isSingleMessageInGroup === false ||
- (this.isSingleMessageInGroup === true && this.isLastMessageInGroup === true))
- ? (
- html`
-
{
- if (this.myAddress === this.messageObj.sender) return
- this.setOpenUserInfo(true)
- this.setUserName(this.messageObj)
- }} class="message-data-avatar">
- ${avatarImg}
-
- `
- ) :
- html`
-
- `}
+ style="${
+ this.isSingleMessageInGroup === true &&
+ this.isLastMessageInGroup === false &&
+ 'margin-bottom: 0'
+ }">
+
+ ${
+ this.isSingleMessageInGroup === false ||
+ (this.isSingleMessageInGroup === true &&
+ this.isLastMessageInGroup === true)
+ ? html`
+
{
+ if (
+ this.myAddress ===
+ this.messageObj.sender
+ )
+ return;
+ this.setOpenUserInfo(true);
+ this.setUserName(
+ this.messageObj
+ );
+ }}
+ class="message-data-avatar"
+ >
+ ${avatarImg}
+
+ `
+ : html`
+
+ `
+ }
+ ${
+ this.myAddress === this.messageObj.sender &&
+ 'message-myBg'
+ }
+ ${
+ ((this.isFirstMessage === true &&
+ this.isSingleMessageInGroup === false) ||
+ (this.isSingleMessageInGroup === true &&
+ this.isLastMessageInGroup === true)) &&
+ this.myAddress !== this.messageObj.sender
+ ? 'message-triangle'
+ : ((this.isFirstMessage === true &&
+ this.isSingleMessageInGroup ===
+ false) ||
+ (this.isSingleMessageInGroup ===
+ true &&
+ this.isLastMessageInGroup ===
+ true)) &&
+ this.myAddress === this.messageObj.sender
+ ? 'message-myTriangle'
+ : null
+ }`}"
+ style="${
+ this.isSingleMessageInGroup === true &&
+ this.isLastMessageInGroup === false
+ ? 'margin-bottom: 0;'
+ : null
+ }
+ ${
+ this.isFirstMessage === false &&
+ this.isSingleMessageInGroup === true &&
+ this.isLastMessageInGroup === false
+ ? 'border-radius: 8px 25px 25px 8px;'
+ : this.isFirstMessage === true &&
+ this.isSingleMessageInGroup === true &&
+ this.isLastMessageInGroup === false
+ ? 'border-radius: 27px 25px 25px 12px;'
+ : this.isFirstMessage === false &&
+ this.isSingleMessageInGroup === true &&
+ this.isLastMessageInGroup === true
+ ? 'border-radius: 10px 25px 25px 0;'
+ : this.isFirstMessage === true &&
+ this.isSingleMessageInGroup === false &&
+ this.isLastMessageInGroup === true
+ ? 'border-radius: 25px 25px 25px 0px;'
+ : null
+ }">
- ${this.isFirstMessage ?
- html`
- {
- if (this.myAddress === this.messageObj.sender) return
- this.setOpenUserInfo(true)
- this.setUserName(this.messageObj)
- }}
- class="message-data-name">
- ${nameMenu}
-
- `
- : null
- }
- ${isForwarded ?
- html`
-
- ${forwarded}
-
- `
- : null
- }
- ${this.isFirstMessage ? (
- html`
- ${levelFounder}
- `
- ) : null}
+ ${
+ this.isFirstMessage
+ ? html`
+ {
+ if (
+ this.myAddress ===
+ this.messageObj
+ .sender
+ )
+ return;
+ this.setOpenUserInfo(
+ true
+ );
+ this.setUserName(
+ this.messageObj
+ );
+ }}
+ class="message-data-name"
+ >
+ ${nameMenu}
+
+ `
+ : null
+ }
+ ${
+ isForwarded
+ ? html`
+
+ ${forwarded}
+
+ `
+ : null
+ }
+ ${
+ this.isFirstMessage
+ ? html`
+ ${levelFounder}
+ `
+ : null
+ }
- ${repliedToData && html`
-
{
- this.goToRepliedMessage(repliedToData, this.messageObj)
- }}>
-
- ${repliedToData.senderName ? repliedToData.senderName : cropAddress(repliedToData.sender)}
-
-
- ${version && version.toString() === '1' ? html`
- ${repliedToData.decodedMessage.messageText}
- ` : ''}
- ${+version > 1 && repliedToMessageText ? html`
- ${unsafeHTML(repliedToMessageText)}
- `
- : ''}
-
-
- `}
- ${image && !isImageDeleted && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
-
{
- this.viewImage = true
- this.addSeenMessage(this.messageObj.signature)
- }}
- class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
- style=${this.isFirstMessage && "margin-top: 10px;"}>
-
- ${translate("chatpage.cchange40")}
-
-
- ` : html``}
- ${!this.isImageLoaded && image && this.viewImage ? html`
-
-
- `: ''}
- ${image && !isImageDeleted && (this.viewImage || this.myAddress === this.messageObj.sender) ? html`
-
- ${imageHTML}
- ${this.myAddress === this.messageObj.sender ? html`
- {
- this.openDeleteImage = true
- }}
- class="image-delete-icon" icon="vaadin:close" slot="icon">
- ` : ''}
-
-
- ` : image && isImageDeleted ? html`
-
${translate("chatpage.cchange80")}
- ` : html``}
- ${gif && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
-
{
- this.viewImage = true
- }}
- class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
- style=${this.isFirstMessage && "margin-top: 10px;"}>
-
- ${translate("gifs.gchange25")}
-
-
- ` : html``}
- ${gif && (this.viewImage || this.myAddress === this.messageObj.sender) ? html`
-
- ${gifHTML}
-
- ` : html``}
- ${attachment && !isAttachmentDeleted ?
- html`
-
await this.downloadAttachment(attachment)} class="attachment-container">
-
-
![attachment-icon](/img/attachment-icon.png)
-
-
-
- ${attachment && attachment.attachmentName}
-
-
- ${roundToNearestDecimal(attachment.attachmentSize)} mb
-
-
-
-
- ${this.myAddress === this.messageObj.sender
- ? html`
-
{
- e.stopPropagation()
- this.openDeleteAttachment = true
- }}
- class="image-delete-icon" icon="vaadin:close" slot="icon">
-
- ` : html``}
-
- `
- : attachment && isAttachmentDeleted ?
- html`
-
-
-
- ${translate("chatpage.cchange82")}
-
-
-
- `
- : html``}
+ ${
+ repliedToData &&
+ html`
+
{
+ this.goToRepliedMessage(
+ repliedToData,
+ this.messageObj
+ );
+ }}
+ >
+
+ ${repliedToData.senderName
+ ? repliedToData.senderName
+ : cropAddress(
+ repliedToData.sender
+ )}
+
+
+ ${version &&
+ version.toString() === '1'
+ ? html`
+ ${repliedToData
+ .decodedMessage
+ .messageText}
+ `
+ : ''}
+ ${+version > 1 &&
+ repliedToMessageText
+ ? html`
+ ${unsafeHTML(
+ repliedToMessageText
+ )}
+ `
+ : ''}
+
+
+ `
+ }
+ ${
+ image &&
+ !isImageDeleted &&
+ !this.viewImage &&
+ this.myAddress !==
+ this.messageObj.sender
+ ? html`
+
{
+ this.viewImage = true;
+ // this.addSeenMessage(this.messageObj.signature)
+ }}
+ class=${[
+ `image-container`,
+ !this.isImageLoaded
+ ? 'defaultSize'
+ : '',
+ ].join(' ')}
+ style=${this
+ .isFirstMessage &&
+ 'margin-top: 10px;'}
+ >
+
+ ${translate(
+ 'chatpage.cchange40'
+ )}
+
+
+ `
+ : html``
+ }
+
+ ${
+ image &&
+ !isImageDeleted &&
+ (this.viewImage ||
+ this.myAddress ===
+ this.messageObj.sender)
+ ? html`
+
+ ${imageHTML}
+ ${this.myAddress ===
+ this.messageObj.sender
+ ? html`
+ {
+ this.openDeleteImage = true;
+ }}
+ class="image-delete-icon"
+ icon="vaadin:close"
+ slot="icon"
+ >
+ `
+ : ''}
+
+ `
+ : image && isImageDeleted
+ ? html`
+
+ ${translate(
+ 'chatpage.cchange80'
+ )}
+
+ `
+ : html``
+ }
+ ${
+ gif &&
+ !this.viewImage &&
+ this.myAddress !==
+ this.messageObj.sender
+ ? html`
+
{
+ this.viewImage = true;
+ }}
+ class=${[
+ `image-container`,
+ !this.isImageLoaded
+ ? 'defaultSize'
+ : '',
+ ].join(' ')}
+ style=${this
+ .isFirstMessage &&
+ 'margin-top: 10px;'}
+ >
+
+ ${translate(
+ 'gifs.gchange25'
+ )}
+
+
+ `
+ : html``
+ }
+ ${
+ gif &&
+ (this.viewImage ||
+ this.myAddress ===
+ this.messageObj.sender)
+ ? html`
+
+ ${gifHTML}
+
+ `
+ : html``
+ }
+ ${
+ attachment && !isAttachmentDeleted
+ ? html`
+
+ await this.downloadAttachment(
+ attachment
+ )}
+ class="attachment-container"
+ >
+
+
![attachment-icon](/img/attachment-icon.png)
+
+
+
+ ${attachment &&
+ attachment.attachmentName}
+
+
+ ${roundToNearestDecimal(
+ attachment.attachmentSize
+ )}
+ mb
+
+
+
+
+ ${this.myAddress ===
+ this.messageObj.sender
+ ? html`
+
{
+ e.stopPropagation();
+ this.openDeleteAttachment = true;
+ }}
+ class="image-delete-icon"
+ icon="vaadin:close"
+ slot="icon"
+ >
+
+ `
+ : html``}
+
+ `
+ : attachment && isAttachmentDeleted
+ ? html`
+
+
+
+ ${translate(
+ 'chatpage.cchange82'
+ )}
+
+
+
+ `
+ : html``
+ }
- ${+version > 1 ? messageVersion2WithLink ? html`${messageVersion2WithLink}` : html`
- ${unsafeHTML(messageVersion2)}
- ` : ''}
+ style=${
+ image &&
+ replacedMessage !== '' &&
+ 'margin-top: 15px;'
+ }>
+ ${
+ +version > 1
+ ? messageVersion2WithLink
+ ? html`${messageVersion2WithLink}`
+ : html`
+ ${unsafeHTML(
+ messageVersion2
+ )}
+ `
+ : ''
+ }
- ${version && version.toString() === '1' ? html`
- ${unsafeHTML(this.emojiPicker.parse(replacedMessage))}
- ` : ''}
- ${version && version.toString() === '0' ? html`
- ${unsafeHTML(this.emojiPicker.parse(replacedMessage))}
- ` : ''}
+ ${
+ version &&
+ version.toString() === '1'
+ ? html`
+ ${unsafeHTML(
+ this.emojiPicker.parse(
+ replacedMessage
+ )
+ )}
+ `
+ : ''
+ }
+ ${
+ version &&
+ version.toString() === '0'
+ ? html`
+ ${unsafeHTML(
+ this.emojiPicker.parse(
+ replacedMessage
+ )
+ )}
+ `
+ : ''
+ }
- ${isEdited ?
- html`
-
- ${edited}
-
- `
- : ''
- }
-
+ style=${
+ isEdited
+ ? 'justify-content: space-between;'
+ : 'justify-content: flex-end;'
+ }
+ class="${
+ (this.isFirstMessage ===
+ false &&
+ this
+ .isSingleMessageInGroup ===
+ true &&
+ this
+ .isLastMessageInGroup ===
+ true) ||
+ (this.isFirstMessage === true &&
+ this
+ .isSingleMessageInGroup ===
+ false &&
+ this
+ .isLastMessageInGroup ===
+ true)
+ ? 'message-data-time'
+ : 'message-data-time-hidden'
+ }">
+ ${
+ isEdited
+ ? html`
+
+ ${edited}
+
+ `
+ : ''
+ }
+ ${
+ this.isInProgress
+ ? html`
+
+ ${translate(
+ 'chatpage.cchange91'
+ )}
+
+ `
+ : html`
+
+ `
+ }
+
-
this.showPrivateMessageModal()}
- .showBlockUserModal=${() => this.showBlockUserModal()}
- .showBlockIconFunc=${(props) => this.showBlockIconFunc(props)}
- .showBlockAddressIcon=${this.showBlockAddressIcon}
- .originalMessage=${{ ...this.messageObj, message }}
- .setRepliedToMessageObj=${this.setRepliedToMessageObj}
- .setEditedMessageObj=${this.setEditedMessageObj}
- .myAddress=${this.myAddress}
- @blur=${() => this.showBlockIconFunc(false)}
- .sendMessage=${this.sendMessage}
- .sendMessageForward=${this.sendMessageForward}
- version=${version}
- .emojiPicker=${this.emojiPicker}
- .setToggledMessage=${this.setToggledMessage}
- .setForwardProperties=${this.setForwardProperties}
- ?firstMessageInChat=${this.messageObj.firstMessageInChat}
- .setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
- .setOpenTipUser=${(val) => this.setOpenTipUser(val)}
- .setUserName=${(val) => this.setUserName(val)}
- .gif=${!!gif}
- >
-
+ ${
+ this.isInProgress
+ ? ''
+ : html`
+
+ this.showPrivateMessageModal()}
+ .showBlockUserModal=${() =>
+ this.showBlockUserModal()}
+ .showBlockIconFunc=${(props) =>
+ this.showBlockIconFunc(
+ props
+ )}
+ .showBlockAddressIcon=${this
+ .showBlockAddressIcon}
+ .originalMessage=${{
+ ...this.messageObj,
+ message,
+ }}
+ .setRepliedToMessageObj=${this
+ .setRepliedToMessageObj}
+ .setEditedMessageObj=${this
+ .setEditedMessageObj}
+ .myAddress=${this.myAddress}
+ @blur=${() =>
+ this.showBlockIconFunc(
+ false
+ )}
+ .sendMessage=${this.sendMessage}
+ .sendMessageForward=${this
+ .sendMessageForward}
+ version=${version}
+ .emojiPicker=${this.emojiPicker}
+ .setToggledMessage=${this
+ .setToggledMessage}
+ .setForwardProperties=${this
+ .setForwardProperties}
+ ?firstMessageInChat=${this
+ .messageObj
+ .firstMessageInChat}
+ .setOpenPrivateMessage=${(
+ val
+ ) =>
+ this.setOpenPrivateMessage(
+ val
+ )}
+ .setOpenTipUser=${(val) =>
+ this.setOpenTipUser(val)}
+ .setUserName=${(val) =>
+ this.setUserName(val)}
+ .gif=${!!gif}
+ >
+
+ `
+ }
+
-
+
${reactions.map((reaction, index) => {
- return html`
- this.sendMessage({
- type: 'reaction',
- editedMessageObj: this.messageObj,
- reaction: reaction.type,
- })}
- id=${`reactions-${index}`}
- class="reactions-bg">
- ${reaction.type}
- ${reaction.qty}
- 3 ?
- (
- `${reaction.users[0].name
- ? reaction.users[0].name
- : cropAddress(reaction.users[0].address)},
- ${reaction.users[1].name
- ? reaction.users[1].name
- : cropAddress(reaction.users[1].address)},
- ${reaction.users[2].name
- ? reaction.users[2].name
- : cropAddress(reaction.users[2].address)}
- ${get("chatpage.cchange71")} ${reaction.users.length - 3} ${get("chatpage.cchange72")}${(reaction.users.length - 3) > 1 ? html`${get("chatpage.cchange73")}` : ""} ${get("chatpage.cchange74")} ${reaction.type}`
- ) : reaction.users.length === 3 ?
- (
- `${reaction.users[0].name
- ? reaction.users[0].name
- : cropAddress(reaction.users[0].address)},
- ${reaction.users[1].name
- ? reaction.users[1].name
- : cropAddress(reaction.users[1].address)}
- ${get("chatpage.cchange71")}
- ${reaction.users[2].name
- ? reaction.users[2].name
- : cropAddress(reaction.users[2].address)} ${get("chatpage.cchange74")} ${reaction.type}`
- ) : reaction.users.length === 2 ?
- (
- `${reaction.users[0].name
- ? reaction.users[0].name
- : cropAddress(reaction.users[0].address)}
- ${get("chatpage.cchange71")}
- ${reaction.users[1].name
- ? reaction.users[1].name
- : cropAddress(reaction.users[1].address)} ${get("chatpage.cchange74")} ${reaction.type}`
- ) : reaction.users.length === 1 ?
- (
- `${reaction.users[0].name
- ? reaction.users[0].name
- : cropAddress(reaction.users[0].address)} ${get("chatpage.cchange74")} ${reaction.type}`
- )
- : ""}>
-
-
- `
- })}
+ return html`
+
+ this.sendMessage({
+ type: 'reaction',
+ editedMessageObj:
+ this.messageObj,
+ reaction: reaction.type,
+ })}
+ id=${`reactions-${index}`}
+ class="reactions-bg"
+ >
+ ${reaction.type} ${reaction.qty}
+ 3
+ ? `${
+ reaction.users[0]
+ .name
+ ? reaction
+ .users[0]
+ .nameMessageTemplate
+ : cropAddress(
+ reaction
+ .users[0]
+ .address
+ )
+ },
+ ${
+ reaction.users[1].name
+ ? reaction.users[1].name
+ : cropAddress(
+ reaction.users[1]
+ .address
+ )
+ },
+ ${
+ reaction.users[2].name
+ ? reaction.users[2].name
+ : cropAddress(
+ reaction.users[2]
+ .address
+ )
+ }
+ ${get('chatpage.cchange71')} ${
+ reaction.users
+ .length - 3
+ } ${get(
+ 'chatpage.cchange72'
+ )}${
+ reaction.users
+ .length -
+ 3 >
+ 1
+ ? html`${get(
+ 'chatpage.cchange73'
+ )}`
+ : ''
+ } ${get(
+ 'chatpage.cchange74'
+ )} ${reaction.type}`
+ : reaction.users.length ===
+ 3
+ ? `${
+ reaction.users[0]
+ .name
+ ? reaction
+ .users[0]
+ .name
+ : cropAddress(
+ reaction
+ .users[0]
+ .address
+ )
+ },
+ ${
+ reaction.users[1].name
+ ? reaction.users[1].name
+ : cropAddress(
+ reaction.users[1]
+ .address
+ )
+ }
+ ${get('chatpage.cchange71')}
+ ${
+ reaction.users[2].name
+ ? reaction.users[2].name
+ : cropAddress(
+ reaction.users[2]
+ .address
+ )
+ } ${get('chatpage.cchange74')} ${
+ reaction.type
+ }`
+ : reaction.users.length ===
+ 2
+ ? `${
+ reaction.users[0]
+ .name
+ ? reaction
+ .users[0]
+ .name
+ : cropAddress(
+ reaction
+ .users[0]
+ .address
+ )
+ }
+ ${get('chatpage.cchange71')}
+ ${
+ reaction.users[1].name
+ ? reaction.users[1]
+ .namMessageTemplatee
+ : cropAddress(
+ reaction.users[1]
+ .address
+ )
+ } ${get('chatpage.cchange74')} ${
+ reaction.type
+ }`
+ : reaction.users.length ===
+ 1
+ ? `${
+ reaction.users[0]
+ .name
+ ? reaction
+ .users[0]
+ .name
+ : cropAddress(
+ reaction
+ .users[0]
+ .address
+ )
+ } ${get(
+ 'chatpage.cchange74'
+ )} ${reaction.type}`
+ : ''}
+ >
+
+
+ `;
+ })}
@@ -1132,7 +2182,11 @@ class MessageTemplate extends LitElement {
this.hidePrivateMessageModal()}
.hideBlockUserModal=${() => this.hideBlockUserModal()}
toblockaddress=${this.messageObj.sender}
@@ -1142,30 +2196,32 @@ class MessageTemplate extends LitElement {
id="showDialogPublicKey"
?open=${this.openDialogImage}
@closed=${() => {
- this.openDialogImage = false
- }}>
+ this.openDialogImage = false;
+ }}>
- ${imageHTMLDialog}
+ ${this.openDialogImage ? html`
+
![](${imageUrl})
+ ` : ''}
+
{
-
- this.openDialogImage = false
- }}
+ this.openDialogImage = false;
+ }}
>
- ${translate("general.close")}
+ ${translate('general.close')}
{
- this.openDialogGif = false
- }}>
+ this.openDialogGif = false;
+ }}>MessageTemplate
${gifHTMLDialog}
@@ -1175,34 +2231,35 @@ class MessageTemplate extends LitElement {
dialogAction="cancel"
class="red"
@click=${() => {
-
- this.openDialogGif = false
- }}
+ this.openDialogGif = false;
+ }}
>
- ${translate("general.close")}
-
+ ${translate('general.close')}
+ MessageTemplate
{
- this.openDeleteImage = false
- }}>
+ this.openDeleteImage = false;
+ }}>
-
${translate("chatpage.cchange78")}
+
${translate('chatpage.cchange78')}
- this.openDeleteImage = false}>
+
+ (this.openDeleteImage = false)}>
@@ -1211,242 +2268,272 @@ class MessageTemplate extends LitElement {
hideActions
?open=${this.openDeleteAttachment}
@closed=${() => {
- this.openDeleteAttachment = false
- }}>
+ this.openDeleteAttachment = false;
+ }}>
-
${translate("chatpage.cchange79")}
+
${translate('chatpage.cchange79')}
-
this.openDeleteAttachment = false}>
+
+ (this.openDeleteAttachment = false)}>
- `
- }
+ `;
+ }
}
-window.customElements.define('message-template', MessageTemplate)
+window.customElements.define('message-template', MessageTemplate);
class ChatMenu extends LitElement {
- static get properties() {
- return {
- menuItems: { type: Array },
- showPrivateMessageModal: { attribute: false },
- showBlockUserModal: { attribute: false },
- toblockaddress: { type: String, attribute: true },
- showBlockIconFunc: { attribute: false },
- showBlockAddressIcon: { type: Boolean },
- originalMessage: { type: Object },
- setRepliedToMessageObj: { attribute: false },
- setEditedMessageObj: { attribute: false },
- myAddress: { type: Object },
- emojiPicker: { attribute: false },
- sendMessage: { attribute: false },
- version: { type: String },
- setToggledMessage: { attribute: false },
- sendMessageForward: { attribute: false },
- setForwardProperties: { attribute: false },
- firstMessageInChat: { type: Boolean },
- setOpenPrivateMessage: { attribute: false },
- setOpenTipUser: { attribute: false },
- setUserName: { attribute: false },
- gif: { type: Boolean }
- }
- }
+ static get properties() {
+ return {
+ menuItems: { type: Array },
+ showPrivateMessageModal: { attribute: false },
+ showBlockUserModal: { attribute: false },
+ toblockaddress: { type: String, attribute: true },
+ showBlockIconFunc: { attribute: false },
+ showBlockAddressIcon: { type: Boolean },
+ originalMessage: { type: Object },
+ setRepliedToMessageObj: { attribute: false },
+ setEditedMessageObj: { attribute: false },
+ myAddress: { type: Object },
+ emojiPicker: { attribute: false },
+ sendMessage: { attribute: false },
+ version: { type: String },
+ setToggledMessage: { attribute: false },
+ sendMessageForward: { attribute: false },
+ setForwardProperties: { attribute: false },
+ firstMessageInChat: { type: Boolean },
+ setOpenPrivateMessage: { attribute: false },
+ setOpenTipUser: { attribute: false },
+ setUserName: { attribute: false },
+ gif: { type: Boolean },
+ };
+ }
- constructor() {
- super()
- this.showPrivateMessageModal = () => { }
- this.showBlockUserModal = () => { }
- }
+ constructor() {
+ super();
+ this.showPrivateMessageModal = () => {};
+ this.showBlockUserModal = () => {};
+ }
- static get styles() {
- return [chatStyles]
- }
+ static get styles() {
+ return [chatStyles];
+ }
- // Copy address to clipboard
- async copyToClipboard(text) {
- try {
- let copyString1 = get("walletpage.wchange4")
- await navigator.clipboard.writeText(text)
- parentEpml.request('showSnackBar', `${copyString1}`)
- } catch (err) {
- let copyString2 = get("walletpage.wchange39")
- parentEpml.request('showSnackBar', `${copyString2}`)
- console.error('Copy to clipboard error:', err)
- }
- }
+ // Copy address to clipboard
+ async copyToClipboard(text) {
+ try {
+ let copyString1 = get('walletpage.wchange4');
+ await navigator.clipboard.writeText(text);
+ parentEpml.request('showSnackBar', `${copyString1}`);
+ } catch (err) {
+ let copyString2 = get('walletpage.wchange39');
+ parentEpml.request('showSnackBar', `${copyString2}`);
+ console.error('Copy to clipboard error:', err);
+ }
+ }
- versionErrorSnack() {
- let errorMsg = get("chatpage.cchange34")
- parentEpml.request('showSnackBar', `${errorMsg}`)
- }
+ versionErrorSnack() {
+ let errorMsg = get('chatpage.cchange34');
+ parentEpml.request('showSnackBar', `${errorMsg}`);
+ }
- async messageForwardFunc() {
- let parsedMessageObj = {}
- let publicKey = {
- hasPubKey: false,
- key: ''
- }
- try {
- parsedMessageObj = JSON.parse(this.originalMessage.decodedMessage)
+ async messageForwardFunc() {
+ let parsedMessageObj = {};
+ let publicKey = {
+ hasPubKey: false,
+ key: '',
+ };
+ try {
+ parsedMessageObj = JSON.parse(this.originalMessage.decodedMessage);
+ } catch (error) {
+ parsedMessageObj = {};
+ }
- } catch (error) {
- parsedMessageObj = {}
- }
+ try {
+ const res = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/addresses/publickey/${this._chatId}`,
+ });
+ if (res.error === 102) {
+ publicKey.key = '';
+ publicKey.hasPubKey = false;
+ } else if (res !== false) {
+ publicKey.key = res;
+ publicKey.hasPubKey = true;
+ } else {
+ publicKey.key = '';
+ publicKey.hasPubKey = false;
+ }
+ } catch (error) { /* empty */ }
- try {
- const res = await parentEpml.request('apiCall', {
- type: 'api',
- url: `/addresses/publickey/${this._chatId}`
- })
- if (res.error === 102) {
- publicKey.key = ''
- publicKey.hasPubKey = false
- } else if (res !== false) {
- publicKey.key = res
- publicKey.hasPubKey = true
- } else {
- publicKey.key = ''
- publicKey.hasPubKey = false
- }
- } catch (error) {
+ try {
+ const message = {
+ ...parsedMessageObj,
+ type: 'forward',
+ };
+ const stringifyMessageObject = JSON.stringify(message);
+ this.setForwardProperties(stringifyMessageObject);
+ } catch (error) { /* empty */ }
+ }
+ render() {
+ return html`
+
+
+
+
+
- }
-
- try {
- const message = {
- ...parsedMessageObj,
- type: 'forward'
- }
- const stringifyMessageObject = JSON.stringify(message)
- this.setForwardProperties(stringifyMessageObject)
-
- } catch (error) {
- }
- }
- render() {
- return html`
-
-
-
-
-
-
-
- ${((this.myAddress === this.originalMessage.sender) && (
- !this.gif)) ? (
- html`
-
- `
- ) : html`
`}
- ${this.myAddress !== this.originalMessage.sender ? (
- html`
-
- `
- ) : html`
`}
-
- ${this.showBlockAddressIcon
- ? html`
-
-
this.showBlockUserModal()}">
-
${translate("blockpage.bcchange1")}
-
-
-
- ` : html`
-
- `
- }
-
- `
- }
+ ${this.myAddress === this.originalMessage.sender && !this.gif
+ ? html`
+
+ `
+ : html`
`}
+ ${this.myAddress !== this.originalMessage.sender
+ ? html`
+
+ `
+ : html`
`}
+
+ ${this.showBlockAddressIcon
+ ? html`
+
+
this.showBlockUserModal()}"
+ >
+
${translate('blockpage.bcchange1')}
+
+
+
+ `
+ : html`
`}
+
+ `;
+ }
}
-window.customElements.define('chat-menu', ChatMenu)
+window.customElements.define('chat-menu', ChatMenu);
diff --git a/plugins/plugins/core/components/ChatTextEditor.js b/plugins/plugins/core/components/ChatTextEditor.js
index 253e9235..99a2522c 100644
--- a/plugins/plugins/core/components/ChatTextEditor.js
+++ b/plugins/plugins/core/components/ChatTextEditor.js
@@ -36,7 +36,8 @@ class ChatTextEditor extends LitElement {
isEnabledChatEnter: {type: Boolean},
openGifModal: { type: Boolean },
setOpenGifModal: { attribute: false },
- chatId: {type: String}
+ chatId: {type: String},
+ messageQueue: {type: Array}
}
}
@@ -170,7 +171,7 @@ class ChatTextEditor extends LitElement {
padding: 0px 10px;
height: 100%;
display: flex;
- align-items: center;
+ align-items: safe center;
}
.element::-webkit-scrollbar-track {
background-color: whitesmoke;
@@ -376,6 +377,8 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
this.userName = window.parent.reduxStore.getState().app.accountInfo.names[0]
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
this.editor = null
+ this.messageQueue = []
+
}
render() {
@@ -499,7 +502,11 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
accept="image/*, .doc, .docx, .pdf, .zip, .pdf, .txt, .odt, .ods, .xls, .xlsx, .ppt, .pptx" />
-
+
@@ -515,7 +522,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
icon="vaadin:check"
slot="icon"
@click=${() => {
- this.sendMessageFunc();
+ this.sendMessageFunc(this.messageQueue);
}}
>
@@ -538,7 +545,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
alt="send-icon"
class="send-icon"
@click=${() => {
- this.sendMessageFunc();
+ this.sendMessageFunc(this.messageQueue);
}}
/>
` :
@@ -569,6 +576,29 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
}
}
+ async handlePasteEvent(e) {
+ if (e.type === 'paste') {
+ e.preventDefault();
+ const item_list = await navigator.clipboard.read();
+ let image_type; // we will feed this later
+ const item = item_list.find( item => // choose the one item holding our image
+ item.types.some( type => {
+ if (type.startsWith( 'image/')) {
+ image_type = type;
+ return true;
+ }
+ })
+ );
+ if(item){
+ const blob = item && await item.getType( image_type );
+ var file = new File([blob], "name", {
+ type: image_type
+ });
+ this.insertFile(file);
+ }
+ }
+ }
+
async firstUpdated() {
window.addEventListener('storage', () => {
const checkTheme = localStorage.getItem('qortalTheme');
@@ -648,7 +678,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b
return;
}
this.chatMessageSize = 0;
- this._sendMessage(props, this.editor.getJSON());
+ this._sendMessage(props, this.editor.getJSON(), this.messageQueue);
}
getMessageSize(message){
diff --git a/plugins/plugins/core/components/ChatWelcomePage.js b/plugins/plugins/core/components/ChatWelcomePage.js
index 1deb25d1..b391f5d7 100644
--- a/plugins/plugins/core/components/ChatWelcomePage.js
+++ b/plugins/plugins/core/components/ChatWelcomePage.js
@@ -217,9 +217,9 @@ class ChatWelcomePage extends LitElement {
this.setOpenPrivateMessage({
- name: "",
- open: true
- })}">
+ name: "",
+ open: true
+ })}">
${translate("welcomepage.wcchange2")}
@@ -240,9 +240,9 @@ class ChatWelcomePage extends LitElement {
{
- this._sendMessage();
- }
- }>
+ this._sendMessage();
+ }
+ }>
${translate("welcomepage.wcchange6")}
{
- this.balance = res
- })
+
})
parentEpml.imReady()
diff --git a/plugins/plugins/core/components/ImageComponent.js b/plugins/plugins/core/components/ImageComponent.js
index cb35702c..80bb6c26 100644
--- a/plugins/plugins/core/components/ImageComponent.js
+++ b/plugins/plugins/core/components/ImageComponent.js
@@ -1,126 +1,133 @@
-import { LitElement, html, css } from 'lit'
-import { render } from 'lit/html.js'
-import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
+import { LitElement, html, css } from 'lit';
+import { render } from 'lit/html.js';
+import {
+ use,
+ get,
+ translate,
+ translateUnsafeHTML,
+ registerTranslateConfig,
+} from 'lit-translate';
export class ImageComponent extends LitElement {
-
-static get properties() {
-return {
-class: { type: String },
-gif: { type: Object },
-alt: { type: String },
-attempts: { type: Number },
-maxAttempts: { type: Number },
-error: { type: Boolean },
-sendMessage: { attribute: false },
-setOpenGifModal: { attribute: false }
-}
-}
-
-static get styles() {
-return css`
-.gif-error-msg {
-margin: 0;
-font-family: Roboto, sans-serif;
-font-size: 17px;
-letter-spacing: 0.3px;
-color: var(--chat-bubble-msg-color);
-font-weight: 300;
-padding: 10px 10px;
-}
-
-.gif-image {
-border-radius: 15px;
-background-color: transparent;
-cursor: pointer;
-width: 100%;
-height: 150px;
-object-fit: cover;
-border: 1px solid transparent;
-transition: all 0.2s cubic-bezier(0, 0.55, 0.45, 1);
-box-shadow: rgb(50 50 93 / 25%) 0px 6px 12px -2px, rgb(0 0 0 / 30%) 0px 3px 7px -3px;
-}
-
-.gif-image:hover {
-border: 1px solid var(--mdc-theme-primary );
-}
-`
-}
-
-constructor() {
- super()
- this.attempts = 0
- this.maxAttempts = 5
-}
-getApiKey() {
- const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
- let apiKey = myNode.apiKey
- return apiKey
-}
-
-async _fetchImage() {
- this.attempts++;
- if (this.attempts > this.maxAttempts) return
- await new Promise((res) => {
- setTimeout(() => {
- res()
- }, 1000)
- })
- try {
- const response = await fetch(this.gif.url)
- const data = await response.json()
- if (data.ok) {
- this.error = false
- this.gif = {
- ...this.gif,
- url: data.src
+ static get properties() {
+ return {
+ class: { type: String },
+ gif: { type: Object },
+ alt: { type: String },
+ attempts: { type: Number },
+ maxAttempts: { type: Number },
+ error: { type: Boolean },
+ sendMessage: { attribute: false },
+ setOpenGifModal: { attribute: false },
};
- this.requestUpdate();
- } else if (!data.ok || data.error) {
- this.error = true
- } else {
- this.error = false
+ }
+
+ static get styles() {
+ return css`
+ .gif-error-msg {
+ margin: 0;
+ font-family: Roboto, sans-serif;
+ font-size: 17px;
+ letter-spacing: 0.3px;
+ color: var(--chat-bubble-msg-color);
+ font-weight: 300;
+ padding: 10px 10px;
+ }
+
+ .gif-image {
+ border-radius: 15px;
+ background-color: transparent;
+ cursor: pointer;
+ width: 100%;
+ height: 150px;
+ object-fit: cover;
+ border: 1px solid transparent;
+ transition: all 0.2s cubic-bezier(0, 0.55, 0.45, 1);
+ box-shadow: rgb(50 50 93 / 25%) 0px 6px 12px -2px,
+ rgb(0 0 0 / 30%) 0px 3px 7px -3px;
+ }
+
+ .gif-image:hover {
+ border: 1px solid var(--mdc-theme-primary);
+ }
+ `;
+ }
+
+ constructor() {
+ super();
+ this.attempts = 0;
+ this.maxAttempts = 5;
+ }
+ getApiKey() {
+ const myNode =
+ window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
+ window.parent.reduxStore.getState().app.nodeConfig.node
+ ];
+ let apiKey = myNode.apiKey;
+ return apiKey;
+ }
+
+ async _fetchImage() {
+ this.attempts++;
+ if (this.attempts > this.maxAttempts) return;
+ await new Promise((res) => {
+ setTimeout(() => {
+ res();
+ }, 1000);
+ });
+ try {
+ const response = await fetch(this.gif.url);
+ const data = await response.json();
+ if (data.ok) {
+ this.error = false;
+ this.gif = {
+ ...this.gif,
+ url: data.src,
+ };
+ this.requestUpdate();
+ } else if (!data.ok || data.error) {
+ this.error = true;
+ } else {
+ this.error = false;
+ }
+ } catch (error) {
+ this.error = true;
+ console.error(error);
+ this._fetchImage();
}
- } catch (error) {
- this.error = true
- console.error(error)
- this._fetchImage()
+ }
+
+ render() {
+ if (this.error && this.attempts <= this.maxAttempts) {
+ setTimeout(() => {
+ this._fetchImage();
+ }, 1000);
+ }
+ return html` ${this.gif && !this.error
+ ? html`
{
+ this.sendMessage({
+ type: 'gif',
+ identifier: this.gif.identifier,
+ name: this.gif.name,
+ filePath: this.gif.filePath,
+ service: 'GIF_REPOSITORY',
+ });
+ this.setOpenGifModal(false);
+ }}
+ @error=${this._fetchImage}
+ />`
+ : this.error && this.attempts <= this.maxAttempts
+ ? html`
+ ${translate('gifs.gchange15')}
+ `
+ : html`
+ ${translate('gifs.gchange16')}
+ `}`;
}
}
-render() {
-if (this.error && this.attempts <= this.maxAttempts) {
- setTimeout(() => {
- this._fetchImage()
- }, 1000)
-}
-return html`
-${this.gif && !this.error
- ? html`
-
{
- this.sendMessage({
- type: 'gif',
- identifier: this.gif.identifier,
- name: this.gif.name,
- filePath: this.gif.filePath,
- service: "GIF_REPOSITORY"
- })
- this.setOpenGifModal(false);
-}}
-@error=${this._fetchImage}
-/>`
- : this.error && this.attempts <= this.maxAttempts ? html`
-${translate('gifs.gchange15')}
-`
- : html`
- ${translate('gifs.gchange16')}
- `
-}`
-}
-}
-
-customElements.define('image-component', ImageComponent)
+customElements.define('image-component', ImageComponent);
diff --git a/plugins/plugins/core/components/LevelFounder.js b/plugins/plugins/core/components/LevelFounder.js
index ade53a0f..635dda40 100644
--- a/plugins/plugins/core/components/LevelFounder.js
+++ b/plugins/plugins/core/components/LevelFounder.js
@@ -4,14 +4,17 @@ import { Epml } from '../../../epml.js'
import snackbar from './snackbar.js'
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
import '@polymer/paper-tooltip/paper-tooltip.js'
+import { RequestQueue } from '../../utils/queue.js'
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
+ const queue = new RequestQueue(3);
+
+
class LevelFounder extends LitElement {
static get properties() {
return {
checkleveladdress: { type: String },
- selectedAddress: { type: String },
config: { type: Object },
memberInfo: { type: Array }
}
@@ -39,7 +42,7 @@ class LevelFounder extends LitElement {
}
h2, h3, h4, h5 {
- color:# var(--black);
+ color: var(--black);
font-weight: 400;
}
@@ -88,7 +91,6 @@ class LevelFounder extends LitElement {
constructor() {
super()
this.memberInfo = []
- this.selectedAddress = window.parent.reduxStore.getState().app.selectedAddress.address
}
render() {
@@ -101,42 +103,37 @@ class LevelFounder extends LitElement {
}
firstUpdated() {
- this.checkAddressInfo()
-
- parentEpml.ready().then(() => {
- parentEpml.subscribe('selected_address', async selectedAddress => {
- this.selectedAddress = {}
- selectedAddress = JSON.parse(selectedAddress)
- if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
- this.selectedAddress = selectedAddress
- })
- })
- parentEpml.imReady()
+ queue.push(() => this.checkAddressInfo());
}
async checkAddressInfo() {
- let toCheck = this.checkleveladdress
- const memberInfo = await parentEpml.request('apiCall', {
- url: `/addresses/${toCheck}`
- })
- this.memberInfo = memberInfo
+ try {
+ let toCheck = this.checkleveladdress
+ const memberInfo = await parentEpml.request('apiCall', {
+ url: `/addresses/${toCheck}`
+ })
+ this.memberInfo = memberInfo
+ } catch (error) {
+ console.error(error)
+ }
+
}
renderFounder() {
let adressfounder = this.memberInfo.flags
if (adressfounder === 1) {
- return html `
+ return html`
F
FOUNDER
`
} else {
- return html ``
+ return html``
}
}
renderLevel() {
let adresslevel = this.memberInfo.level
- return adresslevel ? html `
+ return adresslevel ? html`
${translate("mintingpage.mchange27")} ${adresslevel}
diff --git a/plugins/plugins/core/components/NameMenu.js b/plugins/plugins/core/components/NameMenu.js
index c4359637..9a0d2e5a 100644
--- a/plugins/plugins/core/components/NameMenu.js
+++ b/plugins/plugins/core/components/NameMenu.js
@@ -228,9 +228,9 @@ class NameMenu extends LitElement {
{
- this._sendMessage();
- }
- }>
+ this._sendMessage();
+ }
+ }>
${translate("welcomepage.wcchange6")}
{
- this.getChatBlockedAdresses()
- }, 60000)
+ setInterval(() => {
+ this.getChatBlockedAdresses()
+ }, 60000)
- window.onclick = function(event) {
+ window.onclick = function (event) {
if (!event.target.matches('.block')) {
var dropdowns = document.getElementsByClassName('dropdown-content');
var i;
@@ -290,11 +290,7 @@ class NameMenu extends LitElement {
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
this.selectedAddress = selectedAddress
})
- parentEpml.request('apiCall', {
- url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}`
- }).then(res => {
- this.balance = res
- })
+
})
parentEpml.imReady()
}
@@ -333,7 +329,7 @@ class NameMenu extends LitElement {
relMessages() {
setTimeout(() => {
- window.location.href = window.location.href.split( '#' )[0]
+ window.location.href = window.location.href.split('#')[0]
}, 500)
}
@@ -407,8 +403,8 @@ class NameMenu extends LitElement {
fetch(`${nodeUrl}/names/address/${item}?limit=0&reverse=true`).then(res => {
return res.json()
}).then(jsonRes => {
- if(jsonRes.length) {
- jsonRes.map (item => {
+ if (jsonRes.length) {
+ jsonRes.map(item => {
obj.push(item)
})
} else {
diff --git a/plugins/plugins/core/components/ReusableImage.js b/plugins/plugins/core/components/ReusableImage.js
new file mode 100644
index 00000000..8a61c411
--- /dev/null
+++ b/plugins/plugins/core/components/ReusableImage.js
@@ -0,0 +1,332 @@
+import { LitElement, html, css } from 'lit';
+import {
+ translate,
+} from 'lit-translate';
+import axios from 'axios';
+import { RequestQueueWithPromise } from '../../utils/queue';
+import '@material/mwc-menu';
+import '@material/mwc-list/mwc-list-item.js';
+import '@material/mwc-dialog'
+
+const requestQueue = new RequestQueueWithPromise(5);
+const requestQueue2 = new RequestQueueWithPromise(5);
+
+
+export class ResuableImage extends LitElement {
+ static get properties() {
+ return {
+ resource: { type: Object },
+ isReady: { type: Boolean },
+ status: { type: Object },
+ missingData: {type: Boolean},
+ openDialogImage: { type: Boolean },
+ onLoad: {attribute: false}
+ };
+ }
+
+ static get styles() {
+ return css`
+ * {
+ --mdc-theme-text-primary-on-background: var(--black);
+ --mdc-dialog-max-width: 85vw;
+ --mdc-dialog-max-height: 95vh;
+ }
+ img {
+ width: 100%;
+ height: auto;
+ object-fit: contain;
+ border-radius: 5px;
+ position: relative;
+ }
+ .smallLoading,
+ .smallLoading:after {
+ border-radius: 50%;
+ width: 2px;
+ height: 2px;
+ }
+
+ .smallLoading {
+ border-width: 0.8em;
+ border-style: solid;
+ border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2)
+ rgba(3, 169, 244, 0.2) rgb(3, 169, 244);
+ font-size: 30px;
+ position: relative;
+ text-indent: -9999em;
+ transform: translateZ(0px);
+ animation: 1.1s linear 0s infinite normal none running
+ loadingAnimation;
+ }
+ .imageContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ }
+
+ @-webkit-keyframes loadingAnimation {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+ }
+
+ @keyframes loadingAnimation {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+ }
+ `;
+ }
+
+ constructor() {
+ super();
+ this.resource = {
+ identifier: '',
+ name: '',
+ service: '',
+ };
+ this.status = {
+ status: '',
+ };
+ this.url = '';
+ this.isReady = false;
+ this.nodeUrl = this.getNodeUrl();
+ this.myNode = this.getMyNode();
+ this.hasCalledWhenDownloaded = false;
+ this.isFetching = false;
+ this.missingData = false
+ this.openDialogImage = false
+
+ this.observer = new IntersectionObserver((entries) => {
+ for (const entry of entries) {
+ if (entry.isIntersecting && this.status.status !== 'READY') {
+ this._fetchImage();
+ // Stop observing after the image has started loading
+ this.observer.unobserve(this);
+ }
+ }
+ });
+ }
+ getNodeUrl() {
+ const myNode =
+ window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
+ window.parent.reduxStore.getState().app.nodeConfig.node
+ ];
+
+ const nodeUrl =
+ myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
+ return nodeUrl;
+ }
+ getMyNode() {
+ const myNode =
+ window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
+ window.parent.reduxStore.getState().app.nodeConfig.node
+ ];
+
+ return myNode;
+ }
+
+ getApiKey() {
+ const myNode =
+ window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
+ window.parent.reduxStore.getState().app.nodeConfig.node
+ ];
+ let apiKey = myNode.apiKey;
+ return apiKey;
+ }
+
+ async fetchResource() {
+ try {
+ if (this.isFetching) return;
+ this.isFetching = true;
+
+ await requestQueue2.enqueue(() => {
+ return axios.get(
+ `${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
+ );
+ });
+ this.isFetching = false;
+ } catch (error) {
+ this.isFetching = false;
+ }
+ }
+
+ async fetchVideoUrl() {
+ this.fetchResource();
+ this.url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?async=true&apiKey=${this.myNode.apiKey}`;
+ }
+
+ async fetchStatus() {
+ let isCalling = false;
+ let percentLoaded = 0;
+ let timer = 24;
+ const response = await axios.get(
+ `${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
+ );
+ if (response && response.data && response.data.status === 'READY') {
+ this.status = response.data;
+ this.onLoad()
+ return;
+ }
+ const intervalId = setInterval(async () => {
+ if (isCalling) return;
+ isCalling = true;
+
+ const data = await requestQueue.enqueue(() => {
+ return axios.get(
+ `${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
+ );
+ });
+ const res = data.data;
+
+ isCalling = false;
+ if (res.localChunkCount) {
+ if (res.percentLoaded) {
+ if (
+ res.percentLoaded === percentLoaded &&
+ res.percentLoaded !== 100
+ ) {
+ timer = timer - 5;
+ } else {
+ timer = 24;
+ }
+ if (timer < 0) {
+ timer = 24;
+ isCalling = true;
+ this.status = {
+ ...res,
+ status: 'REFETCHING',
+ };
+
+ setTimeout(() => {
+ isCalling = false;
+ this.fetchResource();
+ }, 25000);
+ return;
+ }
+ percentLoaded = res.percentLoaded;
+ }
+
+ this.status = res;
+ if (this.status.status === 'DOWNLOADED') {
+ this.fetchResource();
+ }
+ }
+
+ // check if progress is 100% and clear interval if true
+ if (res.status === 'READY') {
+ this.onLoad()
+ clearInterval(intervalId);
+ this.status = res;
+ this.isReady = true;
+ }
+
+ if(res.status === 'MISSING_DATA'){
+ this.status = res
+ this.missingData = true
+ clearInterval(intervalId)
+ }
+ }, 5000); // 1 second interval
+ }
+
+ async _fetchImage() {
+ try {
+ this.fetchVideoUrl({
+ name: this.resource.name,
+ service: this.resource.service,
+ identifier: this.resource.identifier,
+ });
+ this.fetchStatus();
+ } catch (error) { /* empty */ }
+ }
+
+ firstUpdated() {
+ this.observer.observe(this);
+ }
+
+ showContextMenu(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const contextMenu = this.shadowRoot.getElementById('contextMenu');
+ const containerRect = e.currentTarget.getBoundingClientRect();
+
+ // Adjusting the positions
+ const adjustedX = e.clientX - containerRect.left;
+ const adjustedY = e.clientY - containerRect.top;
+
+ contextMenu.style.top = `${adjustedY}px`;
+ contextMenu.style.left = `${adjustedX}px`;
+
+ contextMenu.open = true;
+ }
+
+ render() {
+ return html`
+
+ ${this.status.status !== 'READY'
+ ? html`
+
+
+
+ ${`${Math.round(
+ this.status.percentLoaded || 0
+ ).toFixed(0)}% `}${translate(
+ 'chatpage.cchange94'
+ )}
+
+
+ `
+ : ''}
+ ${this.status.status === 'READY'
+ ? html`
+
{
+ this.openDialogImage = true;
+ }}>
+
![](${this.url})
+
+ `
+ : ''}
+
+
+ {
+ this.openDialogImage = false;
+ }}>
+
+
+ ${this.openDialogImage ? html`
+
![](${this.url})
+ ` : ''}
+
+
+ {
+ this.openDialogImage = false;
+ }}
+ >
+ ${translate('general.close')}
+
+
+ `;
+ }
+}
+
+customElements.define('reusable-image', ResuableImage);
diff --git a/plugins/plugins/core/components/TipUser.js b/plugins/plugins/core/components/TipUser.js
index 1a6bf2b4..51434378 100644
--- a/plugins/plugins/core/components/TipUser.js
+++ b/plugins/plugins/core/components/TipUser.js
@@ -24,7 +24,7 @@ export class TipUser extends LitElement {
}
constructor() {
- super()
+ super()
this.sendMoneyLoading = false
this.btnDisable = false
this.errorMessage = ""
@@ -95,167 +95,140 @@ export class TipUser extends LitElement {
}
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.011) > 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
+ const amount = this.shadowRoot.getElementById("amountInput").value;
+ const recipient = this.userName;
+
+ this.sendMoneyLoading = true;
+ this.btnDisable = true;
+
+ // Helper function to reset loading and button state
+ const resetState = () => {
+ this.sendMoneyLoading = false;
+ this.btnDisable = false;
}
- 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 theFee = await this.getSendQortFee()
- let isAddress
-
- try {
- isAddress = await validateAddress(recipient)
- } catch (err) {
- isAddress = false
+
+ if (parseFloat(amount) + parseFloat(0.011) > parseFloat(this.walletBalance)) {
+ resetState();
+ const snack1string = get("chatpage.cchange51");
+ parentEpml.request('showSnackBar', `${snack1string}`);
+ return false;
}
-
- if (isAddress) {
- let myTransaction = await makeTransactionRequest(recipient, lastRef, theFee)
- getTxnRequestResponse(myTransaction)
- } else {
- let myNameRes = await validateName(recipient)
- if (myNameRes !== false) {
- let myNameAddress = myNameRes.owner
- let myTransaction = await makeTransactionRequest(myNameAddress, lastRef, theFee)
- getTxnRequestResponse(myTransaction)
- } else {
- console.error(this.renderReceiverText())
- this.errorMessage = this.renderReceiverText()
- this.sendMoneyLoading = false
- this.btnDisable = false
- }
+
+ if (parseFloat(amount) <= 0) {
+ resetState();
+ const snack2string = get("chatpage.cchange52");
+ parentEpml.request('showSnackBar', `${snack2string}`);
+ return false;
}
- }
-
- const getName = async (recipient)=> {
- try {
- const getNames = await parentEpml.request("apiCall", {
- type: "api",
- url: `/names/address/${recipient}`,
+
+ if (recipient.length === 0) {
+ resetState();
+ const snack3string = get("chatpage.cchange53");
+ parentEpml.request('showSnackBar', `${snack3string}`);
+ return false;
+ }
+
+ const validateName = async (receiverName) => {
+ const myNameRes = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/names/${receiverName}`
});
-
- if (getNames?.length > 0 ) {
- return getNames[0].name
- } else {
- return ''
+ return myNameRes.error === 401 ? false : myNameRes;
+ };
+
+ const validateAddress = async (receiverAddress) => {
+ return await window.parent.validateAddress(receiverAddress);
+ };
+
+ const getName = async (recipient) => {
+ try {
+ const getNames = await parentEpml.request("apiCall", {
+ type: "api",
+ url: `/names/address/${recipient}`
+ });
+ return getNames?.length > 0 ? getNames[0].name : '';
+ } catch (error) {
+ return "";
}
- } catch (error) {
- return ""
- }
- }
-
- const makeTransactionRequest = async (receiver, lastRef, theFee) => {
- let myReceiver = receiver
- let mylastRef = lastRef
- let myFee = theFee
- 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: myFee,
- 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.errorMessage = ''
- this.successMessage = this.renderSuccessText()
- this.sendMoneyLoading = false
- this.btnDisable = false
- setTimeout(() => {
- this.setOpenTipUser(false)
- this.successMessage = ""
- }, 3000)
- } else {
- this.errorMessage = txnResponse.data.message
- this.sendMoneyLoading = false
- this.btnDisable = false
- throw new Error(txnResponse)
- }
- }
- validateReceiver(recipient)
+ };
+
+ const makeTransactionRequest = async (receiver, lastRef) => {
+ const dialogAmount = get("transactions.amount");
+ const dialogAddress = get("login.address");
+ const dialogName = get("login.name");
+ const dialogTo = get("transactions.to");
+ const recipientName = await getName(receiver);
+
+ return await parentEpml.request('transaction', {
+ type: 2,
+ nonce: this.myAddress.nonce,
+ params: {
+ recipient: receiver,
+ recipientName: recipientName,
+ amount: amount,
+ lastReference: lastRef,
+ fee: this.qortPaymentFee,
+ dialogAmount,
+ dialogTo,
+ dialogAddress,
+ dialogName
+ }
+ });
+ };
+
+ const getTxnRequestResponse = (txnResponse) => {
+ if (txnResponse.success === false && txnResponse.message) {
+ this.errorMessage = txnResponse.message;
+ resetState();
+ throw new Error(txnResponse);
+ } else if (txnResponse.success === true && !txnResponse.data.error) {
+ this.shadowRoot.getElementById('amountInput').value = '';
+ this.errorMessage = '';
+ this.successMessage = this.renderSuccessText();
+ resetState();
+ setTimeout(() => {
+ this.setOpenTipUser(false);
+ this.successMessage = "";
+ }, 3000);
+ } else {
+ this.errorMessage = txnResponse.data.message;
+ resetState();
+ throw new Error(txnResponse);
+ }
+ };
+
+ const validateReceiver = async (recipient) => {
+ let lastRef = await this.getLastRef();
+ let isAddress;
+
+ try {
+ isAddress = await validateAddress(recipient);
+ } catch (err) {
+ isAddress = false;
+ }
+
+ if (isAddress) {
+ const myTransaction = await makeTransactionRequest(recipient, lastRef);
+ getTxnRequestResponse(myTransaction);
+ } else {
+ const myNameRes = await validateName(recipient);
+ if (myNameRes !== false) {
+ const myTransaction = await makeTransactionRequest(myNameRes.owner, lastRef);
+ getTxnRequestResponse(myTransaction);
+ } else {
+ this.errorMessage = this.renderReceiverText();
+ resetState();
+ }
+ }
+ };
+
+ await validateReceiver(recipient);
}
+
render() {
return html`
@@ -270,8 +243,8 @@ export class TipUser extends LitElement {
${this.sendMoneyLoading ?
html`
- `
- : html`
+ `
+ : html`
`}
- ${this.successMessage ?
- html`
+ ${this.successMessage ?
+ html`
${this.successMessage}
`
- : this.errorMessage ?
- html`
+ : this.errorMessage ?
+ html`
${this.errorMessage}
`
- : null}
+ : null}
`;
- }
+ }
}
customElements.define('tip-user', TipUser)
diff --git a/plugins/plugins/core/components/webworkerDecodeMessages.js b/plugins/plugins/core/components/webworkerDecodeMessages.js
new file mode 100644
index 00000000..c1fb5cb7
--- /dev/null
+++ b/plugins/plugins/core/components/webworkerDecodeMessages.js
@@ -0,0 +1,2871 @@
+import { Sha256 } from 'asmcrypto.js';
+const nacl = {}
+//(function(nacl) {
+'use strict';
+
+// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.
+// Public domain.
+//
+// Implementation derived from TweetNaCl version 20140427.
+// See for details: http://tweetnacl.cr.yp.to/
+
+var gf = function(init) {
+ var i, r = new Float64Array(16);
+ if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
+ return r;
+};
+
+// Pluggable, initialized in high-level API below.
+var randombytes = function(/* x, n */) { throw new Error('no PRNG'); };
+
+var _0 = new Uint8Array(16);
+var _9 = new Uint8Array(32); _9[0] = 9;
+
+var gf0 = gf(),
+ gf1 = gf([1]),
+ _121665 = gf([0xdb41, 1]),
+ D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]),
+ D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]),
+ X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]),
+ Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]),
+ I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);
+
+function ts64(x, i, h, l) {
+ x[i] = (h >> 24) & 0xff;
+ x[i+1] = (h >> 16) & 0xff;
+ x[i+2] = (h >> 8) & 0xff;
+ x[i+3] = h & 0xff;
+ x[i+4] = (l >> 24) & 0xff;
+ x[i+5] = (l >> 16) & 0xff;
+ x[i+6] = (l >> 8) & 0xff;
+ x[i+7] = l & 0xff;
+}
+
+function vn(x, xi, y, yi, n) {
+ var i,d = 0;
+ for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i];
+ return (1 & ((d - 1) >>> 8)) - 1;
+}
+
+function crypto_verify_16(x, xi, y, yi) {
+ return vn(x,xi,y,yi,16);
+}
+
+function crypto_verify_32(x, xi, y, yi) {
+ return vn(x,xi,y,yi,32);
+}
+
+function core_salsa20(o, p, k, c) {
+ var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24,
+ j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24,
+ j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24,
+ j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24,
+ j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24,
+ j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24,
+ j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24,
+ j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24,
+ j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24,
+ j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24,
+ j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24,
+ j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24,
+ j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24,
+ j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24,
+ j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24,
+ j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24;
+
+ var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7,
+ x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14,
+ x15 = j15, u;
+
+ for (var i = 0; i < 20; i += 2) {
+ u = x0 + x12 | 0;
+ x4 ^= u<<7 | u>>>(32-7);
+ u = x4 + x0 | 0;
+ x8 ^= u<<9 | u>>>(32-9);
+ u = x8 + x4 | 0;
+ x12 ^= u<<13 | u>>>(32-13);
+ u = x12 + x8 | 0;
+ x0 ^= u<<18 | u>>>(32-18);
+
+ u = x5 + x1 | 0;
+ x9 ^= u<<7 | u>>>(32-7);
+ u = x9 + x5 | 0;
+ x13 ^= u<<9 | u>>>(32-9);
+ u = x13 + x9 | 0;
+ x1 ^= u<<13 | u>>>(32-13);
+ u = x1 + x13 | 0;
+ x5 ^= u<<18 | u>>>(32-18);
+
+ u = x10 + x6 | 0;
+ x14 ^= u<<7 | u>>>(32-7);
+ u = x14 + x10 | 0;
+ x2 ^= u<<9 | u>>>(32-9);
+ u = x2 + x14 | 0;
+ x6 ^= u<<13 | u>>>(32-13);
+ u = x6 + x2 | 0;
+ x10 ^= u<<18 | u>>>(32-18);
+
+ u = x15 + x11 | 0;
+ x3 ^= u<<7 | u>>>(32-7);
+ u = x3 + x15 | 0;
+ x7 ^= u<<9 | u>>>(32-9);
+ u = x7 + x3 | 0;
+ x11 ^= u<<13 | u>>>(32-13);
+ u = x11 + x7 | 0;
+ x15 ^= u<<18 | u>>>(32-18);
+
+ u = x0 + x3 | 0;
+ x1 ^= u<<7 | u>>>(32-7);
+ u = x1 + x0 | 0;
+ x2 ^= u<<9 | u>>>(32-9);
+ u = x2 + x1 | 0;
+ x3 ^= u<<13 | u>>>(32-13);
+ u = x3 + x2 | 0;
+ x0 ^= u<<18 | u>>>(32-18);
+
+ u = x5 + x4 | 0;
+ x6 ^= u<<7 | u>>>(32-7);
+ u = x6 + x5 | 0;
+ x7 ^= u<<9 | u>>>(32-9);
+ u = x7 + x6 | 0;
+ x4 ^= u<<13 | u>>>(32-13);
+ u = x4 + x7 | 0;
+ x5 ^= u<<18 | u>>>(32-18);
+
+ u = x10 + x9 | 0;
+ x11 ^= u<<7 | u>>>(32-7);
+ u = x11 + x10 | 0;
+ x8 ^= u<<9 | u>>>(32-9);
+ u = x8 + x11 | 0;
+ x9 ^= u<<13 | u>>>(32-13);
+ u = x9 + x8 | 0;
+ x10 ^= u<<18 | u>>>(32-18);
+
+ u = x15 + x14 | 0;
+ x12 ^= u<<7 | u>>>(32-7);
+ u = x12 + x15 | 0;
+ x13 ^= u<<9 | u>>>(32-9);
+ u = x13 + x12 | 0;
+ x14 ^= u<<13 | u>>>(32-13);
+ u = x14 + x13 | 0;
+ x15 ^= u<<18 | u>>>(32-18);
+ }
+ x0 = x0 + j0 | 0;
+ x1 = x1 + j1 | 0;
+ x2 = x2 + j2 | 0;
+ x3 = x3 + j3 | 0;
+ x4 = x4 + j4 | 0;
+ x5 = x5 + j5 | 0;
+ x6 = x6 + j6 | 0;
+ x7 = x7 + j7 | 0;
+ x8 = x8 + j8 | 0;
+ x9 = x9 + j9 | 0;
+ x10 = x10 + j10 | 0;
+ x11 = x11 + j11 | 0;
+ x12 = x12 + j12 | 0;
+ x13 = x13 + j13 | 0;
+ x14 = x14 + j14 | 0;
+ x15 = x15 + j15 | 0;
+
+ o[ 0] = x0 >>> 0 & 0xff;
+ o[ 1] = x0 >>> 8 & 0xff;
+ o[ 2] = x0 >>> 16 & 0xff;
+ o[ 3] = x0 >>> 24 & 0xff;
+
+ o[ 4] = x1 >>> 0 & 0xff;
+ o[ 5] = x1 >>> 8 & 0xff;
+ o[ 6] = x1 >>> 16 & 0xff;
+ o[ 7] = x1 >>> 24 & 0xff;
+
+ o[ 8] = x2 >>> 0 & 0xff;
+ o[ 9] = x2 >>> 8 & 0xff;
+ o[10] = x2 >>> 16 & 0xff;
+ o[11] = x2 >>> 24 & 0xff;
+
+ o[12] = x3 >>> 0 & 0xff;
+ o[13] = x3 >>> 8 & 0xff;
+ o[14] = x3 >>> 16 & 0xff;
+ o[15] = x3 >>> 24 & 0xff;
+
+ o[16] = x4 >>> 0 & 0xff;
+ o[17] = x4 >>> 8 & 0xff;
+ o[18] = x4 >>> 16 & 0xff;
+ o[19] = x4 >>> 24 & 0xff;
+
+ o[20] = x5 >>> 0 & 0xff;
+ o[21] = x5 >>> 8 & 0xff;
+ o[22] = x5 >>> 16 & 0xff;
+ o[23] = x5 >>> 24 & 0xff;
+
+ o[24] = x6 >>> 0 & 0xff;
+ o[25] = x6 >>> 8 & 0xff;
+ o[26] = x6 >>> 16 & 0xff;
+ o[27] = x6 >>> 24 & 0xff;
+
+ o[28] = x7 >>> 0 & 0xff;
+ o[29] = x7 >>> 8 & 0xff;
+ o[30] = x7 >>> 16 & 0xff;
+ o[31] = x7 >>> 24 & 0xff;
+
+ o[32] = x8 >>> 0 & 0xff;
+ o[33] = x8 >>> 8 & 0xff;
+ o[34] = x8 >>> 16 & 0xff;
+ o[35] = x8 >>> 24 & 0xff;
+
+ o[36] = x9 >>> 0 & 0xff;
+ o[37] = x9 >>> 8 & 0xff;
+ o[38] = x9 >>> 16 & 0xff;
+ o[39] = x9 >>> 24 & 0xff;
+
+ o[40] = x10 >>> 0 & 0xff;
+ o[41] = x10 >>> 8 & 0xff;
+ o[42] = x10 >>> 16 & 0xff;
+ o[43] = x10 >>> 24 & 0xff;
+
+ o[44] = x11 >>> 0 & 0xff;
+ o[45] = x11 >>> 8 & 0xff;
+ o[46] = x11 >>> 16 & 0xff;
+ o[47] = x11 >>> 24 & 0xff;
+
+ o[48] = x12 >>> 0 & 0xff;
+ o[49] = x12 >>> 8 & 0xff;
+ o[50] = x12 >>> 16 & 0xff;
+ o[51] = x12 >>> 24 & 0xff;
+
+ o[52] = x13 >>> 0 & 0xff;
+ o[53] = x13 >>> 8 & 0xff;
+ o[54] = x13 >>> 16 & 0xff;
+ o[55] = x13 >>> 24 & 0xff;
+
+ o[56] = x14 >>> 0 & 0xff;
+ o[57] = x14 >>> 8 & 0xff;
+ o[58] = x14 >>> 16 & 0xff;
+ o[59] = x14 >>> 24 & 0xff;
+
+ o[60] = x15 >>> 0 & 0xff;
+ o[61] = x15 >>> 8 & 0xff;
+ o[62] = x15 >>> 16 & 0xff;
+ o[63] = x15 >>> 24 & 0xff;
+}
+
+function core_hsalsa20(o,p,k,c) {
+ var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24,
+ j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24,
+ j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24,
+ j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24,
+ j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24,
+ j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24,
+ j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24,
+ j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24,
+ j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24,
+ j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24,
+ j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24,
+ j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24,
+ j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24,
+ j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24,
+ j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24,
+ j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24;
+
+ var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7,
+ x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14,
+ x15 = j15, u;
+
+ for (var i = 0; i < 20; i += 2) {
+ u = x0 + x12 | 0;
+ x4 ^= u<<7 | u>>>(32-7);
+ u = x4 + x0 | 0;
+ x8 ^= u<<9 | u>>>(32-9);
+ u = x8 + x4 | 0;
+ x12 ^= u<<13 | u>>>(32-13);
+ u = x12 + x8 | 0;
+ x0 ^= u<<18 | u>>>(32-18);
+
+ u = x5 + x1 | 0;
+ x9 ^= u<<7 | u>>>(32-7);
+ u = x9 + x5 | 0;
+ x13 ^= u<<9 | u>>>(32-9);
+ u = x13 + x9 | 0;
+ x1 ^= u<<13 | u>>>(32-13);
+ u = x1 + x13 | 0;
+ x5 ^= u<<18 | u>>>(32-18);
+
+ u = x10 + x6 | 0;
+ x14 ^= u<<7 | u>>>(32-7);
+ u = x14 + x10 | 0;
+ x2 ^= u<<9 | u>>>(32-9);
+ u = x2 + x14 | 0;
+ x6 ^= u<<13 | u>>>(32-13);
+ u = x6 + x2 | 0;
+ x10 ^= u<<18 | u>>>(32-18);
+
+ u = x15 + x11 | 0;
+ x3 ^= u<<7 | u>>>(32-7);
+ u = x3 + x15 | 0;
+ x7 ^= u<<9 | u>>>(32-9);
+ u = x7 + x3 | 0;
+ x11 ^= u<<13 | u>>>(32-13);
+ u = x11 + x7 | 0;
+ x15 ^= u<<18 | u>>>(32-18);
+
+ u = x0 + x3 | 0;
+ x1 ^= u<<7 | u>>>(32-7);
+ u = x1 + x0 | 0;
+ x2 ^= u<<9 | u>>>(32-9);
+ u = x2 + x1 | 0;
+ x3 ^= u<<13 | u>>>(32-13);
+ u = x3 + x2 | 0;
+ x0 ^= u<<18 | u>>>(32-18);
+
+ u = x5 + x4 | 0;
+ x6 ^= u<<7 | u>>>(32-7);
+ u = x6 + x5 | 0;
+ x7 ^= u<<9 | u>>>(32-9);
+ u = x7 + x6 | 0;
+ x4 ^= u<<13 | u>>>(32-13);
+ u = x4 + x7 | 0;
+ x5 ^= u<<18 | u>>>(32-18);
+
+ u = x10 + x9 | 0;
+ x11 ^= u<<7 | u>>>(32-7);
+ u = x11 + x10 | 0;
+ x8 ^= u<<9 | u>>>(32-9);
+ u = x8 + x11 | 0;
+ x9 ^= u<<13 | u>>>(32-13);
+ u = x9 + x8 | 0;
+ x10 ^= u<<18 | u>>>(32-18);
+
+ u = x15 + x14 | 0;
+ x12 ^= u<<7 | u>>>(32-7);
+ u = x12 + x15 | 0;
+ x13 ^= u<<9 | u>>>(32-9);
+ u = x13 + x12 | 0;
+ x14 ^= u<<13 | u>>>(32-13);
+ u = x14 + x13 | 0;
+ x15 ^= u<<18 | u>>>(32-18);
+ }
+
+ o[ 0] = x0 >>> 0 & 0xff;
+ o[ 1] = x0 >>> 8 & 0xff;
+ o[ 2] = x0 >>> 16 & 0xff;
+ o[ 3] = x0 >>> 24 & 0xff;
+
+ o[ 4] = x5 >>> 0 & 0xff;
+ o[ 5] = x5 >>> 8 & 0xff;
+ o[ 6] = x5 >>> 16 & 0xff;
+ o[ 7] = x5 >>> 24 & 0xff;
+
+ o[ 8] = x10 >>> 0 & 0xff;
+ o[ 9] = x10 >>> 8 & 0xff;
+ o[10] = x10 >>> 16 & 0xff;
+ o[11] = x10 >>> 24 & 0xff;
+
+ o[12] = x15 >>> 0 & 0xff;
+ o[13] = x15 >>> 8 & 0xff;
+ o[14] = x15 >>> 16 & 0xff;
+ o[15] = x15 >>> 24 & 0xff;
+
+ o[16] = x6 >>> 0 & 0xff;
+ o[17] = x6 >>> 8 & 0xff;
+ o[18] = x6 >>> 16 & 0xff;
+ o[19] = x6 >>> 24 & 0xff;
+
+ o[20] = x7 >>> 0 & 0xff;
+ o[21] = x7 >>> 8 & 0xff;
+ o[22] = x7 >>> 16 & 0xff;
+ o[23] = x7 >>> 24 & 0xff;
+
+ o[24] = x8 >>> 0 & 0xff;
+ o[25] = x8 >>> 8 & 0xff;
+ o[26] = x8 >>> 16 & 0xff;
+ o[27] = x8 >>> 24 & 0xff;
+
+ o[28] = x9 >>> 0 & 0xff;
+ o[29] = x9 >>> 8 & 0xff;
+ o[30] = x9 >>> 16 & 0xff;
+ o[31] = x9 >>> 24 & 0xff;
+}
+
+function crypto_core_salsa20(out,inp,k,c) {
+ core_salsa20(out,inp,k,c);
+}
+
+function crypto_core_hsalsa20(out,inp,k,c) {
+ core_hsalsa20(out,inp,k,c);
+}
+
+var sigma = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]);
+ // "expand 32-byte k"
+
+function crypto_stream_salsa20_xor(c,cpos,m,mpos,b,n,k) {
+ var z = new Uint8Array(16), x = new Uint8Array(64);
+ var u, i;
+ for (i = 0; i < 16; i++) z[i] = 0;
+ for (i = 0; i < 8; i++) z[i] = n[i];
+ while (b >= 64) {
+ crypto_core_salsa20(x,z,k,sigma);
+ for (i = 0; i < 64; i++) c[cpos+i] = m[mpos+i] ^ x[i];
+ u = 1;
+ for (i = 8; i < 16; i++) {
+ u = u + (z[i] & 0xff) | 0;
+ z[i] = u & 0xff;
+ u >>>= 8;
+ }
+ b -= 64;
+ cpos += 64;
+ mpos += 64;
+ }
+ if (b > 0) {
+ crypto_core_salsa20(x,z,k,sigma);
+ for (i = 0; i < b; i++) c[cpos+i] = m[mpos+i] ^ x[i];
+ }
+ return 0;
+}
+
+function crypto_stream_salsa20(c,cpos,b,n,k) {
+ var z = new Uint8Array(16), x = new Uint8Array(64);
+ var u, i;
+ for (i = 0; i < 16; i++) z[i] = 0;
+ for (i = 0; i < 8; i++) z[i] = n[i];
+ while (b >= 64) {
+ crypto_core_salsa20(x,z,k,sigma);
+ for (i = 0; i < 64; i++) c[cpos+i] = x[i];
+ u = 1;
+ for (i = 8; i < 16; i++) {
+ u = u + (z[i] & 0xff) | 0;
+ z[i] = u & 0xff;
+ u >>>= 8;
+ }
+ b -= 64;
+ cpos += 64;
+ }
+ if (b > 0) {
+ crypto_core_salsa20(x,z,k,sigma);
+ for (i = 0; i < b; i++) c[cpos+i] = x[i];
+ }
+ return 0;
+}
+
+function crypto_stream(c,cpos,d,n,k) {
+ var s = new Uint8Array(32);
+ crypto_core_hsalsa20(s,n,k,sigma);
+ var sn = new Uint8Array(8);
+ for (var i = 0; i < 8; i++) sn[i] = n[i+16];
+ return crypto_stream_salsa20(c,cpos,d,sn,s);
+}
+
+function crypto_stream_xor(c,cpos,m,mpos,d,n,k) {
+ var s = new Uint8Array(32);
+ crypto_core_hsalsa20(s,n,k,sigma);
+ var sn = new Uint8Array(8);
+ for (var i = 0; i < 8; i++) sn[i] = n[i+16];
+ return crypto_stream_salsa20_xor(c,cpos,m,mpos,d,sn,s);
+}
+
+/*
+* Port of Andrew Moon's Poly1305-donna-16. Public domain.
+* https://github.com/floodyberry/poly1305-donna
+*/
+
+var poly1305 = function(key) {
+ this.buffer = new Uint8Array(16);
+ this.r = new Uint16Array(10);
+ this.h = new Uint16Array(10);
+ this.pad = new Uint16Array(8);
+ this.leftover = 0;
+ this.fin = 0;
+
+ var t0, t1, t2, t3, t4, t5, t6, t7;
+
+ t0 = key[ 0] & 0xff | (key[ 1] & 0xff) << 8; this.r[0] = ( t0 ) & 0x1fff;
+ t1 = key[ 2] & 0xff | (key[ 3] & 0xff) << 8; this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
+ t2 = key[ 4] & 0xff | (key[ 5] & 0xff) << 8; this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
+ t3 = key[ 6] & 0xff | (key[ 7] & 0xff) << 8; this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
+ t4 = key[ 8] & 0xff | (key[ 9] & 0xff) << 8; this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
+ this.r[5] = ((t4 >>> 1)) & 0x1ffe;
+ t5 = key[10] & 0xff | (key[11] & 0xff) << 8; this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
+ t6 = key[12] & 0xff | (key[13] & 0xff) << 8; this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
+ t7 = key[14] & 0xff | (key[15] & 0xff) << 8; this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
+ this.r[9] = ((t7 >>> 5)) & 0x007f;
+
+ this.pad[0] = key[16] & 0xff | (key[17] & 0xff) << 8;
+ this.pad[1] = key[18] & 0xff | (key[19] & 0xff) << 8;
+ this.pad[2] = key[20] & 0xff | (key[21] & 0xff) << 8;
+ this.pad[3] = key[22] & 0xff | (key[23] & 0xff) << 8;
+ this.pad[4] = key[24] & 0xff | (key[25] & 0xff) << 8;
+ this.pad[5] = key[26] & 0xff | (key[27] & 0xff) << 8;
+ this.pad[6] = key[28] & 0xff | (key[29] & 0xff) << 8;
+ this.pad[7] = key[30] & 0xff | (key[31] & 0xff) << 8;
+};
+
+poly1305.prototype.blocks = function(m, mpos, bytes) {
+ var hibit = this.fin ? 0 : (1 << 11);
+ var t0, t1, t2, t3, t4, t5, t6, t7, c;
+ var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9;
+
+ var h0 = this.h[0],
+ h1 = this.h[1],
+ h2 = this.h[2],
+ h3 = this.h[3],
+ h4 = this.h[4],
+ h5 = this.h[5],
+ h6 = this.h[6],
+ h7 = this.h[7],
+ h8 = this.h[8],
+ h9 = this.h[9];
+
+ var r0 = this.r[0],
+ r1 = this.r[1],
+ r2 = this.r[2],
+ r3 = this.r[3],
+ r4 = this.r[4],
+ r5 = this.r[5],
+ r6 = this.r[6],
+ r7 = this.r[7],
+ r8 = this.r[8],
+ r9 = this.r[9];
+
+ while (bytes >= 16) {
+ t0 = m[mpos+ 0] & 0xff | (m[mpos+ 1] & 0xff) << 8; h0 += ( t0 ) & 0x1fff;
+ t1 = m[mpos+ 2] & 0xff | (m[mpos+ 3] & 0xff) << 8; h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
+ t2 = m[mpos+ 4] & 0xff | (m[mpos+ 5] & 0xff) << 8; h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff;
+ t3 = m[mpos+ 6] & 0xff | (m[mpos+ 7] & 0xff) << 8; h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
+ t4 = m[mpos+ 8] & 0xff | (m[mpos+ 9] & 0xff) << 8; h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff;
+ h5 += ((t4 >>> 1)) & 0x1fff;
+ t5 = m[mpos+10] & 0xff | (m[mpos+11] & 0xff) << 8; h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
+ t6 = m[mpos+12] & 0xff | (m[mpos+13] & 0xff) << 8; h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff;
+ t7 = m[mpos+14] & 0xff | (m[mpos+15] & 0xff) << 8; h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
+ h9 += ((t7 >>> 5)) | hibit;
+
+ c = 0;
+
+ d0 = c;
+ d0 += h0 * r0;
+ d0 += h1 * (5 * r9);
+ d0 += h2 * (5 * r8);
+ d0 += h3 * (5 * r7);
+ d0 += h4 * (5 * r6);
+ c = (d0 >>> 13); d0 &= 0x1fff;
+ d0 += h5 * (5 * r5);
+ d0 += h6 * (5 * r4);
+ d0 += h7 * (5 * r3);
+ d0 += h8 * (5 * r2);
+ d0 += h9 * (5 * r1);
+ c += (d0 >>> 13); d0 &= 0x1fff;
+
+ d1 = c;
+ d1 += h0 * r1;
+ d1 += h1 * r0;
+ d1 += h2 * (5 * r9);
+ d1 += h3 * (5 * r8);
+ d1 += h4 * (5 * r7);
+ c = (d1 >>> 13); d1 &= 0x1fff;
+ d1 += h5 * (5 * r6);
+ d1 += h6 * (5 * r5);
+ d1 += h7 * (5 * r4);
+ d1 += h8 * (5 * r3);
+ d1 += h9 * (5 * r2);
+ c += (d1 >>> 13); d1 &= 0x1fff;
+
+ d2 = c;
+ d2 += h0 * r2;
+ d2 += h1 * r1;
+ d2 += h2 * r0;
+ d2 += h3 * (5 * r9);
+ d2 += h4 * (5 * r8);
+ c = (d2 >>> 13); d2 &= 0x1fff;
+ d2 += h5 * (5 * r7);
+ d2 += h6 * (5 * r6);
+ d2 += h7 * (5 * r5);
+ d2 += h8 * (5 * r4);
+ d2 += h9 * (5 * r3);
+ c += (d2 >>> 13); d2 &= 0x1fff;
+
+ d3 = c;
+ d3 += h0 * r3;
+ d3 += h1 * r2;
+ d3 += h2 * r1;
+ d3 += h3 * r0;
+ d3 += h4 * (5 * r9);
+ c = (d3 >>> 13); d3 &= 0x1fff;
+ d3 += h5 * (5 * r8);
+ d3 += h6 * (5 * r7);
+ d3 += h7 * (5 * r6);
+ d3 += h8 * (5 * r5);
+ d3 += h9 * (5 * r4);
+ c += (d3 >>> 13); d3 &= 0x1fff;
+
+ d4 = c;
+ d4 += h0 * r4;
+ d4 += h1 * r3;
+ d4 += h2 * r2;
+ d4 += h3 * r1;
+ d4 += h4 * r0;
+ c = (d4 >>> 13); d4 &= 0x1fff;
+ d4 += h5 * (5 * r9);
+ d4 += h6 * (5 * r8);
+ d4 += h7 * (5 * r7);
+ d4 += h8 * (5 * r6);
+ d4 += h9 * (5 * r5);
+ c += (d4 >>> 13); d4 &= 0x1fff;
+
+ d5 = c;
+ d5 += h0 * r5;
+ d5 += h1 * r4;
+ d5 += h2 * r3;
+ d5 += h3 * r2;
+ d5 += h4 * r1;
+ c = (d5 >>> 13); d5 &= 0x1fff;
+ d5 += h5 * r0;
+ d5 += h6 * (5 * r9);
+ d5 += h7 * (5 * r8);
+ d5 += h8 * (5 * r7);
+ d5 += h9 * (5 * r6);
+ c += (d5 >>> 13); d5 &= 0x1fff;
+
+ d6 = c;
+ d6 += h0 * r6;
+ d6 += h1 * r5;
+ d6 += h2 * r4;
+ d6 += h3 * r3;
+ d6 += h4 * r2;
+ c = (d6 >>> 13); d6 &= 0x1fff;
+ d6 += h5 * r1;
+ d6 += h6 * r0;
+ d6 += h7 * (5 * r9);
+ d6 += h8 * (5 * r8);
+ d6 += h9 * (5 * r7);
+ c += (d6 >>> 13); d6 &= 0x1fff;
+
+ d7 = c;
+ d7 += h0 * r7;
+ d7 += h1 * r6;
+ d7 += h2 * r5;
+ d7 += h3 * r4;
+ d7 += h4 * r3;
+ c = (d7 >>> 13); d7 &= 0x1fff;
+ d7 += h5 * r2;
+ d7 += h6 * r1;
+ d7 += h7 * r0;
+ d7 += h8 * (5 * r9);
+ d7 += h9 * (5 * r8);
+ c += (d7 >>> 13); d7 &= 0x1fff;
+
+ d8 = c;
+ d8 += h0 * r8;
+ d8 += h1 * r7;
+ d8 += h2 * r6;
+ d8 += h3 * r5;
+ d8 += h4 * r4;
+ c = (d8 >>> 13); d8 &= 0x1fff;
+ d8 += h5 * r3;
+ d8 += h6 * r2;
+ d8 += h7 * r1;
+ d8 += h8 * r0;
+ d8 += h9 * (5 * r9);
+ c += (d8 >>> 13); d8 &= 0x1fff;
+
+ d9 = c;
+ d9 += h0 * r9;
+ d9 += h1 * r8;
+ d9 += h2 * r7;
+ d9 += h3 * r6;
+ d9 += h4 * r5;
+ c = (d9 >>> 13); d9 &= 0x1fff;
+ d9 += h5 * r4;
+ d9 += h6 * r3;
+ d9 += h7 * r2;
+ d9 += h8 * r1;
+ d9 += h9 * r0;
+ c += (d9 >>> 13); d9 &= 0x1fff;
+
+ c = (((c << 2) + c)) | 0;
+ c = (c + d0) | 0;
+ d0 = c & 0x1fff;
+ c = (c >>> 13);
+ d1 += c;
+
+ h0 = d0;
+ h1 = d1;
+ h2 = d2;
+ h3 = d3;
+ h4 = d4;
+ h5 = d5;
+ h6 = d6;
+ h7 = d7;
+ h8 = d8;
+ h9 = d9;
+
+ mpos += 16;
+ bytes -= 16;
+ }
+ this.h[0] = h0;
+ this.h[1] = h1;
+ this.h[2] = h2;
+ this.h[3] = h3;
+ this.h[4] = h4;
+ this.h[5] = h5;
+ this.h[6] = h6;
+ this.h[7] = h7;
+ this.h[8] = h8;
+ this.h[9] = h9;
+};
+
+poly1305.prototype.finish = function(mac, macpos) {
+ var g = new Uint16Array(10);
+ var c, mask, f, i;
+
+ if (this.leftover) {
+ i = this.leftover;
+ this.buffer[i++] = 1;
+ for (; i < 16; i++) this.buffer[i] = 0;
+ this.fin = 1;
+ this.blocks(this.buffer, 0, 16);
+ }
+
+ c = this.h[1] >>> 13;
+ this.h[1] &= 0x1fff;
+ for (i = 2; i < 10; i++) {
+ this.h[i] += c;
+ c = this.h[i] >>> 13;
+ this.h[i] &= 0x1fff;
+ }
+ this.h[0] += (c * 5);
+ c = this.h[0] >>> 13;
+ this.h[0] &= 0x1fff;
+ this.h[1] += c;
+ c = this.h[1] >>> 13;
+ this.h[1] &= 0x1fff;
+ this.h[2] += c;
+
+ g[0] = this.h[0] + 5;
+ c = g[0] >>> 13;
+ g[0] &= 0x1fff;
+ for (i = 1; i < 10; i++) {
+ g[i] = this.h[i] + c;
+ c = g[i] >>> 13;
+ g[i] &= 0x1fff;
+ }
+ g[9] -= (1 << 13);
+
+ mask = (g[9] >>> ((2 * 8) - 1)) - 1;
+ for (i = 0; i < 10; i++) g[i] &= mask;
+ mask = ~mask;
+ for (i = 0; i < 10; i++) this.h[i] = (this.h[i] & mask) | g[i];
+
+ this.h[0] = ((this.h[0] ) | (this.h[1] << 13) ) & 0xffff;
+ this.h[1] = ((this.h[1] >>> 3) | (this.h[2] << 10) ) & 0xffff;
+ this.h[2] = ((this.h[2] >>> 6) | (this.h[3] << 7) ) & 0xffff;
+ this.h[3] = ((this.h[3] >>> 9) | (this.h[4] << 4) ) & 0xffff;
+ this.h[4] = ((this.h[4] >>> 12) | (this.h[5] << 1) | (this.h[6] << 14)) & 0xffff;
+ this.h[5] = ((this.h[6] >>> 2) | (this.h[7] << 11) ) & 0xffff;
+ this.h[6] = ((this.h[7] >>> 5) | (this.h[8] << 8) ) & 0xffff;
+ this.h[7] = ((this.h[8] >>> 8) | (this.h[9] << 5) ) & 0xffff;
+
+ f = this.h[0] + this.pad[0];
+ this.h[0] = f & 0xffff;
+ for (i = 1; i < 8; i++) {
+ f = (((this.h[i] + this.pad[i]) | 0) + (f >>> 16)) | 0;
+ this.h[i] = f & 0xffff;
+ }
+
+ mac[macpos+ 0] = (this.h[0] >>> 0) & 0xff;
+ mac[macpos+ 1] = (this.h[0] >>> 8) & 0xff;
+ mac[macpos+ 2] = (this.h[1] >>> 0) & 0xff;
+ mac[macpos+ 3] = (this.h[1] >>> 8) & 0xff;
+ mac[macpos+ 4] = (this.h[2] >>> 0) & 0xff;
+ mac[macpos+ 5] = (this.h[2] >>> 8) & 0xff;
+ mac[macpos+ 6] = (this.h[3] >>> 0) & 0xff;
+ mac[macpos+ 7] = (this.h[3] >>> 8) & 0xff;
+ mac[macpos+ 8] = (this.h[4] >>> 0) & 0xff;
+ mac[macpos+ 9] = (this.h[4] >>> 8) & 0xff;
+ mac[macpos+10] = (this.h[5] >>> 0) & 0xff;
+ mac[macpos+11] = (this.h[5] >>> 8) & 0xff;
+ mac[macpos+12] = (this.h[6] >>> 0) & 0xff;
+ mac[macpos+13] = (this.h[6] >>> 8) & 0xff;
+ mac[macpos+14] = (this.h[7] >>> 0) & 0xff;
+ mac[macpos+15] = (this.h[7] >>> 8) & 0xff;
+};
+
+poly1305.prototype.update = function(m, mpos, bytes) {
+ var i, want;
+
+ if (this.leftover) {
+ want = (16 - this.leftover);
+ if (want > bytes)
+ want = bytes;
+ for (i = 0; i < want; i++)
+ this.buffer[this.leftover + i] = m[mpos+i];
+ bytes -= want;
+ mpos += want;
+ this.leftover += want;
+ if (this.leftover < 16)
+ return;
+ this.blocks(this.buffer, 0, 16);
+ this.leftover = 0;
+ }
+
+ if (bytes >= 16) {
+ want = bytes - (bytes % 16);
+ this.blocks(m, mpos, want);
+ mpos += want;
+ bytes -= want;
+ }
+
+ if (bytes) {
+ for (i = 0; i < bytes; i++)
+ this.buffer[this.leftover + i] = m[mpos+i];
+ this.leftover += bytes;
+ }
+};
+
+function crypto_onetimeauth(out, outpos, m, mpos, n, k) {
+ var s = new poly1305(k);
+ s.update(m, mpos, n);
+ s.finish(out, outpos);
+ return 0;
+}
+
+function crypto_onetimeauth_verify(h, hpos, m, mpos, n, k) {
+ var x = new Uint8Array(16);
+ crypto_onetimeauth(x,0,m,mpos,n,k);
+ return crypto_verify_16(h,hpos,x,0);
+}
+
+function crypto_secretbox(c,m,d,n,k) {
+ var i;
+ if (d < 32) return -1;
+ crypto_stream_xor(c,0,m,0,d,n,k);
+ crypto_onetimeauth(c, 16, c, 32, d - 32, c);
+ for (i = 0; i < 16; i++) c[i] = 0;
+ return 0;
+}
+
+function crypto_secretbox_open(m,c,d,n,k) {
+ var i;
+ var x = new Uint8Array(32);
+ if (d < 32) return -1;
+ crypto_stream(x,0,32,n,k);
+ if (crypto_onetimeauth_verify(c, 16,c, 32,d - 32,x) !== 0) return -1;
+ crypto_stream_xor(m,0,c,0,d,n,k);
+ for (i = 0; i < 32; i++) m[i] = 0;
+ return 0;
+}
+
+function set25519(r, a) {
+ var i;
+ for (i = 0; i < 16; i++) r[i] = a[i]|0;
+}
+
+function car25519(o) {
+ var i, v, c = 1;
+ for (i = 0; i < 16; i++) {
+ v = o[i] + c + 65535;
+ c = Math.floor(v / 65536);
+ o[i] = v - c * 65536;
+ }
+ o[0] += c-1 + 37 * (c-1);
+}
+
+function sel25519(p, q, b) {
+ var t, c = ~(b-1);
+ for (var i = 0; i < 16; i++) {
+ t = c & (p[i] ^ q[i]);
+ p[i] ^= t;
+ q[i] ^= t;
+ }
+}
+
+function pack25519(o, n) {
+ var i, j, b;
+ var m = gf(), t = gf();
+ for (i = 0; i < 16; i++) t[i] = n[i];
+ car25519(t);
+ car25519(t);
+ car25519(t);
+ for (j = 0; j < 2; j++) {
+ m[0] = t[0] - 0xffed;
+ for (i = 1; i < 15; i++) {
+ m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1);
+ m[i-1] &= 0xffff;
+ }
+ m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1);
+ b = (m[15]>>16) & 1;
+ m[14] &= 0xffff;
+ sel25519(t, m, 1-b);
+ }
+ for (i = 0; i < 16; i++) {
+ o[2*i] = t[i] & 0xff;
+ o[2*i+1] = t[i]>>8;
+ }
+}
+
+function neq25519(a, b) {
+ var c = new Uint8Array(32), d = new Uint8Array(32);
+ pack25519(c, a);
+ pack25519(d, b);
+ return crypto_verify_32(c, 0, d, 0);
+}
+
+function par25519(a) {
+ var d = new Uint8Array(32);
+ pack25519(d, a);
+ return d[0] & 1;
+}
+
+function unpack25519(o, n) {
+ var i;
+ for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8);
+ o[15] &= 0x7fff;
+}
+
+function A(o, a, b) {
+ for (var i = 0; i < 16; i++) o[i] = a[i] + b[i];
+}
+
+function Z(o, a, b) {
+ for (var i = 0; i < 16; i++) o[i] = a[i] - b[i];
+}
+
+function M(o, a, b) {
+ var v, c,
+ t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
+ t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
+ t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
+ t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0,
+ b0 = b[0],
+ b1 = b[1],
+ b2 = b[2],
+ b3 = b[3],
+ b4 = b[4],
+ b5 = b[5],
+ b6 = b[6],
+ b7 = b[7],
+ b8 = b[8],
+ b9 = b[9],
+ b10 = b[10],
+ b11 = b[11],
+ b12 = b[12],
+ b13 = b[13],
+ b14 = b[14],
+ b15 = b[15];
+
+ v = a[0];
+ t0 += v * b0;
+ t1 += v * b1;
+ t2 += v * b2;
+ t3 += v * b3;
+ t4 += v * b4;
+ t5 += v * b5;
+ t6 += v * b6;
+ t7 += v * b7;
+ t8 += v * b8;
+ t9 += v * b9;
+ t10 += v * b10;
+ t11 += v * b11;
+ t12 += v * b12;
+ t13 += v * b13;
+ t14 += v * b14;
+ t15 += v * b15;
+ v = a[1];
+ t1 += v * b0;
+ t2 += v * b1;
+ t3 += v * b2;
+ t4 += v * b3;
+ t5 += v * b4;
+ t6 += v * b5;
+ t7 += v * b6;
+ t8 += v * b7;
+ t9 += v * b8;
+ t10 += v * b9;
+ t11 += v * b10;
+ t12 += v * b11;
+ t13 += v * b12;
+ t14 += v * b13;
+ t15 += v * b14;
+ t16 += v * b15;
+ v = a[2];
+ t2 += v * b0;
+ t3 += v * b1;
+ t4 += v * b2;
+ t5 += v * b3;
+ t6 += v * b4;
+ t7 += v * b5;
+ t8 += v * b6;
+ t9 += v * b7;
+ t10 += v * b8;
+ t11 += v * b9;
+ t12 += v * b10;
+ t13 += v * b11;
+ t14 += v * b12;
+ t15 += v * b13;
+ t16 += v * b14;
+ t17 += v * b15;
+ v = a[3];
+ t3 += v * b0;
+ t4 += v * b1;
+ t5 += v * b2;
+ t6 += v * b3;
+ t7 += v * b4;
+ t8 += v * b5;
+ t9 += v * b6;
+ t10 += v * b7;
+ t11 += v * b8;
+ t12 += v * b9;
+ t13 += v * b10;
+ t14 += v * b11;
+ t15 += v * b12;
+ t16 += v * b13;
+ t17 += v * b14;
+ t18 += v * b15;
+ v = a[4];
+ t4 += v * b0;
+ t5 += v * b1;
+ t6 += v * b2;
+ t7 += v * b3;
+ t8 += v * b4;
+ t9 += v * b5;
+ t10 += v * b6;
+ t11 += v * b7;
+ t12 += v * b8;
+ t13 += v * b9;
+ t14 += v * b10;
+ t15 += v * b11;
+ t16 += v * b12;
+ t17 += v * b13;
+ t18 += v * b14;
+ t19 += v * b15;
+ v = a[5];
+ t5 += v * b0;
+ t6 += v * b1;
+ t7 += v * b2;
+ t8 += v * b3;
+ t9 += v * b4;
+ t10 += v * b5;
+ t11 += v * b6;
+ t12 += v * b7;
+ t13 += v * b8;
+ t14 += v * b9;
+ t15 += v * b10;
+ t16 += v * b11;
+ t17 += v * b12;
+ t18 += v * b13;
+ t19 += v * b14;
+ t20 += v * b15;
+ v = a[6];
+ t6 += v * b0;
+ t7 += v * b1;
+ t8 += v * b2;
+ t9 += v * b3;
+ t10 += v * b4;
+ t11 += v * b5;
+ t12 += v * b6;
+ t13 += v * b7;
+ t14 += v * b8;
+ t15 += v * b9;
+ t16 += v * b10;
+ t17 += v * b11;
+ t18 += v * b12;
+ t19 += v * b13;
+ t20 += v * b14;
+ t21 += v * b15;
+ v = a[7];
+ t7 += v * b0;
+ t8 += v * b1;
+ t9 += v * b2;
+ t10 += v * b3;
+ t11 += v * b4;
+ t12 += v * b5;
+ t13 += v * b6;
+ t14 += v * b7;
+ t15 += v * b8;
+ t16 += v * b9;
+ t17 += v * b10;
+ t18 += v * b11;
+ t19 += v * b12;
+ t20 += v * b13;
+ t21 += v * b14;
+ t22 += v * b15;
+ v = a[8];
+ t8 += v * b0;
+ t9 += v * b1;
+ t10 += v * b2;
+ t11 += v * b3;
+ t12 += v * b4;
+ t13 += v * b5;
+ t14 += v * b6;
+ t15 += v * b7;
+ t16 += v * b8;
+ t17 += v * b9;
+ t18 += v * b10;
+ t19 += v * b11;
+ t20 += v * b12;
+ t21 += v * b13;
+ t22 += v * b14;
+ t23 += v * b15;
+ v = a[9];
+ t9 += v * b0;
+ t10 += v * b1;
+ t11 += v * b2;
+ t12 += v * b3;
+ t13 += v * b4;
+ t14 += v * b5;
+ t15 += v * b6;
+ t16 += v * b7;
+ t17 += v * b8;
+ t18 += v * b9;
+ t19 += v * b10;
+ t20 += v * b11;
+ t21 += v * b12;
+ t22 += v * b13;
+ t23 += v * b14;
+ t24 += v * b15;
+ v = a[10];
+ t10 += v * b0;
+ t11 += v * b1;
+ t12 += v * b2;
+ t13 += v * b3;
+ t14 += v * b4;
+ t15 += v * b5;
+ t16 += v * b6;
+ t17 += v * b7;
+ t18 += v * b8;
+ t19 += v * b9;
+ t20 += v * b10;
+ t21 += v * b11;
+ t22 += v * b12;
+ t23 += v * b13;
+ t24 += v * b14;
+ t25 += v * b15;
+ v = a[11];
+ t11 += v * b0;
+ t12 += v * b1;
+ t13 += v * b2;
+ t14 += v * b3;
+ t15 += v * b4;
+ t16 += v * b5;
+ t17 += v * b6;
+ t18 += v * b7;
+ t19 += v * b8;
+ t20 += v * b9;
+ t21 += v * b10;
+ t22 += v * b11;
+ t23 += v * b12;
+ t24 += v * b13;
+ t25 += v * b14;
+ t26 += v * b15;
+ v = a[12];
+ t12 += v * b0;
+ t13 += v * b1;
+ t14 += v * b2;
+ t15 += v * b3;
+ t16 += v * b4;
+ t17 += v * b5;
+ t18 += v * b6;
+ t19 += v * b7;
+ t20 += v * b8;
+ t21 += v * b9;
+ t22 += v * b10;
+ t23 += v * b11;
+ t24 += v * b12;
+ t25 += v * b13;
+ t26 += v * b14;
+ t27 += v * b15;
+ v = a[13];
+ t13 += v * b0;
+ t14 += v * b1;
+ t15 += v * b2;
+ t16 += v * b3;
+ t17 += v * b4;
+ t18 += v * b5;
+ t19 += v * b6;
+ t20 += v * b7;
+ t21 += v * b8;
+ t22 += v * b9;
+ t23 += v * b10;
+ t24 += v * b11;
+ t25 += v * b12;
+ t26 += v * b13;
+ t27 += v * b14;
+ t28 += v * b15;
+ v = a[14];
+ t14 += v * b0;
+ t15 += v * b1;
+ t16 += v * b2;
+ t17 += v * b3;
+ t18 += v * b4;
+ t19 += v * b5;
+ t20 += v * b6;
+ t21 += v * b7;
+ t22 += v * b8;
+ t23 += v * b9;
+ t24 += v * b10;
+ t25 += v * b11;
+ t26 += v * b12;
+ t27 += v * b13;
+ t28 += v * b14;
+ t29 += v * b15;
+ v = a[15];
+ t15 += v * b0;
+ t16 += v * b1;
+ t17 += v * b2;
+ t18 += v * b3;
+ t19 += v * b4;
+ t20 += v * b5;
+ t21 += v * b6;
+ t22 += v * b7;
+ t23 += v * b8;
+ t24 += v * b9;
+ t25 += v * b10;
+ t26 += v * b11;
+ t27 += v * b12;
+ t28 += v * b13;
+ t29 += v * b14;
+ t30 += v * b15;
+
+ t0 += 38 * t16;
+ t1 += 38 * t17;
+ t2 += 38 * t18;
+ t3 += 38 * t19;
+ t4 += 38 * t20;
+ t5 += 38 * t21;
+ t6 += 38 * t22;
+ t7 += 38 * t23;
+ t8 += 38 * t24;
+ t9 += 38 * t25;
+ t10 += 38 * t26;
+ t11 += 38 * t27;
+ t12 += 38 * t28;
+ t13 += 38 * t29;
+ t14 += 38 * t30;
+ // t15 left as is
+
+ // first car
+ c = 1;
+ v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
+ v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
+ v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
+ v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
+ v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
+ v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
+ v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
+ v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
+ v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
+ v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
+ v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
+ v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
+ v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
+ v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
+ v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
+ v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
+ t0 += c-1 + 37 * (c-1);
+
+ // second car
+ c = 1;
+ v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
+ v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
+ v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
+ v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
+ v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
+ v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
+ v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
+ v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
+ v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
+ v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
+ v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
+ v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
+ v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
+ v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
+ v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
+ v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
+ t0 += c-1 + 37 * (c-1);
+
+ o[ 0] = t0;
+ o[ 1] = t1;
+ o[ 2] = t2;
+ o[ 3] = t3;
+ o[ 4] = t4;
+ o[ 5] = t5;
+ o[ 6] = t6;
+ o[ 7] = t7;
+ o[ 8] = t8;
+ o[ 9] = t9;
+ o[10] = t10;
+ o[11] = t11;
+ o[12] = t12;
+ o[13] = t13;
+ o[14] = t14;
+ o[15] = t15;
+}
+
+function S(o, a) {
+ M(o, a, a);
+}
+
+function inv25519(o, i) {
+ var c = gf();
+ var a;
+ for (a = 0; a < 16; a++) c[a] = i[a];
+ for (a = 253; a >= 0; a--) {
+ S(c, c);
+ if(a !== 2 && a !== 4) M(c, c, i);
+ }
+ for (a = 0; a < 16; a++) o[a] = c[a];
+}
+
+function pow2523(o, i) {
+ var c = gf();
+ var a;
+ for (a = 0; a < 16; a++) c[a] = i[a];
+ for (a = 250; a >= 0; a--) {
+ S(c, c);
+ if(a !== 1) M(c, c, i);
+ }
+ for (a = 0; a < 16; a++) o[a] = c[a];
+}
+
+function crypto_scalarmult(q, n, p) {
+ var z = new Uint8Array(32);
+ var x = new Float64Array(80), r, i;
+ var a = gf(), b = gf(), c = gf(),
+ d = gf(), e = gf(), f = gf();
+ for (i = 0; i < 31; i++) z[i] = n[i];
+ z[31]=(n[31]&127)|64;
+ z[0]&=248;
+ unpack25519(x,p);
+ for (i = 0; i < 16; i++) {
+ b[i]=x[i];
+ d[i]=a[i]=c[i]=0;
+ }
+ a[0]=d[0]=1;
+ for (i=254; i>=0; --i) {
+ r=(z[i>>>3]>>>(i&7))&1;
+ sel25519(a,b,r);
+ sel25519(c,d,r);
+ A(e,a,c);
+ Z(a,a,c);
+ A(c,b,d);
+ Z(b,b,d);
+ S(d,e);
+ S(f,a);
+ M(a,c,a);
+ M(c,b,e);
+ A(e,a,c);
+ Z(a,a,c);
+ S(b,a);
+ Z(c,d,f);
+ M(a,c,_121665);
+ A(a,a,d);
+ M(c,c,a);
+ M(a,d,f);
+ M(d,b,x);
+ S(b,e);
+ sel25519(a,b,r);
+ sel25519(c,d,r);
+ }
+ for (i = 0; i < 16; i++) {
+ x[i+16]=a[i];
+ x[i+32]=c[i];
+ x[i+48]=b[i];
+ x[i+64]=d[i];
+ }
+ var x32 = x.subarray(32);
+ var x16 = x.subarray(16);
+ inv25519(x32,x32);
+ M(x16,x16,x32);
+ pack25519(q,x16);
+ return 0;
+}
+
+function crypto_scalarmult_base(q, n) {
+ return crypto_scalarmult(q, n, _9);
+}
+
+function crypto_box_keypair(y, x) {
+ randombytes(x, 32);
+ return crypto_scalarmult_base(y, x);
+}
+
+function crypto_box_beforenm(k, y, x) {
+ var s = new Uint8Array(32);
+ crypto_scalarmult(s, x, y);
+ return crypto_core_hsalsa20(k, _0, s, sigma);
+}
+
+var crypto_box_afternm = crypto_secretbox;
+var crypto_box_open_afternm = crypto_secretbox_open;
+
+function crypto_box(c, m, d, n, y, x) {
+ var k = new Uint8Array(32);
+ crypto_box_beforenm(k, y, x);
+ return crypto_box_afternm(c, m, d, n, k);
+}
+
+function crypto_box_open(m, c, d, n, y, x) {
+ var k = new Uint8Array(32);
+ crypto_box_beforenm(k, y, x);
+ return crypto_box_open_afternm(m, c, d, n, k);
+}
+
+var K = [
+ 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd,
+ 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,
+ 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019,
+ 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,
+ 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe,
+ 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,
+ 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1,
+ 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,
+ 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3,
+ 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,
+ 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483,
+ 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,
+ 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210,
+ 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,
+ 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725,
+ 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,
+ 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926,
+ 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,
+ 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8,
+ 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,
+ 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001,
+ 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,
+ 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910,
+ 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,
+ 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53,
+ 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,
+ 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb,
+ 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,
+ 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60,
+ 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,
+ 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9,
+ 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,
+ 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207,
+ 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,
+ 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6,
+ 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,
+ 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493,
+ 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,
+ 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a,
+ 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817
+];
+
+function crypto_hashblocks_hl(hh, hl, m, n) {
+ var wh = new Int32Array(16), wl = new Int32Array(16),
+ bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7,
+ bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7,
+ th, tl, i, j, h, l, a, b, c, d;
+
+ var ah0 = hh[0],
+ ah1 = hh[1],
+ ah2 = hh[2],
+ ah3 = hh[3],
+ ah4 = hh[4],
+ ah5 = hh[5],
+ ah6 = hh[6],
+ ah7 = hh[7],
+
+ al0 = hl[0],
+ al1 = hl[1],
+ al2 = hl[2],
+ al3 = hl[3],
+ al4 = hl[4],
+ al5 = hl[5],
+ al6 = hl[6],
+ al7 = hl[7];
+
+ var pos = 0;
+ while (n >= 128) {
+ for (i = 0; i < 16; i++) {
+ j = 8 * i + pos;
+ wh[i] = (m[j+0] << 24) | (m[j+1] << 16) | (m[j+2] << 8) | m[j+3];
+ wl[i] = (m[j+4] << 24) | (m[j+5] << 16) | (m[j+6] << 8) | m[j+7];
+ }
+ for (i = 0; i < 80; i++) {
+ bh0 = ah0;
+ bh1 = ah1;
+ bh2 = ah2;
+ bh3 = ah3;
+ bh4 = ah4;
+ bh5 = ah5;
+ bh6 = ah6;
+ bh7 = ah7;
+
+ bl0 = al0;
+ bl1 = al1;
+ bl2 = al2;
+ bl3 = al3;
+ bl4 = al4;
+ bl5 = al5;
+ bl6 = al6;
+ bl7 = al7;
+
+ // add
+ h = ah7;
+ l = al7;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ // Sigma1
+ h = ((ah4 >>> 14) | (al4 << (32-14))) ^ ((ah4 >>> 18) | (al4 << (32-18))) ^ ((al4 >>> (41-32)) | (ah4 << (32-(41-32))));
+ l = ((al4 >>> 14) | (ah4 << (32-14))) ^ ((al4 >>> 18) | (ah4 << (32-18))) ^ ((ah4 >>> (41-32)) | (al4 << (32-(41-32))));
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ // Ch
+ h = (ah4 & ah5) ^ (~ah4 & ah6);
+ l = (al4 & al5) ^ (~al4 & al6);
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ // K
+ h = K[i*2];
+ l = K[i*2+1];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ // w
+ h = wh[i%16];
+ l = wl[i%16];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ th = c & 0xffff | d << 16;
+ tl = a & 0xffff | b << 16;
+
+ // add
+ h = th;
+ l = tl;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ // Sigma0
+ h = ((ah0 >>> 28) | (al0 << (32-28))) ^ ((al0 >>> (34-32)) | (ah0 << (32-(34-32)))) ^ ((al0 >>> (39-32)) | (ah0 << (32-(39-32))));
+ l = ((al0 >>> 28) | (ah0 << (32-28))) ^ ((ah0 >>> (34-32)) | (al0 << (32-(34-32)))) ^ ((ah0 >>> (39-32)) | (al0 << (32-(39-32))));
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ // Maj
+ h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2);
+ l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2);
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ bh7 = (c & 0xffff) | (d << 16);
+ bl7 = (a & 0xffff) | (b << 16);
+
+ // add
+ h = bh3;
+ l = bl3;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = th;
+ l = tl;
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ bh3 = (c & 0xffff) | (d << 16);
+ bl3 = (a & 0xffff) | (b << 16);
+
+ ah1 = bh0;
+ ah2 = bh1;
+ ah3 = bh2;
+ ah4 = bh3;
+ ah5 = bh4;
+ ah6 = bh5;
+ ah7 = bh6;
+ ah0 = bh7;
+
+ al1 = bl0;
+ al2 = bl1;
+ al3 = bl2;
+ al4 = bl3;
+ al5 = bl4;
+ al6 = bl5;
+ al7 = bl6;
+ al0 = bl7;
+
+ if (i%16 === 15) {
+ for (j = 0; j < 16; j++) {
+ // add
+ h = wh[j];
+ l = wl[j];
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = wh[(j+9)%16];
+ l = wl[(j+9)%16];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ // sigma0
+ th = wh[(j+1)%16];
+ tl = wl[(j+1)%16];
+ h = ((th >>> 1) | (tl << (32-1))) ^ ((th >>> 8) | (tl << (32-8))) ^ (th >>> 7);
+ l = ((tl >>> 1) | (th << (32-1))) ^ ((tl >>> 8) | (th << (32-8))) ^ ((tl >>> 7) | (th << (32-7)));
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ // sigma1
+ th = wh[(j+14)%16];
+ tl = wl[(j+14)%16];
+ h = ((th >>> 19) | (tl << (32-19))) ^ ((tl >>> (61-32)) | (th << (32-(61-32)))) ^ (th >>> 6);
+ l = ((tl >>> 19) | (th << (32-19))) ^ ((th >>> (61-32)) | (tl << (32-(61-32)))) ^ ((tl >>> 6) | (th << (32-6)));
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ wh[j] = (c & 0xffff) | (d << 16);
+ wl[j] = (a & 0xffff) | (b << 16);
+ }
+ }
+ }
+
+ // add
+ h = ah0;
+ l = al0;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = hh[0];
+ l = hl[0];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ hh[0] = ah0 = (c & 0xffff) | (d << 16);
+ hl[0] = al0 = (a & 0xffff) | (b << 16);
+
+ h = ah1;
+ l = al1;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = hh[1];
+ l = hl[1];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ hh[1] = ah1 = (c & 0xffff) | (d << 16);
+ hl[1] = al1 = (a & 0xffff) | (b << 16);
+
+ h = ah2;
+ l = al2;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = hh[2];
+ l = hl[2];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ hh[2] = ah2 = (c & 0xffff) | (d << 16);
+ hl[2] = al2 = (a & 0xffff) | (b << 16);
+
+ h = ah3;
+ l = al3;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = hh[3];
+ l = hl[3];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ hh[3] = ah3 = (c & 0xffff) | (d << 16);
+ hl[3] = al3 = (a & 0xffff) | (b << 16);
+
+ h = ah4;
+ l = al4;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = hh[4];
+ l = hl[4];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ hh[4] = ah4 = (c & 0xffff) | (d << 16);
+ hl[4] = al4 = (a & 0xffff) | (b << 16);
+
+ h = ah5;
+ l = al5;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = hh[5];
+ l = hl[5];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ hh[5] = ah5 = (c & 0xffff) | (d << 16);
+ hl[5] = al5 = (a & 0xffff) | (b << 16);
+
+ h = ah6;
+ l = al6;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = hh[6];
+ l = hl[6];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ hh[6] = ah6 = (c & 0xffff) | (d << 16);
+ hl[6] = al6 = (a & 0xffff) | (b << 16);
+
+ h = ah7;
+ l = al7;
+
+ a = l & 0xffff; b = l >>> 16;
+ c = h & 0xffff; d = h >>> 16;
+
+ h = hh[7];
+ l = hl[7];
+
+ a += l & 0xffff; b += l >>> 16;
+ c += h & 0xffff; d += h >>> 16;
+
+ b += a >>> 16;
+ c += b >>> 16;
+ d += c >>> 16;
+
+ hh[7] = ah7 = (c & 0xffff) | (d << 16);
+ hl[7] = al7 = (a & 0xffff) | (b << 16);
+
+ pos += 128;
+ n -= 128;
+ }
+
+ return n;
+}
+
+function crypto_hash(out, m, n) {
+ var hh = new Int32Array(8),
+ hl = new Int32Array(8),
+ x = new Uint8Array(256),
+ i, b = n;
+
+ hh[0] = 0x6a09e667;
+ hh[1] = 0xbb67ae85;
+ hh[2] = 0x3c6ef372;
+ hh[3] = 0xa54ff53a;
+ hh[4] = 0x510e527f;
+ hh[5] = 0x9b05688c;
+ hh[6] = 0x1f83d9ab;
+ hh[7] = 0x5be0cd19;
+
+ hl[0] = 0xf3bcc908;
+ hl[1] = 0x84caa73b;
+ hl[2] = 0xfe94f82b;
+ hl[3] = 0x5f1d36f1;
+ hl[4] = 0xade682d1;
+ hl[5] = 0x2b3e6c1f;
+ hl[6] = 0xfb41bd6b;
+ hl[7] = 0x137e2179;
+
+ crypto_hashblocks_hl(hh, hl, m, n);
+ n %= 128;
+
+ for (i = 0; i < n; i++) x[i] = m[b-n+i];
+ x[n] = 128;
+
+ n = 256-128*(n<112?1:0);
+ x[n-9] = 0;
+ ts64(x, n-8, (b / 0x20000000) | 0, b << 3);
+ crypto_hashblocks_hl(hh, hl, x, n);
+
+ for (i = 0; i < 8; i++) ts64(out, 8*i, hh[i], hl[i]);
+
+ return 0;
+}
+
+function add(p, q) {
+ var a = gf(), b = gf(), c = gf(),
+ d = gf(), e = gf(), f = gf(),
+ g = gf(), h = gf(), t = gf();
+
+ Z(a, p[1], p[0]);
+ Z(t, q[1], q[0]);
+ M(a, a, t);
+ A(b, p[0], p[1]);
+ A(t, q[0], q[1]);
+ M(b, b, t);
+ M(c, p[3], q[3]);
+ M(c, c, D2);
+ M(d, p[2], q[2]);
+ A(d, d, d);
+ Z(e, b, a);
+ Z(f, d, c);
+ A(g, d, c);
+ A(h, b, a);
+
+ M(p[0], e, f);
+ M(p[1], h, g);
+ M(p[2], g, f);
+ M(p[3], e, h);
+}
+
+function cswap(p, q, b) {
+ var i;
+ for (i = 0; i < 4; i++) {
+ sel25519(p[i], q[i], b);
+ }
+}
+
+function pack(r, p) {
+ var tx = gf(), ty = gf(), zi = gf();
+ inv25519(zi, p[2]);
+ M(tx, p[0], zi);
+ M(ty, p[1], zi);
+ pack25519(r, ty);
+ r[31] ^= par25519(tx) << 7;
+}
+
+function scalarmult(p, q, s) {
+ var b, i;
+ set25519(p[0], gf0);
+ set25519(p[1], gf1);
+ set25519(p[2], gf1);
+ set25519(p[3], gf0);
+ for (i = 255; i >= 0; --i) {
+ b = (s[(i/8)|0] >> (i&7)) & 1;
+ cswap(p, q, b);
+ add(q, p);
+ add(p, p);
+ cswap(p, q, b);
+ }
+}
+
+function scalarbase(p, s) {
+ var q = [gf(), gf(), gf(), gf()];
+ set25519(q[0], X);
+ set25519(q[1], Y);
+ set25519(q[2], gf1);
+ M(q[3], X, Y);
+ scalarmult(p, q, s);
+}
+
+function crypto_sign_keypair(pk, sk, seeded) {
+ var d = new Uint8Array(64);
+ var p = [gf(), gf(), gf(), gf()];
+ var i;
+
+ if (!seeded) randombytes(sk, 32);
+ crypto_hash(d, sk, 32);
+ d[0] &= 248;
+ d[31] &= 127;
+ d[31] |= 64;
+
+ scalarbase(p, d);
+ pack(pk, p);
+
+ for (i = 0; i < 32; i++) sk[i+32] = pk[i];
+ return 0;
+}
+
+var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]);
+
+function modL(r, x) {
+ var carry, i, j, k;
+ for (i = 63; i >= 32; --i) {
+ carry = 0;
+ for (j = i - 32, k = i - 12; j < k; ++j) {
+ x[j] += carry - 16 * x[i] * L[j - (i - 32)];
+ carry = (x[j] + 128) >> 8;
+ x[j] -= carry * 256;
+ }
+ x[j] += carry;
+ x[i] = 0;
+ }
+ carry = 0;
+ for (j = 0; j < 32; j++) {
+ x[j] += carry - (x[31] >> 4) * L[j];
+ carry = x[j] >> 8;
+ x[j] &= 255;
+ }
+ for (j = 0; j < 32; j++) x[j] -= carry * L[j];
+ for (i = 0; i < 32; i++) {
+ x[i+1] += x[i] >> 8;
+ r[i] = x[i] & 255;
+ }
+}
+
+function reduce(r) {
+ var x = new Float64Array(64), i;
+ for (i = 0; i < 64; i++) x[i] = r[i];
+ for (i = 0; i < 64; i++) r[i] = 0;
+ modL(r, x);
+}
+
+// Note: difference from C - smlen returned, not passed as argument.
+function crypto_sign(sm, m, n, sk) {
+ var d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64);
+ var i, j, x = new Float64Array(64);
+ var p = [gf(), gf(), gf(), gf()];
+
+ crypto_hash(d, sk, 32);
+ d[0] &= 248;
+ d[31] &= 127;
+ d[31] |= 64;
+
+ var smlen = n + 64;
+ for (i = 0; i < n; i++) sm[64 + i] = m[i];
+ for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i];
+
+ crypto_hash(r, sm.subarray(32), n+32);
+ reduce(r);
+ scalarbase(p, r);
+ pack(sm, p);
+
+ for (i = 32; i < 64; i++) sm[i] = sk[i];
+ crypto_hash(h, sm, n + 64);
+ reduce(h);
+
+ for (i = 0; i < 64; i++) x[i] = 0;
+ for (i = 0; i < 32; i++) x[i] = r[i];
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < 32; j++) {
+ x[i+j] += h[i] * d[j];
+ }
+ }
+
+ modL(sm.subarray(32), x);
+ return smlen;
+}
+
+function unpackneg(r, p) {
+ var t = gf(), chk = gf(), num = gf(),
+ den = gf(), den2 = gf(), den4 = gf(),
+ den6 = gf();
+
+ set25519(r[2], gf1);
+ unpack25519(r[1], p);
+ S(num, r[1]);
+ M(den, num, D);
+ Z(num, num, r[2]);
+ A(den, r[2], den);
+
+ S(den2, den);
+ S(den4, den2);
+ M(den6, den4, den2);
+ M(t, den6, num);
+ M(t, t, den);
+
+ pow2523(t, t);
+ M(t, t, num);
+ M(t, t, den);
+ M(t, t, den);
+ M(r[0], t, den);
+
+ S(chk, r[0]);
+ M(chk, chk, den);
+ if (neq25519(chk, num)) M(r[0], r[0], I);
+
+ S(chk, r[0]);
+ M(chk, chk, den);
+ if (neq25519(chk, num)) return -1;
+
+ if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]);
+
+ M(r[3], r[0], r[1]);
+ return 0;
+}
+
+function crypto_sign_open(m, sm, n, pk) {
+ var i, mlen;
+ var t = new Uint8Array(32), h = new Uint8Array(64);
+ var p = [gf(), gf(), gf(), gf()],
+ q = [gf(), gf(), gf(), gf()];
+
+ mlen = -1;
+ if (n < 64) return -1;
+
+ if (unpackneg(q, pk)) return -1;
+
+ for (i = 0; i < n; i++) m[i] = sm[i];
+ for (i = 0; i < 32; i++) m[i+32] = pk[i];
+ crypto_hash(h, m, n);
+ reduce(h);
+ scalarmult(p, q, h);
+
+ scalarbase(q, sm.subarray(32));
+ add(p, q);
+ pack(t, p);
+
+ n -= 64;
+ if (crypto_verify_32(sm, 0, t, 0)) {
+ for (i = 0; i < n; i++) m[i] = 0;
+ return -1;
+ }
+
+ for (i = 0; i < n; i++) m[i] = sm[i + 64];
+ mlen = n;
+ return mlen;
+}
+
+var crypto_secretbox_KEYBYTES = 32,
+ crypto_secretbox_NONCEBYTES = 24,
+ crypto_secretbox_ZEROBYTES = 32,
+ crypto_secretbox_BOXZEROBYTES = 16,
+ crypto_scalarmult_BYTES = 32,
+ crypto_scalarmult_SCALARBYTES = 32,
+ crypto_box_PUBLICKEYBYTES = 32,
+ crypto_box_SECRETKEYBYTES = 32,
+ crypto_box_BEFORENMBYTES = 32,
+ crypto_box_NONCEBYTES = crypto_secretbox_NONCEBYTES,
+ crypto_box_ZEROBYTES = crypto_secretbox_ZEROBYTES,
+ crypto_box_BOXZEROBYTES = crypto_secretbox_BOXZEROBYTES,
+ crypto_sign_BYTES = 64,
+ crypto_sign_PUBLICKEYBYTES = 32,
+ crypto_sign_SECRETKEYBYTES = 64,
+ crypto_sign_SEEDBYTES = 32,
+ crypto_hash_BYTES = 64;
+
+nacl.lowlevel = {
+ crypto_core_hsalsa20: crypto_core_hsalsa20,
+ crypto_stream_xor: crypto_stream_xor,
+ crypto_stream: crypto_stream,
+ crypto_stream_salsa20_xor: crypto_stream_salsa20_xor,
+ crypto_stream_salsa20: crypto_stream_salsa20,
+ crypto_onetimeauth: crypto_onetimeauth,
+ crypto_onetimeauth_verify: crypto_onetimeauth_verify,
+ crypto_verify_16: crypto_verify_16,
+ crypto_verify_32: crypto_verify_32,
+ crypto_secretbox: crypto_secretbox,
+ crypto_secretbox_open: crypto_secretbox_open,
+ crypto_scalarmult: crypto_scalarmult,
+ crypto_scalarmult_base: crypto_scalarmult_base,
+ crypto_box_beforenm: crypto_box_beforenm,
+ crypto_box_afternm: crypto_box_afternm,
+ crypto_box: crypto_box,
+ crypto_box_open: crypto_box_open,
+ crypto_box_keypair: crypto_box_keypair,
+ crypto_hash: crypto_hash,
+ crypto_sign: crypto_sign,
+ crypto_sign_keypair: crypto_sign_keypair,
+ crypto_sign_open: crypto_sign_open,
+
+ crypto_secretbox_KEYBYTES: crypto_secretbox_KEYBYTES,
+ crypto_secretbox_NONCEBYTES: crypto_secretbox_NONCEBYTES,
+ crypto_secretbox_ZEROBYTES: crypto_secretbox_ZEROBYTES,
+ crypto_secretbox_BOXZEROBYTES: crypto_secretbox_BOXZEROBYTES,
+ crypto_scalarmult_BYTES: crypto_scalarmult_BYTES,
+ crypto_scalarmult_SCALARBYTES: crypto_scalarmult_SCALARBYTES,
+ crypto_box_PUBLICKEYBYTES: crypto_box_PUBLICKEYBYTES,
+ crypto_box_SECRETKEYBYTES: crypto_box_SECRETKEYBYTES,
+ crypto_box_BEFORENMBYTES: crypto_box_BEFORENMBYTES,
+ crypto_box_NONCEBYTES: crypto_box_NONCEBYTES,
+ crypto_box_ZEROBYTES: crypto_box_ZEROBYTES,
+ crypto_box_BOXZEROBYTES: crypto_box_BOXZEROBYTES,
+ crypto_sign_BYTES: crypto_sign_BYTES,
+ crypto_sign_PUBLICKEYBYTES: crypto_sign_PUBLICKEYBYTES,
+ crypto_sign_SECRETKEYBYTES: crypto_sign_SECRETKEYBYTES,
+ crypto_sign_SEEDBYTES: crypto_sign_SEEDBYTES,
+ crypto_hash_BYTES: crypto_hash_BYTES
+};
+
+/* High-level API */
+
+function checkLengths(k, n) {
+ if (k.length !== crypto_secretbox_KEYBYTES) throw new Error('bad key size');
+ if (n.length !== crypto_secretbox_NONCEBYTES) throw new Error('bad nonce size');
+}
+
+function checkBoxLengths(pk, sk) {
+ if (pk.length !== crypto_box_PUBLICKEYBYTES) throw new Error('bad public key size');
+ if (sk.length !== crypto_box_SECRETKEYBYTES) throw new Error('bad secret key size');
+}
+
+function checkArrayTypes() {
+ var t, i;
+ for (i = 0; i < arguments.length; i++) {
+ if ((t = Object.prototype.toString.call(arguments[i])) !== '[object Uint8Array]')
+ throw new TypeError('unexpected type ' + t + ', use Uint8Array');
+ }
+}
+
+function cleanup(arr) {
+ for (var i = 0; i < arr.length; i++) arr[i] = 0;
+}
+
+nacl.util = {};
+
+nacl.util.decodeUTF8 = function(s) {
+ var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
+ for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
+ return b;
+};
+
+nacl.util.encodeUTF8 = function(arr) {
+ var i, s = [];
+ for (i = 0; i < arr.length; i++) s.push(String.fromCharCode(arr[i]));
+ return decodeURIComponent(escape(s.join('')));
+};
+
+nacl.util.encodeBase64 = function(arr) {
+ if (typeof btoa === 'undefined') {
+ return (new Buffer(arr)).toString('base64');
+ } else {
+ var i, s = [], len = arr.length;
+ for (i = 0; i < len; i++) s.push(String.fromCharCode(arr[i]));
+ return btoa(s.join(''));
+ }
+};
+
+nacl.util.decodeBase64 = function(s) {
+ if (typeof atob === 'undefined') {
+ return new Uint8Array(Array.prototype.slice.call(new Buffer(s, 'base64'), 0));
+ } else {
+ var i, d = atob(s), b = new Uint8Array(d.length);
+ for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
+ return b;
+ }
+};
+
+nacl.randomBytes = function(n) {
+ var b = new Uint8Array(n);
+ randombytes(b, n);
+ return b;
+};
+
+nacl.secretbox = function(msg, nonce, key) {
+ checkArrayTypes(msg, nonce, key);
+ checkLengths(key, nonce);
+ var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length);
+ var c = new Uint8Array(m.length);
+ for (var i = 0; i < msg.length; i++) m[i+crypto_secretbox_ZEROBYTES] = msg[i];
+ crypto_secretbox(c, m, m.length, nonce, key);
+ return c.subarray(crypto_secretbox_BOXZEROBYTES);
+};
+
+nacl.secretbox.open = function(box, nonce, key) {
+ checkArrayTypes(box, nonce, key);
+ checkLengths(key, nonce);
+ var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length);
+ var m = new Uint8Array(c.length);
+ for (var i = 0; i < box.length; i++) c[i+crypto_secretbox_BOXZEROBYTES] = box[i];
+ if (c.length < 32) return false;
+ if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return false;
+ return m.subarray(crypto_secretbox_ZEROBYTES);
+};
+
+nacl.secretbox.keyLength = crypto_secretbox_KEYBYTES;
+nacl.secretbox.nonceLength = crypto_secretbox_NONCEBYTES;
+nacl.secretbox.overheadLength = crypto_secretbox_BOXZEROBYTES;
+
+nacl.scalarMult = function(n, p) {
+ checkArrayTypes(n, p);
+ if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size');
+ if (p.length !== crypto_scalarmult_BYTES) throw new Error('bad p size');
+ var q = new Uint8Array(crypto_scalarmult_BYTES);
+ crypto_scalarmult(q, n, p);
+ return q;
+};
+
+nacl.scalarMult.base = function(n) {
+ checkArrayTypes(n);
+ if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size');
+ var q = new Uint8Array(crypto_scalarmult_BYTES);
+ crypto_scalarmult_base(q, n);
+ return q;
+};
+
+nacl.scalarMult.scalarLength = crypto_scalarmult_SCALARBYTES;
+nacl.scalarMult.groupElementLength = crypto_scalarmult_BYTES;
+
+nacl.box = function(msg, nonce, publicKey, secretKey) {
+ var k = nacl.box.before(publicKey, secretKey);
+ return nacl.secretbox(msg, nonce, k);
+};
+
+nacl.box.before = function(publicKey, secretKey) {
+ checkArrayTypes(publicKey, secretKey);
+ checkBoxLengths(publicKey, secretKey);
+ var k = new Uint8Array(crypto_box_BEFORENMBYTES);
+ crypto_box_beforenm(k, publicKey, secretKey);
+ return k;
+};
+
+nacl.box.after = nacl.secretbox;
+
+nacl.box.open = function(msg, nonce, publicKey, secretKey) {
+ var k = nacl.box.before(publicKey, secretKey);
+ return nacl.secretbox.open(msg, nonce, k);
+};
+
+nacl.box.open.after = nacl.secretbox.open;
+
+nacl.box.keyPair = function() {
+ var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES);
+ var sk = new Uint8Array(crypto_box_SECRETKEYBYTES);
+ crypto_box_keypair(pk, sk);
+ return {publicKey: pk, secretKey: sk};
+};
+
+nacl.box.keyPair.fromSecretKey = function(secretKey) {
+ checkArrayTypes(secretKey);
+ if (secretKey.length !== crypto_box_SECRETKEYBYTES)
+ throw new Error('bad secret key size');
+ var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES);
+ crypto_scalarmult_base(pk, secretKey);
+ return {publicKey: pk, secretKey: new Uint8Array(secretKey)};
+};
+
+nacl.box.publicKeyLength = crypto_box_PUBLICKEYBYTES;
+nacl.box.secretKeyLength = crypto_box_SECRETKEYBYTES;
+nacl.box.sharedKeyLength = crypto_box_BEFORENMBYTES;
+nacl.box.nonceLength = crypto_box_NONCEBYTES;
+nacl.box.overheadLength = nacl.secretbox.overheadLength;
+
+nacl.sign = function(msg, secretKey) {
+ checkArrayTypes(msg, secretKey);
+ if (secretKey.length !== crypto_sign_SECRETKEYBYTES)
+ throw new Error('bad secret key size');
+ var signedMsg = new Uint8Array(crypto_sign_BYTES+msg.length);
+ crypto_sign(signedMsg, msg, msg.length, secretKey);
+ return signedMsg;
+};
+
+nacl.sign.open = function(signedMsg, publicKey) {
+ if (arguments.length !== 2)
+ throw new Error('nacl.sign.open accepts 2 arguments; did you mean to use nacl.sign.detached.verify?');
+ checkArrayTypes(signedMsg, publicKey);
+ if (publicKey.length !== crypto_sign_PUBLICKEYBYTES)
+ throw new Error('bad public key size');
+ var tmp = new Uint8Array(signedMsg.length);
+ var mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey);
+ if (mlen < 0) return null;
+ var m = new Uint8Array(mlen);
+ for (var i = 0; i < m.length; i++) m[i] = tmp[i];
+ return m;
+};
+
+nacl.sign.detached = function(msg, secretKey) {
+ var signedMsg = nacl.sign(msg, secretKey);
+ var sig = new Uint8Array(crypto_sign_BYTES);
+ for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i];
+ return sig;
+};
+
+nacl.sign.detached.verify = function(msg, sig, publicKey) {
+ checkArrayTypes(msg, sig, publicKey);
+ if (sig.length !== crypto_sign_BYTES)
+ throw new Error('bad signature size');
+ if (publicKey.length !== crypto_sign_PUBLICKEYBYTES)
+ throw new Error('bad public key size');
+ var sm = new Uint8Array(crypto_sign_BYTES + msg.length);
+ var m = new Uint8Array(crypto_sign_BYTES + msg.length);
+ var i;
+ for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i];
+ for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i];
+ return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0);
+};
+
+nacl.sign.keyPair = function() {
+ var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
+ var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
+ crypto_sign_keypair(pk, sk);
+ return {publicKey: pk, secretKey: sk};
+};
+
+nacl.sign.keyPair.fromSecretKey = function(secretKey) {
+ checkArrayTypes(secretKey);
+ if (secretKey.length !== crypto_sign_SECRETKEYBYTES)
+ throw new Error('bad secret key size');
+ var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
+ for (var i = 0; i < pk.length; i++) pk[i] = secretKey[32+i];
+ return {publicKey: pk, secretKey: new Uint8Array(secretKey)};
+};
+
+nacl.sign.keyPair.fromSeed = function(seed) {
+ checkArrayTypes(seed);
+ if (seed.length !== crypto_sign_SEEDBYTES)
+ throw new Error('bad seed size');
+ var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
+ var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
+ for (var i = 0; i < 32; i++) sk[i] = seed[i];
+ crypto_sign_keypair(pk, sk, true);
+ return {publicKey: pk, secretKey: sk};
+};
+
+nacl.sign.publicKeyLength = crypto_sign_PUBLICKEYBYTES;
+nacl.sign.secretKeyLength = crypto_sign_SECRETKEYBYTES;
+nacl.sign.seedLength = crypto_sign_SEEDBYTES;
+nacl.sign.signatureLength = crypto_sign_BYTES;
+
+nacl.hash = function(msg) {
+ checkArrayTypes(msg);
+ var h = new Uint8Array(crypto_hash_BYTES);
+ crypto_hash(h, msg, msg.length);
+ return h;
+};
+
+nacl.hash.hashLength = crypto_hash_BYTES;
+
+nacl.verify = function(x, y) {
+ checkArrayTypes(x, y);
+ // Zero length arguments are considered not equal.
+ if (x.length === 0 || y.length === 0) return false;
+ if (x.length !== y.length) return false;
+ return (vn(x, 0, y, 0, x.length) === 0) ? true : false;
+};
+
+nacl.setPRNG = function(fn) {
+ randombytes = fn;
+};
+
+(function() {
+ // Initialize PRNG if environment provides CSPRNG.
+ // If not, methods calling randombytes will throw.
+ var crypto;
+ if (typeof window !== 'undefined') {
+ // Browser.
+ if (window.crypto && window.crypto.getRandomValues) {
+ crypto = window.crypto; // Standard
+ } else if (window.msCrypto && window.msCrypto.getRandomValues) {
+ crypto = window.msCrypto; // Internet Explorer 11+
+ }
+ if (crypto) {
+ nacl.setPRNG(function(x, n) {
+ var i, v = new Uint8Array(n);
+ crypto.getRandomValues(v);
+ for (i = 0; i < n; i++) x[i] = v[i];
+ cleanup(v);
+ });
+ }
+ } else if (typeof require !== 'undefined') {
+ // Node.js.
+ crypto = require('crypto');
+ if (crypto) {
+ nacl.setPRNG(function(x, n) {
+ var i, v = crypto.randomBytes(n);
+ for (i = 0; i < n; i++) x[i] = v[i];
+ cleanup(v);
+ });
+ }
+ }
+})();
+
+class Base58 {
+ constructor() {
+ this.ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
+ this.ALPHABET_MAP = {};
+
+ for (let i = 0; i < this.ALPHABET.length; i++) {
+ this.ALPHABET_MAP[this.ALPHABET.charAt(i)] = i;
+ }
+ }
+
+ encode(buffer) {
+ buffer = new Uint8Array(buffer);
+ let carry, digits, j;
+ if (buffer.length === 0) {
+ return '';
+ }
+ let i = 0;
+ digits = [0];
+ while (i < buffer.length) {
+ j = 0;
+ while (j < digits.length) {
+ digits[j] <<= 8;
+ j++;
+ }
+ digits[0] += buffer[i];
+ carry = 0;
+ j = 0;
+ while (j < digits.length) {
+ digits[j] += carry;
+ carry = (digits[j] / 58) | 0;
+ digits[j] %= 58;
+ ++j;
+ }
+ while (carry) {
+ digits.push(carry % 58);
+ carry = (carry / 58) | 0;
+ }
+ i++;
+ }
+ i = 0;
+ while (buffer[i] === 0 && i < buffer.length - 1) {
+ digits.push(0);
+ i++;
+ }
+ return digits.reverse().map(digit => this.ALPHABET[digit]).join('');
+ }
+
+ decode(string) {
+ if (string.length === 0) {
+ return new Uint8Array(0);
+ }
+ let bytes = [0];
+ let i = 0, j, c, carry;
+ while (i < string.length) {
+ c = string[i];
+ if (!(c in this.ALPHABET_MAP)) {
+ throw new Error(`Base58.decode received unacceptable input. Character '${c}' is not in the Base58 alphabet.`);
+ }
+ j = 0;
+ while (j < bytes.length) {
+ bytes[j] *= 58;
+ j++;
+ }
+ bytes[0] += this.ALPHABET_MAP[c];
+ carry = 0;
+ j = 0;
+ while (j < bytes.length) {
+ bytes[j] += carry;
+ carry = bytes[j] >> 8;
+ bytes[j] &= 0xff;
+ ++j;
+ }
+ while (carry) {
+ bytes.push(carry & 0xff);
+ carry >>= 8;
+ }
+ i++;
+ }
+ i = 0;
+ while (string[i] === '1' && i < string.length - 1) {
+ bytes.push(0);
+ i++;
+ }
+ return new Uint8Array(bytes.reverse());
+ }
+}
+
+
+class Curve25519 {
+ constructor() {
+ this.gf0 = this.gf();
+ this.gf1 = this.gf([1]);
+ this.D = this.gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]);
+ this.I = this.gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);
+ }
+
+ gf(init) {
+ let i, r = new Float64Array(16);
+ if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
+ return r;
+ }
+
+ car25519(o) {
+ var c, i;
+ for (i = 0; i < 16; i++) {
+ o[i] += 65536;
+ c = Math.floor(o[i] / 65536);
+ o[(i + 1) * (i < 15 ? 1 : 0)] += c - 1 + 37 * (c - 1) * (i === 15 ? 1 : 0);
+ o[i] -= (c * 65536);
+ }
+ }
+
+ sel25519(p, q, b) {
+ let t, c = ~(b - 1);
+ for (let i = 0; i < 16; i++) {
+ t = c & (p[i] ^ q[i]);
+ p[i] ^= t;
+ q[i] ^= t;
+ }
+ }
+
+ A(o, a, b) {
+ for (let i = 0; i < 16; i++) o[i] = a[i] + b[i];
+ }
+
+ Z(o, a, b) {
+ for (let i = 0; i < 16; i++) o[i] = a[i] - b[i];
+ }
+
+ M(o, a, b) {
+ var i, j, t = new Float64Array(31);
+ for (i = 0; i < 31; i++) t[i] = 0;
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 16; j++) {
+ t[i+j] += a[i] * b[j];
+ }
+ }
+ for (i = 0; i < 15; i++) {
+ t[i] += 38 * t[i+16];
+ }
+ for (i = 0; i < 16; i++) o[i] = t[i];
+ this.car25519(o);
+ this.car25519(o);
+ }
+
+ inv25519(o, i) {
+ var c = this.gf();
+ var a;
+ for (a = 0; a < 16; a++) c[a] = i[a];
+ for (a = 253; a >= 0; a--) {
+ this.S(c, c);
+ if(a !== 2 && a !== 4) this.M(c, c, i);
+ }
+ for (a = 0; a < 16; a++) o[a] = c[a];
+ }
+
+ pack25519(o, n) {
+ var i, j, b;
+ var m = gf(), t = gf();
+ for (i = 0; i < 16; i++) t[i] = n[i];
+ this.car25519(t);
+ this.car25519(t);
+ this.car25519(t);
+ for (j = 0; j < 2; j++) {
+ m[0] = t[0] - 0xffed;
+ for (i = 1; i < 15; i++) {
+ m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1);
+ m[i-1] &= 0xffff;
+ }
+ m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1);
+ b = (m[15]>>16) & 1;
+ m[14] &= 0xffff;
+ this.sel25519(t, m, 1-b);
+ }
+ for (i = 0; i < 16; i++) {
+ o[2*i] = t[i] & 0xff;
+ o[2*i+1] = t[i] >> 8;
+ }
+ }
+ S(o, a) {
+ this.M(o, a, a);
+ }
+
+ unpackneg(r, p) {
+ var t = this.gf(), chk = this.gf(), num = this.gf(),
+ den = this.gf(), den2 = this.gf(), den4 = this.gf(),
+ den6 = this.gf();
+
+ this.set25519(r[2], gf1);
+ this.unpack25519(r[1], p);
+ this.S(num, r[1]);
+ this.M(den, num, D);
+ this.Z(num, num, r[2]);
+ this.A(den, r[2], den);
+
+ this.S(den2, den);
+ this.S(den4, den2);
+ this.M(den6, den4, den2);
+ this.M(t, den6, num);
+ this.M(t, t, den);
+
+ this.pow2523(t, t);
+ this.M(t, t, num);
+ this.M(t, t, den);
+ this.M(t, t, den);
+ this.M(r[0], t, den);
+
+ this.S(chk, r[0]);
+ this.M(chk, chk, den);
+ if (this.neq25519(chk, num)) this.M(r[0], r[0], I);
+
+ this.S(chk, r[0]);
+ this.M(chk, chk, den);
+ if (this.neq25519(chk, num)) return -1;
+
+ if (this.par25519(r[0]) === (p[31] >> 7)) this.Z(r[0], gf0, r[0]);
+
+ this.M(r[3], r[0], r[1]);
+ return 0;
+ }
+
+ neq25519(a, b) {
+ var c = new Uint8Array(32), d = new Uint8Array(32);
+ this.pack25519(c, a);
+ this.pack25519(d, b);
+ return this.crypto_verify_32(c, 0, d, 0);
+ }
+ par25519(a) {
+ var d = new Uint8Array(32);
+ this.pack25519(d, a);
+ return d[0] & 1;
+ }
+ unpack25519(o, n) {
+ var i;
+ for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8);
+ o[15] &= 0x7fff;
+ }
+
+ crypto_verify_32(x, xi, y, yi) {
+ return this.vn(x, xi, y, yi, 32);
+ }
+
+ pow2523(o, i) {
+ var c = this.gf();
+ var a;
+ for (a = 0; a < 16; a++) c[a] = i[a];
+ for (a = 250; a >= 0; a--) {
+ this.S(c, c);
+ if (a !== 1) this.M(c, c, i);
+ }
+ for (a = 0; a < 16; a++) o[a] = c[a];
+ }
+
+ vn(x, xi, y, yi, n) {
+ var i, d = 0;
+ for (i = 0; i < n; i++) d |= x[xi + i] ^ y[yi + i];
+ return (1 & ((d - 1) >>> 8)) - 1;
+ }
+ set25519(r, a) {
+ var i;
+ for (i = 0; i < 16; i++) r[i] = a[i] | 0;
+ }
+
+ convertPublicKey(pk) {
+ var z = new Uint8Array(32),
+ q = [this.gf(), this.gf(), this.gf(), this.gf()],
+ a = this.gf(), b = this.gf();
+
+ if (this.unpackneg(q, pk)) return null; // reject invalid key
+
+ var y = q[1];
+
+ this.A(a, gf1, y);
+ this.Z(b, gf1, y);
+ this.inv25519(b, b);
+ this.M(a, a, b);
+
+ this.pack25519(z, a);
+ return z;
+ }
+
+ convertSecretKey(sk) {
+ var d = new Uint8Array(64), o = new Uint8Array(32), i;
+ nacl.lowlevel.crypto_hash(d, sk, 32);
+ d[0] &= 248;
+ d[31] &= 127;
+ d[31] |= 64;
+ for (i = 0; i < 32; i++) o[i] = d[i];
+ for (i = 0; i < 64; i++) d[i] = 0;
+ return o;
+ }
+
+ convertKeyPair(edKeyPair) {
+ var publicKey = this.convertPublicKey(edKeyPair.publicKey);
+ if (!publicKey) return null;
+ return {
+ publicKey: publicKey,
+ secretKey: this.convertSecretKey(edKeyPair.secretKey)
+ };
+ }
+}
+
+
+
+const base58Instant = new Base58();
+
+const curve25519Instance = new Curve25519();
+
+
+self.addEventListener('message', async (e) => {
+
+ try {
+ const decodeMsgs = e.data.messages.map((eachMessage) => {
+ return decodeMessage(
+ eachMessage,
+ e.data.isReceipient,
+ e.data._publicKey,
+ e.data.privateKey
+ );
+ });
+ postMessage(decodeMsgs);
+ } catch (error) {
+ console.log('error', error);
+ postMessage({
+ type: 'error',
+ message: error.message || 'An error occurred',
+ });
+ }
+});
+
+const decode = (string) => {
+ const binaryString = atob(string);
+ const binaryLength = binaryString.length;
+ const bytes = new Uint8Array(binaryLength);
+
+ for (let i = 0; i < binaryLength; i++) {
+ bytes[i] = binaryString.charCodeAt(i);
+ }
+
+ const decoder = new TextDecoder();
+ const decodedString = decoder.decode(bytes);
+ return decodedString;
+};
+
+export const decryptChatMessageBase64 = (
+ encryptedMessage,
+ privateKey,
+ recipientPublicKey,
+ lastReference
+) => {
+
+ let _encryptedMessage = atob(encryptedMessage);
+ const binaryLength = _encryptedMessage.length;
+ const bytes = new Uint8Array(binaryLength);
+
+ for (let i = 0; i < binaryLength; i++) {
+ bytes[i] = _encryptedMessage.charCodeAt(i);
+ }
+
+ let _base58RecipientPublic = recipientPublicKey
+
+ try {
+ _base58RecipientPublic = recipientPublicKey.key
+ } catch (error) {
+ _base58RecipientPublic = recipientPublicKey
+ }
+
+ const _base58RecipientPublicKey =
+ _base58RecipientPublic instanceof Uint8Array
+ ? base58Instant.encode(_base58RecipientPublic)
+ : _base58RecipientPublic;
+ const _recipientPublicKey = base58Instant.decode(_base58RecipientPublicKey);
+ const _lastReference =
+ lastReference instanceof Uint8Array
+ ? lastReference
+ : base58Instant.decode(lastReference);
+
+ const convertedPrivateKey = curve25519Instance.convertSecretKey(privateKey);
+ const convertedPublicKey = curve25519Instance.convertPublicKey(_recipientPublicKey);
+ const sharedSecret = new Uint8Array(32);
+ nacl.lowlevel.crypto_scalarmult(
+ sharedSecret,
+ convertedPrivateKey,
+ convertedPublicKey
+ );
+
+ const _chatEncryptionSeed = new Sha256()
+ .process(sharedSecret)
+ .finish().result;
+ const _decryptedMessage = nacl.secretbox.open(
+ bytes,
+ _lastReference.slice(0, 24),
+ _chatEncryptionSeed
+ );
+
+ if (_decryptedMessage === false) {
+ return _decryptedMessage;
+ }
+ return new TextDecoder('utf-8').decode(_decryptedMessage);
+};
+
+const decodeMessage = (
+ encodedMessageObj,
+ isReceipient,
+ _publicKey,
+ privateKey
+) => {
+ let isReceipientVar;
+ let _publicKeyVar;
+ try {
+ isReceipientVar = isReceipient;
+ _publicKeyVar = _publicKey;
+ } catch (error) {
+ isReceipientVar = isReceipient;
+ _publicKeyVar = _publicKey;
+ }
+
+ let decodedMessageObj = {};
+
+ if (isReceipientVar === true) {
+ // direct chat
+ if (
+ encodedMessageObj.isEncrypted === true &&
+ _publicKeyVar.hasPubKey === true &&
+ encodedMessageObj.data
+ ) {
+ let decodedMessage = decryptChatMessageBase64(
+ encodedMessageObj.data,
+ privateKey,
+ _publicKeyVar,
+ encodedMessageObj.reference
+ );
+ decodedMessageObj = { ...encodedMessageObj, decodedMessage };
+ } else if (
+ encodedMessageObj.isEncrypted === false &&
+ encodedMessageObj.data
+ ) {
+ let decodedMessage = decode(encodedMessageObj.data);
+ decodedMessageObj = { ...encodedMessageObj, decodedMessage };
+ } else {
+ decodedMessageObj = {
+ ...encodedMessageObj,
+ decodedMessage: 'Cannot Decrypt Message!',
+ };
+ }
+ } else {
+ // group chat
+ let decodedMessage = decode(encodedMessageObj.data);
+ decodedMessageObj = { ...encodedMessageObj, decodedMessage };
+ }
+ return decodedMessageObj;
+};
diff --git a/plugins/plugins/core/components/webworkerSortMessages.js b/plugins/plugins/core/components/webworkerSortMessages.js
new file mode 100644
index 00000000..2f290b40
--- /dev/null
+++ b/plugins/plugins/core/components/webworkerSortMessages.js
@@ -0,0 +1,15 @@
+
+
+
+self.addEventListener('message', async e => {
+ const response = e.data.list.sort(function (a, b) {
+ return a.timestamp
+ - b.timestamp
+})
+postMessage(response)
+})
+
+
+
+
+
diff --git a/plugins/plugins/core/group-management/group-management.src.js b/plugins/plugins/core/group-management/group-management.src.js
index bd35cc58..3503aaa8 100644
--- a/plugins/plugins/core/group-management/group-management.src.js
+++ b/plugins/plugins/core/group-management/group-management.src.js
@@ -1743,6 +1743,12 @@ class GroupManagement extends LitElement {
})
return joinedG
}
+ const getGroupInfo = async (groupId) => {
+ let joinedG = await parentEpml.request('apiCall', {
+ url: `/groups/${groupId}`
+ })
+ return joinedG
+ }
const getGroupInvites = async () => {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
@@ -1839,6 +1845,25 @@ class GroupManagement extends LitElement {
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
this.selectedAddress = selectedAddress
})
+ parentEpml.subscribe('side_effect_action', async sideEffectActionParam => {
+ const sideEffectAction = JSON.parse(sideEffectActionParam)
+
+ if(sideEffectAction && sideEffectAction.type === 'openJoinGroupModal'){
+ const res = await getGroupInfo(sideEffectAction.data)
+ if(res && res.groupId){
+ if(res.isOpen){
+ this.joinGroup(res)
+
+ } else {
+ let snackbarstring = get("managegroup.mg45")
+ parentEpml.request('showSnackBar', `${snackbarstring}`)
+ }
+ }
+ window.parent.reduxStore.dispatch(
+ window.parent.reduxAction.setSideEffectAction(null)
+ );
+ }
+ })
parentEpml.subscribe('config', c => {
if (!configLoaded) {
setTimeout(getOpen_JoinedGroups, 1)
@@ -2849,6 +2874,17 @@ class GroupManagement extends LitElement {
}
}
+ setTxNotification(tx){
+ window.parent.reduxStore.dispatch(
+ window.parent.reduxAction.setNewNotification({
+ type: 'JOIN_GROUP',
+ status: 'confirming',
+ reference: tx,
+ timestamp: Date.now()
+ })
+ );
+ }
+
async _joinGroup(groupId, groupName) {
this.resetDefaultSettings()
const joinFeeInput = this.joinFee
@@ -2885,7 +2921,8 @@ class GroupManagement extends LitElement {
lastReference: lastRef,
groupdialog1: groupdialog1,
groupdialog2: groupdialog2
- }
+ },
+ apiVersion: 2
})
return myTxnrequest
}
@@ -2897,6 +2934,12 @@ class GroupManagement extends LitElement {
throw new Error(txnResponse)
} else if (txnResponse.success === true && !txnResponse.data.error) {
this.message = this.renderErr8Text()
+ this.setTxNotification({
+ groupName,
+ groupId,
+ timestamp: Date.now(),
+ ...(txnResponse.data || {})
+ })
this.error = false
} else {
this.error = true
diff --git a/plugins/plugins/core/messaging/chain-messaging/chain-messaging.js b/plugins/plugins/core/messaging/chain-messaging/chain-messaging.js
index ab09e3f6..69d972b6 100644
--- a/plugins/plugins/core/messaging/chain-messaging/chain-messaging.js
+++ b/plugins/plugins/core/messaging/chain-messaging/chain-messaging.js
@@ -1,4 +1,4 @@
-!function(t){"function"==typeof define&&define.amd?define(t):t()}((function(){"use strict";const t=window,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const r=(t,...e)=>{const s=1===t.length?t[0]:e.reduce(((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1]),t[0]);return new n(s,t,i)},o=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t;var l;const h=window,a=h.trustedTypes,d=a?a.emptyScript:"",c=h.reactiveElementPolyfillSupport,u={toAttribute(t,e){switch(e){case Boolean:t=t?d:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},p=(t,e)=>e!==t&&(e==e||t==t),v={attribute:!0,type:String,converter:u,reflect:!1,hasChanged:p};let $=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))})),t}static createProperty(t,e=v){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||v}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(o(t))}else void 0!==t&&e.push(o(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])}))}createRenderRoot(){var i;const s=null!==(i=this.shadowRoot)&&void 0!==i?i:this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{e?i.adoptedStyleSheets=s.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):s.forEach((e=>{const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}))})(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=v){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:u).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:u;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||p)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,e)=>this[e]=t)),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$EO(e,this[e],t))),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}};var _;$.finalized=!0,$.elementProperties=new Map,$.elementStyles=[],$.shadowRootOptions={mode:"open"},null==c||c({ReactiveElement:$}),(null!==(l=h.reactiveElementVersions)&&void 0!==l?l:h.reactiveElementVersions=[]).push("1.6.1");const f=window,m=f.trustedTypes,g=m?m.createPolicy("lit-html",{createHTML:t=>t}):void 0,A="$lit$",y=`lit$${(Math.random()+"").slice(9)}$`,E="?"+y,S=`<${E}>`,b=document,w=()=>b.createComment(""),C=t=>null===t||"object"!=typeof t&&"function"!=typeof t,x=Array.isArray,U="[ \t\n\f\r]",P=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,H=/-->/g,T=/>/g,N=RegExp(`>|${U}(?:([^\\s"'>=/]+)(${U}*=${U}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),k=/'/g,O=/"/g,R=/^(?:script|style|textarea|title)$/i,M=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),L=Symbol.for("lit-noChange"),I=Symbol.for("lit-nothing"),j=new WeakMap,z=b.createTreeWalker(b,129,null,!1),B=(t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==g?g.createHTML(l):l,s]};class D{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,r=0;const o=t.length-1,l=this.parts,[h,a]=B(t,e);if(this.el=D.createElement(h,i),z.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(s=z.nextNode())&&l.length0){s.textContent=m?m.emptyScript:"";for(let i=0;ix(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==I&&C(this._$AH)?this._$AA.nextSibling.data=t:this.$(b.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=D.createElement(s.h,this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new q(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=j.get(t.strings);return void 0===e&&j.set(t.strings,e=new D(t)),e}T(t){x(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new W(this.k(w()),this.k(w()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=I}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=V(this,t,e,0),r=!C(t)||t!==this._$AH&&t!==L,r&&(this._$AH=t);else{const s=t;let o,l;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new W(e.insertBefore(w(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return L}}et.finalized=!0,et._$litElement$=!0,null===(Y=globalThis.litElementHydrateSupport)||void 0===Y||Y.call(globalThis,{LitElement:et});const it=globalThis.litElementPolyfillSupport;null==it||it({LitElement:et}),(null!==(tt=globalThis.litElementVersions)&&void 0!==tt?tt:globalThis.litElementVersions=[]).push("3.3.2");window.customElements.define("chain-messaging",class extends et{static get properties(){return{loading:{type:Boolean},theme:{type:String,reflect:!0}}}static get styles(){return r`
+!function(t){"function"==typeof define&&define.amd?define(t):t()}((function(){"use strict";const t=window,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s=Symbol(),r=new WeakMap;let i=class{constructor(t,e,r){if(this._$cssResult$=!0,r!==s)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const s=this.t;if(e&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=r.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&r.set(s,t))}return t}toString(){return this.cssText}};const n=(t,...e)=>{const r=1===t.length?t[0]:e.reduce(((e,s,r)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+t[r+1]),t[0]);return new i(r,t,s)},o=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const s of t.cssRules)e+=s.cssText;return(t=>new i("string"==typeof t?t:t+"",void 0,s))(e)})(t):t;var a;const l=window,h=l.trustedTypes,c=h?h.emptyScript:"",u=l.reactiveElementPolyfillSupport,d={toAttribute(t,e){switch(e){case Boolean:t=t?c:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let s=t;switch(e){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t)}catch(t){s=null}}return s}},p=(t,e)=>e!==t&&(e==e||t==t),g={attribute:!0,type:String,converter:d,reflect:!1,hasChanged:p},f="finalized";let y=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,s)=>{const r=this._$Ep(s,e);void 0!==r&&(this._$Ev.set(r,s),t.push(r))})),t}static createProperty(t,e=g){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,r=this.getPropertyDescriptor(t,s,e);void 0!==r&&Object.defineProperty(this.prototype,t,r)}}static getPropertyDescriptor(t,e,s){return{get(){return this[e]},set(r){const i=this[t];this[e]=r,this.requestUpdate(t,i,s)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||g}static finalize(){if(this.hasOwnProperty(f))return!1;this[f]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of e)this.createProperty(s,t[s])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const s=new Set(t.flat(1/0).reverse());for(const t of s)e.unshift(o(t))}else void 0!==t&&e.push(o(t));return e}static _$Ep(t,e){const s=e.attribute;return!1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,s;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])}))}createRenderRoot(){var s;const r=null!==(s=this.shadowRoot)&&void 0!==s?s:this.attachShadow(this.constructor.shadowRootOptions);return((s,r)=>{e?s.adoptedStyleSheets=r.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):r.forEach((e=>{const r=document.createElement("style"),i=t.litNonce;void 0!==i&&r.setAttribute("nonce",i),r.textContent=e.cssText,s.appendChild(r)}))})(r,this.constructor.elementStyles),r}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,s){this._$AK(t,s)}_$EO(t,e,s=g){var r;const i=this.constructor._$Ep(t,s);if(void 0!==i&&!0===s.reflect){const n=(void 0!==(null===(r=s.converter)||void 0===r?void 0:r.toAttribute)?s.converter:d).toAttribute(e,s.type);this._$El=t,null==n?this.removeAttribute(i):this.setAttribute(i,n),this._$El=null}}_$AK(t,e){var s;const r=this.constructor,i=r._$Ev.get(t);if(void 0!==i&&this._$El!==i){const t=r.getPropertyOptions(i),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(s=t.converter)||void 0===s?void 0:s.fromAttribute)?t.converter:d;this._$El=i,this[i]=n.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,s){let r=!0;void 0!==t&&(((s=s||this.constructor.getPropertyOptions(t)).hasChanged||p)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===s.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):r=!1),!this.isUpdatePending&&r&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,e)=>this[e]=t)),this._$Ei=void 0);let e=!1;const s=this._$AL;try{e=this.shouldUpdate(s),e?(this.willUpdate(s),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(s)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(s)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$EO(e,this[e],t))),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}};var m;y[f]=!0,y.elementProperties=new Map,y.elementStyles=[],y.shadowRootOptions={mode:"open"},null==u||u({ReactiveElement:y}),(null!==(a=l.reactiveElementVersions)&&void 0!==a?a:l.reactiveElementVersions=[]).push("1.6.3");const _=window,v=_.trustedTypes,$=v?v.createPolicy("lit-html",{createHTML:t=>t}):void 0,w="$lit$",E=`lit$${(Math.random()+"").slice(9)}$`,A="?"+E,b=`<${A}>`,S=document,T=()=>S.createComment(""),M=t=>null===t||"object"!=typeof t&&"function"!=typeof t,C=Array.isArray,R="[ \t\n\f\r]",P=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,x=/-->/g,O=/>/g,N=RegExp(`>|${R}(?:([^\\s"'>=/]+)(${R}*=${R}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),U=/'/g,I=/"/g,D=/^(?:script|style|textarea|title)$/i,k=(t=>(e,...s)=>({_$litType$:t,strings:e,values:s}))(1),H=Symbol.for("lit-noChange"),q=Symbol.for("lit-nothing"),L=new WeakMap,j=S.createTreeWalker(S,129,null,!1);function B(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==$?$.createHTML(e):e}const V=(t,e)=>{const s=t.length-1,r=[];let i,n=2===e?"":"")),r]};class z{constructor({strings:t,_$litType$:e},s){let r;this.parts=[];let i=0,n=0;const o=t.length-1,a=this.parts,[l,h]=V(t,e);if(this.el=z.createElement(l,s),j.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(r=j.nextNode())&&a.length0){r.textContent=v?v.emptyScript:"";for(let s=0;sC(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==q&&M(this._$AH)?this._$AA.nextSibling.data=t:this.$(S.createTextNode(t)),this._$AH=t}g(t){var e;const{values:s,_$litType$:r}=t,i="number"==typeof r?this._$AC(t):(void 0===r.el&&(r.el=z.createElement(B(r.h,r.h[0]),this.options)),r);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===i)this._$AH.v(s);else{const t=new W(i,this),e=t.u(this.options);t.v(s),this.$(e),this._$AH=t}}_$AC(t){let e=L.get(t.strings);return void 0===e&&L.set(t.strings,e=new z(t)),e}T(t){C(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let s,r=0;for(const i of t)r===e.length?e.push(s=new F(this.k(T()),this.k(T()),this,this.options)):s=e[r],s._$AI(i),r++;r2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=q}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,s,r){const i=this.strings;let n=!1;if(void 0===i)t=K(this,t,e,0),n=!M(t)||t!==this._$AH&&t!==H,n&&(this._$AH=t);else{const r=t;let o,a;for(t=i[0],o=0;o{var r,i;const n=null!==(r=null==s?void 0:s.renderBefore)&&void 0!==r?r:e;let o=n._$litPart$;if(void 0===o){const t=null!==(i=null==s?void 0:s.renderBefore)&&void 0!==i?i:null;n._$litPart$=o=new F(e.insertBefore(T(),t),t,void 0,null!=s?s:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return H}}rt.finalized=!0,rt._$litElement$=!0,null===(et=globalThis.litElementHydrateSupport)||void 0===et||et.call(globalThis,{LitElement:rt});const it=globalThis.litElementPolyfillSupport;null==it||it({LitElement:rt}),(null!==(st=globalThis.litElementVersions)&&void 0!==st?st:globalThis.litElementVersions=[]).push("3.3.3");class nt{static prepareOutgoingData(t){return JSON.stringify(t)}constructor(t){if(!t)throw new Error("Source must be spcified");if(!this.constructor.type)throw new Error("Type not defined");if(this.constructor.name||console.warn("No name provided"),this.constructor.description||console.warn("No description provided"),!this.sendMessage)throw new Error("A new target requires a sendMessage method")}}const ot={},at={};class lt{static registerPlugin(t,e){return t.init(lt,e),lt}static registerTargetType(t,e){if(t in at)throw new Error("Target type has already been registered");if(!(e.prototype instanceof nt))throw new Error("Target constructors must inherit from the Target base class");return at[t]=e,lt}static registerEpmlMessageType(t,e){return ot[t]=e,lt}registerPlugin(t){return t.init(this),this}static handleMessage(t,e){const s=lt.prepareIncomingData(t);"EpmlMessageType"in s&&ot[s.EpmlMessageType](s,e,this)}static prepareIncomingData(t){return"string"!=typeof t?t:JSON.parse(t)}static createTargets(t){Array.isArray(t)||(t=[t]);const e=[];for(const s of t)void 0===s.allowObjects&&(s.allowObjects=!1),e.push(...lt.createTarget(s));return e}static createTarget(t){if(!at[t.type])throw new Error(`Target type '${t.type}' not registered`);let e=new at[t.type](t.source);Array.isArray(e)||(e=[e]);for(const s of e)s.allowObjects=t.allowObjects;return e}constructor(t){this.targets=this.constructor.createTargets(t)}}var ht=(t,e)=>{for(e=t="";t++<36;e+=51*t&52?(15^t?8^Math.random()*(20^t?16:4):4).toString(16):"-");return e};const ct=15,ut="EPML_READY_STATE_CHECK",dt="EPML_READY_STATE_CHECK_RESPONSE",pt={},gt={init:(t,e)=>{if(t.prototype.ready)throw new Error("Epml.prototype.ready is already defined");if(t.prototype.imReady)throw new Error("Epml.prototype.imReady is already defined");t.prototype.ready=mt,t.prototype.resetReadyCheck=_t,t.prototype.imReady=yt,t.registerEpmlMessageType(ut,ft),t.registerEpmlMessageType(dt,$t)}};function ft(t,e){e._i_am_ready&&e.sendMessage({EpmlMessageType:dt,requestID:t.requestID})}function yt(){for(const t of this.targets)t._i_am_ready=!0}function mt(){return this._ready_plugin=this._ready_plugin||{},this._ready_plugin.pendingReadyResolves=this._ready_plugin.pendingReadyResolves?this._ready_plugin.pendingReadyResolves:[],this._pending_ready_checking||(this._pending_ready_checking=!0,vt.call(this,this.targets).then((()=>{this._ready_plugin.pendingReadyResolves.forEach((t=>t()))}))),new Promise((t=>{this._ready_plugin.isReady?t():this._ready_plugin.pendingReadyResolves.push(t)}))}function _t(){this._ready_plugin=this._ready_plugin||{},this._ready_plugin.isReady=!1}function vt(t){return this._ready_plugin=this._ready_plugin||{},this._ready_plugin.pendingReadyResolves=[],Promise.all(t.map((t=>new Promise(((e,s)=>{const r=ht(),i=setInterval((()=>{t.sendMessage({EpmlMessageType:ut,requestID:r})}),ct);pt[r]=()=>{clearInterval(i),e()}}))))).then((()=>{this._ready_plugin.isReady=!0}))}function $t(t,e){e._ready_plugin=e._ready_plugin||{},e._ready_plugin._is_ready=!0,pt[t.requestID]()}const wt=new Map;class Et extends nt{static get sources(){return Array.from(wt.keys())}static get targets(){return Array.from(wt.values())}static getTargetFromSource(t){return wt.get(t)}static hasTarget(t){return wt.has(t)}static get type(){return"WINDOW"}static get name(){return"Content window plugin"}static get description(){return"Allows Epml to communicate with iframes and popup windows."}static test(t){return"object"==typeof t&&t===t.self}isFrom(t){}constructor(t){if(super(t),wt.has(t))return wt.get(t);if(!this.constructor.test(t))throw new Error("Source can not be used with target");this._source=t,this._sourceOrigin="*",wt.set(t,this)}get source(){return this._source}sendMessage(t){t=nt.prepareOutgoingData(t),this._source.postMessage(t,this._sourceOrigin)}}var At={init:function(t){!function(t,e,s){if(t.addEventListener)t.addEventListener(e,s,!1);else{if(!t.attachEvent)throw new Error("Could not bind event.");t.attachEvent("on"+e,s)}}(window,"message",(e=>{Et.hasTarget(e.source)&&t.handleMessage(e.data,Et.getTargetFromSource(e.source))})),t.registerTargetType(Et.type,Et)}};const bt="REQUEST",St="REQUEST_RESPONSE",Tt=new Map,Mt={},Ct={init:(t,e)=>{if(t.prototype.request)throw new Error("Epml.prototype.request is already defined");if(t.prototype.route)throw new Error("Empl.prototype.route is already defined");t.prototype.request=Rt,t.prototype.route=Ot,t.registerEpmlMessageType(bt,xt),t.registerEpmlMessageType(St,Pt)}},Rt=function(t,e,s){return Promise.all(this.targets.map((r=>{const i=ht(),n={EpmlMessageType:bt,requestOrResponse:"request",requestID:i,requestType:t,data:e};return r.sendMessage(n),new Promise(((t,e)=>{let r;s&&(r=setTimeout((()=>{delete Mt[i],e(new Error("Request timed out"))}),s)),Mt[i]=(...e)=>{r&&clearTimeout(r),t(...e)}}))}))).then((t=>{if(1===this.targets.length)return t[0]}))};function Pt(t,e,s){if(t.requestID in Mt){const e=t.data;Mt[t.requestID](e)}else console.warn("requestID not found in pendingRequests")}function xt(t,e){if(!Tt.has(e))return void console.warn("Route does not exist - missing target");const s=Tt.get(e)[t.requestType];s?s(t,e):console.warn("Route does not exist")}function Ot(t,e){if(this.routes||(this.routes={}),!this.routes[t])for(const s of this.targets){Tt.has(s)||Tt.set(s,{});Tt.get(s)[t]=(t,s)=>{Promise.resolve(e(t)).catch((t=>t instanceof Error?t.message:t)).then((e=>{s.sendMessage({data:e,EpmlMessageType:St,requestOrResponse:"request",requestID:t.requestID})}))}}}const Nt="PROXY_MESSAGE",Ut=new class{constructor(t){this._map=t||new Map,this._revMap=new Map,this._map.forEach(((t,e)=>{this._revMap.set(e,t)}))}values(){return this._map.values()}entries(){return this._map.entries()}push(t,e){this._map.set(t,e),this._revMap.set(e,t)}getByKey(t){return this._map.get(t)}getByValue(t){return this._revMap.get(t)}hasKey(t){return this._map.has(t)}hasValue(t){return this._revMap.has(t)}deleteByKey(t){const e=this._map.get(t);this._map.delete(t),this._revMap.delete(e)}deleteByValue(t){const e=this._revMap.get(t);this._map.delete(e),this._revMap.delete(t)}};class It extends nt{static get proxySources(){return Ut}static get sources(){for(const[t,e]of Ut)for(const[t]of e);Array.from(Ut.entries()).map(((t,e)=>({proxy:t,target:Array.from(e.keys())[0]})))}static get targets(){return Array.from(Ut.values())}static getTargetFromSource(t){return Ut.getByValue(t)}static hasTarget(t){return Ut.hasValue(t)}static get type(){return"PROXY"}static get name(){return"Proxy target"}static get description(){return"Uses other target, and proxies requests, allowing things like iframes to communicate through their host"}static test(t){return"object"==typeof t&&t.proxy instanceof this.Epml}isFrom(t){}constructor(t){if(super(t),this.constructor.proxySources.push(t.id,this),!this.constructor.test(t))throw new Error("Source can not be used with target");this._source=t}get source(){return this._source}sendMessage(t){const e=ht();t=nt.prepareOutgoingData(t),t={EpmlMessageType:Nt,state:"TRANSIT",requestID:e,target:this._source.target,message:t,id:this._source.id},this._source.proxy.targets[0].sendMessage(t)}}const Dt=It.proxySources;let kt;var Ht={init:function(t){Object.defineProperty(It,"Epml",{get:()=>t}),kt=t,t.registerTargetType(It.type,It),t.registerProxyInstance=Lt,t.registerEpmlMessageType(Nt,qt)}};function qt(t,e){if("TRANSIT"===t.state){const e=Dt.getByKey(t.target);if(!e)return void console.warn(`Target ${t.target} not registered.`);t.state="DELIVERY",e.targets.forEach((e=>e.sendMessage(t)))}else if("DELIVERY"===t.state){if(!Dt.getByKey(t.target))return void console.warn(`Target ${t.target} not registered.`);const e=Dt.getByKey(t.target);kt.handleMessage(t.message,e)}}function Lt(t,e){Dt.hasKey(t)&&console.warn(`${t} is already defined. Overwriting...`),Dt.push(t,e)}const jt="STREAM_UPDATE",Bt={};class Vt{static get streams(){return Bt}constructor(t,e=(()=>{})){if(this._name=t,this.targets=[],this._subscriptionFn=e,t in Bt)return console.warn(`Stream with name ${t} already exists! Returning it instead`),Bt[t];Bt[t]=this}async subscribe(t){t in this.targets&&console.info("Target is already subscribed to this stream");const e=await this._subscriptionFn();this._sendMessage(e,t),this.targets.push(t)}_sendMessage(t,e){e.sendMessage({data:nt.prepareOutgoingData(t),EpmlMessageType:jt,streamName:this._name})}emit(t){this.targets.forEach((e=>this._sendMessage(t,e)))}}const zt="JOIN_STREAM",Kt={},Wt={init:(t,e)=>{if(t.prototype.subscribe)throw new Error("Epml.prototype.subscribe is already defined");if(t.prototype.createStream)throw new Error("Empl.prototype.createStream is already defined");t.prototype.subscribe=Yt,t.registerEpmlMessageType(zt,Ft),t.registerEpmlMessageType(jt,Jt)}},Ft=function(t,e){const s=t.data.name,r=Vt.streams[s];r?r.subscribe(e):console.warn(`No stream with name ${s}`,this)},Yt=function(t,e){this.targets.forEach((e=>{e.sendMessage({EpmlMessageType:zt,data:{name:t}})})),Kt[t]=Kt[t]||[],Kt[t].push(e)},Jt=function(t,e){Kt[t.streamName].forEach((e=>e(t.data)))};lt.registerPlugin(Ct),lt.registerPlugin(gt),lt.registerPlugin(At),lt.registerPlugin(Wt),lt.registerPlugin(Ht),lt.allowProxying=!0;var Qt="undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{};function Xt(){throw new Error("setTimeout has not been defined")}function Zt(){throw new Error("clearTimeout has not been defined")}var Gt=Xt,te=Zt;function ee(t){if(Gt===setTimeout)return setTimeout(t,0);if((Gt===Xt||!Gt)&&setTimeout)return Gt=setTimeout,setTimeout(t,0);try{return Gt(t,0)}catch(e){try{return Gt.call(null,t,0)}catch(e){return Gt.call(this,t,0)}}}"function"==typeof Qt.setTimeout&&(Gt=setTimeout),"function"==typeof Qt.clearTimeout&&(te=clearTimeout);var se,re=[],ie=!1,ne=-1;function oe(){ie&&se&&(ie=!1,se.length?re=se.concat(re):ne=-1,re.length&&ae())}function ae(){if(!ie){var t=ee(oe);ie=!0;for(var e=re.length;e;){for(se=re,re=[];++ne1)for(var s=1;s=0)}));new lt({type:"WINDOW",source:window.parent});window.customElements.define("chain-messaging",class extends rt{static get properties(){return{loading:{type:Boolean},theme:{type:String,reflect:!0}}}static get styles(){return n`
* {
--mdc-theme-primary: rgb(3, 169, 244);
--paper-input-container-focus-color: var(--mdc-theme-primary);
@@ -15,8 +15,8 @@
background: var(--white);
}
- `}constructor(){super(),this.theme=localStorage.getItem("qortalTheme")?localStorage.getItem("qortalTheme"):"light"}render(){return M`
+ `}constructor(){super(),this.theme=localStorage.getItem("qortalTheme")?localStorage.getItem("qortalTheme"):"light"}render(){return k`
Coming Soon!
- `}firstUpdated(){this.changeTheme(),setInterval((()=>{this.changeTheme()}),100),window.addEventListener("contextmenu",(t=>{t.preventDefault()})),window.addEventListener("click",(()=>{})),window.onkeyup=t=>{t.keyCode}}changeTheme(){const t=localStorage.getItem("qortalTheme");this.theme="dark"===t?"dark":"light",document.querySelector("html").setAttribute("theme",this.theme)}isEmptyArray(t){return!t||0===t.length}})}));
+ `}firstUpdated(){this.changeTheme(),window.addEventListener("storage",(()=>{const t=localStorage.getItem("qortalTheme");this.theme="dark"===t?"dark":"light",document.querySelector("html").setAttribute("theme",this.theme)})),Ee()&&window.addEventListener("contextmenu",(t=>{t.preventDefault(),window.parent.electronAPI.showMyMenu()}))}clearConsole(){Ee()&&(console.clear(),window.parent.electronAPI.clearCache())}changeTheme(){const t=localStorage.getItem("qortalTheme");this.theme="dark"===t?"dark":"light",document.querySelector("html").setAttribute("theme",this.theme)}isEmptyArray(t){return!t||0===t.length}})}));
diff --git a/plugins/plugins/core/messaging/q-chat/q-chat-css.src.js b/plugins/plugins/core/messaging/q-chat/q-chat-css.src.js
index eb4f7ca3..80b11a08 100644
--- a/plugins/plugins/core/messaging/q-chat/q-chat-css.src.js
+++ b/plugins/plugins/core/messaging/q-chat/q-chat-css.src.js
@@ -157,6 +157,10 @@ export const qchatStyles = css`
padding-top: 20px;
padding-left: 20px;
padding-right: 20px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ justify-content: space-between;
}
.center {
@@ -172,13 +176,13 @@ export const qchatStyles = css`
border-radius: 5px;
border: none;
display: inline-block;
- padding: 14px;
color: #fff;
- background: var(--tradehead);
width: 100%;
font-size: 15px;
text-align: center;
cursor: pointer;
+ display: flex;
+ flex: 0;
}
.people-list .create-chat:hover {
diff --git a/plugins/plugins/core/messaging/q-chat/q-chat.src.js b/plugins/plugins/core/messaging/q-chat/q-chat.src.js
index 838f42d2..f73558c4 100644
--- a/plugins/plugins/core/messaging/q-chat/q-chat.src.js
+++ b/plugins/plugins/core/messaging/q-chat/q-chat.src.js
@@ -1,10 +1,9 @@
-import { LitElement, html, css } from 'lit'
+import { LitElement, html } from 'lit'
import { render } from 'lit/html.js'
import { passiveSupport } from 'passive-events-support/src/utils'
import { Epml } from '../../../../epml.js'
-import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
+import { get, translate } from 'lit-translate'
import { qchatStyles } from './q-chat-css.src.js'
-import { repeat } from 'lit/directives/repeat.js'
import { Editor, Extension } from '@tiptap/core'
import isElectron from 'is-electron'
import WebWorker from 'web-worker:./computePowWorker.src.js'
@@ -13,19 +12,22 @@ import Underline from '@tiptap/extension-underline';
import Placeholder from '@tiptap/extension-placeholder'
import Highlight from '@tiptap/extension-highlight'
import snackbar from '../../components/snackbar.js'
+import ShortUniqueId from 'short-unique-id';
import '../../components/ChatWelcomePage.js'
import '../../components/ChatHead.js'
import '../../components/ChatPage.js'
import '../../components/WrapperModal.js'
import '../../components/ChatSearchResults.js'
-
+import '../../components/ChatGroupsModal.js'
import '@material/mwc-button'
import '@material/mwc-dialog'
import '@material/mwc-icon'
import '@material/mwc-snackbar'
import '@polymer/paper-spinner/paper-spinner-lite.js'
import '@vaadin/grid'
+import '@vaadin/tooltip';
+
passiveSupport({ events: ['touchstart'] })
@@ -53,15 +55,20 @@ class Chat extends LitElement {
userFoundModalOpen: { type: Boolean },
userSelected: { type: Object },
editor: {type: Object},
- groupInvites: { type: Array }
+ groupInvites: { type: Array },
+ loggedInUserName: {type: String},
+ loggedInUserAddress: {type: String},
+ openDialogGroupsModal: {type: Boolean}
}
}
- static styles = [qchatStyles]
+ static get styles() {
+ return [qchatStyles];
+ }
constructor() {
super()
- this.selectedAddress = {}
+ this.selectedAddress = window.parent.reduxStore.getState().app.selectedAddress
this.config = {
user: {
node: {
@@ -92,12 +99,15 @@ class Chat extends LitElement {
this.userFoundModalOpen = false
this.userSelected = {}
this.groupInvites = []
+ this.loggedInUserName = ""
+ this.openDialogGroupsModal = false
+ this.uid = new ShortUniqueId();
+
}
async setActiveChatHeadUrl(url) {
- this.activeChatHeadUrl = ''
- await this.updateComplete;
this.activeChatHeadUrl = url
+ this.requestUpdate()
}
resetChatEditor(){
@@ -146,6 +156,8 @@ class Chat extends LitElement {
this.unsubscribeStore = window.parent.reduxStore.subscribe(() => {
try {
+ const currentState = window.parent.reduxStore.getState();
+
if(window.parent.location && window.parent.location.search) {
const queryString = window.parent.location.search
const params = new URLSearchParams(queryString)
@@ -157,8 +169,13 @@ class Chat extends LitElement {
this.setActiveChatHeadUrl(chat)
}
}
- } catch (error) {
- }
+ if(currentState.app.accountInfo && currentState.app.accountInfo.names && currentState.app.accountInfo.names.length > 0 && this.loggedInUserName !== currentState.app.accountInfo.names[0].name){
+ this.loggedInUserName = currentState.app.accountInfo.names[0].name
+ }
+ if(currentState.app.accountInfo && currentState.app.accountInfo.addressInfo && currentState.app.accountInfo.addressInfo.address && this.loggedInUserAddress !== currentState.app.accountInfo.addressInfo.address){
+ this.loggedInUserAddress = currentState.app.accountInfo.addressInfo.address
+ }
+ } catch (error) { /* empty */ }
})
}
@@ -178,44 +195,74 @@ class Chat extends LitElement {
})
}
+ setOpenDialogGroupsModal(val){
+ this.openDialogGroupsModal = val
+ }
+
+ openTabToGroupManagement(){
+ window.parent.reduxStore.dispatch(
+ window.parent.reduxAction.setNewTab({
+ url: `group-management`,
+ id: this.uid.rnd(),
+ myPlugObj: {
+ "url": "group-management",
+ "domain": "core",
+ "page": "group-management/index.html",
+ "title": "Group Management",
+ "icon": "vaadin:group",
+ "mwcicon": "group",
+ "pluginNumber": "plugin-fJZNpyLGTl",
+ "menus": [],
+ "parent": false
+ },
+ openExisting: true
+ })
+ );
+ }
+
render() {
return html`
-
{ this.openPrivateMessage = true }}>
- ${translate("chatpage.cchange1")}
+
{ this.openPrivateMessage = true }}>
+ edit_square
+
+
+
+
+
+
{ this.openTabToGroupManagement() }}>
+ group_add
+
+
+
+
{ this.shadowRoot.querySelector('#blockedUserDialog').show() }}>
+ person_off
+
+
+
${this.isEmptyArray(this.chatHeads) ? this.renderLoadingText() : this.renderChatHead(this.chatHeads)}
-
-
- this.shadowRoot.querySelector('#blockedUserDialog').show()}>
-
-
+
this.scrollToBottom()}>
@@ -370,7 +417,7 @@ class Chat extends LitElement {
-
+
`
}
@@ -432,23 +479,32 @@ class Chat extends LitElement {
document.querySelector('html').setAttribute('theme', this.theme)
})
- if (!isElectron()) {
- } else {
+ if (!isElectron()) { /* empty */ } else {
window.addEventListener('contextmenu', (event) => {
- event.preventDefault()
- window.parent.electronAPI.showMyMenu()
- })
+ // Check if the clicked element has the class
+ let target = event.target;
+ while (target !== null) {
+ if (target.classList && target.classList.contains('customContextMenuDiv')) {
+ // Your custom context menu logic
+ this.showContextMenu(event);
+ return;
+ }
+ target = target.parentNode;
+ }
+
+ // If it doesn't, show the default Electron context menu
+ event.preventDefault();
+ window.parent.electronAPI.showMyMenu();
+ });
+
+
+
}
let configLoaded = false
parentEpml.ready().then(() => {
- parentEpml.subscribe('selected_address', async selectedAddress => {
- this.selectedAddress = {}
- selectedAddress = JSON.parse(selectedAddress)
- if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
- this.selectedAddress = selectedAddress
- })
+
parentEpml.subscribe('config', c => {
if (!configLoaded) {
setTimeout(getBlockedUsers, 1)
@@ -464,6 +520,7 @@ class Chat extends LitElement {
url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}`
}).then(res => {
this.balance = res
+ this.requestUpdate()
})
})
parentEpml.imReady()
@@ -473,9 +530,10 @@ class Chat extends LitElement {
}, 60000)
}
+
+
clearConsole() {
- if (!isElectron()) {
- } else {
+ if (!isElectron()) { /* empty */ } else {
console.clear()
window.parent.electronAPI.clearCache()
}
@@ -565,7 +623,7 @@ class Chat extends LitElement {
recipient = _recipient
} else {
recipient = myNameRes.owner
- };
+ }
const getAddressPublicKey = async () => {
let isEncrypted;
@@ -620,7 +678,7 @@ class Chat extends LitElement {
const worker = new WebWorker()
let nonce = null
let chatBytesArray = null;
- await new Promise((res, rej) => {
+ await new Promise((res) => {
worker.postMessage({chatBytes, path, difficulty})
worker.onmessage = e => {
worker.terminate()
@@ -669,7 +727,7 @@ class Chat extends LitElement {
}
renderLoadingText() {
- return html`${translate("chatpage.cchange2")}`
+ return html`
`
}
renderSendText() {
@@ -833,6 +891,9 @@ class Chat extends LitElement {
chatId=${this.activeChatHeadUrl}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
.setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}
+ balance=${this.balance}
+ loggedInUserName=${this.loggedInUserName}
+ loggedInUserAddress=${this.loggedInUserAddress}
>
`
@@ -897,7 +958,15 @@ class Chat extends LitElement {
scrollToBottom() {
const viewElement = this.shadowRoot.querySelector('chat-page').shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('viewElement')
- viewElement.scroll({ top: viewElement.scrollHeight, left: 0, behavior: 'smooth' })
+
+ const chatScrollerElement = this.shadowRoot.querySelector('chat-page').shadowRoot.querySelector('chat-scroller')
+ if (chatScrollerElement && chatScrollerElement.disableAddingNewMessages) {
+ const chatPageElement = this.shadowRoot.querySelector('chat-page')
+ if(chatPageElement && chatPageElement.getLastestMessages)
+ chatPageElement.getLastestMessages()
+ } else {
+ viewElement.scroll({ top: viewElement.scrollHeight, left: 0, behavior: 'smooth' })
+ }
}
showNewMessageBar() {
diff --git a/plugins/plugins/core/name-registration/name-registration.src.js b/plugins/plugins/core/name-registration/name-registration.src.js
index fd7d80b4..ecd51d15 100644
--- a/plugins/plugins/core/name-registration/name-registration.src.js
+++ b/plugins/plugins/core/name-registration/name-registration.src.js
@@ -21,6 +21,7 @@ import '@vaadin/icon'
import '@vaadin/icons'
import '@vaadin/grid'
import '@vaadin/text-field'
+import { warningModal } from '../../utils/warning-modal.js'
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
@@ -753,7 +754,16 @@ class NameRegistration extends LitElement {
return html`
this.openUpdateNameDialog(nameObj)}>update ${translate("publishpage.pchange2")} ${translate("login.name")}`
}
- openUpdateNameDialog(nameObj) {
+ async openUpdateNameDialog(nameObj) {
+ const res = await warningModal.showModalAndWaitPublish(
+ {
+ message: get('registernamepage.nchange48')
+ }
+ );
+ if (res.action !== 'accept'){
+ this.closeUpdateNameDialog()
+ return
+ }
this.toUpdateName = ''
this.shadowRoot.getElementById("oldNameInput").value = ''
this.shadowRoot.getElementById("newNameInput").value = ''
diff --git a/plugins/plugins/core/q-app/q-apps.src.js b/plugins/plugins/core/q-app/q-apps.src.js
index 37b502e2..7a2b0792 100644
--- a/plugins/plugins/core/q-app/q-apps.src.js
+++ b/plugins/plugins/core/q-app/q-apps.src.js
@@ -4,6 +4,7 @@ import { Epml } from '../../../epml.js'
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
import { columnBodyRenderer, gridRowDetailsRenderer } from '@vaadin/grid/lit.js'
import isElectron from 'is-electron'
+import '@polymer/paper-spinner/paper-spinner-lite.js'
registerTranslateConfig({
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
@@ -42,7 +43,9 @@ class QApps extends LitElement {
blockedResources: { type: Array },
textStatus: { type: String },
textProgress: { type: String },
- theme: { type: String, reflect: true }
+ theme: { type: String, reflect: true },
+ hasInitiallyFetched: {type:Boolean},
+ isLoading: {type: Boolean}
}
}
@@ -369,6 +372,7 @@ class QApps extends LitElement {
this.blockedNames = []
this.relayMode = null
this.isLoading = false
+ this.hasInitiallyFetched = false
this.btnDisabled = false
this.searchName = ''
this.searchResources = []
@@ -421,13 +425,14 @@ class QApps extends LitElement {
- ${this.pageRes == null ? html`
- Loading...
+ ${this.isLoading ? html`
+
` : ''}
- ${this.isEmptyArray(this.pageRes) ? html`
+ ${this.isEmptyArray(this.pageRes) && this.hasInitiallyFetched ? html`
${translate("appspage.schange10")}
` : ''}
${this.renderRelayModeText()}
+
@@ -738,11 +743,16 @@ class QApps extends LitElement {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
let jsonOffsetUrl = `${nodeUrl}/arbitrary/resources?service=APP&default=true&limit=20&offset=${offset}&reverse=false&includestatus=true&includemetadata=true&excludeblocked=true`
-
+ this.isLoading = true
+
const jsonOffsetRes = await fetch(jsonOffsetUrl)
const jsonOffsetData = await jsonOffsetRes.json()
this.pageRes = jsonOffsetData
+ if(!this.hasInitiallyFetched){
+ this.hasInitiallyFetched = true
+ }
+ this.isLoading = false
}
async updateItemsFromPage(page) {
diff --git a/plugins/plugins/core/qdn/publish/publish.src.js b/plugins/plugins/core/qdn/publish/publish.src.js
index 37710534..fd720190 100644
--- a/plugins/plugins/core/qdn/publish/publish.src.js
+++ b/plugins/plugins/core/qdn/publish/publish.src.js
@@ -276,7 +276,7 @@ class PublishData extends LitElement {
${this.categories.map((c, index) => html`
- ${c.name}
+ ${c.name}
`)}
@@ -300,7 +300,9 @@ class PublishData extends LitElement {
${this.successMessage}
${this.loading ? html`
` : ''}
-
this.shadowRoot.querySelector('#publishWithFeeDialog').close()}> ${translate("appspage.schange40")}
+
{
+ this.doPublish(e, true, false)}
+ }> ${translate("appspage.schange40")}
{
this.doPublish(e, false, true)
}}> ${translate("publishpage.pchange11")}
diff --git a/plugins/plugins/utils/id-generation.js b/plugins/plugins/utils/id-generation.js
new file mode 100644
index 00000000..88323156
--- /dev/null
+++ b/plugins/plugins/utils/id-generation.js
@@ -0,0 +1,14 @@
+export function simpleHash(str) {
+ let hash = 0;
+ for (let i = 0; i < str.length; i++) {
+ hash = (hash << 5) - hash + str.charCodeAt(i);
+ hash = hash & hash; // Convert to 32bit integer
+ }
+ return hash.toString();
+}
+
+export function generateIdFromAddresses(address1, address2) {
+ // Sort addresses lexicographically and concatenate
+ const sortedAddresses = [address1, address2].sort().join('');
+ return simpleHash(sortedAddresses);
+}
\ No newline at end of file
diff --git a/plugins/plugins/utils/queue.js b/plugins/plugins/utils/queue.js
new file mode 100644
index 00000000..00a235e6
--- /dev/null
+++ b/plugins/plugins/utils/queue.js
@@ -0,0 +1,71 @@
+export class RequestQueue {
+ constructor(maxConcurrent = 5) {
+ this.queue = [];
+ this.maxConcurrent = maxConcurrent;
+ this.currentConcurrent = 0;
+ }
+
+ push(request) {
+ return new Promise((resolve, reject) => {
+ this.queue.push({
+ request,
+ resolve,
+ reject,
+ });
+ this.checkQueue();
+ });
+ }
+
+ checkQueue() {
+ if (this.queue.length === 0 || this.currentConcurrent >= this.maxConcurrent) return;
+
+ const { request, resolve, reject } = this.queue.shift();
+ this.currentConcurrent++;
+
+ request()
+ .then(resolve)
+ .catch(reject)
+ .finally(() => {
+ this.currentConcurrent--;
+ this.checkQueue();
+ });
+ }
+}
+
+export class RequestQueueWithPromise {
+ constructor(maxConcurrent = 5) {
+ this.queue = [];
+ this.maxConcurrent = maxConcurrent;
+ this.currentlyProcessing = 0;
+ }
+
+ // Add a request to the queue and return a promise
+ enqueue(request) {
+ return new Promise((resolve, reject) => {
+ // Push the request and its resolve and reject callbacks to the queue
+ this.queue.push({ request, resolve, reject });
+ this.process();
+ });
+ }
+
+ // Process requests in the queue
+ async process() {
+ while (this.queue.length > 0 && this.currentlyProcessing < this.maxConcurrent) {
+ this.currentlyProcessing++;
+ const { request, resolve, reject } = this.queue.shift();
+ try {
+ const response = await request();
+ resolve(response);
+ } catch (error) {
+ reject(error);
+ } finally {
+ this.currentlyProcessing--;
+ this.process();
+ }
+ }
+ }
+ }
+
+
+
+
diff --git a/plugins/plugins/utils/replace-messages-edited.js b/plugins/plugins/utils/replace-messages-edited.js
index ad815fe8..129a4a8b 100644
--- a/plugins/plugins/utils/replace-messages-edited.js
+++ b/plugins/plugins/utils/replace-messages-edited.js
@@ -1,117 +1,251 @@
-export const replaceMessagesEdited = async ({
- decodedMessages,
- parentEpml,
- isReceipient,
- decodeMessageFunc,
- _publicKey
-}) => {
- const findNewMessages = decodedMessages.map(async (msg) => {
- let msgItem = msg
- try {
- let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`
- if (!isReceipient) {
- msgQuery = `&txGroupId=${msg.txGroupId}`
- }
- const response = await parentEpml.request("apiCall", {
- type: "api",
- url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}&encoding=BASE64`,
- })
+// export const replaceMessagesEdited = async ({
+// decodedMessages,
+// parentEpml,
+// isReceipient,
+// decodeMessageFunc,
+// _publicKey,
+// addToUpdateMessageHashmap
+// }) => {
+// const findNewMessages = decodedMessages.map(async (msg) => {
+// let msgItem = null
+// try {
+// let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`
+// if (!isReceipient) {
+// msgQuery = `&txGroupId=${msg.txGroupId}`
+// }
+// const response = await parentEpml.request("apiCall", {
+// type: "api",
+// url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}&encoding=BASE64`,
+// })
- if (response && Array.isArray(response) && response.length !== 0) {
- let responseItem = { ...response[0] }
- const decodeResponseItem = decodeMessageFunc(responseItem, isReceipient, _publicKey)
- delete decodeResponseItem.timestamp
+// if (response && Array.isArray(response) && response.length !== 0) {
+// let responseItem = { ...response[0] }
+// const decodeResponseItem = decodeMessageFunc(responseItem, isReceipient, _publicKey)
+// delete decodeResponseItem.timestamp
- msgItem = {
- ...msg,
- ...decodeResponseItem,
- senderName: msg.senderName,
- sender: msg.sender,
- editedTimestamp: response[0].timestamp,
- }
- }
- } catch (error) {
- }
+// msgItem = {
+// ...msg,
+// ...decodeResponseItem,
+// senderName: msg.senderName,
+// sender: msg.sender,
+// editedTimestamp: response[0].timestamp,
+// originalSignature: msg.signature
+// }
+// }
+// } catch (error) {
+// }
- return msgItem
- })
- const updateMessages = await Promise.all(findNewMessages)
- const findNewMessages2 = updateMessages.map(async (msg) => {
- let parsedMessageObj = msg
- try {
- parsedMessageObj = JSON.parse(msg.decodedMessage)
- } catch (error) {
- return msg
- }
- let msgItem = msg
- try {
- let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`
- if (!isReceipient) {
- msgQuery = `&txGroupId=${msg.txGroupId}`
- }
- if (parsedMessageObj.repliedTo) {
- let originalReply
- if(+parsedMessageObj.version > 2){
- originalReply = await parentEpml.request("apiCall", {
- type: "api",
- url: `/chat/message/${parsedMessageObj.repliedTo}?encoding=BASE64`,
- })
- }
- if(+parsedMessageObj.version < 3){
- originalReply = await parentEpml.request("apiCall", {
- type: "api",
- url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&encoding=BASE64`,
- })
- }
+// return msgItem
+// })
+// const updateMessages = await Promise.all(findNewMessages)
+// const filterOutNull = updateMessages.filter((item)=> item !== 'null' && item !== null)
+
+// const findNewMessages2 = filterOutNull.map(async (msg) => {
+// let parsedMessageObj = msg
+// try {
+// parsedMessageObj = JSON.parse(msg.decodedMessage)
+// } catch (error) {
+// return msg
+// }
+// let msgItem = msg
+// try {
+// let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`
+// if (!isReceipient) {
+// msgQuery = `&txGroupId=${msg.txGroupId}`
+// }
+// if (parsedMessageObj.repliedTo) {
+// let originalReply
+// if(+parsedMessageObj.version > 2){
+// originalReply = await parentEpml.request("apiCall", {
+// type: "api",
+// url: `/chat/message/${parsedMessageObj.repliedTo}?encoding=BASE64`,
+// })
+// }
+// if(+parsedMessageObj.version < 3){
+// originalReply = await parentEpml.request("apiCall", {
+// type: "api",
+// url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&encoding=BASE64`,
+// })
+// }
- const originalReplyMessage = originalReply.timestamp ? originalReply : originalReply.length !== 0 ? originalReply[0] : null
+// const originalReplyMessage = originalReply.timestamp ? originalReply : originalReply.length !== 0 ? originalReply[0] : null
- const response = await parentEpml.request("apiCall", {
+// const response = await parentEpml.request("apiCall", {
+// type: "api",
+// url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}&encoding=BASE64`,
+// })
+
+// if (
+// originalReplyMessage &&
+// response &&
+// Array.isArray(response) &&
+// response.length !== 0
+// ) {
+// const decodeOriginalReply = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey)
+
+// const decodeUpdatedReply = decodeMessageFunc(response[0], isReceipient, _publicKey)
+// const formattedRepliedToData = {
+// ...decodeUpdatedReply,
+// senderName: decodeOriginalReply.senderName,
+// sender: decodeOriginalReply.sender,
+// }
+// msgItem = {
+// ...msg,
+// repliedToData: formattedRepliedToData,
+// }
+// } else {
+
+
+// if (
+// originalReplyMessage
+// ) {
+
+// msgItem = {
+// ...msg,
+// repliedToData: decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey),
+// }
+// }
+// }
+// }
+// } catch (error) {
+// }
+
+// return msgItem
+// })
+// const updateMessages2 = await Promise.all(findNewMessages2)
+// console.log({updateMessages2})
+// updateMessages2.forEach((item)=> {
+// addToUpdateMessageHashmap(item.originalSignature, item)
+// })
+// return updateMessages2
+// }
+
+
+export const replaceMessagesEdited = async ({
+ decodedMessages,
+ parentEpml,
+ isReceipient,
+ decodeMessageFunc,
+ _publicKey,
+ addToUpdateMessageHashmap
+}) => {
+ const MAX_CONCURRENT_REQUESTS = 5; // Maximum number of concurrent requests
+
+ const executeWithConcurrencyLimit = async (array, asyncFn) => {
+ const results = [];
+ const concurrencyPool = [];
+
+ for (const item of array) {
+ const promise = asyncFn(item);
+ concurrencyPool.push(promise);
+
+ if (concurrencyPool.length >= MAX_CONCURRENT_REQUESTS) {
+ results.push(...await Promise.all(concurrencyPool));
+ concurrencyPool.length = 0; // Clear the concurrency pool
+ }
+ }
+
+ if (concurrencyPool.length > 0) {
+ results.push(...await Promise.all(concurrencyPool));
+ }
+
+ return results;
+ };
+
+ const findUpdatedMessage = async (msg) => {
+ let msgItem = { ...msg };
+
+ try {
+ let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}`;
+ if (!isReceipient) {
+ msgQuery = `&txGroupId=${msg.txGroupId}`;
+ }
+
+ // Find new messages first
+ const newMsgResponse = await parentEpml.request("apiCall", {
+ type: "api",
+ url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}&encoding=BASE64`,
+ });
+
+ if (Array.isArray(newMsgResponse) && newMsgResponse.length > 0) {
+ const decodeResponseItem = decodeMessageFunc(newMsgResponse[0], isReceipient, _publicKey);
+ delete decodeResponseItem.timestamp;
+
+ msgItem = {
+ ...msgItem,
+ ...decodeResponseItem,
+ senderName: msg.senderName,
+ sender: msg.sender,
+ editedTimestamp: newMsgResponse[0].timestamp,
+ originalSignature: msg.signature
+ };
+ }
+
+ // Then check and find replies in the same iteration
+ let parsedMessageObj;
+ try {
+ parsedMessageObj = JSON.parse(msg.decodedMessage);
+ } catch (error) {
+ // If parsing fails, return the msgItem as is
+ return msgItem;
+ }
+
+ if (parsedMessageObj.repliedTo) {
+ let originalReply;
+ if(+parsedMessageObj.version > 2){
+ originalReply = await parentEpml.request("apiCall", {
+ type: "api",
+ url: `/chat/message/${parsedMessageObj.repliedTo}?encoding=BASE64`,
+ });
+ } else {
+ originalReply = await parentEpml.request("apiCall", {
+ type: "api",
+ url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&encoding=BASE64`,
+ });
+ }
+
+ const originalReplyMessage = originalReply.timestamp ? originalReply : originalReply.length > 0 ? originalReply[0] : null;
+
+ const replyResponse = await parentEpml.request("apiCall", {
type: "api",
url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}&encoding=BASE64`,
- })
-
+ });
+
if (
originalReplyMessage &&
- response &&
- Array.isArray(response) &&
- response.length !== 0
+ Array.isArray(replyResponse) &&
+ replyResponse.length !== 0
) {
- const decodeOriginalReply = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey)
-
- const decodeUpdatedReply = decodeMessageFunc(response[0], isReceipient, _publicKey)
- const formattedRepliedToData = {
+ const decodeOriginalReply = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey);
+ const decodeUpdatedReply = decodeMessageFunc(replyResponse[0], isReceipient, _publicKey);
+
+ msgItem.repliedToData = {
...decodeUpdatedReply,
senderName: decodeOriginalReply.senderName,
sender: decodeOriginalReply.sender,
- }
- msgItem = {
- ...msg,
- repliedToData: formattedRepliedToData,
- }
- } else {
-
-
- if (
- originalReplyMessage
- ) {
-
- msgItem = {
- ...msg,
- repliedToData: decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey),
- }
- }
+ };
+ } else if (originalReplyMessage) {
+ msgItem.repliedToData = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey);
}
}
+
} catch (error) {
+ // Handle or log the error gracefully
+ console.error(error);
}
-
- return msgItem
- })
- const updateMessages2 = await Promise.all(findNewMessages2)
-
- return updateMessages2
-}
+
+ return msgItem;
+ };
+
+ const sortedMessages = decodedMessages.sort((a, b) => b.timestamp - a.timestamp);
+
+ // Execute the functions with concurrency limit
+ const updatedMessages = await executeWithConcurrencyLimit(sortedMessages, findUpdatedMessage);
+ addToUpdateMessageHashmap(updatedMessages);
+
+ return updatedMessages;
+
+};
diff --git a/plugins/plugins/utils/warning-modal.js b/plugins/plugins/utils/warning-modal.js
new file mode 100644
index 00000000..7bda31cd
--- /dev/null
+++ b/plugins/plugins/utils/warning-modal.js
@@ -0,0 +1,252 @@
+import { get } from 'lit-translate';
+
+export class WarningModal {
+ constructor() {
+ this.initializeStyles();
+ }
+
+
+ async showModalAndWaitPublish(data) {
+ return new Promise((resolve) => {
+ const modal = this.createModal(data);
+ document.body.appendChild(modal);
+ this.addModalEventListeners(modal, resolve);
+ });
+ }
+
+ createModal(data) {
+ const modal = document.createElement('div');
+ modal.id = "backdrop";
+ modal.classList.add("backdrop");
+ modal.innerHTML = `
+
+
+
+
+
+
+
+
+
+ `;
+ return modal;
+ }
+
+ addModalEventListeners(modal, resolve) {
+ // Event listener for the 'OK' button
+ const okButton = modal.querySelector('#ok-button');
+ okButton.addEventListener('click', () => {
+ const userData = { isWithFee: true };
+ if (modal.parentNode === document.body) {
+ document.body.removeChild(modal);
+ }
+ resolve({ action: 'accept', userData });
+ });
+
+ // Prevent modal content from closing the modal
+ const modalContent = modal.querySelector('.modal-content');
+ modalContent.addEventListener('click', e => {
+ e.stopPropagation();
+ });
+
+ // Event listeners for backdrop and 'Cancel' button
+ const backdropClick = document.getElementById('backdrop');
+ backdropClick.addEventListener('click', () => {
+ if (modal.parentNode === document.body) {
+ document.body.removeChild(modal);
+ }
+ resolve({ action: 'reject' });
+ });
+
+ const cancelButton = modal.querySelector('#cancel-button');
+ cancelButton.addEventListener('click', () => {
+ if (modal.parentNode === document.body) {
+ document.body.removeChild(modal);
+ }
+ resolve({ action: 'reject' });
+ });
+ }
+
+ initializeStyles() {
+ const styles = `
+ * {
+ --mdc-theme-primary: rgb(3, 169, 244);
+ --mdc-theme-secondary: var(--mdc-theme-primary);
+ --paper-input-container-focus-color: var(--mdc-theme-primary);
+ --mdc-checkbox-unchecked-color: var(--black);
+ --mdc-theme-on-surface: var(--black);
+ --mdc-checkbox-disabled-color: var(--black);
+ --mdc-checkbox-ink-color: var(--black);
+ }
+
+ .backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgb(186 186 186 / 26%);
+ overflow: hidden;
+ animation: backdrop_blur cubic-bezier(0.22, 1, 0.36, 1) 0.1s forwards;
+ z-index: 1000000;
+ }
+
+ @keyframes backdrop_blur {
+ 0% {
+ backdrop-filter: blur(0px);
+ background: transparent;
+ }
+ 100% {
+ backdrop-filter: blur(5px);
+ background: rgb(186 186 186 / 26%);
+ }
+ }
+
+ @keyframes modal_transition {
+ 0% {
+ visibility: hidden;
+ opacity: 0;
+ }
+ 100% {
+ visibility: visible;
+ opacity: 1;
+ }
+ }
+
+ .modal {
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ animation: 0.1s cubic-bezier(0.22, 1, 0.36, 1) 0s 1 normal forwards running modal_transition;
+ z-index: 1000001;
+ }
+
+ @keyframes modal_transition {
+ 0% {
+ visibility: hidden;
+ opacity: 0;
+ }
+ 100% {
+ visibility: visible;
+ opacity: 1;
+ }
+ }
+
+ .modal-content {
+ background-color: var(--white);
+ border-radius: 10px;
+ padding: 20px;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
+ max-width: 650px;
+ min-width: 300px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ }
+
+ .modal-body {
+ padding: 25px;
+ }
+
+ .modal-subcontainer {
+ color: var(--black);
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 15px;
+ }
+
+ .modal-subcontainer-error {
+ color: var(--black);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 15px;
+ }
+
+ .modal-paragraph-error {
+ font-family: Roboto, sans-serif;
+ font-size: 20px;
+ letter-spacing: 0.3px;
+ font-weight: 700;
+ color: var(--black);
+ margin: 0;
+ }
+
+ .modal-paragraph {
+ font-family: Roboto, sans-serif;
+ font-size: 18px;
+ letter-spacing: 0.3px;
+ font-weight: 300;
+ color: var(--black);
+ margin: 0;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ }
+
+ .capitalize-first {
+ text-transform: capitalize;
+ }
+
+ .checkbox-row {
+ display: flex;
+ align-items: center;
+ font-family: Montserrat, sans-serif;
+ font-weight: 600;
+ color: var(--black);
+ }
+
+ .modal-buttons {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 20px;
+ }
+
+ .modal-buttons button {
+ background-color: #4caf50;
+ border: none;
+ color: #fff;
+ padding: 10px 20px;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ }
+
+ .modal-buttons button:hover {
+ background-color: #3e8e41;
+ }
+
+ #cancel-button {
+ background-color: #f44336;
+ }
+
+ #cancel-button:hover {
+ background-color: #d32f2f;
+ }
+ `;
+
+ const styleSheet = new CSSStyleSheet();
+ styleSheet.replaceSync(styles);
+
+ document.adoptedStyleSheets = [styleSheet];
+ }
+
+
+ static getInstance() {
+ if (!WarningModal.instance) {
+ WarningModal.instance = new WarningModal();
+ }
+ return WarningModal.instance;
+ }
+}
+
+export const warningModal = WarningModal.getInstance();
\ No newline at end of file