Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | chat: added drag/drop support for files. Images get previewed like those pasted from the clipboard. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | chatroom-dev |
| Files: | files | file ages | folders |
| SHA3-256: |
4c0146f180ef15b10ddd5914d26c3224 |
| User & Date: | stephan 2020-12-23 10:23:29.382 |
Context
|
2020-12-23
| ||
| 10:28 | Chat style tweaks. ... (check-in: 3e956a2354 user: stephan tags: chatroom-dev) | |
| 10:23 | chat: added drag/drop support for files. Images get previewed like those pasted from the clipboard. ... (check-in: 4c0146f180 user: stephan tags: chatroom-dev) | |
| 08:43 | Removed some dead code. Updated some docs. ... (check-in: cf789fa7e1 user: stephan tags: chatroom-dev) | |
Changes
Changes to src/chat.c.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
@ flex: 5 1 auto;
@ }
@ #chat-input-file {
@ display: flex;
@ flex-direction: row;
@ align-items: center;
@ }
@ #chat-input-file > input {
@ flex: 1 0 auto;
@ }
@ .chat-timestamp {
@ font-family: monospace;
@ font-size: 0.8em;
@ white-space: pre;
@ text-align: left;
@ opacity: 0.8;
@ }
@ </style>
@ <form accept-encoding="utf-8" id="chat-form">
@ <div id='chat-input-area'>
@ <div id='chat-input-line'>
| > > > > > > > > > > > > > > > > > > > | | > | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
@ flex: 5 1 auto;
@ }
@ #chat-input-file {
@ display: flex;
@ flex-direction: row;
@ align-items: center;
@ }
@ #chat-input-file > span,
@ #chat-input-file > input[type=file] {
@ align-self: flex-start;
@ }
@ #chat-input-file > input {
@ flex: 1 0 auto;
@ }
@ .chat-timestamp {
@ font-family: monospace;
@ font-size: 0.8em;
@ white-space: pre;
@ text-align: left;
@ opacity: 0.8;
@ }
@ #chat-drop-zone {
@ box-sizing: content-box;
@ background-color: #e0e0e0;
@ flex: 3 1 auto;
@ padding: 0.5em 1em;
@ border: 1px solid #808080;
@ border-radius: 0.25em;
@ }
@ #chat-drop-zone.dragover {
@ border: 1px dashed green;
@ }
@ #chat-drop-details {
@ white-space: pre;
@ font-family: monospace;
@ }
@ </style>
@ <form accept-encoding="utf-8" id="chat-form">
@ <div id='chat-input-area'>
@ <div id='chat-input-line'>
@ <input type="text" name="msg" id="sbox" \
@ placeholder="Type message here.">
@ <input type="submit" value="Send">
@ </div>
@ <div id='chat-input-file'>
@ <span>File:</span>
@ <input type="file" name="file">
@ <div id="chat-drop-zone">
@ Or drag/drop a file in this spot, or paste an image from
@ the clipboard if supported by your environment.
@ <div id="chat-drop-details"></div>
@ </div>
@ </div>
@ </div>
@ </form>
@ <hr>
/* New chat messages get inserted immediately after this element */
|
| ︙ | ︙ |
Changes to src/chat.js.
1 2 3 |
(function(){
const form = document.querySelector('#chat-form');
let mxMsg = 0;
| | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
(function(){
const form = document.querySelector('#chat-form');
let mxMsg = 0;
const F = window.fossil, D = F.dom;
const _me = F.user.name;
/* State for paste and drag/drop */
const BlobXferState = {
dropZone: document.querySelector('#chat-drop-zone'),
dropDetails: document.querySelector('#chat-drop-details'),
imgTag: document.querySelector('#chat-drop-details img'),
blob: undefined
};
/** Updates the paste/drop zone with details of the pasted/dropped
data. */
const updateDropZoneContent = function(blob){
const bx = BlobXferState, dd = bx.dropDetails;
bx.blob = blob;
D.clearElement(dd);
D.append(dd, D.br(), "Name: ", blob.name,
D.br(), "Size: ",blob.size);
if(blob.type && blob.type.startsWith("image/")){
const img = D.img();
D.append(dd, D.br(), img);
const reader = new FileReader();
reader.onload = (e)=>img.setAttribute('src', e.target.result);
reader.readAsDataURL(blob);
}
};
////////////////////////////////////////////////////////////
// File drag/drop.
// Adapted from: https://stackoverflow.com/a/58677161
const dropHighlight = BlobXferState.dropZone /* target zone */;
const dropEvents = {
drop: function(ev){
ev.preventDefault();
D.removeClass(dropHighlight, 'dragover');
const file = ev.dataTransfer.files[0];
if(file) {
updateDropZoneContent(file);
}
},
dragenter: function(ev){
ev.preventDefault();
ev.dataTransfer.dropEffect = "copy";
D.addClass(dropHighlight, 'dragover');
},
dragover: function(ev){
ev.preventDefault();
},
dragend: function(ev){
ev.preventDefault();
},
dragleave: function(ev){
ev.preventDefault();
D.removeClass(dropHighlight, 'dragover');
}
};
/*
The idea here is to accept drops at multiple points or, ideally,
document.body, and apply them to P.e.taContent, but the precise
combination of event handling needed to pull this off is eluding
me.
*/
[BlobXferState.dropZone
/* ideally we'd link only to document.body, but the events seem to
get out of whack, with dropleave being triggered at unexpected
points. */
].forEach(function(e){
Object.keys(dropEvents).forEach(
(k)=>e.addEventListener(k, dropEvents[k], true)
);
});
form.addEventListener('submit',(e)=>{
e.preventDefault();
const fd = new FormData(form);
if(BlobXferState.blob/*replace file content with this*/){
fd.set("file", BlobXferState.blob);
}
if( form.msg.value.length>0 || form.file.value.length>0 || BlobXferState.blob ){
fetch("chat-send",{
method: 'POST',
body: fd
});
}
BlobXferState.blob = undefined;
D.clearElement(BlobXferState.dropDetails);
form.msg.value = "";
form.file.value = "";
form.msg.focus();
});
/* Handle image paste from clipboard. TODO: figure out how we can
paste non-image binary data as if it had been selected via the
file selection element. */
document.onpaste = function(event){
const items = event.clipboardData.items,
item = items[0];
if(!item || !item.type) return;
//console.debug("pasted item =",item);
if('file'===item.kind){
updateDropZoneContent(items[0].getAsFile());
}else if('string'===item.kind){
item.getAsString((v)=>form.msg.value = v);
}
};
/* Injects element e as a new row in the chat, at the top of the list */
const injectMessage = function f(e){
if(!f.injectPoint){
|
| ︙ | ︙ |