kmc-0.0.14.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. const LOCAL_STORAGE_SESSION_KEY = 'matrix-session';
  2. var initSync = false;
  3. var highlightedRoom = 0;
  4. var rooms = [];
  5. var highlightedMsg;
  6. var page = 'login';
  7. var currentRoom;
  8. var highlightedLoginInput = 0;
  9. var scrollMsg;
  10. var editingEvent;
  11. var replyingToEvent;
  12. var previousConv;
  13. const messageBuilder = (event) => {
  14. const content = event.getContent();
  15. const sender = event.getSender();
  16. const date = event.getDate();
  17. const dateString = date.toLocaleString();
  18. const relation = event.getRelation();
  19. const floatDirection = sender === window.mClient.getUserId() ?
  20. 'align-self:flex-end;margin-right:10px;background-color:#4dc860' :
  21. 'align-self:flex-start;margin-left:10px;background-color:white';
  22. const dateDirection = sender === window.mClient.getUserId() ?
  23. 'text-align:right;margin-right:10px;' :
  24. 'text-align:left;margin-left:10px;';
  25. const reactDirection = sender === window.mClient.getUserId() ?
  26. 'justify-content:flex-end;margin-right:10px;' :
  27. 'justify-content:flex-start;margin-left:10px;';
  28. const body = content.body ? content.body.trim() : '';
  29. window.mClient.sendReadReceipt(event, "m.read");
  30. if(content.msgtype === 'm.image') {
  31. console.log(event);
  32. const imageUrl = window.mClient.mxcUrlToHttp(content.url, 50, 50, 'scale', true);
  33. return `
  34. <div id="msg-${event.getId()}" class="msgItem" sender="${sender}">
  35. <div id="innermsg-${event.getId()}" class="msgInnerItem" style="${floatDirection}">
  36. <img id="image-${event.getId()}" src="${imageUrl}" width=50 height=50>
  37. </img>
  38. </div>
  39. <p id="datetime-${event.getId()}" style="font-size:10px;color:gray;${dateDirection}">${dateString}</p>
  40. <div id="reactions-${event.getId()}" class="reactions" style="${reactDirection}"></div>
  41. </div>
  42. `;
  43. } else {
  44. return `
  45. <div id="msg-${event.getId()}" class="msgItem" sender="${sender}">
  46. <div id="innermsg-${event.getId()}" class="msgInnerItem" style="${floatDirection}">
  47. <pre>
  48. ${body}
  49. </pre>
  50. </div>
  51. <p id="datetime-${event.getId()}" style="font-size:10px;color:gray;${dateDirection}">${dateString}</p>
  52. <div id="reactions-${event.getId()}" class="reactions" style="${reactDirection}"></div>
  53. </div>
  54. `
  55. }
  56. };
  57. const gotoRoomView = () => {
  58. if(initSync) {
  59. highlightedRoom = 0;
  60. }
  61. const roomz = window.mClient.getRooms();
  62. const roomList = roomz.sort((roomA, roomB) => {
  63. return roomA.name.localeCompare(roomB.name);
  64. }).reduce((a, room) => {
  65. if (a === '<div>') {
  66. return a + `
  67. <div id="room-${room.roomId}" class="roomItem active">
  68. ${room.name}
  69. </div>
  70. <br/>
  71. `;
  72. } else {
  73. return a + `
  74. <div id="room-${room.roomId}" class="roomItem">
  75. ${room.name}
  76. </div>
  77. <br/>
  78. `;
  79. }
  80. }, '<div>'); + '</div>'
  81. document.getElementById('room-menu').style = 'display: unset';
  82. document.getElementById('chat').style = 'display: none';
  83. document.getElementById('chat-textbox').value = '';
  84. document.getElementById('chat-textbox').selectionStart = 0
  85. document.getElementById('chat-textbox').style.height = '24px';
  86. document.getElementById('conversation').style.height = '100%';
  87. document.getElementById('editing').style.bottom = '55px';
  88. document.getElementById('replying').style.bottom = '55px';
  89. document.getElementById('login').style = 'display: none';
  90. document.getElementById("left").innerHTML = "";
  91. document.getElementById("right").innerHTML = "";
  92. document.getElementById("middle").innerHTML = "SELECT";
  93. document.getElementById("title-text").innerHTML = 'Rooms';
  94. document.getElementById("room-menu").innerHTML = roomList;
  95. initSync = true;
  96. rooms = document.getElementsByClassName('roomItem');
  97. page = 'rooms';
  98. highlightedMsg = null;
  99. };
  100. const markReaction = (event, relation) => {
  101. const {
  102. event_id,
  103. rel_type,
  104. key
  105. } = relation;
  106. const msgItem = document.getElementById(`msg-${event_id}`);
  107. const reactionsEl = document.getElementById(`reactions-${event_id}`);
  108. if(msgItem && reactionsEl) {
  109. reactionsEl.innerHTML = reactionsEl.innerHTML + `
  110. <div id="react-${event.getId()}" class="${relation.key} ${event.getSender()}" style="font-size:16px;">
  111. ${relation.key}
  112. </div>
  113. `
  114. // if the last message gets reacted, scroll down to see it
  115. const msgs = document.getElementsByClassName('msgItem');
  116. if(msgItem === msgs[msgs.length - 1]) {
  117. const conversationEl = document.getElementById("conversation");
  118. conversationEl.scrollTo(0, conversationEl.scrollHeight);
  119. }
  120. }
  121. };
  122. const removeReaction = (eventId) => {
  123. const reaction = document.getElementById(`react-${eventId}`);
  124. if(reaction)
  125. reaction.remove();
  126. };
  127. const replaceBody = (event, relation) => {
  128. const content = event.getContent();
  129. const innerMsgItem = document.getElementById(`innermsg-${relation.event_id}`);
  130. const newBody = content['m.new_content'].body;
  131. if(innerMsgItem)
  132. innerMsgItem.innerHTML = `
  133. <pre>
  134. ${newBody + '<br/><b class="edited-marker" style="color:black">(edited)</b>'}
  135. </pre>
  136. `;
  137. };
  138. const prepareSession = (client) => {
  139. document.getElementById("room-menu").innerHTML = "Loading..."
  140. document.getElementById("title-text").innerHTML = 'Rooms';
  141. document.getElementById("left").innerHTML = "";
  142. document.getElementById("right").innerHTML = "";
  143. document.getElementById("middle").innerHTML = "SELECT";
  144. window.mClient = window.matrixcs.createClient({
  145. baseUrl: client.baseUrl,
  146. accessToken: client.access_token,
  147. userId: client.user_id,
  148. deviceId: client.device_id,
  149. sessionStore: new window.matrixcs.WebStorageSessionStore(localStorage),
  150. });
  151. window.mClient.on("sync", function(event) {
  152. if(!initSync) {
  153. gotoRoomView();
  154. // window.mClient.setDeviceVerified(window.mClient.getUserId(), window.mClient.getDeviceId()).then((d) => {
  155. // console.log('DEVICE VERIFIED');
  156. // })
  157. // window.mClient.uploadKeys();
  158. }
  159. });
  160. window.mClient.on("Room.timeline", function(event, room, toStartOfTimeline) {
  161. const eventType = event.getType();
  162. const relation = event.getRelation();
  163. // console.log(event);
  164. if (page === 'chat' && currentRoom === room.roomId ) {
  165. switch(eventType) {
  166. case 'm.room.message': {
  167. if(!relation) {
  168. const newMsg = messageBuilder(event);
  169. const conversationEl = document.getElementById("conversation");
  170. const typing = document.getElementsByClassName('typing');
  171. let newTyping = '';
  172. for(var i = 0; i < typing.length; i++) {
  173. const div = typing[i];
  174. div.remove();
  175. newTyping = newTyping + div.outerHTML;
  176. }
  177. conversationEl.innerHTML = conversationEl.innerHTML + newMsg + newTyping;
  178. if(document.activeElement.id === 'chat-textbox') {
  179. conversationEl.scrollTo(0, conversationEl.scrollHeight);
  180. }
  181. } else {
  182. if(relation.rel_type === 'm.replace' && !event.isEncrypted)
  183. replaceBody(event, relation);
  184. }
  185. return;
  186. }
  187. case 'm.reaction': {
  188. if(relation && relation.rel_type === 'm.annotation') {
  189. markReaction(event, relation);
  190. }
  191. return;
  192. }
  193. case 'm.room.redaction': {
  194. removeReaction(event.event.redacts);
  195. return;
  196. }
  197. }
  198. }
  199. });
  200. window.mClient.on("RoomMember.typing", function(event, member) {
  201. const room = window.mClient.getRoom(currentRoom);
  202. if(room) {
  203. const members = room.getMembers().map(({userId}) => userId);
  204. if (page === 'chat' && members.indexOf(member.userId) > -1) {
  205. if (member.typing) {
  206. const typingDiv = `<div id="typing-${member.userId}" class="typing">${member.name} is typing...</div>`;
  207. const conv = document.getElementById('conversation');
  208. conv.innerHTML = conv.innerHTML + typingDiv;
  209. if(conv.clientHeight - conv.scrollTop < 20) {
  210. conv.scrollTo(0, conv.scrollHeight);
  211. }
  212. }
  213. else {
  214. const typingDiv = document.getElementById(`typing-${member.userId}`);
  215. if(typingDiv)
  216. typingDiv.remove();
  217. }
  218. }
  219. }
  220. });
  221. // window.mClient.on("Room.receipt", function(event, member) {
  222. // const content = event.getContent();
  223. // const reads = Object.keys(content);
  224. // reads.forEach((eventId) => {
  225. // if(page === 'chat') {
  226. // const msg = document.getElementById(`msg-${eventId}`);
  227. // if(member.userId !== msg.getAttribute('sender')) {
  228. // const dateTime = document.getElementById(`datetime-${eventId}`);
  229. // if(dateTime)
  230. // dateTime.innerHTML = 'read <br/>' + dateTime.innerHTML;
  231. // }
  232. // }
  233. // })
  234. // })
  235. // window.mClient.initCrypto().then(() => {
  236. window.mClient.startClient({
  237. initialSyncLimit: 10,
  238. lazyLoadMembers: true,
  239. });
  240. // });
  241. };
  242. const login = (baseUrl, user, password) => {
  243. const client = window.matrixcs.createClient(baseUrl);
  244. client.loginWithPassword(user, password).then((d) => {
  245. const session = {...d, baseUrl};
  246. localStorage.setItem(LOCAL_STORAGE_SESSION_KEY, JSON.stringify(session));
  247. prepareSession(session);
  248. }).catch((e) => {
  249. console.error(e);
  250. alert('Login error! Check all inputs.')
  251. document.getElementById('middle').innerHTML = 'LOGIN';
  252. });
  253. };
  254. const sendReaction = (emoji) => {
  255. const el = document.getElementsByClassName('msgItem msgItem-selected')[0];
  256. if(el) {
  257. if(el.innerHTML.indexOf(emoji) > -1) { // has one already
  258. const react = el.getElementsByClassName(`${emoji} ${window.mClient.getUserId()}`)[0];
  259. if (react) {
  260. const eventId = react.id.substring(6);
  261. return window.mClient.redactEvent(currentRoom, eventId).then(() => {
  262. console.log('redacted!');
  263. }).catch((e) => {
  264. console.log(e);
  265. alert('Failed to redact reaction to message: ', JSON.stringify(e));
  266. });
  267. }
  268. }
  269. const event_id = el.id.substring(4);
  270. const content = {
  271. 'm.relates_to': {
  272. key: emoji,
  273. event_id,
  274. rel_type: 'm.annotation',
  275. }
  276. };
  277. const date = new Date();
  278. const tnxId = date.getTime()/1000;
  279. window.mClient.sendEvent(currentRoom, "m.reaction", content, tnxId).then(({event_id}) => {
  280. const room = window.mClient.getRoom(currentRoom);
  281. const newReact = document.getElementById(`react-~${room.roomId}:${tnxId}`);
  282. newReact.id = `react-${event_id}`;
  283. }).catch((e) => {
  284. console.log(e);
  285. alert(e.message);
  286. });
  287. }
  288. };
  289. const keydownListener = (e) => {
  290. if(page === 'login') {
  291. if(e.key === 'ArrowUp') {
  292. var inputs = document.getElementsByClassName('login-text');
  293. inputs[highlightedLoginInput].blur();
  294. highlightedLoginInput = highlightedLoginInput - 1;
  295. if(highlightedLoginInput === -1) {
  296. highlightedLoginInput = inputs.length - 1;
  297. }
  298. inputs[highlightedLoginInput].focus();
  299. } else if (e.key === 'ArrowDown') {
  300. var inputs = document.getElementsByClassName('login-text');
  301. inputs[highlightedLoginInput].blur();
  302. highlightedLoginInput = highlightedLoginInput + 1;
  303. if(highlightedLoginInput === inputs.length) {
  304. highlightedLoginInput = 0
  305. }
  306. inputs[highlightedLoginInput].focus();
  307. } else if (e.key === 'Enter' && document.getElementById('middle').innerHTML !== 'Loading...') {
  308. var baseUrl = document.getElementById('homeserver').value;
  309. var user = document.getElementById('user').value;
  310. var password = document.getElementById('password').value;
  311. document.getElementById('middle').innerHTML = 'Loading...'
  312. login(baseUrl, user, password);
  313. }
  314. return e;
  315. } else if (page === 'image') {
  316. if(e.key === 'Backspace') {
  317. setTimeout(() => {
  318. const conv = document.getElementById('conversation');
  319. conv.innerHTML = previousConv;
  320. previousConv = null;
  321. page = 'chat'
  322. const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
  323. if(msgItem) {
  324. msgItem.scrollIntoView({
  325. behavor: 'smooth',
  326. block: 'center',
  327. inline: 'nearest'
  328. });
  329. }
  330. }, 500);
  331. }
  332. } else {
  333. e.preventDefault();
  334. switch (e.key) {
  335. case "Enter": {
  336. if(page === 'rooms') {
  337. gotoChat();
  338. page = 'chat';
  339. } else if(page === 'chat') {
  340. const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
  341. if(msgItem) {
  342. const eventId = msgItem.id.substring(4);
  343. const image = document.getElementById(`image-${eventId}`)
  344. if(image) {
  345. const imageUrl = image.getAttribute('src');
  346. const conv = document.getElementById('conversation');
  347. previousConv = conv.innerHTML;
  348. conv.innerHTML = `<img src="${imageUrl}"/>`;
  349. page = 'image';
  350. }
  351. }
  352. }
  353. return;
  354. }
  355. case "ArrowUp": {
  356. if(page === 'rooms') {
  357. const cur = rooms[highlightedRoom];
  358. let num = highlightedRoom - 1
  359. if(num < 0) {
  360. num = rooms.length - 1;
  361. }
  362. const next = rooms[num];
  363. cur.className = "roomItem";
  364. next.className = "roomItem active";
  365. highlightedRoom = num;
  366. } else if(page === 'chat') {
  367. var msgs = document.getElementsByClassName('msgItem');
  368. if (highlightedMsg === 0) {
  369. document.getElementById('left').innerHTML = ''
  370. document.getElementById('middle').innerHTML = 'Loading...'
  371. document.getElementById('right').innerHTML = ''
  372. const room = window.mClient.getRoom(currentRoom);
  373. const timeline = room.getLiveTimeline();
  374. const originalLength = msgs.length;
  375. window.mClient.paginateEventTimeline(timeline, {backwards: true, limit: 10}).then((d) => {
  376. document.getElementById('left').innerHTML = 'Reply'
  377. document.getElementById('middle').innerHTML = 'SELECT';
  378. document.getElementById('right').innerHTML = 'Edit'
  379. const events = room.timeline;
  380. var relations = [];
  381. const eventList = events.reduce((a, event) => {
  382. const relation = event.getRelation();
  383. if(relation) {
  384. relations.push({event, relation});
  385. return a + '';
  386. }
  387. if (event.getType() === 'm.room.message') {
  388. return a + messageBuilder(event);
  389. }
  390. return a + '';
  391. }, '');
  392. document.getElementById("conversation").innerHTML = eventList;
  393. const newLength = document.getElementsByClassName('msgItem').length;
  394. highlightedMsg = newLength - originalLength;
  395. var newCurrent = document.getElementsByClassName('msgItem')[highlightedMsg];
  396. newCurrent.className = 'msgItem msgItem-selected';
  397. newCurrent.scrollIntoView({
  398. behavor: 'smooth',
  399. block: 'nearest',
  400. inline: 'nearest'
  401. });
  402. // highlightedMsg = document.getElementsByClassName('msgItem').findIndex((e) => e = newCurrent);
  403. relations.forEach(({event, relation}) => {
  404. const {
  405. event_id,
  406. rel_type,
  407. } = relation;
  408. const msgItem = document.getElementById(`msg-${event_id}`);
  409. if(msgItem) {
  410. if(rel_type === 'm.annotation') {
  411. markReaction(event, relation);
  412. } else if(rel_type === 'm.replace') {
  413. replaceBody(event, relation);
  414. }
  415. }
  416. return;
  417. });
  418. });
  419. return;
  420. } else if(highlightedMsg !== msgs.length) {
  421. msgs[highlightedMsg].className = 'msgItem';
  422. }
  423. highlightedMsg = highlightedMsg - 1;
  424. msgs[highlightedMsg].className = 'msgItem msgItem-selected';
  425. msgs[highlightedMsg].scrollIntoView({
  426. behavor: 'smooth',
  427. block: 'nearest',
  428. inline: 'nearest'
  429. });
  430. }
  431. return;
  432. }
  433. case "ArrowDown": {
  434. if(page === 'rooms') {
  435. const cur = rooms[highlightedRoom];
  436. let num = highlightedRoom + 1
  437. if(num > rooms.length - 1) {
  438. num = 0;
  439. }
  440. const next = rooms[num];
  441. cur.className = "roomItem";
  442. next.className = "roomItem active";
  443. highlightedRoom = num;
  444. } else if (page === 'chat') {
  445. var msgs = document.getElementsByClassName('msgItem');
  446. var mNext = highlightedMsg + 1;
  447. if(mNext === msgs.length && !scrollMsg) {
  448. msgs[highlightedMsg].className = 'msgItem';
  449. highlightedMsg = msgs.length;
  450. document.getElementById('chat-textbox').focus();
  451. } else {
  452. if ((msgs[mNext] && msgs[mNext].clientHeight > document.getElementById('conversation').clientHeight) || scrollMsg) {
  453. if(!scrollMsg) {
  454. scrollMsg = msgs[mNext].clientHeight;
  455. document.getElementById('conversation').scrollBy({
  456. top: 100,
  457. left: 0,
  458. behavor: 'smooth',
  459. });
  460. scrollMsg = scrollMsg - 100;
  461. } else if(scrollMsg <= document.getElementById('conversation').clientHeight) {
  462. scrollMsg = null;
  463. } else {
  464. document.getElementById('conversation').scrollBy({
  465. top: 100,
  466. left: 0,
  467. behavor: 'smooth',
  468. });
  469. scrollMsg = scrollMsg - 100;
  470. return;
  471. }
  472. }
  473. msgs[highlightedMsg].className = 'msgItem';
  474. if (mNext >= msgs.length) {
  475. document.getElementById('chat-textbox').focus();
  476. } else {
  477. highlightedMsg = mNext
  478. msgs[highlightedMsg].className = 'msgItem msgItem-selected';
  479. msgs[highlightedMsg].scrollIntoView({
  480. behavor: 'smooth',
  481. block: 'nearest',
  482. inline: 'nearest'
  483. });
  484. }
  485. }
  486. }
  487. return;
  488. }
  489. case "Shift":
  490. case "SoftRight": {
  491. if(page === 'chat') {
  492. const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
  493. if(msgItem) {
  494. const eventId = msgItem.id.substring(4);
  495. const innerMsg = document.getElementById(`innermsg-${eventId}`);
  496. if(innerMsg.style['align-self'] === 'flex-end') {
  497. let msg = innerMsg.innerText.trim();
  498. if(innerMsg.getElementsByClassName('edited-marker').length > 0) {
  499. msg = msg.slice(0, -9); // removed the "\n(edited)" part
  500. }
  501. document.getElementById('chat-textbox').value = msg;
  502. const editing = document.getElementById('editing');
  503. editing.style.display = 'unset';
  504. editingEvent = eventId;
  505. const chatBox = document.getElementById('chat-textbox');
  506. chatBox.focus();
  507. highlightedMsg += 1;
  508. if(chatBox.scrollHeight < 122) {
  509. chatBox.style.height = "auto";
  510. chatBox.style.height = (chatBox.scrollHeight) + "px";
  511. document.getElementById('editing').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
  512. document.getElementById('replying').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
  513. }
  514. }
  515. }
  516. }
  517. return;
  518. }
  519. case "Control":
  520. case "SoftLeft": {
  521. if(page === 'chat') {
  522. const msgItem = document.getElementsByClassName('msgItem msgItem-selected')[0];
  523. if(msgItem) {
  524. const eventId = msgItem.id.substring(4);
  525. const innerMsg = document.getElementById(`innermsg-${eventId}`);
  526. let msg = innerMsg.innerText.trim();
  527. if(innerMsg.getElementsByClassName('edited-marker').length > 0) {
  528. msg = msg.slice(0, -9); // removed the "\n(edited)" part
  529. }
  530. const replying = document.getElementById('replying');
  531. replying.style.display = 'unset';
  532. replyingToEvent = {
  533. eventId,
  534. sender: msgItem.getAttribute('sender'),
  535. body: msg,
  536. };
  537. const chatBox = document.getElementById('chat-textbox');
  538. chatBox.focus();
  539. highlightedMsg += 1;
  540. if(chatBox.scrollHeight < 122) {
  541. chatBox.style.height = "auto";
  542. chatBox.style.height = (chatBox.scrollHeight) + "px";
  543. document.getElementById('editing').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
  544. document.getElementById('replying').style.bottom = (chatBox.scrollHeight + 55 - 29) + "px";
  545. }
  546. }
  547. }
  548. return;
  549. }
  550. case "End":
  551. case "EndCall": {
  552. window.mClient.stopClient();
  553. close();
  554. return;
  555. }
  556. case "Backspace": {
  557. if(page === 'chat') {
  558. gotoRoomView();
  559. } else if (page === 'rooms') {
  560. // if(confirm("Close app?")) {
  561. window.mClient.stopClient();
  562. close();
  563. // }
  564. }
  565. return;
  566. }
  567. case "1": {
  568. sendReaction('👍');
  569. return;
  570. }
  571. case "2": {
  572. sendReaction('👎');
  573. return;
  574. }
  575. case "3": {
  576. sendReaction('❤️');
  577. return;
  578. }
  579. case "4": {
  580. sendReaction('🔥');
  581. return;
  582. }
  583. case "5": {
  584. sendReaction('🥰');
  585. return;
  586. }
  587. case "6": {
  588. sendReaction('🎉');
  589. return;
  590. }
  591. case "7": {
  592. sendReaction('😁');
  593. return;
  594. }
  595. case "8": {
  596. sendReaction('🤔');
  597. return;
  598. }
  599. case "9": {
  600. sendReaction('🤯');
  601. return;
  602. }
  603. case "0": {
  604. sendReaction('😱');
  605. return;
  606. }
  607. case "#": {
  608. sendReaction('😭');
  609. return;
  610. }
  611. case "*": {
  612. sendReaction('💩');
  613. return;
  614. }
  615. }
  616. }
  617. };
  618. document.addEventListener("DOMContentLoaded", async () => {
  619. const session = localStorage.getItem(LOCAL_STORAGE_SESSION_KEY);
  620. if(session) {
  621. const client = JSON.parse(session);
  622. prepareSession(client);
  623. } else {
  624. document.getElementById("title-text").innerHTML = 'Login';
  625. document.getElementById("login").style = 'display: unset';
  626. document.getElementById("left").innerHTML = "";
  627. document.getElementById("right").innerHTML = "";
  628. document.getElementById("middle").innerHTML = "LOGIN";
  629. document.getElementById('homeserver').focus();
  630. }
  631. const chatBox = document.getElementById('chat-textbox');
  632. const chatDownKeydownListener = (e) => {
  633. const editing = document.getElementById('editing');
  634. const replying = document.getElementById('replying');
  635. if(e.key === 'ArrowUp' && chatBox.selectionStart === 0) {
  636. chatBox.blur();
  637. chatBox.removeEventListener('keydown', chatDownKeydownListener);
  638. var msgs = document.getElementsByClassName('msgItem');
  639. if(msgs[highlightedMsg])
  640. msgs[highlightedMsg].className = 'msgItem';
  641. highlightedMsg = msgs.length;
  642. // msgs[highlightedMsg].style = 'background-color: lightgray';
  643. } else if (e.key === 'Call' || e.key === 'F1') {
  644. var msgs = document.getElementsByClassName('msgItem');
  645. if(msgs[highlightedMsg]) {
  646. chatBox.blur();
  647. chatBox.removeEventListener('keydown', chatDownKeydownListener);
  648. highlightedMsg = highlightedMsg - 1;
  649. msgs[highlightedMsg].scrollIntoView({
  650. behavor: 'smooth',
  651. block: 'center',
  652. inline: 'nearest'
  653. });
  654. }
  655. } else if(e.key === 'Backspace' && chatBox.selectionStart === 0) {
  656. setTimeout(() => {
  657. chatBox.blur();
  658. }, 500);
  659. gotoRoomView(chatBox);
  660. } else if ((e.key === "SoftLeft" || e.key === "Control") && page === 'chat' && document.getElementById("left").innerHTML !== '') {
  661. if (editing.style.display === 'unset') { // editing
  662. const body = document.getElementById('chat-textbox').value.trim();
  663. const content = {
  664. body: ' * ' + body,
  665. msgtype: "m.text",
  666. 'm.relates_to': {
  667. event_id: editingEvent,
  668. rel_type: 'm.replace',
  669. },
  670. 'm.new_content': {
  671. body,
  672. msgtype: 'm.text',
  673. }
  674. };
  675. if (body !== '') {
  676. document.getElementById("left").innerHTML = "";
  677. document.getElementById("right").innerHTML = "";
  678. document.getElementById("middle").innerHTML = "SAVING...";
  679. document.getElementById("chat-textbox").disabled = true;
  680. const date = new Date();
  681. const tnxId = date.getTime()/1000;
  682. window.mClient.sendEvent(currentRoom, "m.room.message", content, tnxId).then(({event_id}) => {
  683. document.getElementById("left").innerHTML = "Send";
  684. document.getElementById("right").innerHTML = "";
  685. document.getElementById("middle").innerHTML = "ENTER";
  686. document.getElementById('chat-textbox').value = '';
  687. document.getElementById("chat-textbox").disabled = false;
  688. document.getElementById("chat-textbox").style.height = "24px";
  689. document.getElementById('chat-textbox').selectionStart = 0
  690. document.getElementById('conversation').style.height = '100%';
  691. document.getElementById('editing').style.bottom = "55px";
  692. editing.style.display = 'none';
  693. editingEvent = null;
  694. // const msg = document.getElementsByClassName('msgItem msgItem-selected')[0];
  695. // msg.className = 'msgItem';
  696. // highlightedMsg = null;
  697. // const room = window.mClient.getRoom(currentRoom);
  698. // const newMsg = document.getElementById(`msg-~${room.roomId}:${tnxId}`);
  699. // const newInnerMsg = document.getElementById(`innermsg-~${room.roomId}:${tnxId}`);
  700. // newMsg.id = `msg-${event_id}`;
  701. // newInnerMsg.id = `innermsg-${event_id}`;
  702. return;
  703. }).catch((err) => {
  704. alert('Failed to send message: ', JSON.stringify(err));
  705. console.log(err);
  706. document.getElementById("left").innerHTML = "Send";
  707. document.getElementById("right").innerHTML = "";
  708. document.getElementById("middle").innerHTML = "ENTER";
  709. });
  710. }
  711. } else if (replying.style.display === 'unset') { // replying
  712. const body = document.getElementById('chat-textbox').value.trim();
  713. const content = {
  714. body: `> <${replyingToEvent.sender}> ${replyingToEvent.body}` + '\n\n' + body,
  715. format: 'org.matrix.custom.html',
  716. formatted_body: `<mx-reply><blockquote><a href=\"https://matrix.to/#/${currentRoom}/${replyingToEvent.eventId}">In reply to</a> <a href="https://matrix.to/#/${replyingToEvent.sender}">${replyingToEvent.sender}</a><br>${replyingToEvent.body}</blockquote></mx-reply>${body}`,
  717. msgtype: "m.text",
  718. 'm.relates_to': {
  719. 'm.in_reply_to': {
  720. event_id: replyingToEvent.eventId,
  721. },
  722. },
  723. };
  724. if (body !== '') {
  725. document.getElementById("left").innerHTML = "";
  726. document.getElementById("right").innerHTML = "";
  727. document.getElementById("middle").innerHTML = "SENDING...";
  728. document.getElementById("chat-textbox").disabled = true;
  729. const date = new Date();
  730. const tnxId = date.getTime()/1000;
  731. window.mClient.sendEvent(currentRoom, "m.room.message", content, tnxId).then(({event_id}) => {
  732. document.getElementById("left").innerHTML = "Send";
  733. document.getElementById("right").innerHTML = "";
  734. document.getElementById("middle").innerHTML = "ENTER";
  735. document.getElementById('chat-textbox').value = '';
  736. document.getElementById("chat-textbox").disabled = false;
  737. document.getElementById("chat-textbox").style.height = "24px";
  738. document.getElementById('chat-textbox').selectionStart = 0
  739. document.getElementById('conversation').style.height = '100%';
  740. document.getElementById('editing').style.bottom = "55px";
  741. replying.style.display = 'none';
  742. replyingToEvent = null;
  743. // const msg = document.getElementsByClassName('msgItem msgItem-selected')[0];
  744. // msg.className = 'msgItem';
  745. // highlightedMsg = null;
  746. const room = window.mClient.getRoom(currentRoom);
  747. const newMsg = document.getElementById(`msg-~${room.roomId}:${tnxId}`);
  748. const newInnerMsg = document.getElementById(`innermsg-~${room.roomId}:${tnxId}`);
  749. newMsg.id = `msg-${event_id}`;
  750. newInnerMsg.id = `innermsg-${event_id}`;
  751. return;
  752. }).catch((err) => {
  753. alert('Failed to send message: ', JSON.stringify(err));
  754. console.log(err);
  755. document.getElementById("left").innerHTML = "Send";
  756. document.getElementById("right").innerHTML = "";
  757. document.getElementById("middle").innerHTML = "ENTER";
  758. });
  759. }
  760. } else {
  761. const body = document.getElementById('chat-textbox').value.trim();
  762. const content = {
  763. body,
  764. msgtype: "m.text"
  765. };
  766. if (body !== '') {
  767. document.getElementById("left").innerHTML = "";
  768. document.getElementById("right").innerHTML = "";
  769. document.getElementById("middle").innerHTML = "SENDING...";
  770. document.getElementById("chat-textbox").disabled = true;
  771. const date = new Date();
  772. const tnxId = date.getTime()/1000;
  773. window.mClient.sendEvent(currentRoom, "m.room.message", content, tnxId).then(({event_id}) => {
  774. document.getElementById("left").innerHTML = "Send";
  775. document.getElementById("right").innerHTML = "";
  776. document.getElementById("middle").innerHTML = "ENTER";
  777. document.getElementById('chat-textbox').value = '';
  778. document.getElementById("chat-textbox").disabled = false;
  779. document.getElementById("chat-textbox").style.height = "24px";
  780. document.getElementById('chat-textbox').selectionStart = 0
  781. document.getElementById('conversation').style.height = '100%';
  782. document.getElementById('replying').style.bottom = "55px";
  783. const room = window.mClient.getRoom(currentRoom);
  784. const newMsg = document.getElementById(`msg-~${room.roomId}:${tnxId}`);
  785. const newInnerMsg = document.getElementById(`innermsg-~${room.roomId}:${tnxId}`);
  786. newMsg.id = `msg-${event_id}`;
  787. newInnerMsg.id = `innermsg-${event_id}`;
  788. return;
  789. }).catch((err) => {
  790. alert('Failed to send message: ', JSON.stringify(err));
  791. console.log(err);
  792. document.getElementById("left").innerHTML = "Send";
  793. document.getElementById("right").innerHTML = "";
  794. document.getElementById("middle").innerHTML = "ENTER";
  795. });
  796. }
  797. }
  798. } else if ((e.key === "SoftRight" || e.key === "Shift") && page === 'chat' && document.getElementById("right").innerHTML !== '') { // cancel edit
  799. editing.style.display = 'none';
  800. replying.style.display = 'none';
  801. document.getElementById('chat-textbox').value = '';
  802. document.getElementById('chat-textbox').selectionStart = 0
  803. document.getElementById("chat-textbox").style.height = "24px";
  804. document.getElementById('editing').style.bottom = "55px";
  805. document.getElementById('replying').style.bottom = "55px";
  806. document.getElementById("left").innerHTML = "Send";
  807. document.getElementById("right").innerHTML = "";
  808. document.getElementById("middle").innerHTML = "ENTER";
  809. editingEvent = null;
  810. replyingToEvent = null;
  811. const msg = document.getElementsByClassName('msgItem msgItem-selected')[0];
  812. msg.className = 'msgItem';
  813. highlightedMsg = null;
  814. }
  815. };
  816. chatBox.addEventListener("focus", (e) => {
  817. const editing = document.getElementById('editing');
  818. const replying = document.getElementById('replying');
  819. if(editing.style.display === 'unset') { // is editting
  820. document.getElementById("left").innerHTML = "Save";
  821. document.getElementById("right").innerHTML = "Discard";
  822. document.getElementById("middle").innerHTML = "ENTER";
  823. } else if (replying.style.display === 'unset') {
  824. document.getElementById("left").innerHTML = "Send";
  825. document.getElementById("right").innerHTML = "Discard";
  826. document.getElementById("middle").innerHTML = "ENTER";
  827. } else { // new msg
  828. document.getElementById("left").innerHTML = "Send";
  829. document.getElementById("right").innerHTML = "";
  830. document.getElementById("middle").innerHTML = "ENTER";
  831. const scrollHeight = document.getElementById("conversation").scrollHeight;
  832. document.getElementById("conversation").scrollTo(0, scrollHeight);
  833. }
  834. document.removeEventListener('keydown', keydownListener);
  835. chatBox.addEventListener('keydown', chatDownKeydownListener)
  836. });
  837. chatBox.addEventListener("blur", (e) => {
  838. if(page === 'chat') {
  839. document.getElementById("left").innerHTML = "Reply";
  840. document.getElementById("right").innerHTML = "Edit";
  841. document.getElementById("middle").innerHTML = "SELECT";
  842. }
  843. document.addEventListener('keydown', keydownListener);
  844. });
  845. // auto resize the text boxes
  846. const tx = document.getElementsByTagName("textarea");
  847. for (let i = 0; i < tx.length; i++) {
  848. tx[i].setAttribute("style", "height:24px;overflow-y:hidden;");
  849. tx[i].addEventListener("input", OnInput, false);
  850. };
  851. function OnInput() {
  852. if(this.scrollHeight < 122) {
  853. this.style.height = "auto";
  854. this.style.height = (this.scrollHeight) + "px";
  855. const conv = document.getElementById("conversation");
  856. conv.style.height = `calc(100% - ${this.scrollHeight + 52}px)`;
  857. if(document.getElementById('editing').style.display === 'none'
  858. &&
  859. document.getElementById('replying').style.display === 'none') {
  860. conv.scrollTo(0, conv.scrollHeight);
  861. }
  862. document.getElementById('editing').style.bottom = (this.scrollHeight + 55 - 29) + "px";
  863. document.getElementById('replying').style.bottom = (this.scrollHeight + 55 - 29) + "px";
  864. }
  865. };
  866. });
  867. const gotoChat = () => {
  868. const roomEl = document.getElementsByClassName('roomItem active')[0];
  869. const id = roomEl.id.substring(5);
  870. const room = window.mClient.getRoom(id);
  871. // const timeline = room.getLiveTimeline();
  872. // const rest = window.mClient.paginateEventTimeline(timeline, {backwards: true}).then((d) => {
  873. const events = window.mClient.getRoom(id).timeline;
  874. var relations = [];
  875. const eventList = events.reduce((a, event) => {
  876. const relation = event.getRelation();
  877. if(relation) {
  878. relations.push({event, relation});
  879. return a + '';
  880. }
  881. if (event.getType() === 'm.room.message') {
  882. // window.mClient.decryptEventIfNeeded(event, {isRetry: false, emit: true}).then((d) => {
  883. // const events = window.mClient.getRoom(id).timeline;
  884. // console.log('DECRYPTED: ', d);
  885. // })
  886. return a + messageBuilder(event);
  887. }
  888. return a + '';
  889. }, '');
  890. document.getElementById("conversation").innerHTML = eventList;
  891. relations.forEach(({event, relation}) => {
  892. const {
  893. event_id,
  894. rel_type,
  895. } = relation;
  896. const msgItem = document.getElementById(`msg-${event_id}`);
  897. if(msgItem) {
  898. if(rel_type === 'm.annotation') {
  899. markReaction(event, relation);
  900. } else if(rel_type === 'm.replace') {
  901. replaceBody(event, relation);
  902. }
  903. }
  904. return;
  905. });
  906. document.getElementById('room-menu').style = 'display: none';
  907. document.getElementById('chat').style = 'display: flex';
  908. document.getElementById('chat-textbox').value = '';
  909. document.getElementById('chat-textbox').focus();
  910. document.getElementById("title-text").innerHTML = room.name;
  911. document.getElementById("left").innerHTML = "Send";
  912. document.getElementById("right").innerHTML = "";
  913. document.getElementById("middle").innerHTML = "ENTER";
  914. setTimeout(() => {
  915. const scrollHeight = document.getElementById("conversation").scrollHeight;
  916. document.getElementById("conversation").scrollTo(0, scrollHeight);
  917. }, 500);
  918. currentRoom = id;
  919. };
  920. document.addEventListener("keydown", keydownListener);