Browse Source

added copy paste feature

resolve-20231003
PhilReact 1 year ago
parent
commit
1443f5e7df
  1. 4
      core/language/us.json
  2. 112
      plugins/plugins/core/components/ChatImage.js
  3. 5
      plugins/plugins/core/components/ChatPage.js
  4. 4
      plugins/plugins/core/components/ChatScroller-css.js
  5. 29
      plugins/plugins/core/components/ChatTextEditor.js
  6. 21
      plugins/plugins/core/messaging/q-chat/q-chat.src.js

4
core/language/us.json

@ -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",

112
plugins/plugins/core/components/ChatImage.js

@ -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>
` `

5
plugins/plugins/core/components/ChatPage.js

@ -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")

4
plugins/plugins/core/components/ChatScroller-css.js

@ -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;

29
plugins/plugins/core/components/ChatTextEditor.js

@ -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');

21
plugins/plugins/core/messaging/q-chat/q-chat.src.js

@ -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…
Cancel
Save