diff options
Diffstat (limited to 'src/main.ts')
-rw-r--r-- | src/main.ts | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..cec62d8 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,399 @@ +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: string): Promise<any> { + let f = fetch(`${host}/api/${location}`); + return f.then((response) => response.json()); + +} + +function xhttp_send(method: string, location: string, json: string) { + let f = fetch(`${host}/api/${location}`, { + method: method, + headers: {"Content-Type": "application/json"}, + body: json + }) + + return f.then((response) => response.json()); +} + + +async function format_message(m: Message): Promise<string> { + var sender = await get_user_name(m.sender); + var sclass = ""; + + if (m.deleted) { + m.message = "*deleted*" + sclass = "deleted" + } + + 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: number) { + let as_str = `${t}`; + if (as_str.length < 2) { + return `0${t}` + } else { + return `${t}` + } +} + +function format_date(d: number) { + let date = new Date(d * 1000); + let hours = format_time(date.getHours()); + let minutes = format_time(date.getMinutes()); + let seconds = format_time(date.getSeconds()); + return `${hours}:${minutes}:${seconds}`; +} + +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); + 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 = 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.value}) + + xhttp_send("POST", "send_message", message).then((id) => { + if (id.Ok != undefined) { + get_message(id.Ok); + + input.value = ""; + sent = true + } else { + alert(id.Err) + sent = true + } + }) + }); +} + +function get_newest_cache_id() { + let last = -1; + for (let k of messages.keys()) { + if (k > last) { + last = k + } + } + + if (last == -1) { + return undefined + } else { + return last + } +} + +function get_newest_messages() { + xhttp_request("get_message_id_newest").then(async (newest_id) => { + let newest_cache_id = get_newest_cache_id(); + + if (newest_id.Err) { + return + } else { + newest_id = newest_id.Ok + } + + + let final_array: Message[] = []; + + if (newest_id == newest_cache_id) { + return + } + + if (newest_cache_id == undefined) { + let id = newest_id; + if (id - 25 < 0) { + get_message(0) + } else { + get_message(newest_id - 25) + } + return get_newest_messages() + } + + if (newest_id > newest_cache_id) { + let delta = newest_id - newest_cache_id ; + if (delta > 25) { + let oldest = newest_id - 25; + let newest = delta; + + + let list = await get_message_list(oldest, newest); + for (let x of list.values()) { + final_array.push(x) + } + + newest_id = oldest; + if (oldest - 25 < 0) { + newest_cache_id = 0 + } else { + newest_cache_id = oldest - 25 + } + } + } + + let list = await get_message_list(newest_cache_id, newest_id) + for (let x of list.values()) { + final_array.push(x) + } + + // Insert all values into the message map + for (let x of final_array.values()) { + messages.set(x.id, x) + } + + chatwindow().then() + }); +} + +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: number): void { + let cached = messages.get(id) + if (cached != undefined) { + chatwindow(id) + } + + // Get message + xhttp_request("get_message/" + id).then((data) => { + if (data.Ok != undefined) { + let msg = data.Ok; + messages.set(msg.id, msg) + + chatwindow(msg.id) + } + }) +} + +async function get_user_from_name() { + let v = getelementbyid("username").value; + + for (let x of users.values()) { + if (x.username == v) { + return x.id + } + } + + + let user = await xhttp_send("POST", "get_user_by_name", JSON.stringify(v)); + if (user.Ok) { + users.set(user.id, user) + return user.Ok.id + } else { + return await create_user(v) + } +} + +async function create_user(username) { + let response = await xhttp_send("POST", "create_user", JSON.stringify(username)); + if (response.Ok != undefined) { + await get_user_name(response.Ok); + return response.Ok + + } else { + alert("Could not create user") + return undefined + } +} + +async function get_user_name(id) { + let cached = users.get(id) + if (cached != undefined) { + return cached.username + } + + + let data = await xhttp_request("get_user/" + id,); + let user = data.Ok; + + users.set(user.id, user) + + return user.username +} + +// 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: 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: Message[] = []; + + for (let v of messages.values()) { + array.push(v); + } + + array.sort( + function(a, b) { + return a.id - b.id + } + ); + + let old_date = new Date(0); + let latest_v; + 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()) { + old_date = date; + window.append(to_dom(`<span class="date">${date.getDate()}/${date.getMonth()}/${date.getFullYear()}</span>`)) + } + + let data = await format_message(v).then() + let dom: Node = to_dom(data); + + window.appendChild(dom); + } + + if (hash == null) { + return + } + + if (window.scrollTop == pre_scroll) { + if (latest_v != undefined && hash == undefined) { + getelementbyid(latest_v.id).scrollIntoView(); + } + } else { + new_messages.style.display = "unset" + } + + if (hash != undefined) { + getelementbyid(hash.toString()).scrollIntoView(); + } +} + +function onscrollchat() { + let window: HTMLDivElement = getelementbyid("chatwindow"); + let new_messages: HTMLSpanElement = getelementbyid("new_messages") + let pre_scroll = window.scrollHeight - window.clientHeight; + + if (window.scrollTop == pre_scroll) { + new_messages.style.display = ""; + } +} + +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 + + let key = event.key + let shift = event.shiftKey; + if (key == "Enter" && !shift && comp == 0) { + send() + } +} + +function clearnewlineinput(): void { + let window: HTMLTextAreaElement = getelementbyid("input"); + + if (window.value == "\n" && sent == true) { + window.value = "" + sent = false + } +} + +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) => { + + 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 ) { + const interval = setInterval(get_newest_messages, 1000); + } else { + // Initiate websocket "polling" + } + }) +} |