diff --git a/assets/HypertextPages/list-rooms.nytl.html b/assets/HypertextPages/list-rooms.nytl.html index 1fd7a4c..a1a94e4 100644 --- a/assets/HypertextPages/list-rooms.nytl.html +++ b/assets/HypertextPages/list-rooms.nytl.html @@ -65,8 +65,9 @@ x + -{% ENDELDEF %} \ No newline at end of file +{% ENDELDEF %} diff --git a/assets/css/list-rooms.css b/assets/css/list-rooms.css index 9d65f23..d473575 100644 --- a/assets/css/list-rooms.css +++ b/assets/css/list-rooms.css @@ -3,7 +3,8 @@ } #CL-bacbe { - margin: 6px; + margin-top: 6px; + margin-bottom: 4px; } .CL-my-chat-box { diff --git a/assets/js/chat.js b/assets/js/chat.js index 6ce80db..d7f5974 100644 --- a/assets/js/chat.js +++ b/assets/js/chat.js @@ -28,9 +28,6 @@ let members = new Map(); for (let memberSt of initial_chatUpdResp.members){ members.set(memberSt.id, memberSt); } -// members.set(1, {id: 1, name: 'grisha', nickname: 'gri', role: 'admin'}); -// members.set(2, {id: 2, name: 'Pavlov Vladimir', nickname: 'pv', role: 'regular'}); -// members.set(3, {id: 3, name: 'Ivan', nickname: 'ivan', role: 'read-only'}); function updateOffsetOfVisibleMsg(msgId, offset){ visibleMessages.get(msgId).style.bottom = String(offset) + "px"; @@ -180,13 +177,10 @@ function test(id, uid){ } let mainloopTimeout = null; - let mainloopPoller = null; - function setMainloopTimeout(){ mainloopTimeout = setTimeout(mainloopPoller, 1000); } - mainloopPoller = function(){ try { console.log("Hello, World!"); diff --git a/assets/js/common.js b/assets/js/common.js new file mode 100644 index 0000000..1b1a01a --- /dev/null +++ b/assets/js/common.js @@ -0,0 +1,52 @@ +async function apiRequest(type, req){ + let A = await fetch("/api/" + type, + {method: 'POST', body: JSON.stringify(req)}); + let B = await A.json(); + if (B.status !== 0) + throw Error("Server returned non-zero status"); + return B; +} + +/* Framework for pages with mainloop (it can be npt only polling, but also literally anything else */ +let __mainloopDelayMs = 3000; +let mainloopTimeout = null; +let mainloopPoller = null; +let __guestMainloopPollerAction = null; +function setMainloopTimeout(){ + mainloopTimeout = setTimeout(mainloopPoller, __mainloopDelayMs); +} +function cancelMainloopTimeout(){ + clearTimeout(mainloopTimeout); +} +mainloopPoller = function(){ + try { + console.log("Hello, World!"); + __guestMainloopPollerAction(); + } catch (error){ + console.log(error) + } + setMainloopTimeout(); +} + +// 1 +const userChatRoleAdmin = "admin"; +// 2 +const userChatRoleRegular = "regular"; +// 3 +const userChatRoleReadOnly = "read-only"; +// 4 +const userChatRoleDeleted = "not-a-member"; + +function roleToColor(role) { + if (role === userChatRoleAdmin) { + return "#aafff3"; + } else if (role === userChatRoleRegular){ + return "#ffffff"; + + } else if (role === userChatRoleReadOnly){ + return "#bfb2b2"; + } else if (role === userChatRoleDeleted) { + return "#fb4a4a"; + } + return "#286500" // Bug +} diff --git a/assets/js/list-rooms.js b/assets/js/list-rooms.js index 09d51d1..dc42473 100644 --- a/assets/js/list-rooms.js +++ b/assets/js/list-rooms.js @@ -1,12 +1,35 @@ let LocalHistoryId = 0; +function genSentBase(){ + return { + 'chatListUpdReq': { + 'LocalHistoryId': LocalHistoryId + } + }; +} + let myChats = new Map(); let chatBoxes = new Map(); -const roleDeleted = "not-a-member"; +/* Generate text that is displayed on the right side of chat intro box */ +function youAreXHere(myRoleHere){ + return "You are " + myRoleHere + " here"; +} + + +let chatRenunciationWinStoredId = -1; + +/* Updating chat html box after myMembershipSt in it was updated */ +function updateBoxWithNewSt(box, myMembershipSt){ + let ID = myMembershipSt.chatId; + let roleP = box.querySelector(".CL-my-chat-box-my-role"); + roleP.innerText = youAreXHere(myMembershipSt.myRoleHere); + box.style.backgroundColor = roleToColor(myMembershipSt.myRoleHere); +} function convertStToBox(myMembershipSt){ let chatURI = "/user/" + myMembershipSt.chatNickname; + let ID = myMembershipSt.chatId; let box = document.createElement("div"); chatBoxes.set(myMembershipSt.chatId, box); @@ -15,10 +38,9 @@ function convertStToBox(myMembershipSt){ let inBoxNickname = document.createElement("a"); box.appendChild(inBoxNickname); inBoxNickname.className = "entity-nickname-txt CL-my-chat-box-nickname"; - console.log(myMembershipSt); - console.log(myMembershipSt.chatNickname); inBoxNickname.innerText = myMembershipSt.chatNickname; inBoxNickname.href = chatURI; + box.style.backgroundColor = roleToColor(myMembershipSt.myRoleHere); let inBoxName = document.createElement("a"); box.appendChild(inBoxName); @@ -29,9 +51,7 @@ function convertStToBox(myMembershipSt){ let inBoxMyRoleHere = document.createElement("p"); box.appendChild(inBoxMyRoleHere); inBoxMyRoleHere.className = "entity-reg-field-txt CL-my-chat-box-my-role"; - inBoxMyRoleHere.innerText = "You are " + myMembershipSt.myRoleHere + " here"; - - let ID = myMembershipSt.chatId; + inBoxMyRoleHere.innerText = youAreXHere(myMembershipSt.myRoleHere); let inBoxLeaveBtn = document.createElement("img"); box.appendChild(inBoxLeaveBtn); @@ -40,19 +60,62 @@ function convertStToBox(myMembershipSt){ inBoxLeaveBtn.onclick = function (ev) { if (ev.button !== 0) return; - console.log("Tried to leave chat" + ID); + chatRenunciationWinStoredId = ID; activatePopupWindowById("chat-renunciation-win"); + document.getElementById("chat-renunciation-win-title").innerText = + "Do you really want to leave chat " + myMembershipSt.chatNickname + "?"; }; return box; } +function updateLocalStateFromChatListUpdResp(chatListUpdResp){ + LocalHistoryId = chatListUpdResp.HistoryId; + + let literalChatList = document.getElementById("CL-dblec"); + + for (let myMembershipSt of chatListUpdResp.myChats){ + let chatId = myMembershipSt.chatId; + console.log(myMembershipSt); + if (myChats.has(chatId)){ + myChats.set(chatId, myMembershipSt); + updateBoxWithNewSt(chatBoxes.get(chatId), myMembershipSt); + } else { + if (myMembershipSt.myRoleHere === userChatRoleDeleted) + continue; + myChats.set(chatId, myMembershipSt); + let box = convertStToBox(myMembershipSt) + chatBoxes.set(chatId, box); + literalChatList.appendChild(box); + } + } +} + +/* Use it ONLY if `Recv` reported success */ +function updateLocalStateFromRecv(Recv){ + updateLocalStateFromChatListUpdResp(Recv.chatListUpdResp); +} + function configureChatCreationInterface(){ document.getElementById("chat-creation-win-yes").onclick = function (ev) { if (ev.button !== 0) return; + let chatNicknameInput = document.getElementById("chat-nickname-input"); + let chatNameInput = document.getElementById("chat-name-input"); + let nickname = chatNicknameInput.value; + let name = chatNameInput.value; deactivateActivePopup(); - // todo: create chat, send request + let Sent = genSentBase(); + Sent.content = {}; + Sent.content.nickname = nickname; + Sent.content.name = name; + apiRequest("createChat", Sent + ).then((Recv) => { + updateLocalStateFromRecv(Recv); + }).catch((e) => { + alert("Failed to create chat"); + console.log(e); + }); }; document.getElementById("chat-creation-win-no").onclick = function (ev) { @@ -64,6 +127,10 @@ function configureChatCreationInterface(){ document.getElementById("CL-bacbe").onclick = function (ev){ if (ev.button !== 0) return; + let chatNicknameInput = document.getElementById("chat-nickname-input"); + let chatNameInput = document.getElementById("chat-name-input"); + chatNicknameInput.value = ""; + chatNameInput.value = ""; activatePopupWindowById("chat-creation-win"); console.log("Tried to show chat creation window"); }; @@ -74,7 +141,18 @@ function configureChatRenunciationInterfaceWinPart(){ if (ev.button !== 0) return; deactivateActivePopup(); - // todo: actually leave chat + if (chatRenunciationWinStoredId < 0) + throw new Error("chatRenunciationWinStoredId < 0"); + let chatId = chatRenunciationWinStoredId; + let Sent = genSentBase(); + Sent.chatId = chatId; + apiRequest("leaveChat", Sent + ).then((Recv) => { + updateLocalStateFromRecv(Recv); + }).catch((e) => { + alert("Failed to leave chat"); + console.log(e); + }); } document.getElementById("chat-renunciation-win-no").onclick = function(ev) { @@ -84,24 +162,21 @@ function configureChatRenunciationInterfaceWinPart(){ } } +__mainloopDelayMs = 3000; +__guestMainloopPollerAction = function(){ + let Sent = genSentBase(); + apiRequest("chatListPollEvents", Sent + ).then((Recv) => { + console.log("Got a response"); + console.log(Recv); + updateLocalStateFromRecv(Recv); + }); +} + window.onload = function () { console.log("Loading complete"); - LocalHistoryId = initial_chatListUpdResp.HistoryId; - - console.log(initial_chatListUpdResp); - - let literalChatList = document.getElementById("CL-dblec"); - - for (let myMembershipSt of initial_chatListUpdResp.myChats){ - console.log(myMembershipSt); - if (myMembershipSt.myRoleHere !== roleDeleted){ - myChats.set(myMembershipSt.chatId, myMembershipSt); - let box = convertStToBox(myMembershipSt) - chatBoxes.set(myMembershipSt.chatId, box); - literalChatList.appendChild(box); - } - } - + updateLocalStateFromChatListUpdResp(initial_chatListUpdResp); configureChatCreationInterface(); configureChatRenunciationInterfaceWinPart(); + mainloopPoller(); }; diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_createchat.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_createchat.cpp index 0eaa384..794e64c 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_createchat.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_createchat.cpp @@ -10,10 +10,13 @@ namespace iu9cawebchat { return json::JSON(json::jdict{{"status", json::JSON(-1l)}}); if (is_nickname_taken(conn, new_chat_nickname)) return json::JSON(json::jdict{{"status", json::JSON(-2l)}}); + if (is_nickname_taken(conn, new_chat_nickname)) + return json::JSON(json::jdict{{"status", json::JSON(-3l)}}); reserve_nickname(conn, new_chat_nickname); sqlite_nooutput(conn, - "INSERT INTO `chat` (`nickname`, `name`, `it_HistoryId`, `lastMsgId`) VALUES (?1, ?2, 0, -1)"); + "INSERT INTO `chat` (`nickname`, `name`, `it_HistoryId`, `lastMsgId`) VALUES (?1, ?2, 0, -1)", + {}, {{1, new_chat_nickname}, {2, new_chat_name}}); int64_t CHAT_ID = sqlite_trsess_last_insert_rowid(conn); diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp index ade7044..363c61c 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp @@ -4,6 +4,7 @@ namespace iu9cawebchat { json::JSON poll_update_chat_list_resp(SqliteConnection& conn, int64_t userId, int64_t LocalHistoryId) { + printf("Userid: %ld\n", userId); json::JSON chatListUpdResp; SqliteStatement my_membership_changes(conn, "SELECT `chatId`, `role` FROM `user_chat_membership` WHERE `userId` = ?1 " @@ -32,7 +33,8 @@ namespace iu9cawebchat { void poll_update_chat_list(SqliteConnection& conn, int64_t userId, const json::JSON& Sent, json::JSON& Recv) { Recv["status"].asInteger() = json::Integer(0l); // todo: in libjsonincpp: get rid of Integer - poll_update_chat_list_resp(conn, userId, Sent["chatListUpdReq"]["LocalHistoryId"].asInteger().get_int()); + printf("%s\n", json::generate_str(Sent, json::print_pretty).c_str()); + Recv["chatListUpdResp"] = poll_update_chat_list_resp(conn, userId, Sent["chatListUpdReq"]["LocalHistoryId"].asInteger().get_int()); } json::JSON make_messageSt_obj(int64_t id, int64_t senderUserId, bool exists, bool isSystem, const std::string& text) { @@ -158,6 +160,7 @@ namespace iu9cawebchat { json::JSON internalapi_chatListPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { json::JSON Recv; poll_update_chat_list(conn, uid, Sent, Recv); + printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str()); return Recv; } diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp index 5fefcc1..eafd5af 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp @@ -144,7 +144,6 @@ namespace iu9cawebchat { /* All the api calls processing is done in dedicated files. * All functions related to polling are defined in api_pollevents.cpp */ - // todo: move everything to dedicated files int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId) { SqliteStatement req(conn, "SELECT `it_HistoryId` FROM `chat` WHERE `id` = ?1", {{1, chatId}}, {}); fsql_integer_or_null HistoryId; diff --git a/src/web_chat/iu9_ca_web_chat_service/service.cpp b/src/web_chat/iu9_ca_web_chat_service/service.cpp index 3ae6a95..fea961a 100644 --- a/src/web_chat/iu9_ca_web_chat_service/service.cpp +++ b/src/web_chat/iu9_ca_web_chat_service/service.cpp @@ -36,8 +36,7 @@ int main(int argc, char** argv){ iu9cawebchat::run_website(config); } else een9_THROW("unknown action (known are 'run', 'initialize')"); - // todo: put back execption after debug - } catch (std::bad_weak_ptr& e) { + } catch (std::exception& e) { printf("System failure\n%s\n", e.what()); } return 0;