diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | src/index.html (renamed from index.html) | 2 | ||||
-rw-r--r-- | src/main.css (renamed from main.css) | 31 | ||||
-rw-r--r-- | src/main.ts (renamed from main.js) | 170 | ||||
-rw-r--r-- | tsconfig.json | 17 |
7 files changed, 171 insertions, 63 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build
\ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a93bc93 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: build + +build: copy + mkdir -p build + tsc --outDir build/ + +copy: + cp src/index.html build/ + cp src/main.css build/ + +clean: + rm -r build
\ No newline at end of file @@ -0,0 +1 @@ +Reply to messages diff --git a/index.html b/src/index.html index a3fb0ab..38bbbc8 100644 --- a/index.html +++ b/src/index.html @@ -5,7 +5,7 @@ <link rel="stylesheet" href="main.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css" integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY=" crossorigin="anonymous"> </head> - <body onload="onload()"> + <body onload="load()"> <span id="navbar"> <span>Localhost</span> <span id = "info" class="UI">Server Information: </span> @@ -5,6 +5,7 @@ display: flex; flex-flow: column; overflow-y: scroll; + overflow-x: clip; word-break: break-word; scroll-snap-align: end; flex-basis: 100vh; @@ -15,12 +16,12 @@ } .message { display: flex; - height: 1.6em; + flex-basis: 1.6em; padding-left: 2px; padding-right: 2px; - align-items: center; + flex-shrink: 0; } -.message:hover > :last-child { +.message:hover > :last-child > :first-child { display: flex; } .messagetext { @@ -29,13 +30,34 @@ } .messagedate { font-size: 12px; + align-self: start; + margin-top: 4px; + word-break: keep-all; + } .message:not(.deleted) > .messagedate { color: #666; } +.message_buttons_anchor { + position: relative; +} .message_buttons { display: none; + position: absolute; + /* calc (((button height + anchor padding) / 2) * -1) */ + top: calc(((1.6em + 4px) / 2) * -1); + /* calc ((buttons amount(button size + button padding) + anchor padding) * -1) */ + left: calc((3(1.6em + 4px) + 4px) * -1); + right: 0; + bottom: 0; + + margin-right: 8px; + padding: 2px; + height: min-content; + background-color: white; + border: 1px solid black; + border-radius: 2px; } .message_buttons > button { height: 1.6em; @@ -55,7 +77,8 @@ display: flex; } #inputbox > button { - padding: 0 5px 0 5px; + /* padding: 0 10px 0 10px; */ + width: 4em; } #input { resize: none; @@ -1,16 +1,31 @@ -const messages = new Map(); -const users = new Map(); +interface User { + id: number, + username: string, + deleted: boolean, +} +interface Message { + id: number, + sender: number, + message: string, + reply_to: number, + date: number, + deleted: boolean, +} + + +const messages = new Map<number, Message>(); +const users = new Map<number, User>(); let sent = false; const host = location.protocol + "//" + location.host; -function xhttp_request(location) { +function xhttp_request(location: string): Promise<any> { let f = fetch(`${host}/api/${location}`); return f.then((response) => response.json()); } -function xhttp_send(method, location, json) { +function xhttp_send(method: string, location: string, json: string) { let f = fetch(`${host}/api/${location}`, { method: method, headers: {"Content-Type": "application/json"}, @@ -21,8 +36,7 @@ function xhttp_send(method, location, json) { } -async function format_message(m) { - var s; +async function format_message(m: Message): Promise<string> { var sender = await get_user_name(m.sender); var sclass = ""; @@ -30,17 +44,22 @@ async function format_message(m) { m.message = "*deleted*" sclass = "deleted" } - s = `<span id="${m.id}" class="message ${sclass}"><p class="messagedate">${format_date(m.date)}</p><p class="messagetext"> ${sender}: ${m.message}</p> - <span class="message_buttons"> - <button title="${m.id}"><i class="fa fa-info" aria-hidden="true"></i></button> - <button title="reply"><i class="fa fa-reply" aria-hidden="true"></i></button> - <button title="delete" onclick="delete_message(${m.id})"><i class="fa fa-trash-o" aria-hidden="true"></i></button> + + let s = `<span id="${m.id}" class="message ${sclass}"> + <p class="messagedate">${format_date(m.date)}</p> + <p class="messagetext"> ${sender}: ${m.message}</p> + <span class="message_buttons_anchor"> + <span class="message_buttons"> + <button title="information" onclick="message_info(${m.id})"><i class="fa fa-info" aria-hidden="true"></i></button> + <button title="reply"><i class="fa fa-reply" aria-hidden="true"></i></button> + <button title="delete" onclick="delete_message(${m.id})"><i class="fa fa-trash-o" aria-hidden="true"></i></button> + </span> </span></span>` return s } -function format_time(t) { +function format_time(t: number) { let as_str = `${t}`; if (as_str.length < 2) { return `0${t}` @@ -49,7 +68,7 @@ function format_time(t) { } } -function format_date(d) { +function format_date(d: number) { let date = new Date(d * 1000); let hours = format_time(date.getHours()); let minutes = format_time(date.getMinutes()); @@ -57,36 +76,50 @@ function format_date(d) { return `${hours}:${minutes}:${seconds}`; } -function delete_message(id) { +function delete_message(id: number) { xhttp_send("DELETE", "delete_message", JSON.stringify(id)).then((response) => { if (response.Ok == undefined) { alert("Could not delete message") } else { let old_msg = messages.get(id); - old_msg.message = "*deleted*"; - old_msg.deleted = true; - messages.set(id, old_msg); - - chatwindow(null).then(); + if (old_msg != undefined) { + old_msg.message = "*deleted*"; + old_msg.deleted = true; + messages.set(id, old_msg); + + chatwindow(null).then(); + } else { + throw new Error("Could not find message") + } } }) } +function message_info(id: number): void { + let msg = messages.get(id); + + if (msg != undefined) { + let final = `id: ${msg.id}\nsender: ${msg.sender}\nmessage: ${msg.message}\ndate: ${msg.date}\nreply_to: ${msg.reply_to}\ndelted: ${msg.deleted}`; + alert(final) + } +} + function send() { - var input = document.getElementById("input").value; - let user_id = get_user_from_name().then((user_id) => { + var input = getelementbyid("input"); + + get_user_from_name().then((user_id) => { if (user_id == undefined) { alert("User not found") return undefined } - let message = JSON.stringify({"sender": user_id, "message": input}) + let message = JSON.stringify({"sender": user_id, "message": input.value}) - var id = xhttp_send("POST", "send_message", message).then((id) => { + xhttp_send("POST", "send_message", message).then((id) => { if (id.Ok != undefined) { get_message(id.Ok); - document.getElementById("input").value = "" + input.value = ""; sent = true } else { alert(id.Err) @@ -98,7 +131,7 @@ function send() { function get_newest_cache_id() { let last = -1; - for (k of messages.keys()) { + for (let k of messages.keys()) { if (k > last) { last = k } @@ -122,7 +155,7 @@ function get_newest_messages() { } - let final_array = []; + let final_array: Message[] = []; if (newest_id == newest_cache_id) { return @@ -145,8 +178,8 @@ function get_newest_messages() { let newest = delta; - let list = get_message_list(oldest, newest); - for (x of list.values()) { + let list = await get_message_list(oldest, newest); + for (let x of list.values()) { final_array.push(x) } @@ -160,12 +193,12 @@ function get_newest_messages() { } let list = await get_message_list(newest_cache_id, newest_id) - for (x of list.values()) { + for (let x of list.values()) { final_array.push(x) } // Insert all values into the message map - for (x of final_array.values()) { + for (let x of final_array.values()) { messages.set(x.id, x) } @@ -173,17 +206,17 @@ function get_newest_messages() { }); } -async function get_message_list(oldest, newest) { +async function get_message_list(oldest, newest): Promise<Message[]> { let data = await xhttp_request(`get_message_list/${oldest}/${newest}`).then(); let req = data.Ok; return req } -function get_message(id) { +function get_message(id: number): void { let cached = messages.get(id) if (cached != undefined) { - return to_dom(format_message(cached)) - } + chatwindow(id) + } // Get message xhttp_request("get_message/" + id).then((data) => { @@ -197,9 +230,9 @@ function get_message(id) { } async function get_user_from_name() { - let v = document.getElementById("username").value; + let v = getelementbyid("username").value; - for (x of users.values()) { + for (let x of users.values()) { if (x.username == v) { return x.id } @@ -242,20 +275,32 @@ async function get_user_name(id) { return user.username } -function to_dom(x) { - // Pretty hacky if I do say so myself - let dom = new DOMParser().parseFromString(x, "text/html").firstChild.lastChild.firstChild; - return dom +// Pretty hacky if I do say so myself +function to_dom(x: string): Node { + let dom = new DOMParser().parseFromString(x, "text/html"); + + if (dom != null) { + if (dom.firstChild) { + if (dom.firstChild.lastChild) { + if (dom.firstChild.lastChild.firstChild) { + return dom.firstChild.lastChild.firstChild + } + } + } + } + + throw new Error("Could not convert to dom") } -async function chatwindow(hash) { - let window = document.getElementById("chatwindow"); +async function chatwindow(hash: any = undefined) { + let window: HTMLDivElement = getelementbyid("chatwindow"); + let new_messages: HTMLSpanElement = getelementbyid("new_messages") let pre_scroll = window.scrollHeight - window.clientHeight; window.innerHTML = ""; - let array = []; + let array: Message[] = []; - for (v of messages.values()) { + for (let v of messages.values()) { array.push(v); } @@ -267,7 +312,7 @@ async function chatwindow(hash) { let old_date = new Date(0); let latest_v; - for (v of array) { + for (let v of array) { latest_v = v; let date = new Date(v.date * 1000); if (date.getDate() > old_date.getDate() || date.getFullYear() > old_date.getFullYear() || date.getMonth() > date.getMonth()) { @@ -276,10 +321,9 @@ async function chatwindow(hash) { } let data = await format_message(v).then() - let dom = to_dom(data); + let dom: Node = to_dom(data); window.appendChild(dom); - } if (hash == null) { @@ -288,28 +332,29 @@ async function chatwindow(hash) { if (window.scrollTop == pre_scroll) { if (latest_v != undefined && hash == undefined) { - document.getElementById(v.id).scrollIntoView(); + getelementbyid(latest_v.id).scrollIntoView(); } } else { - document.getElementById("new_messages").style.display = "unset" + new_messages.style.display = "unset" } if (hash != undefined) { - document.getElementById(hash).scrollIntoView(); + getelementbyid(hash.toString()).scrollIntoView(); } } function onscrollchat() { - let window = document.getElementById("chatwindow"); + let window: HTMLDivElement = getelementbyid("chatwindow"); + let new_messages: HTMLSpanElement = getelementbyid("new_messages") let pre_scroll = window.scrollHeight - window.clientHeight; if (window.scrollTop == pre_scroll) { - document.getElementById("new_messages").style.display = ""; + new_messages.style.display = ""; } } -function inputkey(event) { - let window = document.getElementById("input"); +function inputkey(event: KeyboardEvent) { + let window: HTMLTextAreaElement = getelementbyid("input"); // If the cursor pos is at the end of the textarea let comp = window.selectionEnd - window.value.length @@ -321,8 +366,8 @@ function inputkey(event) { } } -function clearnewlineinput() { - let window = document.getElementById("input"); +function clearnewlineinput(): void { + let window: HTMLTextAreaElement = getelementbyid("input"); if (window.value == "\n" && sent == true) { window.value = "" @@ -330,10 +375,19 @@ function clearnewlineinput() { } } -function onload() { +function getelementbyid(s: string): any { + let x = document.getElementById(s); + if (x == null) { + throw new Error("Could not find element") + } else { + return x + } +} + +function load(): void { xhttp_request("").then((server_info) => { - document.getElementById("info").innerHTML += server_info.version + ", " + server_info.name + ", users: " + server_info.users; + getelementbyid("info").innerHTML += server_info.version + ", " + server_info.name + ", users: " + server_info.users; // Get ready for websockets implementation in rocket_test 0.3 if (server_info.version[2] < 3 ) { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9443036 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "node", + "target": "ES2020", + "jsx": "preserve", + "strictNullChecks": true, + "strictFunctionTypes": true + }, + "include": [ + "src/*" + ], + "exclude": [ + "node_modules", + "**/node_modules/*" + ] +}
\ No newline at end of file |