4
1
mirror of https://github.com/Qortal/qortal-ui.git synced 2025-02-11 17:55:51 +00:00

File Import Initial Version

This commit is contained in:
Justin Ferrari 2023-01-20 16:30:01 -05:00
parent 47a048c5e9
commit 998ad27ee3
11 changed files with 776 additions and 458 deletions

BIN
img/attachment-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

View File

@ -14,9 +14,9 @@
@font-face { @font-face {
font-family: 'Material Symbols Outlined'; font-family: 'Material Symbols Outlined';
font-style: normal; font-style: normal;
src: local('MaterialSymbolsOutlined'), src: local('MaterialSymbolsOutlined'),
url(MaterialSymbolsOutlined.ttf) format('truetype'), url(MaterialSymbolsOutlined.ttf) format('truetype'),
url(MaterialSymbolsOutlined.woff2) format('woff2') url(MaterialSymbolsOutlined.woff2) format('woff2')
} }
@font-face { @font-face {
@ -26,6 +26,13 @@
url(Montserrat.ttf) format('truetype'); url(Montserrat.ttf) format('truetype');
} }
@font-face {
font-family: 'WorkSans';
src: local('WorkSans'),
local('WorkSans'),
url(WorkSans.ttf) format('truetype');
}
@font-face { @font-face {
font-family: 'Raleway'; font-family: 'Raleway';
src: local('Raleway'), src: local('Raleway'),
@ -77,7 +84,8 @@
font-family: 'Material Symbols Outlined'; font-family: 'Material Symbols Outlined';
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-size: 24px; /* Preferred icon size */ font-size: 24px;
/* Preferred icon size */
display: inline-block; display: inline-block;
line-height: 1; line-height: 1;
text-transform: none; text-transform: none;

View File

@ -570,7 +570,10 @@
"cchange61": "Error when fetching group invites. Please try again!", "cchange61": "Error when fetching group invites. Please try again!",
"cchange62": "Wrong Username and Address Inputted! Please try again!", "cchange62": "Wrong Username and Address Inputted! Please try again!",
"cchange63": "Enter Enabled", "cchange63": "Enter Enabled",
"cchange64": "Enter Disabled" "cchange64": "Enter Disabled",
"cchange65": "Uploading attachment. This may take up to one minute.",
"cchange66": "Deleting attachment. This may take up to one minute.",
"cchange67": "Attachment size exceeds 1 MB"
}, },
"welcomepage": { "welcomepage": {
"wcchange1": "Welcome to Q-Chat", "wcchange1": "Welcome to Q-Chat",

View File

@ -21,15 +21,17 @@
"@material/mwc-list": "0.27.0", "@material/mwc-list": "0.27.0",
"@material/mwc-select": "0.27.0", "@material/mwc-select": "0.27.0",
"@tiptap/core": "2.0.0-beta.209", "@tiptap/core": "2.0.0-beta.209",
"@tiptap/extension-highlight": "2.0.0-beta.209",
"@tiptap/extension-image": "2.0.0-beta.209", "@tiptap/extension-image": "2.0.0-beta.209",
"@tiptap/extension-placeholder": "2.0.0-beta.209", "@tiptap/extension-placeholder": "2.0.0-beta.209",
"@tiptap/extension-underline": "2.0.0-beta.209", "@tiptap/extension-underline": "2.0.0-beta.209",
"@tiptap/extension-highlight": "2.0.0-beta.209",
"@tiptap/html": "2.0.0-beta.209", "@tiptap/html": "2.0.0-beta.209",
"@tiptap/starter-kit": "2.0.0-beta.209", "@tiptap/starter-kit": "2.0.0-beta.209",
"asmcrypto.js": "2.3.2", "asmcrypto.js": "2.3.2",
"axios": "1.2.3",
"compressorjs": "1.1.1", "compressorjs": "1.1.1",
"emoji-picker-js": "https://github.com/Qortal/emoji-picker-js", "emoji-picker-js": "https://github.com/Qortal/emoji-picker-js",
"localforage": "1.10.0",
"prosemirror-commands": "1.5.0", "prosemirror-commands": "1.5.0",
"prosemirror-dropcursor": "1.6.1", "prosemirror-dropcursor": "1.6.1",
"prosemirror-gapcursor": "1.3.1", "prosemirror-gapcursor": "1.3.1",
@ -40,7 +42,6 @@
"prosemirror-state": "1.4.2", "prosemirror-state": "1.4.2",
"prosemirror-transform": "1.7.0", "prosemirror-transform": "1.7.0",
"prosemirror-view": "1.29.1", "prosemirror-view": "1.29.1",
"localforage": "1.10.0",
"short-unique-id": "4.4.4" "short-unique-id": "4.4.4"
}, },
"devDependencies": { "devDependencies": {

View File

@ -39,7 +39,7 @@ import { replaceMessagesEdited } from '../../utils/replace-messages-edited.js';
import { publishData } from '../../utils/publish-image.js'; import { publishData } from '../../utils/publish-image.js';
import { EmojiPicker } from 'emoji-picker-js'; import { EmojiPicker } from 'emoji-picker-js';
import WebWorker from 'web-worker:./computePowWorker.js'; import WebWorker from 'web-worker:./computePowWorker.js';
import WebWorkerImage from 'web-worker:./computePowWorkerImage.js'; import WebWorkerFile from 'web-worker:./computePowWorkerFile.js';
import '@polymer/paper-dialog/paper-dialog.js' import '@polymer/paper-dialog/paper-dialog.js'
// const messagesCache = localForage.createInstance({ // const messagesCache = localForage.createInstance({
@ -77,7 +77,9 @@ class ChatPage extends LitElement {
editedMessageObj: { type: Object }, editedMessageObj: { type: Object },
iframeHeight: { type: Number }, iframeHeight: { type: Number },
imageFile: { type: Object }, imageFile: { type: Object },
attachment: { type: Object },
isUploadingImage: { type: Boolean }, isUploadingImage: { type: Boolean },
isUploadingAttachment: { type: Boolean },
userLanguage: { type: String }, userLanguage: { type: String },
lastMessageRefVisible: { type: Boolean }, lastMessageRefVisible: { type: Boolean },
isLoadingOldMessages: { type: Boolean }, isLoadingOldMessages: { type: Boolean },
@ -94,7 +96,7 @@ class ChatPage extends LitElement {
userFound: { type: Array }, userFound: { type: Array },
userFoundModalOpen: { type: Boolean }, userFoundModalOpen: { type: Boolean },
webWorker: { type: Object }, webWorker: { type: Object },
webWorkerImage: { type: Object }, webWorkerFile: { type: Object },
myTrimmedMeassage: { type: String }, myTrimmedMeassage: { type: String },
editor: {type: Object}, editor: {type: Object},
currentEditor: {type: String}, currentEditor: {type: String},
@ -822,6 +824,30 @@ class ChatPage extends LitElement {
cursor: pointer; cursor: pointer;
color: #4e5054; color: #4e5054;
} }
.attachment-icon-container {
display: flex;
align-items: center;
justify-content: center;
height: 120px;
width: 120px;
border-radius: 50%;
border: none;
background-color: var(--mdc-theme-primary);
}
.attachment-icon {
width: 70%;
}
.attachment-name {
font-family: Work Sans, sans-serif;
font-size: 20px;
color: var(--chat-bubble-msg-color);
margin: 0px;
letter-spacing: 1px;
padding: 5px 0px;
}
` `
} }
@ -829,7 +855,7 @@ class ChatPage extends LitElement {
super() super()
this.getOldMessage = this.getOldMessage.bind(this) this.getOldMessage = this.getOldMessage.bind(this)
this._sendMessage = this._sendMessage.bind(this) this._sendMessage = this._sendMessage.bind(this)
this.insertImage = this.insertImage.bind(this) this.insertFile = this.insertFile.bind(this)
this.toggleEnableChatEnter = this.toggleEnableChatEnter.bind(this) this.toggleEnableChatEnter = this.toggleEnableChatEnter.bind(this)
this._downObserverhandler = this._downObserverhandler.bind(this) this._downObserverhandler = this._downObserverhandler.bind(this)
this.setOpenTipUser = this.setOpenTipUser.bind(this) this.setOpenTipUser = this.setOpenTipUser.bind(this)
@ -859,6 +885,7 @@ class ChatPage extends LitElement {
this.editedMessageObj = null this.editedMessageObj = null
this.iframeHeight = 42 this.iframeHeight = 42
this.imageFile = null this.imageFile = null
this.attachment = null
this.uid = new ShortUniqueId() this.uid = new ShortUniqueId()
this.userLanguage = "" this.userLanguage = ""
this.lastMessageRefVisible = false this.lastMessageRefVisible = false
@ -886,7 +913,7 @@ class ChatPage extends LitElement {
selected: false selected: false
} }
this.webWorker = null; this.webWorker = null;
this.webWorkerImage = null; this.webWorkerFile = null;
this.currentEditor = '_chatEditorDOM' this.currentEditor = '_chatEditorDOM'
this.initialChat = this.initialChat.bind(this) this.initialChat = this.initialChat.bind(this)
this.isEnabledChatEnter = true this.isEnabledChatEnter = true
@ -906,7 +933,6 @@ class ChatPage extends LitElement {
} }
setUserName(props) { setUserName(props) {
console.log({props})
this.userName = props.senderName ? props.senderName : props.sender; this.userName = props.senderName ? props.senderName : props.sender;
this.setSelectedHead(props); this.setSelectedHead(props);
} }
@ -925,7 +951,6 @@ class ChatPage extends LitElement {
} }
render() { render() {
console.log(this.userName, "username here");
return html` return html`
<div class="main-container"> <div class="main-container">
<div <div
@ -985,11 +1010,11 @@ class ChatPage extends LitElement {
` : ''} ` : ''}
${this.repliedToMessageObj.toString() === '2' ? html` ${this.repliedToMessageObj.toString() === '2' ? html`
${unsafeHTML(generateHTML(this.repliedToMessageObj.message, [ ${unsafeHTML(generateHTML(this.repliedToMessageObj.message, [
StarterKit, StarterKit,
Underline, Underline,
Highlight Highlight
// other extensions … // other extensions …
]))} ` : ''} ]))} ` : ''}
</div> </div>
<vaadin-icon <vaadin-icon
@ -1006,20 +1031,22 @@ class ChatPage extends LitElement {
<div class="repliedTo-subcontainer"> <div class="repliedTo-subcontainer">
<vaadin-icon class="reply-icon" icon="vaadin:pencil" slot="icon"></vaadin-icon> <vaadin-icon class="reply-icon" icon="vaadin:pencil" slot="icon"></vaadin-icon>
<div class="repliedTo-message"> <div class="repliedTo-message">
<p class="senderName">${translate("chatpage.cchange25")}</p> <p class="senderName">
${unsafeHTML(generateHTML(this.editedMessageObj.message, [ ${translate("chatpage.cchange25")}
StarterKit, </p>
Underline, ${unsafeHTML(generateHTML(this.editedMessageObj.message,
Highlight [StarterKit,
// other extensions … Underline,
]))} Highlight
// other extensions …
]))}
</div> </div>
<vaadin-icon <vaadin-icon
class="close-icon" class="close-icon"
icon="vaadin:close-big" icon="vaadin:close-big"
slot="icon" slot="icon"
@click=${() => this.closeEditMessageContainer()} @click=${() => this.closeEditMessageContainer()}>
></vaadin-icon> </vaadin-icon>
</div> </div>
</div> </div>
`} `}
@ -1030,7 +1057,7 @@ class ChatPage extends LitElement {
placeholder=${this.chatEditorPlaceholder} placeholder=${this.chatEditorPlaceholder}
._sendMessage=${this._sendMessage} ._sendMessage=${this._sendMessage}
.imageFile=${this.imageFile} .imageFile=${this.imageFile}
.insertImage=${this.insertImage} .insertFile=${this.insertFile}
.editedMessageObj=${this.editedMessageObj} .editedMessageObj=${this.editedMessageObj}
?isLoading=${this.isLoading} ?isLoading=${this.isLoading}
?isLoadingMessages=${this.isLoadingMessages} ?isLoadingMessages=${this.isLoadingMessages}
@ -1047,25 +1074,38 @@ class ChatPage extends LitElement {
</div> </div>
</div> </div>
${(this.isUploadingImage || this.isDeletingImage) ? html` ${(this.isUploadingImage || this.isDeletingImage) ? html`
<div class="dialogCustom"> <div class="dialogCustom">
<div class="dialogCustomInner"> <div class="dialogCustomInner">
<div class="dialog-container-loader"> <div class="dialog-container-loader">
<div class=${`smallLoading marginLoader`}></div> <div class=${`smallLoading marginLoader`}></div>
<p> <p>
${this.isDeletingImage ? ${this.isDeletingImage ?
translate("chatpage.cchange31") : translate("chatpage.cchange30")} translate("chatpage.cchange31") : translate("chatpage.cchange30")}
</p> </p>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> `: ''}
`: ''} ${(this.isUploadingAttachment) ? html`
<div class="dialogCustom">
<div class="dialogCustomInner">
<div class="dialog-container-loader">
<div class=${`smallLoading marginLoader`}></div>
<p>
${translate("chatpage.cchange65")}
</p>
</div>
</div>
</div>
</div>
`: ''}
<wrapper-modal <wrapper-modal
.onClickFunc=${() => { .onClickFunc=${() => {
this.removeImage(); this.removeImage();
}} }}
style=${(this.imageFile && !this.isUploadingImage) ? "visibility:visible;z-index:50" : "visibility: hidden;z-index:-100"}> style=${(this.imageFile && !this.isUploadingImage) ? "visibility:visible; z-index:50" : "visibility: hidden;z-index:-100"}>
<div> <div>
<div class="dialog-container"> <div class="dialog-container">
${this.imageFile && html` ${this.imageFile && html`
@ -1078,7 +1118,7 @@ class ChatPage extends LitElement {
placeholder=${this.chatEditorPlaceholder} placeholder=${this.chatEditorPlaceholder}
._sendMessage=${this._sendMessage} ._sendMessage=${this._sendMessage}
.imageFile=${this.imageFile} .imageFile=${this.imageFile}
.insertImage=${this.insertImage} .insertFile=${this.insertFile}
.editedMessageObj=${this.editedMessageObj} .editedMessageObj=${this.editedMessageObj}
?isLoading=${this.isLoading} ?isLoading=${this.isLoading}
?isLoadingMessages=${this.isLoadingMessages} ?isLoadingMessages=${this.isLoadingMessages}
@ -1101,7 +1141,6 @@ class ChatPage extends LitElement {
const chatTextEditor = this.shadowRoot.getElementById('chatTextCaption') const chatTextEditor = this.shadowRoot.getElementById('chatTextCaption')
chatTextEditor.sendMessageFunc({ chatTextEditor.sendMessageFunc({
type: 'image', type: 'image',
imageFile: this.imageFile,
}) })
}} }}
> >
@ -1111,6 +1150,58 @@ class ChatPage extends LitElement {
</div> </div>
</div> </div>
</wrapper-modal> </wrapper-modal>
<wrapper-modal
.onClickFunc=${() => {
this.removeAttachment();
}}
style=${this.attachment && !this.isUploadingAttachment ? "visibility: visible; z-index: 50" : "visibility: hidden; z-index: -100"}>
<div>
<div class="dialog-container">
${this.attachment && html`
<div class="attachment-icon-container">
<img src="/img/attachment-icon.png" alt="attachment-icon" class="attachment-icon" />
</div>
`}
<p class="attachment-name">${this.attachment && this.attachment.name}</p>
<div class="caption-container">
<chat-text-editor
iframeId="newAttachmentChat"
?hasGlobalEvents=${false}
placeholder=${this.chatEditorPlaceholder}
._sendMessage=${this._sendMessage}
.imageFile=${this.imageFile}
.attachment=${this.attachment}
.insertFile=${this.insertFile}
.editedMessageObj=${this.editedMessageObj}
?isLoading=${this.isLoading}
?isLoadingMessages=${this.isLoadingMessages}
id="chatAttachmentId"
.editor=${this.editorAttachment}
.updatePlaceholder=${(editor, value)=> this.updatePlaceholder(editor, value)}
>
</chat-text-editor>
</div>
<div class="modal-button-row">
<button class="modal-button-red" @click=${() => {
this.removeAttachment();
}}>
${translate("chatpage.cchange33")}
</button>
<button
class="modal-button"
@click=${() => {
const chatTextEditor = this.shadowRoot.getElementById('chatAttachmentId');
chatTextEditor.sendMessageFunc({
type: 'attachment',
})
}}
>
${translate("chatpage.cchange9")}
</button>
</div>
</div>
</div>
</wrapper-modal>
<paper-dialog class="warning" id="confirmDialog" modal> <paper-dialog class="warning" id="confirmDialog" modal>
<h2 style="color: var(--black);">${translate("chatpage.cchange41")}</h2> <h2 style="color: var(--black);">${translate("chatpage.cchange41")}</h2>
<hr> <hr>
@ -1332,14 +1423,15 @@ class ChatPage extends LitElement {
async connectedCallback() { async connectedCallback() {
super.connectedCallback(); super.connectedCallback();
this.webWorker = new WebWorker(); this.webWorker = new WebWorker();
this.webWorkerImage = new WebWorkerImage(); this.webWorkerFile = new WebWorkerFile();
await this.getUpdateCompleteTextEditor(); await this.getUpdateCompleteTextEditor();
const elementChatId = this.shadowRoot.getElementById('_chatEditorDOM').shadowRoot.getElementById('_chatEditorDOM') const elementChatId = this.shadowRoot.getElementById('_chatEditorDOM').shadowRoot.getElementById('_chatEditorDOM');
const elementChatImageId = this.shadowRoot.getElementById('chatTextCaption').shadowRoot.getElementById('newChat') const elementChatImageId = this.shadowRoot.getElementById('chatTextCaption').shadowRoot.getElementById('newChat');
const elementChatAttachmentId = this.shadowRoot.getElementById('chatAttachmentId').shadowRoot.getElementById('newAttachmentChat');
this.editor = new Editor({ this.editor = new Editor({
onUpdate: ()=> { onUpdate: ()=> {
this.shadowRoot.getElementById('_chatEditorDOM').getMessageSize(this.editor.getJSON()) this.shadowRoot.getElementById('_chatEditorDOM').getMessageSize(this.editor.getJSON())
@ -1396,11 +1488,37 @@ class ChatPage extends LitElement {
Extension.create({ Extension.create({
addKeyboardShortcuts:()=> { addKeyboardShortcuts:()=> {
return { return {
'Enter':()=> { 'Enter':() => {
const chatTextEditor = this.shadowRoot.getElementById('chatTextCaption') const chatTextEditor = this.shadowRoot.getElementById('chatTextCaption')
chatTextEditor.sendMessageFunc({ chatTextEditor.sendMessageFunc({
type: 'image', type: 'image'
imageFile: this.imageFile, })
return true
}
}
}})
]
})
this.editorAttachment = new Editor({
onUpdate: () => {
this.shadowRoot.getElementById('chatAttachmentId').getMessageSize(this.editorAttachment.getJSON())
},
element: elementChatAttachmentId,
extensions: [
StarterKit,
Underline,
Highlight,
Placeholder.configure({
placeholder: 'Write something …',
}),
Extension.create({
addKeyboardShortcuts:()=> {
return {
'Enter':()=> {
const chatTextEditor = this.shadowRoot.getElementById('chatAttachmentId')
chatTextEditor.sendMessageFunc({
type: 'attachment'
}) })
return true return true
} }
@ -1411,17 +1529,18 @@ class ChatPage extends LitElement {
document.addEventListener('keydown', this.initialChat); document.addEventListener('keydown', this.initialChat);
} }
disconnectedCallback() { disconnectedCallback() {
super.disconnectedCallback(); super.disconnectedCallback();
this.webWorker.terminate(); this.webWorker.terminate();
this.webWorkerImage.terminate(); this.webWorkerFile.terminate();
this.editor.destroy() this.editor.destroy();
this.editorImage.destroy() this.editorImage.destroy();
document.removeEventListener('keydown', this.initialChat); this.editorAttachment.destroy();
} document.removeEventListener('keydown', this.initialChat);
}
initialChat(e) { initialChat(e) {
if (this.editor && !this.editor.isFocused && this.currentEditor === '_chatEditorDOM' && !this.openForwardOpen && !this.openTipUser) { if (this.editor && !this.editor.isFocused && this.currentEditor === '_chatEditorDOM' && !this.openForwardOpen && !this.openTipUser) {
// WARNING: Deprecated methods from KeyBoard Event // WARNING: Deprecated methods from KeyBoard Event
if (e.code === "Space" || e.keyCode === 32 || e.which === 32) { if (e.code === "Space" || e.keyCode === 32 || e.which === 32) {
} else if (inputKeyCodes.includes(e.keyCode)) { } else if (inputKeyCodes.includes(e.keyCode)) {
@ -1431,8 +1550,6 @@ class ChatPage extends LitElement {
this.editor.commands.focus('end') this.editor.commands.focus('end')
} }
} }
} }
async userSearch() { async userSearch() {
@ -1498,19 +1615,29 @@ class ChatPage extends LitElement {
} }
insertImage(file) { insertFile(file) {
if (file.type.includes('image')) { if (file.type.includes('image')) {
this.imageFile = file; this.imageFile = file;
this.currentEditor = 'newChat' this.currentEditor = 'newChat';
return;
} else {
this.attachment = file;
this.currentEditor = "newAttachmentChat";
return; return;
} }
parentEpml.request('showSnackBar', get("chatpage.cchange28")); // parentEpml.request('showSnackBar', get("chatpage.cchange28"));
} }
removeImage() { removeImage() {
this.imageFile = null; this.imageFile = null;
this.resetChatEditor() this.resetChatEditor();
this.currentEditor = '_chatEditorDOM' this.currentEditor = '_chatEditorDOM';
}
removeAttachment() {
this.attachment = null;
this.resetChatEditor();
this.currentEditor = '_chatEditorDOM';
} }
changeMsgInput(id) { changeMsgInput(id) {
@ -1616,7 +1743,6 @@ class ChatPage extends LitElement {
this.groupAdmin = membersAdminsWithName this.groupAdmin = membersAdminsWithName
this.groupMembers = membersWithName this.groupMembers = membersWithName
this.groupInfo = getGroupInfo this.groupInfo = getGroupInfo
console.log({membersAdminsWithName})
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
@ -1678,7 +1804,6 @@ class ChatPage extends LitElement {
this.editor.setEditable(true) this.editor.setEditable(true)
} }
} }
} }
async getName (recipient) { async getName (recipient) {
@ -2002,8 +2127,6 @@ class ChatPage extends LitElement {
let decodedMessageObj = {}; let decodedMessageObj = {};
if (isReceipientVar === true) { if (isReceipientVar === true) {
console.log('encoded', encodedMessageObj.isEncrypted, _publicKeyVar.hasPubKey,encodedMessageObj.data)
// direct chat
if (encodedMessageObj.isEncrypted === true && _publicKeyVar.hasPubKey === true && encodedMessageObj.data) { if (encodedMessageObj.isEncrypted === true && _publicKeyVar.hasPubKey === true && encodedMessageObj.data) {
let decodedMessage = window.parent.decryptChatMessage(encodedMessageObj.data, window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey, _publicKeyVar.key, encodedMessageObj.reference); let decodedMessage = window.parent.decryptChatMessage(encodedMessageObj.data, window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey, _publicKeyVar.key, encodedMessageObj.reference);
decodedMessageObj = { ...encodedMessageObj, decodedMessage }; decodedMessageObj = { ...encodedMessageObj, decodedMessage };
@ -2215,6 +2338,9 @@ class ChatPage extends LitElement {
if(this.currentEditor === 'newChat'){ if(this.currentEditor === 'newChat'){
this.editorImage.commands.setContent('') this.editorImage.commands.setContent('')
} }
if(this.currentEditor === 'newAttachmentChat'){
this.editorAttachment.commands.setContent('')
}
} }
async _sendMessage(outSideMsg, msg) { async _sendMessage(outSideMsg, msg) {
@ -2258,6 +2384,7 @@ class ChatPage extends LitElement {
// find specific object property in local // find specific object property in local
let typeMessage = 'regular'; let typeMessage = 'regular';
let workerImage; let workerImage;
let workerAttachment;
this.isLoading = true; this.isLoading = true;
const trimmedMessage = msg const trimmedMessage = msg
@ -2286,10 +2413,10 @@ class ChatPage extends LitElement {
let compressedFile = '' let compressedFile = ''
var str = "iVBORw0KGgoAAAANSUhEUgAAAsAAAAGMAQMAAADuk4YmAAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAADlJREFUeF7twDEBAAAAwiD7p7bGDlgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAGJrAABgPqdWQAAAABJRU5ErkJggg=="; var str = "iVBORw0KGgoAAAANSUhEUgAAAsAAAAGMAQMAAADuk4YmAAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAADlJREFUeF7twDEBAAAAwiD7p7bGDlgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAGJrAABgPqdWQAAAABJRU5ErkJggg==";
if (this.webWorkerImage) { if (this.webWorkerFile) {
workerImage = this.webWorkerImage; workerImage = this.webWorkerFile;
} else { } else {
this.webWorkerImage = new WebWorkerImage(); this.webWorkerFile = new WebWorkerFile();
} }
const b64toBlob = (b64Data, contentType='', sliceSize=512) => { const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
@ -2368,7 +2495,6 @@ class ChatPage extends LitElement {
const stringifyMessageObject = JSON.stringify(messageObject); const stringifyMessageObject = JSON.stringify(messageObject);
this.sendMessage(stringifyMessageObject, typeMessage, chatReference); this.sendMessage(stringifyMessageObject, typeMessage, chatReference);
} }
else if (outSideMsg && outSideMsg.type === 'image') { else if (outSideMsg && outSideMsg.type === 'image') {
this.isUploadingImage = true; this.isUploadingImage = true;
@ -2379,10 +2505,10 @@ class ChatPage extends LitElement {
return; return;
} }
if (this.webWorkerImage) { if (this.webWorkerFile) {
workerImage = this.webWorkerImage; workerImage = this.webWorkerFile;
} else { } else {
this.webWorkerImage = new WebWorkerImage(); this.webWorkerFile = new WebWorkerFile();
} }
const image = this.imageFile const image = this.imageFile
@ -2412,41 +2538,101 @@ class ChatPage extends LitElement {
this.isUploadingImage = false; this.isUploadingImage = false;
return; return;
} }
try { try {
await publishData({ await publishData({
registeredName: userName, registeredName: userName,
file : compressedFile, file : compressedFile,
service: 'QCHAT_IMAGE', service: 'QCHAT_IMAGE',
identifier : identifier, identifier : identifier,
parentEpml, parentEpml,
metaData: undefined, metaData: undefined,
uploadType: 'file', uploadType: 'file',
selectedAddress: this.selectedAddress, selectedAddress: this.selectedAddress,
worker: workerImage worker: workerImage
}); });
this.isUploadingImage = false;
this.removeImage();
} catch (error) {
console.error(error);
this.isLoading = false;
this.isUploadingImage = false; this.isUploadingImage = false;
this.removeImage() return;
} catch (error) { }
this.isLoading = false;
this.isUploadingImage = false;
return;
}
const messageObject = {
messageText: trimmedMessage,
images: [{
service: "QCHAT_IMAGE",
name: userName,
identifier: identifier
}],
isImageDeleted: false,
repliedTo: '',
version: 2
};
const stringifyMessageObject = JSON.stringify(messageObject);
this.sendMessage(stringifyMessageObject, typeMessage);
}
else if (outSideMsg && outSideMsg.type === 'attachment') {
this.isUploadingAttachment = true;
const userName = await getName(this.selectedAddress.address);
if (!userName) {
parentEpml.request('showSnackBar', get("chatpage.cchange27"));
this.isLoading = false;
return;
}
if (this.webWorkerFile) {
workerAttachment = this.webWorkerFile;
} else {
this.webWorkerFile = new WebWorkerFile();
}
const messageObject = { const attachment = this.attachment;
messageText: trimmedMessage, const id = this.uid();
images: [{ const identifier = `qchat_${id}`;
service: "QCHAT_IMAGE", const fileSize = attachment.size;
name: userName, if (fileSize > 1000000) {
identifier: identifier parentEpml.request('showSnackBar', get("chatpage.cchange67"));
}], this.isLoading = false;
isImageDeleted: false, this.isUploadingAttachment = false;
repliedTo: '', return;
version: 2 }
}; try {
const stringifyMessageObject = JSON.stringify(messageObject); await publishData({
this.sendMessage(stringifyMessageObject, typeMessage); registeredName: userName,
file : attachment,
service: 'QCHAT_ATTACHMENT',
identifier : identifier,
parentEpml,
metaData: undefined,
uploadType: 'file',
selectedAddress: this.selectedAddress,
worker: workerAttachment
});
this.isUploadingAttachment = false;
this.removeAttachment();
} catch (error) {
console.error(error);
this.isLoading = false;
this.isUploadingAttachment = false;
return;
}
const messageObject = {
messageText: trimmedMessage,
attachments: [{
service: 'QCHAT_ATTACHMENT',
name: userName,
identifier: identifier,
attachmentName: attachment.name,
attachmentSize: attachment.size
}],
isAttachmentDeleted: false,
repliedTo: '',
version: 2
};
const stringifyMessageObject = JSON.stringify(messageObject);
this.sendMessage(stringifyMessageObject, typeMessage);
} else if (outSideMsg && outSideMsg.type === 'reaction') { } else if (outSideMsg && outSideMsg.type === 'reaction') {
typeMessage = 'edit'; typeMessage = 'edit';
let chatReference = outSideMsg.editedMessageObj.reference; let chatReference = outSideMsg.editedMessageObj.reference;

View File

@ -612,8 +612,9 @@ export const chatStyles = css`
font-family: 'JetBrainsMono', monospace; font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
border-radius: 0.5rem; border-radius: 0.5rem;
white-space: pre-wrap; white-space: pre-wrap;
} }
.replied-message pre code { .replied-message pre code {
color: inherit; color: inherit;
padding: 0; padding: 0;
@ -621,12 +622,10 @@ export const chatStyles = css`
font-size: 0.8rem; font-size: 0.8rem;
} }
.replied-message img { .replied-message img {
width: 1.7em; width: 1.7em;
height: 1.5em; height: 1.5em;
margin: 0px; margin: 0px;
} }
.replied-message blockquote { .replied-message blockquote {
@ -639,4 +638,77 @@ export const chatStyles = css`
border-top: 2px solid rgba(#0D0D0D, 0.1); border-top: 2px solid rgba(#0D0D0D, 0.1);
margin: 2rem 0; margin: 2rem 0;
} }
.attachment-container {
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 5px 0 10px 0;
gap: 20px;
cursor: pointer;
}
.attachment-container:hover .download-icon::before {
background-color: rgb(161 158 158 / 41%);
}
.attachment-icon-container {
display: flex;
align-items: center;
justify-content: center;
height: 50px;
width: 50px;
border-radius: 50%;
border: none;
background-color: var(--mdc-theme-primary);
}
.attachment-icon {
width: 70%;
}
.attachment-info {
display: flex;
flex-direction: column;
gap: 5px;
}
.attachment-name {
font-family: Work Sans, sans-serif;
font-size: 16px;
color: var(--chat-bubble-msg-color);
margin: 0;
letter-spacing: 0.4px;
padding: 5px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.attachment-size {
font-family: Roboto, sans-serif;
font-size: 16px;
color: var(--chat-bubble-msg-color);
margin: 0;
letter-spacing: 0.3px;
font-weight: 300;
}
.download-icon {
position: relative;
color: var(--chat-bubble-msg-color);
width: 19px;
background-color: transparent;
}
.download-icon::before {
content: "";
position: absolute;
border-radius: 50%;
padding: 18px;
background-color: transparent;
transition: all 0.3s ease-in-out;
}
` `

View File

@ -6,6 +6,7 @@ import {unsafeHTML} from 'lit/directives/unsafe-html.js';
import { chatStyles } from './ChatScroller-css.js' import { chatStyles } from './ChatScroller-css.js'
import { Epml } from "../../../epml"; import { Epml } from "../../../epml";
import { cropAddress } from "../../utils/cropAddress"; import { cropAddress } from "../../utils/cropAddress";
import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js';
import './LevelFounder.js'; import './LevelFounder.js';
import './NameMenu.js'; import './NameMenu.js';
import './ChatModals.js'; import './ChatModals.js';
@ -17,7 +18,9 @@ import '@material/mwc-button';
import '@material/mwc-dialog'; import '@material/mwc-dialog';
import '@material/mwc-icon'; import '@material/mwc-icon';
import { EmojiPicker } from 'emoji-picker-js'; import { EmojiPicker } from 'emoji-picker-js';
import { generateHTML } from '@tiptap/core' import { generateHTML } from '@tiptap/core';
import { saveAs } from 'file-saver';
import axios from "axios";
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline'; import Underline from '@tiptap/extension-underline';
import Highlight from '@tiptap/extension-highlight' import Highlight from '@tiptap/extension-highlight'
@ -50,7 +53,7 @@ class ChatScroller extends LitElement {
openTipUser: { type: Boolean }, openTipUser: { type: Boolean },
openUserInfo: { type: Boolean }, openUserInfo: { type: Boolean },
userName: { type: String }, userName: { type: String },
selectedHead: { type: Object } selectedHead: { type: Object },
} }
} }
@ -270,7 +273,7 @@ class MessageTemplate extends LitElement {
setOpenTipUser: { attribute: false }, setOpenTipUser: { attribute: false },
setOpenUserInfo: { attribute: false }, setOpenUserInfo: { attribute: false },
setUserName: { attribute: false }, setUserName: { attribute: false },
openTipUser:{ type: Boolean } openTipUser:{ type: Boolean },
} }
} }
@ -318,6 +321,22 @@ class MessageTemplate extends LitElement {
} }
} }
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{
const res = await axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}`, { responseType: 'blob' })
.then(response =>{
let filename = attachment.attachmentName;
console.log({response: response.data});
let blob = new Blob([response.data], { type:"application/octet-stream" });
saveAs(blob , filename);
})
} catch (error) {
console.error(error);
}
}
render() { render() {
const hidemsg = this.hideMessages; const hidemsg = this.hideMessages;
let message = ""; let message = "";
@ -328,8 +347,10 @@ class MessageTemplate extends LitElement {
let isImageDeleted = false; let isImageDeleted = false;
let version = 0; let version = 0;
let isForwarded = false let isForwarded = false
let attachment = null;
try { try {
const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage); const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage);
console.log({parsedMessageObj});
if(parsedMessageObj.version.toString() === '2'){ if(parsedMessageObj.version.toString() === '2'){
messageVersion2 = generateHTML(parsedMessageObj.messageText, [ messageVersion2 = generateHTML(parsedMessageObj.messageText, [
@ -345,6 +366,10 @@ class MessageTemplate extends LitElement {
reactions = parsedMessageObj.reactions || []; reactions = parsedMessageObj.reactions || [];
version = parsedMessageObj.version version = parsedMessageObj.version
isForwarded = parsedMessageObj.type === 'forward' isForwarded = parsedMessageObj.type === 'forward'
if (parsedMessageObj.attachments && Array.isArray(parsedMessageObj.attachments) && parsedMessageObj.attachments.length > 0) {
attachment = parsedMessageObj.attachments[0];
}
console.log({parsedMessageObj});
if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) { if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) {
image = parsedMessageObj.images[0]; image = parsedMessageObj.images[0];
} }
@ -403,12 +428,11 @@ class MessageTemplate extends LitElement {
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`; imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`;
if(this.viewImage || this.myAddress === this.messageObj.sender){ if (this.viewImage || this.myAddress === this.messageObj.sender) {
imageHTML = createImage(imageUrl); imageHTML = createImage(imageUrl);
imageHTMLDialog = createImage(imageUrl) imageHTMLDialog = createImage(imageUrl)
imageHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px"; imageHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px";
} }
} }
nameMenu = html` nameMenu = html`
@ -529,12 +553,12 @@ class MessageTemplate extends LitElement {
${repliedToData.decodedMessage.messageText} ${repliedToData.decodedMessage.messageText}
` : ''} ` : ''}
${version.toString() === '2' ? html` ${version.toString() === '2' ? html`
${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText, [ ${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText,
StarterKit, [StarterKit,
Underline, Underline,
Highlight Highlight
// other extensions … // other extensions …
]))} ]))}
` : ''} ` : ''}
<!-- ${repliedToData.decodedMessage.messageText} --> <!-- ${repliedToData.decodedMessage.messageText} -->
@ -548,10 +572,9 @@ class MessageTemplate extends LitElement {
}} }}
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
style=${this.isFirstMessage && "margin-top: 10px;"}> style=${this.isFirstMessage && "margin-top: 10px;"}>
<div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black)"> <div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black)">
${translate("chatpage.cchange40")} ${translate("chatpage.cchange40")}
</div> </div>
</div> </div>
` : html``} ` : html``}
${image && !isImageDeleted && (this.viewImage || this.myAddress === this.messageObj.sender) ? html` ${image && !isImageDeleted && (this.viewImage || this.myAddress === this.messageObj.sender) ? html`
@ -561,13 +584,37 @@ class MessageTemplate extends LitElement {
${imageHTML}<vaadin-icon ${imageHTML}<vaadin-icon
@click=${() => { @click=${() => {
this.openDeleteImage = true; this.openDeleteImage = true;
this.chatE
}} }}
class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon> class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon>
</div> </div>
` : image && isImageDeleted ? html` ` : image && isImageDeleted ? html`
<p class="image-deleted-msg">This image has been deleted</p> <p class="image-deleted-msg">This image has been deleted</p>
` : html``} ` : html``}
${attachment ?
html`
<div @click=${async () => await this.downloadAttachment(attachment)} class="attachment-container">
<div class="attachment-icon-container">
<img
src="/img/attachment-icon.png"
alt="attachment-icon"
class="attachment-icon" />
</div>
<div class="attachment-info">
<p class="attachment-name">
${attachment && attachment.attachmentName}
</p>
<p class="attachment-size">
${roundToNearestDecimal(attachment.attachmentSize)} mb
</p>
</div>
<vaadin-icon
icon="vaadin:download-alt"
slot="icon"
class="download-icon">
</vaadin-icon>
</div>
`
: html``}
<div <div
id="messageContent" id="messageContent"
class="message" class="message"

View File

@ -14,7 +14,8 @@ class ChatTextEditor extends LitElement {
_sendMessage: { attribute: false }, _sendMessage: { attribute: false },
placeholder: { type: String }, placeholder: { type: String },
imageFile: { type: Object }, imageFile: { type: Object },
insertImage: { attribute: false }, attachment: { type: Object },
insertFile: { attribute: false },
iframeHeight: { type: Number }, iframeHeight: { type: Number },
editedMessageObj: { type: Object }, editedMessageObj: { type: Object },
repliedToMessageObj: {type: Object}, repliedToMessageObj: {type: Object},
@ -157,30 +158,32 @@ class ChatTextEditor extends LitElement {
color: var(--black); color: var(--black);
padding: 0px 10px; padding: 0px 10px;
height: 100%; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.element::-webkit-scrollbar-track { .element::-webkit-scrollbar-track {
background-color: whitesmoke; background-color: whitesmoke;
border-radius: 7px; border-radius: 7px;
} }
.element::-webkit-scrollbar { .element::-webkit-scrollbar {
width: 6px; width: 6px;
border-radius: 7px; border-radius: 7px;
background-color: whitesmoke; background-color: whitesmoke;
} }
.element::-webkit-scrollbar-thumb { .element::-webkit-scrollbar-thumb {
background-color: rgb(180, 176, 176); background-color: rgb(180, 176, 176);
border-radius: 7px; border-radius: 7px;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
.element::-webkit-scrollbar-thumb:hover {
background-color: rgb(148, 146, 146);
cursor: pointer;
}
.element::-webkit-scrollbar-thumb:hover {
background-color: rgb(148, 146, 146);
cursor: pointer;
}
.ProseMirror:focus { .ProseMirror:focus {
outline: none; outline: none;
} }
@ -189,158 +192,158 @@ class ChatTextEditor extends LitElement {
background-color: var(--white) background-color: var(--white)
} }
.ProseMirror > * + * { .ProseMirror > * + * {
margin-top: 0.75em; margin-top: 0.75em;
outline: none; outline: none;
} }
.ProseMirror ul, .ProseMirror ul,
ol { ol {
padding: 0 1rem; padding: 0 1rem;
}
.ProseMirror h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
}
.ProseMirror code {
background-color: rgba(#616161, 0.1);
color: #616161;
}
.ProseMirror pre {
background: #0D0D0D;
color: #FFF;
font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
white-space: pre-wrap;
}
.ProseMirror pre code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
} }
.ProseMirror h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
}
.ProseMirror img { .ProseMirror code {
width: 1.7em; background-color: rgba(#616161, 0.1);
height: 1.5em; color: #616161;
margin: 0px; }
} .ProseMirror pre {
background: #0D0D0D;
color: #FFF;
font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
white-space: pre-wrap;
}
.ProseMirror pre code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
}
.ProseMirror blockquote {
padding-left: 1rem;
border-left: 2px solid rgba(#0D0D0D, 0.1);
}
.ProseMirror hr { .ProseMirror img {
border: none; width: 1.7em;
border-top: 2px solid rgba(#0D0D0D, 0.1); height: 1.5em;
margin: 2rem 0; margin: 0px;
}
.chatbar-button-single {
background: var(--white);
outline: none;
border: none;
color: var(--black);
padding: 4px;
border-radius: 5px;
cursor: pointer;
margin-right: 2px;
filter: brightness(100%);
transition: all 0.2s;
display: none;
}
.chatbar-button-single:hover {
filter: brightness(120%);
} }
.chatbar-buttons { .ProseMirror blockquote {
margin-bottom: 5px; padding-left: 1rem;
flex-shrink: 0; border-left: 2px solid rgba(#0D0D0D, 0.1);
} }
.show-chatbar-buttons { .ProseMirror hr {
display: flex; border: none;
align-items: center; border-top: 2px solid rgba(#0D0D0D, 0.1);
justify-content: center; margin: 2rem 0;
} }
:host(:hover) .chatbar-button-single { .chatbar-button-single {
background: var(--white);
outline: none;
border: none;
color: var(--black);
padding: 4px;
border-radius: 5px;
cursor: pointer;
margin-right: 2px;
filter: brightness(100%);
transition: all 0.2s;
display: none;
}
.chatbar-button-single:hover {
filter: brightness(120%);
display: flex; }
align-items: center;
justify-content: center;
}
.ProseMirror p.is-editor-empty:first-child::before {
color: #adb5bd;
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
.ProseMirror p {
font-size: 18px;
margin-block-start: 0px;
margin-block-end: 0px;
overflow-wrap: anywhere;
}
.ProseMirror { .chatbar-buttons {
width: 100%; margin-bottom: 5px;
box-sizing: border-box; flex-shrink: 0;
word-break: break-all; }
}
.ProseMirror mark { .show-chatbar-buttons {
background-color: #ffe066; display: flex;
border-radius: 0.25em; align-items: center;
box-decoration-break: clone; justify-content: center;
padding: 0.125em 0; }
} :host(:hover) .chatbar-button-single {
.material-icons { display: flex;
font-family: 'Material Icons'; align-items: center;
font-weight: normal; justify-content: center;
font-style: normal; }
font-size: 24px; .ProseMirror p.is-editor-empty:first-child::before {
/* Preferred icon size */ color: #adb5bd;
display: inline-block; content: attr(data-placeholder);
line-height: 1; float: left;
text-transform: none; height: 0;
letter-spacing: normal; pointer-events: none;
word-wrap: normal; }
white-space: nowrap; .ProseMirror p {
direction: ltr; font-size: 18px;
margin-block-start: 0px;
margin-block-end: 0px;
overflow-wrap: anywhere;
}
} .ProseMirror {
width: 100%;
box-sizing: border-box;
word-break: break-all;
}
.material-symbols-outlined { .ProseMirror mark {
font-family: 'Material Symbols Outlined'; background-color: #ffe066;
font-weight: normal; border-radius: 0.25em;
font-style: normal; box-decoration-break: clone;
font-size: 18px; /* Preferred icon size */ padding: 0.125em 0;
display: inline-block; }
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
}
.hide-styling {
display: none;
}
` .material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
/* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
}
.material-symbols-outlined {
font-family: 'Material Symbols Outlined';
font-weight: normal;
font-style: normal;
font-size: 18px; /* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
}
.hide-styling {
display: none;
}
`
} }
constructor() { constructor() {
@ -357,163 +360,159 @@ class ChatTextEditor extends LitElement {
} }
render() { render() {
return html` return html`
<div <div
class=${["chatbar-container", "chatbar-buttons", this.iframeId !=="_chatEditorDOM" && 'hide-styling'].join(" ")} class=${["chatbar-container", "chatbar-buttons", this.iframeId !=="_chatEditorDOM" && 'hide-styling'].join(" ")}
style="align-items: center;"> style="align-items: center;">
<button <button
@click=${() => this.editor.chain().focus().toggleBold().run()} @click=${() => this.editor.chain().focus().toggleBold().run()}
?disabled=${ ?disabled=${
this.editor && this.editor &&
!this.editor.can() !this.editor.can()
.chain() .chain()
.focus() .focus()
.toggleBold() .toggleBold()
.run() .run()}
} class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('bold') ? 'is-active' : ''].join(" ")}>
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('bold') ? 'is-active' : ''].join(" ")} <!-- <mwc-icon >format_bold</mwc-icon> -->
> <span class="material-symbols-outlined">&#xe238;</span>
<!-- <mwc-icon >format_bold</mwc-icon> --> </button>
<span class="material-symbols-outlined">&#xe238;</span> <button
</button> @click=${() => this.editor.chain().focus().toggleItalic().run()}
<button ?disabled=${ this.editor &&
@click=${() => this.editor.chain().focus().toggleItalic().run()} !this.editor.can()
?disabled=${ this.editor && .chain()
!this.editor.can() .focus()
.chain() .toggleItalic()
.focus() .run()
.toggleItalic() }
.run() class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('italic') ? 'is-active' : ''].join(' ')}
} >
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('italic') ? 'is-active' : ''].join(' ')} <span class="material-symbols-outlined">&#xe23f;</span>
> </button>
<span class="material-symbols-outlined">&#xe23f;</span> <button
</button> @click=${() => this.editor.chain().focus().toggleUnderline().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('underline') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe249;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleHighlight().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('highlight') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf82b;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleCodeBlock().run()}
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf84d;</span>
</button>
<button
@click=${()=> this.toggleEnableChatEnter() }
style="height: 26px; box-sizing: border-box;"
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
${this.isEnabledChatEnter ? html`
${translate("chatpage.cchange63")}
` : html`
${translate("chatpage.cchange64")}
`}
</button>
</div>
<div
class=${["chatbar-container", (this.iframeId === "newChat" || this.iframeId === "newAttachmentChat" || this.iframeId === "privateMessage") ? "chatbar-caption" : ""].join(" ")}
style="align-items: flex-end; position: relative">
<button
@click=${() => this.editor.chain().focus().toggleUnderline().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('underline') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe249;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleHighlight().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('highlight') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf82b;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleCodeBlock().run()}
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf84d;</span>
</button>
<button
@click=${()=> this.toggleEnableChatEnter() }
style="height: 26px; box-sizing: border-box;"
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
${this.isEnabledChatEnter ? html`
${translate("chatpage.cchange63")}
` : html`
${translate("chatpage.cchange64")}
`}
</button>
</div>
<div <div
class=${["chatbar-container", (this.iframeId === "newChat" || this.iframeId === "privateMessage") ? "chatbar-caption" : ""].join(" ")} style=${this.iframeId === "privateMessage" ? "display: none" : "display: block"}
style="align-items: flex-end; position: relative"> class="file-picker-container"
@click=${(e) => {
<div this.preventUserSendingImage(e)
style=${this.iframeId === "privateMessage" ? "display: none" : "display: block"} }}>
class="file-picker-container" <vaadin-icon
@click=${(e) => { class="paperclip-icon"
this.preventUserSendingImage(e) icon="vaadin:paperclip"
}}> slot="icon"
<vaadin-icon >
class="paperclip-icon" </vaadin-icon>
icon="vaadin:paperclip" <div class="file-picker-input-container">
slot="icon" <input
> @change="${e => {
</vaadin-icon> this.insertFile(e.target.files[0]);
<div class="file-picker-input-container"> const filePickerInput = this.shadowRoot.getElementById('file-picker');
<input if (filePickerInput) {
@change="${e => { filePickerInput.value = "";
this.insertImage(e.target.files[0]);
const filePickerInput = this.shadowRoot.getElementById('file-picker')
if(filePickerInput){
filePickerInput.value = ""
} }
} }
}" }"
id="file-picker" id="file-picker"
class="file-picker-input" type="file" name="myImage" accept="image/*" /> class="file-picker-input"
</div> type="file"
name="myImage"
accept="image/*, .doc, .docx, .pdf, .zip, .pdf, .txt, .odt, .ods, .xls, .xlsx, .ppt, .pptx" />
</div> </div>
<textarea style="color: var(--black);" tabindex='1' ?autofocus=${true} ?disabled=${this.isLoading || this.isLoadingMessages} id="messageBox" rows="1"></textarea>
<div id=${this.iframeId}
class=${["element", this.iframeId === "privateMessage" ? "privateMessageMargin" : ""].join(" ")}
></div>
<button class="emoji-button" ?disabled=${this.isLoading || this.isLoadingMessages}>
${html`<img class="emoji" draggable="false" alt="😀" src="/emoji/svg/1f600.svg" />`}
</button>
${this.editedMessageObj ? (
html`
<div style="margin-bottom: 10px">
${this.isLoading === false ? html`
<vaadin-icon
class="checkmark-icon"
icon="vaadin:check"
slot="icon"
@click=${() => {
this.sendMessageFunc();
}}
>
</vaadin-icon>
` :
html`
<paper-spinner-lite active></paper-spinner-lite>
`}
</div>
`
) :
html`
<div
style="margin-bottom: 10px;
${this.iframeId === 'newChat'
? 'display: none;'
: 'display: flex;'}">
${this.isLoading === false ? html`
<img
src="/img/qchat-send-message-icon.svg"
alt="send-icon"
class="send-icon"
@click=${() => {
this.sendMessageFunc();
}}
/>
` :
html`
<paper-spinner-lite active></paper-spinner-lite>
`}
</div>
`
}
</div>
${this.chatMessageSize >= 750 ?
html`
<div class="message-size-container" style=${this.imageFile && "margin-top: 10px;"}>
<div class="message-size" style="${this.chatMessageSize > 1000 && 'color: #bd1515'}">
${`Your message size is of ${this.chatMessageSize} bytes out of a maximum of 1000`}
</div>
</div>
` :
html``}
</div> </div>
<textarea style="color: var(--black);" tabindex='1' ?autofocus=${true} ?disabled=${this.isLoading || this.isLoadingMessages} id="messageBox" rows="1"></textarea>
<div id=${this.iframeId}
class=${["element", this.iframeId === "privateMessage" ? "privateMessageMargin" : ""].join(" ")}
></div>
<button class="emoji-button" ?disabled=${this.isLoading || this.isLoadingMessages}>
${html`<img class="emoji" draggable="false" alt="😀" src="/emoji/svg/1f600.svg" />`}
</button>
${this.editedMessageObj ? (
html`
<div style="margin-bottom: 10px">
${this.isLoading === false ? html`
<vaadin-icon
class="checkmark-icon"
icon="vaadin:check"
slot="icon"
@click=${() => {
this.sendMessageFunc();
}}
>
</vaadin-icon>
` :
html`
<paper-spinner-lite active></paper-spinner-lite>
`}
</div>
`
) :
html`
<div
style="margin-bottom: 10px;
${this.iframeId === 'newChat' || this.iframeId === "newAttachmentChat"
? 'display: none;'
: 'display: flex;'}">
${this.isLoading === false ? html`
<img
src="/img/qchat-send-message-icon.svg"
alt="send-icon"
class="send-icon"
@click=${() => {
this.sendMessageFunc();
}}
/>
` :
html`
<paper-spinner-lite active></paper-spinner-lite>
`}
</div>
`
}
</div>
${this.chatMessageSize >= 750 ?
html`
<div class="message-size-container" style=${this.imageFile && "margin-top: 10px;"}>
<div class="message-size" style="${this.chatMessageSize > 1000 && 'color: #bd1515'}">
${`Your message size is of ${this.chatMessageSize} bytes out of a maximum of 1000`}
</div>
</div>
` :
html``}
</div>
` `
} }
@ -524,14 +523,7 @@ class ChatTextEditor extends LitElement {
} }
} }
async firstUpdated() { async firstUpdated() {
window.addEventListener('storage', () => { window.addEventListener('storage', () => {
const checkTheme = localStorage.getItem('qortalTheme'); const checkTheme = localStorage.getItem('qortalTheme');
const chatbar = this.shadowRoot.querySelector('.element') const chatbar = this.shadowRoot.querySelector('.element')
@ -548,7 +540,6 @@ class ChatTextEditor extends LitElement {
this.emojiPickerHandler = this.shadowRoot.querySelector('.emoji-button'); this.emojiPickerHandler = this.shadowRoot.querySelector('.emoji-button');
this.mirrorChatInput = this.shadowRoot.getElementById('messageBox'); this.mirrorChatInput = this.shadowRoot.getElementById('messageBox');
this.chatMessageInput = this.shadowRoot.querySelector('.element')
this.emojiPicker = new EmojiPicker({ this.emojiPicker = new EmojiPicker({
style: "twemoji", style: "twemoji",
@ -590,10 +581,6 @@ class ChatTextEditor extends LitElement {
if (changedProperties && changedProperties.has('placeholder') && this.updatePlaceholder && this.editor) { if (changedProperties && changedProperties.has('placeholder') && this.updatePlaceholder && this.editor) {
this.updatePlaceholder(this.editor, this.placeholder ) this.updatePlaceholder(this.editor, this.placeholder )
} }
if (changedProperties && changedProperties.has("imageFile")) {
this.chatMessageInput = "newChat";
}
} }
shouldUpdate(changedProperties) { shouldUpdate(changedProperties) {
@ -603,7 +590,7 @@ class ChatTextEditor extends LitElement {
} }
sendMessageFunc(props) { sendMessageFunc(props) {
if(this.editor.isEmpty) return if(this.editor.isEmpty && (!this.imageFile || !this.attachment)) return
this.getMessageSize(this.editor.getJSON()) this.getMessageSize(this.editor.getJSON())
if (this.chatMessageSize > 1000 ) { if (this.chatMessageSize > 1000 ) {
parentEpml.request('showSnackBar', get("chatpage.cchange29")); parentEpml.request('showSnackBar', get("chatpage.cchange29"));
@ -615,8 +602,7 @@ class ChatTextEditor extends LitElement {
getMessageSize(message){ getMessageSize(message){
try { try {
const trimmedMessage = message;
const trimmedMessage = message
let messageObject = {}; let messageObject = {};
if (this.repliedToMessageObj) { if (this.repliedToMessageObj) {
@ -636,20 +622,33 @@ class ChatTextEditor extends LitElement {
const parsedMessageObj = JSON.parse(this.editedMessageObj.decodedMessage); const parsedMessageObj = JSON.parse(this.editedMessageObj.decodedMessage);
message = parsedMessageObj; message = parsedMessageObj;
} catch (error) { } catch (error) {
message = this.messageObj.decodedMessage message = this.messageObj.decodedMessage;
} }
messageObject = { messageObject = {
...message, ...message,
messageText: trimmedMessage, messageText: trimmedMessage,
} }
} else if(this.imageFile && this.iframeId === 'newChat') { } else if (this.imageFile && this.iframeId === 'newChat') {
messageObject = { messageObject = {
messageText: trimmedMessage, messageText: trimmedMessage,
images: [{ images: [{
service: "QCHAT_IMAGE", service: "QCHAT_IMAGE",
name: '123456789123456789123456789', name: '123456789123456789123456789',
identifier: '123456' identifier: '123456'
}], }],
repliedTo: '',
version: 2
};
} else if (this.attachment && this.iframeId === 'newAttachmentChat') {
messageObject = {
messageText: trimmedMessage,
attachments: [{
service: "QCHAT_ATTACHMENT",
name: '123456789123456789123456789',
identifier: '123456',
attachmentName: "123456789123456789123456789",
attachmentSize: "123456"
}],
repliedTo: '', repliedTo: '',
version: 2 version: 2
}; };
@ -661,7 +660,6 @@ class ChatTextEditor extends LitElement {
version: 2 version: 2
}; };
} }
const stringified = JSON.stringify(messageObject); const stringified = JSON.stringify(messageObject);
const size = new Blob([stringified]).size; const size = new Blob([stringified]).size;
this.chatMessageSize = size; this.chatMessageSize = size;
@ -671,7 +669,6 @@ class ChatTextEditor extends LitElement {
} }
} }
window.customElements.define("chat-text-editor", ChatTextEditor) window.customElements.define("chat-text-editor", ChatTextEditor)

View File

@ -0,0 +1,4 @@
export function roundToNearestDecimal(num) {
const mb = num / 1000000;
return Math.round(mb * 10) / 10;
}