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",
"cchange90": "No messages",
"cchange91": "Sending...",
"cchange92": "Unread messages below"
"cchange92": "Unread messages below",
"cchange93": "Image copied to clipboard",
"cchange94": "loaded"
},
"welcomepage": {
"wcchange1": "Welcome to Q-Chat",

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

@ -9,8 +9,11 @@ import {
} from 'lit-translate';
import axios from 'axios'
import { RequestQueueWithPromise } from '../../utils/queue';
import '@material/mwc-menu';
import '@material/mwc-list/mwc-list-item.js'
import { Epml } from '../../../epml';
const requestQueue = new RequestQueueWithPromise(5);
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
export class ChatImage extends LitElement {
static get properties() {
@ -24,11 +27,15 @@ export class ChatImage extends LitElement {
static get styles() {
return css`
* {
--mdc-theme-text-primary-on-background: var(--black);
}
img {
max-width:45vh;
max-height:40vh;
border-radius: 5px;
cursor: pointer;
position: relative;
}
.smallLoading,
.smallLoading:after {
@ -54,6 +61,10 @@ export class ChatImage extends LitElement {
height: 40vh;
}
mwc-menu {
position: absolute;
}
@ -96,6 +107,7 @@ export class ChatImage extends LitElement {
this.nodeUrl = this.getNodeUrl()
this.myNode = this.getMyNode()
this.hasCalledWhenDownloaded = false
this.isFetching = false
this.observer = new IntersectionObserver(entries => {
for (const entry of entries) {
@ -130,16 +142,14 @@ getMyNode(){
async fetchResource() {
try {
// await qortalRequest({
// action: 'GET_QDN_RESOURCE_PROPERTIES',
// name,
// service,
// identifier
// })
if(this.isFetching) return
this.isFetching = true
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() {
@ -200,7 +210,9 @@ getMyNode(){
}
this.status = res
if(this.status.status === 'DOWNLOADED'){
this.fetchResource()
}
}
// check if progress is 100% and clear interval if true
@ -237,15 +249,71 @@ getMyNode(){
return true
}
async updated(changedProperties) {
if (changedProperties && changedProperties.has('status')) {
if(this.hasCalledWhenDownloaded === false && this.status.status === 'DOWNLOADED'){
this.fetchResource()
this.hasCalledWhenDownloaded = true
showContextMenu(e) {
e.preventDefault();
e.stopPropagation();
const contextMenu = this.shadowRoot.getElementById('contextMenu');
const containerRect = e.currentTarget.getBoundingClientRect();
// Adjusting the positions
const adjustedX = e.clientX - containerRect.left;
const adjustedY = e.clientY - containerRect.top;
contextMenu.style.top = `${adjustedY}px`;
contextMenu.style.left = `${adjustedX}px`;
contextMenu.open = true;
}
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() {
return html`
@ -269,17 +337,23 @@ getMyNode(){
<div
class=${`smallLoading`}
></div>
<p>${`${Math.round(this.status.percentLoaded || 0
).toFixed(0)}% loaded`}</p>
<p style="color: var(--black)">${`${Math.round(this.status.percentLoaded || 0
).toFixed(0)}% `}${translate('chatpage.cchange94')}</p>
</div>
`
: ''
}
${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>
`

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

@ -2172,6 +2172,7 @@ class ChatPage extends LitElement {
}
}
async pasteImage(e) {
const event = e
const handleTransferIntoURL = (dataTransfer) => {
@ -2185,7 +2186,7 @@ class ChatPage extends LitElement {
if (event.clipboardData) {
const blobFound = handleTransferIntoURL(event.clipboardData)
if (blobFound) {
this.insertImage(blobFound)
this.insertFile(blobFound)
return
} else {
const item_list = await navigator.clipboard.read()
@ -2204,7 +2205,7 @@ class ChatPage extends LitElement {
let file = new File([blob], "name", {
type: image_type
})
this.insertImage(file)
this.insertFile(file)
} catch (error) {
console.error(error)
let errorMsg = get("chatpage.cchange81")

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

@ -756,9 +756,9 @@ export const chatStyles = css`
.unread-divider {
width: 100%;
background: #9B111E;
padding: 5px;
color: #FAEBD7;
color: var(--black);
border-bottom: 1px solid var(--black);
display: flex;
justify-content: center;
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" />
</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}
class=${["element", this.iframeId === "privateMessage" ? "privateMessageMargin" : ""].join(" ")}
></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() {
window.addEventListener('storage', () => {
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()) {
} else {
window.addEventListener('contextmenu', (event) => {
event.preventDefault()
window.parent.electronAPI.showMyMenu()
})
// Check if the clicked element has the class
let target = event.target;
while (target !== null) {
if (target.classList && target.classList.contains('customContextMenuDiv')) {
// Your custom context menu logic
this.showContextMenu(event);
return;
}
target = target.parentNode;
}
// If it doesn't, show the default Electron context menu
event.preventDefault();
window.parent.electronAPI.showMyMenu();
});
}
let configLoaded = false

Loading…
Cancel
Save