mirror of
https://github.com/Qortal/qortal-ui.git
synced 2025-02-11 17:55:51 +00:00
image functionality rough draft
This commit is contained in:
parent
b73b9a0ab2
commit
af71ec3508
@ -509,7 +509,8 @@
|
|||||||
"bcchange9": "Private Message",
|
"bcchange9": "Private Message",
|
||||||
"bcchange10": "More",
|
"bcchange10": "More",
|
||||||
"bcchange11": "Reply",
|
"bcchange11": "Reply",
|
||||||
"bcchange12": "Edit"
|
"bcchange12": "Edit",
|
||||||
|
"bcchange13": "Reaction"
|
||||||
},
|
},
|
||||||
"grouppage": {
|
"grouppage": {
|
||||||
"gchange1": "Qortal Groups",
|
"gchange1": "Qortal Groups",
|
||||||
|
@ -19,8 +19,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material/mwc-list": "0.27.0",
|
"@material/mwc-list": "0.27.0",
|
||||||
"@material/mwc-select": "0.27.0",
|
"@material/mwc-select": "0.27.0",
|
||||||
|
"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"
|
"localforage": "^1.10.0",
|
||||||
|
"short-unique-id": "^4.4.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.19.3",
|
"@babel/core": "7.19.3",
|
||||||
|
@ -6,6 +6,8 @@ import localForage from "localforage";
|
|||||||
registerTranslateConfig({
|
registerTranslateConfig({
|
||||||
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
|
||||||
})
|
})
|
||||||
|
import ShortUniqueId from 'short-unique-id';
|
||||||
|
import Compressor from 'compressorjs';
|
||||||
|
|
||||||
import { escape, unescape } from 'html-escaper';
|
import { escape, unescape } from 'html-escaper';
|
||||||
import { inputKeyCodes } from '../../utils/keyCodes.js'
|
import { inputKeyCodes } from '../../utils/keyCodes.js'
|
||||||
@ -20,6 +22,7 @@ import '@material/mwc-button'
|
|||||||
import '@material/mwc-dialog'
|
import '@material/mwc-dialog'
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
import { replaceMessagesEdited } from '../../utils/replace-messages-edited.js';
|
import { replaceMessagesEdited } from '../../utils/replace-messages-edited.js';
|
||||||
|
import { publishData } from '../../utils/publish-image.js';
|
||||||
|
|
||||||
const messagesCache = localForage.createInstance({
|
const messagesCache = localForage.createInstance({
|
||||||
name: "messages-cache",
|
name: "messages-cache",
|
||||||
@ -53,7 +56,8 @@ class ChatPage extends LitElement {
|
|||||||
messagesRendered: { type: Array },
|
messagesRendered: { type: Array },
|
||||||
repliedToMessageObj: { type: Object },
|
repliedToMessageObj: { type: Object },
|
||||||
editedMessageObj: { type: Object },
|
editedMessageObj: { type: Object },
|
||||||
chatMessageSize: { type: String}
|
chatMessageSize: { type: Number},
|
||||||
|
imageFile: {type: Object}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +193,8 @@ 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.getMessageSize = this.getMessageSize.bind(this)
|
||||||
this._downObserverhandler = this._downObserverhandler.bind(this)
|
this._downObserverhandler = this._downObserverhandler.bind(this)
|
||||||
this.selectedAddress = {}
|
this.selectedAddress = {}
|
||||||
this.chatId = ''
|
this.chatId = ''
|
||||||
@ -210,11 +216,54 @@ class ChatPage extends LitElement {
|
|||||||
this.messagesRendered = []
|
this.messagesRendered = []
|
||||||
this.repliedToMessageObj = null
|
this.repliedToMessageObj = null
|
||||||
this.editedMessageObj = null
|
this.editedMessageObj = null
|
||||||
|
this.chatMessageSize = 5
|
||||||
|
this.imageFile = null
|
||||||
|
this.uid = new ShortUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
${this.isLoadingMessages ? html`<h1>${translate("chatpage.cchange22")}</h1>` : this.renderChatScroller(this._initialMessages)}
|
${this.isLoadingMessages ? html`<h1>${translate("chatpage.cchange22")}</h1>` : this.renderChatScroller(this._initialMessages)}
|
||||||
|
<mwc-dialog id="showDialogPublicKey" ?open=${this.imageFile}>
|
||||||
|
<div class="dialog-header" >
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="dialog-container">
|
||||||
|
hello
|
||||||
|
${this.imageFile && html`
|
||||||
|
<img src=${URL.createObjectURL(this.imageFile)} />
|
||||||
|
`}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
dialogAction="cancel"
|
||||||
|
class="red"
|
||||||
|
@click=${()=>{
|
||||||
|
|
||||||
|
this._sendMessage({
|
||||||
|
type: 'image',
|
||||||
|
imageFile: this.imageFile,
|
||||||
|
caption: 'This is a caption'
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
send
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
dialogAction="cancel"
|
||||||
|
class="red"
|
||||||
|
@click=${()=>{
|
||||||
|
|
||||||
|
this.imageFile = null
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${translate("general.close")}
|
||||||
|
</mwc-button>
|
||||||
|
</mwc-dialog>
|
||||||
<div class="chat-text-area">
|
<div class="chat-text-area">
|
||||||
<div class="typing-area">
|
<div class="typing-area">
|
||||||
${this.repliedToMessageObj && html`
|
${this.repliedToMessageObj && html`
|
||||||
@ -274,8 +323,18 @@ class ChatPage extends LitElement {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
async firstUpdated() {
|
|
||||||
|
|
||||||
|
|
||||||
|
insertImage(file){
|
||||||
|
this.imageFile = file
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async firstUpdated() {
|
||||||
|
|
||||||
// TODO: Load and fetch messages from localstorage (maybe save messages to localstorage...)
|
// TODO: Load and fetch messages from localstorage (maybe save messages to localstorage...)
|
||||||
|
|
||||||
|
|
||||||
@ -627,11 +686,10 @@ class ChatPage extends LitElement {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@ -991,7 +1049,7 @@ class ChatPage extends LitElement {
|
|||||||
// Add to the messages... TODO: Save messages to localstorage and fetch from it to make it persistent...
|
// Add to the messages... TODO: Save messages to localstorage and fetch from it to make it persistent...
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendMessage(outSideMsg) {
|
async _sendMessage(outSideMsg) {
|
||||||
// have params to determine if it's a reply or not
|
// have params to determine if it's a reply or not
|
||||||
// have variable to determine if it's a response, holds signature in constructor
|
// have variable to determine if it's a response, holds signature in constructor
|
||||||
// need original message signature
|
// need original message signature
|
||||||
@ -1006,8 +1064,161 @@ class ChatPage extends LitElement {
|
|||||||
// Format and Sanitize Message
|
// Format and Sanitize Message
|
||||||
const sanitizedMessage = messageText.replace(/ /gi, ' ').replace(/<br\s*[\/]?>/gi, '\n');
|
const sanitizedMessage = messageText.replace(/ /gi, ' ').replace(/<br\s*[\/]?>/gi, '\n');
|
||||||
const trimmedMessage = sanitizedMessage.trim();
|
const trimmedMessage = sanitizedMessage.trim();
|
||||||
|
|
||||||
|
const getName = async (recipient)=> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const getNames = await parentEpml.request("apiCall", {
|
||||||
|
type: "api",
|
||||||
|
url: `/names/address/${recipient}`,
|
||||||
|
})
|
||||||
|
if(Array.isArray(getNames) && getNames.length > 0 ){
|
||||||
|
return getNames[0].name
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(outSideMsg && outSideMsg.type === 'reaction'){
|
if(outSideMsg && outSideMsg.type === 'delete'){
|
||||||
|
const userName = outSideMsg.name
|
||||||
|
const identifier = outSideMsg.identifier
|
||||||
|
let compressedFile = ''
|
||||||
|
var str =
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAsAAAAGMAQMAAADuk4YmAAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAADlJREFUeF7twDEBAAAAwiD7p7bGDlgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAGJrAABgPqdWQAAAABJRU5ErkJggg==";
|
||||||
|
|
||||||
|
const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
|
||||||
|
const byteCharacters = atob(b64Data);
|
||||||
|
const byteArrays = [];
|
||||||
|
|
||||||
|
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
||||||
|
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
||||||
|
|
||||||
|
const byteNumbers = new Array(slice.length);
|
||||||
|
for (let i = 0; i < slice.length; i++) {
|
||||||
|
byteNumbers[i] = slice.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const byteArray = new Uint8Array(byteNumbers);
|
||||||
|
byteArrays.push(byteArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = new Blob(byteArrays, {type: contentType});
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
const blob = b64toBlob(str, 'image/png');
|
||||||
|
|
||||||
|
await new Promise(resolve =>{
|
||||||
|
new Compressor( blob, {
|
||||||
|
quality: 0.6,
|
||||||
|
maxWidth: 500,
|
||||||
|
success(result){
|
||||||
|
console.log({result})
|
||||||
|
const file = new File([result], "name", {
|
||||||
|
type: 'image/png'
|
||||||
|
});
|
||||||
|
console.log({file})
|
||||||
|
compressedFile = file
|
||||||
|
resolve()
|
||||||
|
},
|
||||||
|
error(err) {
|
||||||
|
console.log(err.message);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
console.log({userName, compressedFile, identifier, selectedAddress: this.selectedAddress})
|
||||||
|
await publishData({
|
||||||
|
registeredName: userName ,
|
||||||
|
file : compressedFile ,
|
||||||
|
service: 'IMAGE',
|
||||||
|
identifier : identifier,
|
||||||
|
parentEpml,
|
||||||
|
metaData: undefined,
|
||||||
|
uploadType: 'file',
|
||||||
|
selectedAddress: this.selectedAddress
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typeMessage = 'edit'
|
||||||
|
let chatReference = outSideMsg.editedMessageObj.reference
|
||||||
|
|
||||||
|
if(outSideMsg.editedMessageObj.chatReference){
|
||||||
|
chatReference = outSideMsg.editedMessageObj.chatReference
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = ""
|
||||||
|
try {
|
||||||
|
const parsedMessageObj = JSON.parse(outSideMsg.editedMessageObj.decodedMessage)
|
||||||
|
message = parsedMessageObj
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
message = outSideMsg.editedMessageObj.decodedMessage
|
||||||
|
}
|
||||||
|
const messageObject = {
|
||||||
|
...message,
|
||||||
|
isImageDeleted: true
|
||||||
|
}
|
||||||
|
const stringifyMessageObject = JSON.stringify(messageObject)
|
||||||
|
this.sendMessage(stringifyMessageObject, typeMessage, chatReference);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(outSideMsg && outSideMsg.type === 'image'){
|
||||||
|
const userName = await getName(this.selectedAddress.address)
|
||||||
|
const id = this.uid();
|
||||||
|
const identifier = `qchat_${id}`
|
||||||
|
let compressedFile = ''
|
||||||
|
await new Promise(resolve =>{
|
||||||
|
new Compressor( outSideMsg.imageFile, {
|
||||||
|
quality: 0.6,
|
||||||
|
maxWidth: 500,
|
||||||
|
success(result){
|
||||||
|
const file = new File([result], "name", {
|
||||||
|
type: outSideMsg.imageFile.type
|
||||||
|
});
|
||||||
|
compressedFile = file
|
||||||
|
resolve()
|
||||||
|
},
|
||||||
|
error(err) {
|
||||||
|
console.log(err.message);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await publishData({
|
||||||
|
registeredName: userName ,
|
||||||
|
file : compressedFile ,
|
||||||
|
service: 'IMAGE',
|
||||||
|
identifier : identifier,
|
||||||
|
parentEpml,
|
||||||
|
metaData: undefined,
|
||||||
|
uploadType: 'file',
|
||||||
|
selectedAddress: this.selectedAddress
|
||||||
|
})
|
||||||
|
const messageObject = {
|
||||||
|
messageText: outSideMsg.caption,
|
||||||
|
images: [{
|
||||||
|
service: "IMAGE",
|
||||||
|
name: userName,
|
||||||
|
identifier: identifier
|
||||||
|
}],
|
||||||
|
isImageDeleted: false,
|
||||||
|
repliedTo: '',
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
const stringifyMessageObject = JSON.stringify(messageObject)
|
||||||
|
this.sendMessage(stringifyMessageObject, typeMessage);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else if(outSideMsg && outSideMsg.type === 'reaction'){
|
||||||
typeMessage = 'edit'
|
typeMessage = 'edit'
|
||||||
let chatReference = outSideMsg.editedMessageObj.reference
|
let chatReference = outSideMsg.editedMessageObj.reference
|
||||||
|
|
||||||
@ -1261,10 +1472,12 @@ class ChatPage extends LitElement {
|
|||||||
return arr.length === 0
|
return arr.length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
initChatEditor() {
|
initChatEditor() {
|
||||||
|
|
||||||
const ChatEditor = function (editorConfig) {
|
const ChatEditor = function (editorConfig) {
|
||||||
|
|
||||||
const ChatEditor = function () {
|
const ChatEditor = function () {
|
||||||
const editor = this;
|
const editor = this;
|
||||||
editor.init();
|
editor.init();
|
||||||
@ -1411,11 +1624,14 @@ class ChatPage extends LitElement {
|
|||||||
editor.mirror.value = unescapedValue;
|
editor.mirror.value = unescapedValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
ChatEditor.prototype.listenChanges = function () {
|
ChatEditor.prototype.listenChanges = function () {
|
||||||
const editor = this;
|
const editor = this;
|
||||||
|
|
||||||
['drop', 'contextmenu', 'mouseup', 'click', 'touchend', 'keydown', 'blur', 'paste'].map(function (event) {
|
const events = ['drop', 'contextmenu', 'mouseup', 'click', 'touchend', 'keydown', 'blur', 'paste']
|
||||||
editor.content.body.addEventListener(event, function (e) {
|
|
||||||
|
for (let i = 0; i < events.length; i++) {
|
||||||
|
const event = events[i]
|
||||||
|
editor.content.body.addEventListener(event, async function (e) {
|
||||||
|
|
||||||
editorConfig.getMessageSize(editorConfig.mirrorElement.value)
|
editorConfig.getMessageSize(editorConfig.mirrorElement.value)
|
||||||
if (e.type === 'click') {
|
if (e.type === 'click') {
|
||||||
@ -1425,10 +1641,29 @@ class ChatPage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e.type === 'paste') {
|
if (e.type === 'paste') {
|
||||||
|
|
||||||
e.preventDefault();
|
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 => { // does this item have our type
|
||||||
|
if( type.startsWith( 'image/' ) ) {
|
||||||
|
image_type = type; // store which kind of image type it is
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
);
|
||||||
|
const blob = item && await item.getType( image_type );
|
||||||
|
var file = new File([blob], "name", {
|
||||||
|
type: image_type
|
||||||
|
});
|
||||||
|
|
||||||
|
editorConfig.insertImage(file)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
navigator.clipboard.readText().then(clipboardText => {
|
navigator.clipboard.readText().then(clipboardText => {
|
||||||
|
|
||||||
let escapedText = editorConfig.escape(clipboardText);
|
let escapedText = editorConfig.escape(clipboardText);
|
||||||
|
|
||||||
editor.insertText(escapedText);
|
editor.insertText(escapedText);
|
||||||
@ -1496,7 +1731,7 @@ class ChatPage extends LitElement {
|
|||||||
|
|
||||||
editor.updateMirror();
|
editor.updateMirror();
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
editor.content.addEventListener('click', function (event) {
|
editor.content.addEventListener('click', function (event) {
|
||||||
|
|
||||||
@ -1535,7 +1770,10 @@ class ChatPage extends LitElement {
|
|||||||
emojiPicker: this.emojiPicker,
|
emojiPicker: this.emojiPicker,
|
||||||
escape: escape,
|
escape: escape,
|
||||||
unescape: unescape,
|
unescape: unescape,
|
||||||
placeholder: this.chatEditorPlaceholder
|
placeholder: this.chatEditorPlaceholder,
|
||||||
|
imageFile: this.imageFile,
|
||||||
|
requestUpdate: this.requestUpdate,
|
||||||
|
insertImage: this.insertImage
|
||||||
};
|
};
|
||||||
this.chatEditor = new ChatEditor(editorConfig);
|
this.chatEditor = new ChatEditor(editorConfig);
|
||||||
}
|
}
|
||||||
|
@ -329,4 +329,22 @@ export const chatStyles = css`
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.image-delete-icon {
|
||||||
|
margin-left: 5px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: .2s all;
|
||||||
|
opacity: .8
|
||||||
|
}
|
||||||
|
.image-delete-icon:hover {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
.message-parent:hover .image-delete-icon {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
@ -210,19 +210,26 @@ class MessageTemplate extends LitElement {
|
|||||||
let message = ""
|
let message = ""
|
||||||
let reactions = []
|
let reactions = []
|
||||||
let repliedToData = null
|
let repliedToData = null
|
||||||
|
let image = null
|
||||||
|
let isImageDeleted = false
|
||||||
try {
|
try {
|
||||||
const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage)
|
const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage)
|
||||||
message = parsedMessageObj.messageText
|
message = parsedMessageObj.messageText
|
||||||
repliedToData = this.messageObj.repliedToData
|
repliedToData = this.messageObj.repliedToData
|
||||||
|
isImageDeleted = parsedMessageObj.isImageDeleted
|
||||||
reactions = parsedMessageObj.reactions || []
|
reactions = parsedMessageObj.reactions || []
|
||||||
|
if(parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0){
|
||||||
|
image = parsedMessageObj.images[0]
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message = this.messageObj.decodedMessage
|
message = this.messageObj.decodedMessage
|
||||||
}
|
}
|
||||||
let avatarImg = ''
|
let avatarImg = ''
|
||||||
|
let imageHTML = ''
|
||||||
let nameMenu = ''
|
let nameMenu = ''
|
||||||
let levelFounder = ''
|
let levelFounder = ''
|
||||||
let hideit = hidemsg.includes(this.messageObj.sender)
|
let hideit = hidemsg.includes(this.messageObj.sender)
|
||||||
|
|
||||||
|
|
||||||
levelFounder = html`<level-founder checkleveladdress="${this.messageObj.sender}"></level-founder>`
|
levelFounder = html`<level-founder checkleveladdress="${this.messageObj.sender}"></level-founder>`
|
||||||
|
|
||||||
@ -234,6 +241,13 @@ class MessageTemplate extends LitElement {
|
|||||||
} else {
|
} else {
|
||||||
avatarImg = html`<img src='/img/incognito.png' style="max-width:100%; max-height:100%;" onerror="this.onerror=null;" />`
|
avatarImg = html`<img src='/img/incognito.png' style="max-width:100%; max-height:100%;" onerror="this.onerror=null;" />`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
const imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`
|
||||||
|
imageHTML = html`<img src="${imageUrl}" style="max-width:45vh; max-height:40vh; border-radius: 5px" onerror="this.onerror=null; this.src='/img/incognito.png';" />`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this.messageObj.sender === this.myAddress) {
|
if (this.messageObj.sender === this.myAddress) {
|
||||||
@ -268,9 +282,21 @@ class MessageTemplate extends LitElement {
|
|||||||
<p class="replied-message">${repliedToData.decodedMessage.messageText}</p>
|
<p class="replied-message">${repliedToData.decodedMessage.messageText}</p>
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
${image && !isImageDeleted ? html`
|
||||||
|
<div class="image-container">
|
||||||
|
${imageHTML}<vaadin-icon
|
||||||
|
@click=${() => this.sendMessage({
|
||||||
|
type: 'delete',
|
||||||
|
name: image.name,
|
||||||
|
identifier: image.identifier,
|
||||||
|
editedMessageObj: this.messageObj,
|
||||||
|
|
||||||
|
})}
|
||||||
|
class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon>
|
||||||
|
</div>
|
||||||
|
` : html``}
|
||||||
<div id="messageContent" class="message">
|
<div id="messageContent" class="message">
|
||||||
${unsafeHTML(this.emojiPicker.parse(this.escapeHTML(message)))}
|
${unsafeHTML(this.emojiPicker.parse(this.escapeHTML(message)))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
${reactions.map((reaction)=> {
|
${reactions.map((reaction)=> {
|
||||||
@ -387,6 +413,15 @@ class ChatMenu extends LitElement {
|
|||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<div
|
||||||
|
class="menu-icon tooltip reaction"
|
||||||
|
data-text="${translate("blockpage.bcchange13")}"
|
||||||
|
@click=${(e) => {
|
||||||
|
this.emojiPicker.togglePicker(e.target)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<vaadin-icon icon="vaadin:smiley-o" slot="icon"></vaadin-icon>
|
||||||
|
</div>
|
||||||
<div class="menu-icon tooltip" data-text="${translate("blockpage.bcchange9")}" @click="${() => this.showPrivateMessageModal()}">
|
<div class="menu-icon tooltip" data-text="${translate("blockpage.bcchange9")}" @click="${() => this.showPrivateMessageModal()}">
|
||||||
<vaadin-icon icon="vaadin:paperplane" slot="icon"></vaadin-icon>
|
<vaadin-icon icon="vaadin:paperplane" slot="icon"></vaadin-icon>
|
||||||
</div>
|
</div>
|
||||||
@ -402,15 +437,7 @@ class ChatMenu extends LitElement {
|
|||||||
}}">
|
}}">
|
||||||
<vaadin-icon icon="vaadin:reply" slot="icon"></vaadin-icon>
|
<vaadin-icon icon="vaadin:reply" slot="icon"></vaadin-icon>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="menu-icon tooltip reaction"
|
|
||||||
data-text="${translate("blockpage.bcchange13")}"
|
|
||||||
@click=${(e) => {
|
|
||||||
this.emojiPicker.togglePicker(e.target)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<vaadin-icon icon="vaadin:smiley-o" slot="icon"></vaadin-icon>
|
|
||||||
</div>
|
|
||||||
${this.myAddress === this.originalMessage.sender ? (
|
${this.myAddress === this.originalMessage.sender ? (
|
||||||
html`
|
html`
|
||||||
<div
|
<div
|
||||||
|
166
qortal-ui-plugins/plugins/utils/publish-image.js
Normal file
166
qortal-ui-plugins/plugins/utils/publish-image.js
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
const getApiKey = () => {
|
||||||
|
const myNode =
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
|
||||||
|
window.parent.reduxStore.getState().app.nodeConfig.node
|
||||||
|
]
|
||||||
|
let apiKey = myNode.apiKey
|
||||||
|
return apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
export const publishData = async ({
|
||||||
|
registeredName,
|
||||||
|
path,
|
||||||
|
file,
|
||||||
|
service,
|
||||||
|
identifier,
|
||||||
|
parentEpml,
|
||||||
|
metaData,
|
||||||
|
uploadType,
|
||||||
|
selectedAddress,
|
||||||
|
}) => {
|
||||||
|
const validateName = async (receiverName) => {
|
||||||
|
let nameRes = await parentEpml.request("apiCall", {
|
||||||
|
type: "api",
|
||||||
|
url: `/names/${receiverName}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nameRes
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertBytesForSigning = async (transactionBytesBase58) => {
|
||||||
|
let convertedBytes = await parentEpml.request("apiCall", {
|
||||||
|
type: "api",
|
||||||
|
method: "POST",
|
||||||
|
url: `/transactions/convert`,
|
||||||
|
body: `${transactionBytesBase58}`,
|
||||||
|
})
|
||||||
|
return convertedBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
const signAndProcess = async (transactionBytesBase58) => {
|
||||||
|
let convertedBytesBase58 = await convertBytesForSigning(
|
||||||
|
transactionBytesBase58
|
||||||
|
)
|
||||||
|
if (convertedBytesBase58.error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertedBytes =
|
||||||
|
window.parent.Base58.decode(convertedBytesBase58)
|
||||||
|
const _convertedBytesArray = Object.keys(convertedBytes).map(
|
||||||
|
function (key) {
|
||||||
|
return convertedBytes[key]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const convertedBytesArray = new Uint8Array(_convertedBytesArray)
|
||||||
|
const convertedBytesHash = new window.parent.Sha256()
|
||||||
|
.process(convertedBytesArray)
|
||||||
|
.finish().result
|
||||||
|
const hashPtr = window.parent.sbrk(32, window.parent.heap)
|
||||||
|
const hashAry = new Uint8Array(
|
||||||
|
window.parent.memory.buffer,
|
||||||
|
hashPtr,
|
||||||
|
32
|
||||||
|
)
|
||||||
|
|
||||||
|
hashAry.set(convertedBytesHash)
|
||||||
|
const difficulty = 14
|
||||||
|
const workBufferLength = 8 * 1024 * 1024
|
||||||
|
const workBufferPtr = window.parent.sbrk(
|
||||||
|
workBufferLength,
|
||||||
|
window.parent.heap
|
||||||
|
)
|
||||||
|
let nonce = window.parent.computePow(
|
||||||
|
hashPtr,
|
||||||
|
workBufferPtr,
|
||||||
|
workBufferLength,
|
||||||
|
difficulty
|
||||||
|
)
|
||||||
|
let response = await parentEpml.request("sign_arbitrary", {
|
||||||
|
nonce: selectedAddress.nonce,
|
||||||
|
arbitraryBytesBase58: transactionBytesBase58,
|
||||||
|
arbitraryBytesForSigningBase58: convertedBytesBase58,
|
||||||
|
arbitraryNonce: nonce,
|
||||||
|
})
|
||||||
|
let myResponse = { error: "" }
|
||||||
|
if (response === false) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
myResponse = response
|
||||||
|
}
|
||||||
|
|
||||||
|
return myResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = async () => {
|
||||||
|
let validNameRes = await validateName(registeredName)
|
||||||
|
if (validNameRes.error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let transactionBytes = await uploadData(registeredName, path, file)
|
||||||
|
if (transactionBytes.error) {
|
||||||
|
return
|
||||||
|
} else if (
|
||||||
|
transactionBytes.includes("Error 500 Internal Server Error")
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let signAndProcessRes = await signAndProcess(transactionBytes)
|
||||||
|
if (signAndProcessRes.error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadData = async (registeredName, path, file) => {
|
||||||
|
if (identifier != null && identifier.trim().length > 0) {
|
||||||
|
let postBody = path
|
||||||
|
let urlSuffix = ""
|
||||||
|
if (file != null) {
|
||||||
|
// If we're sending zipped data, make sure to use the /zip version of the POST /arbitrary/* API
|
||||||
|
if (uploadType === "zip") {
|
||||||
|
urlSuffix = "/zip"
|
||||||
|
}
|
||||||
|
// If we're sending file data, use the /base64 version of the POST /arbitrary/* API
|
||||||
|
else if (uploadType === "file") {
|
||||||
|
urlSuffix = "/base64"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64 encode the file to work around compatibility issues between javascript and java byte arrays
|
||||||
|
let fileBuffer = new Uint8Array(await file.arrayBuffer())
|
||||||
|
postBody = Buffer.from(fileBuffer).toString("base64")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional metadata
|
||||||
|
|
||||||
|
// let title = encodeURIComponent(metaData.title || "")
|
||||||
|
// let description = encodeURIComponent(metaData.description || "")
|
||||||
|
// let category = encodeURIComponent(metaData.category || "")
|
||||||
|
// let tag1 = encodeURIComponent(metaData.tag1 || "")
|
||||||
|
// let tag2 = encodeURIComponent(metaData.tag2 || "")
|
||||||
|
// let tag3 = encodeURIComponent(metaData.tag3 || "")
|
||||||
|
// let tag4 = encodeURIComponent(metaData.tag4 || "")
|
||||||
|
// let tag5 = encodeURIComponent(metaData.tag5 || "")
|
||||||
|
|
||||||
|
// let metadataQueryString = `title=${title}&description=${description}&category=${category}&tags=${tag1}&tags=${tag2}&tags=${tag3}&tags=${tag4}&tags=${tag5}`
|
||||||
|
|
||||||
|
let uploadDataUrl = `/arbitrary/${service}/${registeredName}${urlSuffix}?apiKey=${getApiKey()}`
|
||||||
|
if (identifier != null && identifier.trim().length > 0) {
|
||||||
|
uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}${urlSuffix}?apiKey=${getApiKey()}`
|
||||||
|
}
|
||||||
|
let uploadDataRes = await parentEpml.request("apiCall", {
|
||||||
|
type: "api",
|
||||||
|
method: "POST",
|
||||||
|
url: `${uploadDataUrl}`,
|
||||||
|
body: `${postBody}`,
|
||||||
|
})
|
||||||
|
return uploadDataRes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
await validate()
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user