forked from Qortal/qortal-ui
added copy paste feature
This commit is contained in:
parent
5c6529d269
commit
1443f5e7df
@ -833,7 +833,9 @@
|
|||||||
"cchange82": "This attachment has been deleted",
|
"cchange82": "This attachment has been deleted",
|
||||||
"cchange90": "No messages",
|
"cchange90": "No messages",
|
||||||
"cchange91": "Sending...",
|
"cchange91": "Sending...",
|
||||||
"cchange92": "Unread messages below"
|
"cchange92": "Unread messages below",
|
||||||
|
"cchange93": "Image copied to clipboard",
|
||||||
|
"cchange94": "loaded"
|
||||||
},
|
},
|
||||||
"welcomepage": {
|
"welcomepage": {
|
||||||
"wcchange1": "Welcome to Q-Chat",
|
"wcchange1": "Welcome to Q-Chat",
|
||||||
|
@ -9,8 +9,11 @@ import {
|
|||||||
} from 'lit-translate';
|
} from 'lit-translate';
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { RequestQueueWithPromise } from '../../utils/queue';
|
import { RequestQueueWithPromise } from '../../utils/queue';
|
||||||
|
import '@material/mwc-menu';
|
||||||
|
import '@material/mwc-list/mwc-list-item.js'
|
||||||
|
import { Epml } from '../../../epml';
|
||||||
const requestQueue = new RequestQueueWithPromise(5);
|
const requestQueue = new RequestQueueWithPromise(5);
|
||||||
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
export class ChatImage extends LitElement {
|
export class ChatImage extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@ -24,11 +27,15 @@ export class ChatImage extends LitElement {
|
|||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
|
* {
|
||||||
|
--mdc-theme-text-primary-on-background: var(--black);
|
||||||
|
}
|
||||||
img {
|
img {
|
||||||
max-width:45vh;
|
max-width:45vh;
|
||||||
max-height:40vh;
|
max-height:40vh;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.smallLoading,
|
.smallLoading,
|
||||||
.smallLoading:after {
|
.smallLoading:after {
|
||||||
@ -54,6 +61,10 @@ export class ChatImage extends LitElement {
|
|||||||
height: 40vh;
|
height: 40vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mwc-menu {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -96,6 +107,7 @@ export class ChatImage extends LitElement {
|
|||||||
this.nodeUrl = this.getNodeUrl()
|
this.nodeUrl = this.getNodeUrl()
|
||||||
this.myNode = this.getMyNode()
|
this.myNode = this.getMyNode()
|
||||||
this.hasCalledWhenDownloaded = false
|
this.hasCalledWhenDownloaded = false
|
||||||
|
this.isFetching = false
|
||||||
|
|
||||||
this.observer = new IntersectionObserver(entries => {
|
this.observer = new IntersectionObserver(entries => {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
@ -130,16 +142,14 @@ getMyNode(){
|
|||||||
|
|
||||||
async fetchResource() {
|
async fetchResource() {
|
||||||
try {
|
try {
|
||||||
// await qortalRequest({
|
if(this.isFetching) return
|
||||||
// action: 'GET_QDN_RESOURCE_PROPERTIES',
|
this.isFetching = true
|
||||||
// name,
|
|
||||||
// service,
|
|
||||||
// identifier
|
|
||||||
// })
|
|
||||||
await axios.get(`${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`)
|
await 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) {
|
||||||
} catch (error) {}
|
this.isFetching = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchVideoUrl() {
|
async fetchVideoUrl() {
|
||||||
@ -200,7 +210,9 @@ getMyNode(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.status = res
|
this.status = res
|
||||||
|
if(this.status.status === 'DOWNLOADED'){
|
||||||
|
this.fetchResource()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if progress is 100% and clear interval if true
|
// check if progress is 100% and clear interval if true
|
||||||
@ -237,15 +249,71 @@ getMyNode(){
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
async updated(changedProperties) {
|
|
||||||
if (changedProperties && changedProperties.has('status')) {
|
|
||||||
if(this.hasCalledWhenDownloaded === false && this.status.status === 'DOWNLOADED'){
|
|
||||||
this.fetchResource()
|
showContextMenu(e) {
|
||||||
this.hasCalledWhenDownloaded = true
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async handleCopy(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const image = this.shadowRoot.querySelector('img');
|
||||||
|
|
||||||
|
// Create a canvas and draw the image on it.
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = image.naturalWidth;
|
||||||
|
canvas.height = image.naturalHeight;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(image, 0, 0);
|
||||||
|
|
||||||
|
// Convert canvas image to blob
|
||||||
|
canvas.toBlob(blob => {
|
||||||
|
try {
|
||||||
|
const clipboardItem = new ClipboardItem({ 'image/png': blob });
|
||||||
|
navigator.clipboard.write([clipboardItem]).then(() => {
|
||||||
|
const msg = get("chatpage.cchange93")
|
||||||
|
parentEpml.request('showSnackBar', msg)
|
||||||
|
console.log('Image copied to clipboard');
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Failed to copy image: ', err);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error copying the image: ', error);
|
||||||
}
|
}
|
||||||
|
}, 'image/png');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
handleMenuBlur() {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.isMenuItemClicked) {
|
||||||
|
const contextMenu = this.shadowRoot.getElementById('contextMenu');
|
||||||
|
contextMenu.open = false;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -269,17 +337,23 @@ getMyNode(){
|
|||||||
<div
|
<div
|
||||||
class=${`smallLoading`}
|
class=${`smallLoading`}
|
||||||
></div>
|
></div>
|
||||||
<p>${`${Math.round(this.status.percentLoaded || 0
|
<p style="color: var(--black)">${`${Math.round(this.status.percentLoaded || 0
|
||||||
).toFixed(0)}% loaded`}</p>
|
).toFixed(0)}% `}${translate('chatpage.cchange94')}</p>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
${this.status.status === 'READY' ? html`
|
${this.status.status === 'READY' ? html`
|
||||||
<img @click=${()=> this.setOpenDialogImage(true)} src=${this.url} />
|
<div class="customContextMenuDiv" @contextmenu="${this.showContextMenu}" style="position:relative">
|
||||||
|
<img crossOrigin="anonymous" @click=${()=> this.setOpenDialogImage(true)} src=${this.url} />
|
||||||
|
<mwc-menu id="contextMenu" @blur="${this.handleMenuBlur}">
|
||||||
|
<mwc-list-item @click="${this.handleCopy}">Copy</mwc-list-item>
|
||||||
|
</mwc-menu>
|
||||||
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
||||||
|
@ -2172,6 +2172,7 @@ class ChatPage extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async pasteImage(e) {
|
async pasteImage(e) {
|
||||||
const event = e
|
const event = e
|
||||||
const handleTransferIntoURL = (dataTransfer) => {
|
const handleTransferIntoURL = (dataTransfer) => {
|
||||||
@ -2185,7 +2186,7 @@ class ChatPage extends LitElement {
|
|||||||
if (event.clipboardData) {
|
if (event.clipboardData) {
|
||||||
const blobFound = handleTransferIntoURL(event.clipboardData)
|
const blobFound = handleTransferIntoURL(event.clipboardData)
|
||||||
if (blobFound) {
|
if (blobFound) {
|
||||||
this.insertImage(blobFound)
|
this.insertFile(blobFound)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
const item_list = await navigator.clipboard.read()
|
const item_list = await navigator.clipboard.read()
|
||||||
@ -2204,7 +2205,7 @@ class ChatPage extends LitElement {
|
|||||||
let file = new File([blob], "name", {
|
let file = new File([blob], "name", {
|
||||||
type: image_type
|
type: image_type
|
||||||
})
|
})
|
||||||
this.insertImage(file)
|
this.insertFile(file)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
let errorMsg = get("chatpage.cchange81")
|
let errorMsg = get("chatpage.cchange81")
|
||||||
|
@ -756,9 +756,9 @@ export const chatStyles = css`
|
|||||||
|
|
||||||
.unread-divider {
|
.unread-divider {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #9B111E;
|
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
color: #FAEBD7;
|
color: var(--black);
|
||||||
|
border-bottom: 1px solid var(--black);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
@ -502,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" />
|
accept="image/*, .doc, .docx, .pdf, .zip, .pdf, .txt, .odt, .ods, .xls, .xlsx, .ppt, .pptx" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea style="color: var(--black);" tabindex='1' ?autofocus=${true} ?disabled=${this.isLoading || this.isLoadingMessages} id="messageBox" rows="1"></textarea>
|
<textarea style="color: var(--black);" tabindex='1' ?autofocus=${true} ?disabled=${this.isLoading || this.isLoadingMessages} id="messageBox" rows="1"
|
||||||
|
|
||||||
|
>
|
||||||
|
|
||||||
|
></textarea>
|
||||||
<div id=${this.iframeId}
|
<div id=${this.iframeId}
|
||||||
class=${["element", this.iframeId === "privateMessage" ? "privateMessageMargin" : ""].join(" ")}
|
class=${["element", this.iframeId === "privateMessage" ? "privateMessageMargin" : ""].join(" ")}
|
||||||
></div>
|
></div>
|
||||||
@ -572,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() {
|
async firstUpdated() {
|
||||||
window.addEventListener('storage', () => {
|
window.addEventListener('storage', () => {
|
||||||
const checkTheme = localStorage.getItem('qortalTheme');
|
const checkTheme = localStorage.getItem('qortalTheme');
|
||||||
|
@ -445,9 +445,24 @@ class Chat extends LitElement {
|
|||||||
if (!isElectron()) {
|
if (!isElectron()) {
|
||||||
} else {
|
} else {
|
||||||
window.addEventListener('contextmenu', (event) => {
|
window.addEventListener('contextmenu', (event) => {
|
||||||
event.preventDefault()
|
// Check if the clicked element has the class
|
||||||
window.parent.electronAPI.showMyMenu()
|
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
|
let configLoaded = false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user