Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Removed the cumbersome and platform-dependent file selection widget from view and now proxy its activation via a new toolbar button. Saves space and looks nicer. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | chat-input-rework |
| Files: | files | file ages | folders |
| SHA3-256: |
2b07b66d5942c69844c43710abc960f7 |
| User & Date: | stephan 2021-10-01 17:40:13.881 |
Context
|
2021-10-01
| ||
| 18:01 | Added the chat input area resize option to compact mode so there is a recovery strategy if someone manages to paste a whole book into that field. ... (check-in: 797e33ba6b user: stephan tags: chat-input-rework) | |
| 17:40 | Removed the cumbersome and platform-dependent file selection widget from view and now proxy its activation via a new toolbar button. Saves space and looks nicer. ... (check-in: 2b07b66d59 user: stephan tags: chat-input-rework) | |
| 17:14 | In compact mode, move the buttons below the input field in order to stop truncation and button layout shifting as the input field automatically resizes during editing. Takes up more a bit more space but provides better UX. ... (check-in: fe0760c95d user: stephan tags: chat-input-rework) | |
Changes
Changes to src/chat.c.
| ︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 173 174 | @ <div contenteditable id="chat-input-field" \ @ data-placeholder0="%h(zInputPlaceholder0)" \ @ data-placeholder="%h(zInputPlaceholder0)" \ @ class=""></div> @ <div id='chat-edit-buttons'> @ <button id="chat-preview-button" \ @ title="Preview message (Shift-Enter)">👁</button> @ <button id="chat-settings-button" \ @ title="Configure chat">⚙</button> @ <button id="chat-message-submit" \ @ title="Send message (Ctrl-Enter)">📤</button> @ </div> @ </div> | > > | | < < < < < | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | @ <div contenteditable id="chat-input-field" \ @ data-placeholder0="%h(zInputPlaceholder0)" \ @ data-placeholder="%h(zInputPlaceholder0)" \ @ class=""></div> @ <div id='chat-edit-buttons'> @ <button id="chat-preview-button" \ @ title="Preview message (Shift-Enter)">👁</button> @ <button id="chat-message-attach" \ @ title="Attach file to message">📎</button> @ <button id="chat-settings-button" \ @ title="Configure chat">⚙</button> @ <button id="chat-message-submit" \ @ title="Send message (Ctrl-Enter)">📤</button> @ </div> @ </div> @ <div id='chat-input-file-area'> @ <div class='file-selection-wrapper hidden'> @ <input type="file" name="file" id="chat-input-file"> @ </div> @ <div id="chat-drop-details"></div> @ </div> @ </div> @ <div id='chat-user-list-wrapper' class='hidden'> @ <div class='legend'> |
| ︙ | ︙ |
Changes to src/fossil.page.chat.js.
| ︙ | ︙ | |||
125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
pageTitle: E1('head title'),
loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
inputWrapper: E1("#chat-input-area"),
inputLine: E1('#chat-input-line'),
fileSelectWrapper: E1('#chat-input-file-area'),
viewMessages: E1('#chat-messages-wrapper'),
btnSubmit: E1('#chat-message-submit'),
inputField: E1('#chat-input-field'),
inputFile: E1('#chat-input-file'),
contentDiv: E1('div.content'),
viewConfig: E1('#chat-config'),
viewPreview: E1('#chat-preview'),
previewContent: E1('#chat-preview-content'),
btnPreview: E1('#chat-preview-button'),
| > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
pageTitle: E1('head title'),
loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
inputWrapper: E1("#chat-input-area"),
inputLine: E1('#chat-input-line'),
fileSelectWrapper: E1('#chat-input-file-area'),
viewMessages: E1('#chat-messages-wrapper'),
btnSubmit: E1('#chat-message-submit'),
btnAttach: E1('#chat-message-attach'),
inputField: E1('#chat-input-field'),
inputFile: E1('#chat-input-file'),
contentDiv: E1('div.content'),
viewConfig: E1('#chat-config'),
viewPreview: E1('#chat-preview'),
previewContent: E1('#chat-preview-content'),
btnPreview: E1('#chat-preview-button'),
|
| ︙ | ︙ | |||
379 380 381 382 383 384 385 |
"images-inline": !!F.config.chat.imagesInline,
"edit-ctrl-send": false,
"edit-compact-mode": true,
"monospace-messages": true,
"chat-only-mode": false,
"audible-alert": true,
"active-user-list": false,
| | < | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
"images-inline": !!F.config.chat.imagesInline,
"edit-ctrl-send": false,
"edit-compact-mode": true,
"monospace-messages": true,
"chat-only-mode": false,
"audible-alert": true,
"active-user-list": false,
"active-user-list-timestamps": false
}
},
/** Plays a new-message notification sound IF the audible-alert
setting is true, else this is a no-op. Returns this.
*/
playNewMessageSound: function f(){
if(f.uri){
|
| ︙ | ︙ | |||
1034 1035 1036 1037 1038 1039 1040 |
}
if(theMsg) f.popup.show(theMsg);
}/*_handleLegendClicked()*/
};
return cf;
})()/*MessageWidget*/;
| | | 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 |
}
if(theMsg) f.popup.show(theMsg);
}/*_handleLegendClicked()*/
};
return cf;
})()/*MessageWidget*/;
const BlobXferState = (function(){
/* State for paste and drag/drop */
const bxs = {
dropDetails: document.querySelector('#chat-drop-details'),
blob: undefined,
clear: function(){
this.blob = undefined;
D.clearElement(this.dropDetails);
|
| ︙ | ︙ | |||
1057 1058 1059 1060 1061 1062 1063 |
const dd = bxs.dropDetails;
bxs.blob = blob;
D.clearElement(dd);
if(!blob){
Chat.e.inputFile.value = '';
return;
}
| | > > > < < < | 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 |
const dd = bxs.dropDetails;
bxs.blob = blob;
D.clearElement(dd);
if(!blob){
Chat.e.inputFile.value = '';
return;
}
D.append(dd, "Attached: ", blob.name,
D.br(), "Size: ",blob.size);
const btn = D.button("Cancel");
D.append(dd, D.br(), btn);
btn.addEventListener('click', ()=>updateDropZoneContent(), false);
if(blob.type && (blob.type.startsWith("image/") || blob.type==='BITMAP')){
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);
}
};
Chat.e.inputFile.addEventListener('change', function(ev){
updateDropZoneContent(this.files && this.files[0] ? this.files[0] : undefined)
});
/* 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. */
|
| ︙ | ︙ | |||
1105 1106 1107 1108 1109 1110 1111 |
}
ev.target.textContent += pastedText;
ev.preventDefault();
return false;
};
Chat.e.inputField.addEventListener('paste', onPastePlainText, false);
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < | > < | | 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 |
}
ev.target.textContent += pastedText;
ev.preventDefault();
return false;
};
Chat.e.inputField.addEventListener('paste', onPastePlainText, false);
}
const noDragDropEvents = function(ev){
/* contenteditable tries to do its own thing with dropped data,
which is not compatible with how we use it, so... */
ev.dataTransfer.effectAllowed = 'none';
ev.dataTransfer.dropEffect = 'none';
ev.preventDefault();
ev.stopPropagation();
return false;
};
['drop','dragenter','dragleave','dragend'].forEach(
(k)=>{
Chat.inputElement().addEventListener(k, noDragDropEvents, false);
}
);
return bxs;
})()/*drag/drop/paste*/;
const tzOffsetToString = function(off){
const hours = Math.round(off/60), min = Math.round(off % 30);
return ''+(hours + (min ? '.5' : ''));
};
const pad2 = (x)=>('0'+x).substr(-2);
const localTime8601 = function(d){
|
| ︙ | ︙ | |||
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 |
};
Chat.e.inputField.addEventListener('keydown', inputWidgetKeydown, false);
Chat.e.btnSubmit.addEventListener('click',(e)=>{
e.preventDefault();
Chat.submitMessage();
return false;
});
(function(){/*Set up #chat-settings-button and related bits */
if(window.innerWidth<window.innerHeight){
// Must be set up before config view is...
/* Alignment of 'my' messages: right alignment is conventional
for mobile chat apps but can be difficult to read in wide
windows (desktop/tablet landscape mode), so we default to a
| > > | 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 |
};
Chat.e.inputField.addEventListener('keydown', inputWidgetKeydown, false);
Chat.e.btnSubmit.addEventListener('click',(e)=>{
e.preventDefault();
Chat.submitMessage();
return false;
});
Chat.e.btnAttach.addEventListener(
'click', ()=>Chat.e.inputFile.click(), false);
(function(){/*Set up #chat-settings-button and related bits */
if(window.innerWidth<window.innerHeight){
// Must be set up before config view is...
/* Alignment of 'my' messages: right alignment is conventional
for mobile chat apps but can be difficult to read in wide
windows (desktop/tablet landscape mode), so we default to a
|
| ︙ | ︙ | |||
1397 1398 1399 1400 1401 1402 1403 |
hint: "Whether to show images inline or as a hyperlink.",
boolValue: 'images-inline'
},{
label: "Timestamps in active users list",
hint: "Whether to show last-message timestamps.",
boolValue: 'active-user-list-timestamps'
},
| | < < < < < | 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 |
hint: "Whether to show images inline or as a hyperlink.",
boolValue: 'images-inline'
},{
label: "Timestamps in active users list",
hint: "Whether to show last-message timestamps.",
boolValue: 'active-user-list-timestamps'
},
namedOptions.activeUsers];
/** Set up selection list of notification sounds. */
if(1){
const selectSound = D.select();
D.option(selectSound, "", "(no audio)");
const firstSoundIndex = selectSound.options.length;
F.config.chat.alerts.forEach((a)=>D.option(selectSound, a));
|
| ︙ | ︙ | |||
1528 1529 1530 1531 1532 1533 1534 |
Chat.chatOnlyMode(s.value);
});
Chat.settings.addListener('edit-compact-mode',function(s){
Chat.e.inputLine.classList[
s.value ? 'add' : 'remove'
]('compact');
});
| < < < < < | 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 |
Chat.chatOnlyMode(s.value);
});
Chat.settings.addListener('edit-compact-mode',function(s){
Chat.e.inputLine.classList[
s.value ? 'add' : 'remove'
]('compact');
});
Chat.settings.addListener('edit-ctrl-send',function(s){
const label = (s.value ? "Ctrl-" : "")+"Enter submits messages.";
const eInput = Chat.inputElement();
eInput.dataset.placeholder = eInput.dataset.placeholder0 + " " +label;
Chat.e.btnSubmit.title = label;
F.toast.message(label);
});
|
| ︙ | ︙ |
Changes to src/style.chat.css.
| ︙ | ︙ | |||
264 265 266 267 268 269 270 |
padding: initial/*some skins mess this up for buttons*/;
line-height: inherit/*undo skin funkiness*/;
min-width: 4ex;
}
body.chat #chat-input-line:not(.compact) #chat-edit-buttons > * {
max-width: 6ex;
min-width: 6ex;
| | | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
padding: initial/*some skins mess this up for buttons*/;
line-height: inherit/*undo skin funkiness*/;
min-width: 4ex;
}
body.chat #chat-input-line:not(.compact) #chat-edit-buttons > * {
max-width: 6ex;
min-width: 6ex;
min-height: 5ex;
max-height: 6ex;
margin: 0.125em;
}
body.chat #chat-input-line:not(.compact) #chat-input-field {
/*border-left-style: double;
border-left-width: 3px;
|
| ︙ | ︙ | |||
323 324 325 326 327 328 329 330 |
body.chat #chat-input-line.compact > input[type=text] {
margin: 0 0 0.25em 0/* gap for if/when buttons wrap*/;
}
/* Widget holding the file selection control and preview */
body.chat #chat-input-file-area {
display: flex;
flex-direction: row;
align-items: center;
| > > < < < < < < < | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
body.chat #chat-input-line.compact > input[type=text] {
margin: 0 0 0.25em 0/* gap for if/when buttons wrap*/;
}
/* Widget holding the file selection control and preview */
body.chat #chat-input-file-area {
display: flex;
flex-direction: row;
margin: 0;
justify-content: flex-end;
align-items: center;
}
body.chat #chat-input-file-area > .file-selection-wrapper {
align-self: flex-start;
margin-right: 0.5em;
flex: 0 1 auto;
padding: 0.25em 0.5em;
white-space: nowrap;
}
body.chat #chat-input-file {
border:1px solid rgba(0,0,0,0);/*avoid UI shift during drop-targeting*/
border-radius: 0.25em;
padding: 0.25em;
}
body.chat #chat-input-file > input {
flex: 1 0 auto;
}
/* Indicator when a drag/drop is in progress */
body.chat #chat-input-file.dragover {
border: 1px dashed green;
}
/* Widget holding the details of a selected/dropped file/image. */
body.chat #chat-drop-details {
padding: 0.5em 1em;
margin-left: 0.5em;
white-space: pre;
font-family: monospace;
}
body.chat #chat-drop-details img {
|
| ︙ | ︙ |