Compare commits

...

2 Commits

11 changed files with 289 additions and 41 deletions

View File

@ -15,16 +15,36 @@
let userinfo = {% PUT jsinsert userinfo %}; let userinfo = {% PUT jsinsert userinfo %};
let initial_chatListUpdResp = {% PUT jsinsert initial_chatListUpdResp %}; let initial_chatListUpdResp = {% PUT jsinsert initial_chatListUpdResp %};
</script> </script>
<div id="popup-overlay-veil"></div>
<div id="chat-creation-win" class="popup-window"> <div id="chat-creation-win" class="popup-window">
<h1>Input identifying information for your new chat</h1> <h1 class="popup-window-msg">Input identifying information for your new chat</h1>
<table class="id-str-input-table">
<tr>
<td class="id-str-input-td1">
<label for="chat-nickname-input">Enter nickname for new chat:</label>
</td>
<td class="id-str-input-td2">
<input name="name" id="chat-nickname-input" type="text" placeholder="Take a nickname" class="one-line-input" required>
</td>
</tr>
<tr>
<td class="id-str-input-td1">
<label for="chat-name-input">Enter name for new chat:</label>
</td>
<td class="id-str-input-td2">
<input name="password" id="chat-name-input" type="text" placeholder="Come up with name" class="one-line-input" required>
</td>
</tr>
</table>
<h1 class="popup-window-msg">Create new chat?</h1>
<button class="popup-window-btn-yes" id="chat-creation-win-yes">Yes, create</button>
<button class="popup-window-btn-no" id="chat-creation-win-no">No, cancel</button>
</div> </div>
<div id="chat-renunciation-win" class="popup-window"> <div id="chat-renunciation-win" class="popup-window">
<!-- header will actually be rewritten before showing the window to include chat nickname --> <!-- header will actually be rewritten before showing the window to include chat nickname -->
<h1 id="chat-renunciation-win-title">Are you sure you want to leave chat?</h1> <h1 id="chat-renunciation-win-title" class="popup-window-msg">Are you sure you want to leave chat?</h1>
<button class="chat-renunciation-win-yes">Yes, leave</button> <button class="popup-window-btn-yes" id="chat-renunciation-win-yes">Yes, leave</button>
<button class="chat-renunciation-win-no">No, cancel</button> <button class="popup-window-btn-no" id="chat-renunciation-win-no">No, cancel</button>
</div> </div>
x x
@ -45,7 +65,9 @@ x
</div> </div>
</div> </div>
</div> </div>
<script src="/assets/js/common.js"></script>
<script src="/assets/js/common-popup.js"></script>
<script src="/assets/js/list-rooms.js"></script> <script src="/assets/js/list-rooms.js"></script>
</body> </body>
</html> </html>
{% ENDELDEF %} {% ENDELDEF %}

View File

@ -1,10 +1,10 @@
#popup-overlay-veil { .popup-overlay-veil {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.6);
z-index: 99; z-index: 99;
display: none; /* Hidden by default */ display: none; /* Hidden by default */
@ -27,4 +27,24 @@
display: inline; display: inline;
padding: 5px; padding: 5px;
border-bottom: 3px; border-bottom: 3px;
}
.popup-window-btn-yes {
background-color: #0c7f0e;
border-radius: 5px;
padding: 12px;
color: white;
}
.popup-window-btn-no {
background-color: #ff0005;
border-radius: 5px;
padding: 12px;
color: white;
}
.popup-window-msg {
padding-left: 20px;
font-weight: bold;
font-size: 1.3em;
} }

View File

@ -3,7 +3,8 @@
} }
#CL-bacbe { #CL-bacbe {
margin: 6px; margin-top: 6px;
margin-bottom: 4px;
} }
.CL-my-chat-box { .CL-my-chat-box {
@ -33,3 +34,22 @@
width: 16px; width: 16px;
cursor: pointer; cursor: pointer;
} }
/* The morbid thing */
table.id-str-input-table {
width: 100%;
border-collapse: collapse; /* Combine borders */
}
.id-str-input-td1, .id-str-input-td2 {
border: none;
}
.id-str-input-td1 {
text-align: left;
padding-right: 5px;
white-space: nowrap; /* Prevent text wrap, keeping it in one line */
overflow: hidden; /* Hide overflow content */
text-overflow: ellipsis; /* Show ellipsis for overflowing text */
}
.id-str-input-td2 {
width: 100%;
}

View File

@ -28,9 +28,6 @@ let members = new Map();
for (let memberSt of initial_chatUpdResp.members){ for (let memberSt of initial_chatUpdResp.members){
members.set(memberSt.id, memberSt); 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){ function updateOffsetOfVisibleMsg(msgId, offset){
visibleMessages.get(msgId).style.bottom = String(offset) + "px"; visibleMessages.get(msgId).style.bottom = String(offset) + "px";
@ -180,13 +177,10 @@ function test(id, uid){
} }
let mainloopTimeout = null; let mainloopTimeout = null;
let mainloopPoller = null; let mainloopPoller = null;
function setMainloopTimeout(){ function setMainloopTimeout(){
mainloopTimeout = setTimeout(mainloopPoller, 1000); mainloopTimeout = setTimeout(mainloopPoller, 1000);
} }
mainloopPoller = function(){ mainloopPoller = function(){
try { try {
console.log("Hello, World!"); console.log("Hello, World!");

View File

@ -0,0 +1,26 @@
let activePopupWinId = "";
function activatePopupWindow__(el){
let veil = document.createElement("div");
veil.id = "popup-overlay-veil-OBJ"
veil.className = "popup-overlay-veil";
veil.style.display = "block";
document.body.appendChild(veil);
el.style.display = "block";
}
function activatePopupWindowById(id){
if (activePopupWinId !== "")
return;
/* Lmao, this thing is just... SO unsafe */
activePopupWinId = id;
activatePopupWindow__(document.getElementById(id))
}
function deactivateActivePopup(){
if (activePopupWinId === "")
return
document.getElementById("popup-overlay-veil-OBJ").remove();
document.getElementById(activePopupWinId).style.display = "none";
activePopupWinId = "";
}

52
assets/js/common.js Normal file
View File

@ -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
}

View File

@ -1,12 +1,35 @@
let LocalHistoryId = 0; let LocalHistoryId = 0;
function genSentBase(){
return {
'chatListUpdReq': {
'LocalHistoryId': LocalHistoryId
}
};
}
let myChats = new Map(); let myChats = new Map();
let chatBoxes = 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){ function convertStToBox(myMembershipSt){
let chatURI = "/user/" + myMembershipSt.chatNickname; let chatURI = "/user/" + myMembershipSt.chatNickname;
let ID = myMembershipSt.chatId;
let box = document.createElement("div"); let box = document.createElement("div");
chatBoxes.set(myMembershipSt.chatId, box); chatBoxes.set(myMembershipSt.chatId, box);
@ -15,10 +38,9 @@ function convertStToBox(myMembershipSt){
let inBoxNickname = document.createElement("a"); let inBoxNickname = document.createElement("a");
box.appendChild(inBoxNickname); box.appendChild(inBoxNickname);
inBoxNickname.className = "entity-nickname-txt CL-my-chat-box-nickname"; inBoxNickname.className = "entity-nickname-txt CL-my-chat-box-nickname";
console.log(myMembershipSt);
console.log(myMembershipSt.chatNickname);
inBoxNickname.innerText = myMembershipSt.chatNickname; inBoxNickname.innerText = myMembershipSt.chatNickname;
inBoxNickname.href = chatURI; inBoxNickname.href = chatURI;
box.style.backgroundColor = roleToColor(myMembershipSt.myRoleHere);
let inBoxName = document.createElement("a"); let inBoxName = document.createElement("a");
box.appendChild(inBoxName); box.appendChild(inBoxName);
@ -29,44 +51,132 @@ function convertStToBox(myMembershipSt){
let inBoxMyRoleHere = document.createElement("p"); let inBoxMyRoleHere = document.createElement("p");
box.appendChild(inBoxMyRoleHere); box.appendChild(inBoxMyRoleHere);
inBoxMyRoleHere.className = "entity-reg-field-txt CL-my-chat-box-my-role"; inBoxMyRoleHere.className = "entity-reg-field-txt CL-my-chat-box-my-role";
inBoxMyRoleHere.innerText = "You are " + myMembershipSt.myRoleHere + " here"; inBoxMyRoleHere.innerText = youAreXHere(myMembershipSt.myRoleHere);
let ID = myMembershipSt.chatId;
let inBoxLeaveBtn = document.createElement("img"); let inBoxLeaveBtn = document.createElement("img");
box.appendChild(inBoxLeaveBtn); box.appendChild(inBoxLeaveBtn);
inBoxLeaveBtn.className = "CL-my-chat-box-leave-btn"; inBoxLeaveBtn.className = "CL-my-chat-box-leave-btn";
inBoxLeaveBtn.src = "/assets/img/delete.svg"; inBoxLeaveBtn.src = "/assets/img/delete.svg";
inBoxLeaveBtn.onclick = function (ev) { inBoxLeaveBtn.onclick = function (ev) {
if (ev.button === 0){ if (ev.button !== 0)
console.log("Tried to leave chat" + ID); return;
} chatRenunciationWinStoredId = ID;
activatePopupWindowById("chat-renunciation-win");
document.getElementById("chat-renunciation-win-title").innerText =
"Do you really want to leave chat " + myMembershipSt.chatNickname + "?";
}; };
return box; return box;
} }
window.onload = function () { function updateLocalStateFromChatListUpdResp(chatListUpdResp){
console.log("Loading complete"); LocalHistoryId = chatListUpdResp.HistoryId;
LocalHistoryId = initial_chatListUpdResp.HistoryId;
console.log(initial_chatListUpdResp);
let literalChatList = document.getElementById("CL-dblec"); let literalChatList = document.getElementById("CL-dblec");
for (let myMembershipSt of initial_chatListUpdResp.myChats){ for (let myMembershipSt of chatListUpdResp.myChats){
let chatId = myMembershipSt.chatId;
console.log(myMembershipSt); console.log(myMembershipSt);
if (myMembershipSt.myRoleHere !== roleDeleted){ if (myChats.has(chatId)){
myChats.set(myMembershipSt.chatId, myMembershipSt); myChats.set(chatId, myMembershipSt);
updateBoxWithNewSt(chatBoxes.get(chatId), myMembershipSt);
} else {
if (myMembershipSt.myRoleHere === userChatRoleDeleted)
continue;
myChats.set(chatId, myMembershipSt);
let box = convertStToBox(myMembershipSt) let box = convertStToBox(myMembershipSt)
chatBoxes.set(myMembershipSt.chatId, box); chatBoxes.set(chatId, box);
literalChatList.appendChild(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();
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) {
if (ev.button !== 0)
return;
deactivateActivePopup();
}
document.getElementById("CL-bacbe").onclick = function (ev){ document.getElementById("CL-bacbe").onclick = function (ev){
if (ev.button === 0){ 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");
}; };
}
function configureChatRenunciationInterfaceWinPart(){
document.getElementById("chat-renunciation-win-yes").onclick = function (ev){
if (ev.button !== 0)
return;
deactivateActivePopup();
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) {
if (ev.button !== 0)
return;
deactivateActivePopup();
}
}
__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");
updateLocalStateFromChatListUpdResp(initial_chatListUpdResp);
configureChatCreationInterface();
configureChatRenunciationInterfaceWinPart();
mainloopPoller();
}; };

View File

@ -10,10 +10,13 @@ namespace iu9cawebchat {
return json::JSON(json::jdict{{"status", json::JSON(-1l)}}); return json::JSON(json::jdict{{"status", json::JSON(-1l)}});
if (is_nickname_taken(conn, new_chat_nickname)) if (is_nickname_taken(conn, new_chat_nickname))
return json::JSON(json::jdict{{"status", json::JSON(-2l)}}); 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); reserve_nickname(conn, new_chat_nickname);
sqlite_nooutput(conn, 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); int64_t CHAT_ID = sqlite_trsess_last_insert_rowid(conn);

View File

@ -4,6 +4,7 @@
namespace iu9cawebchat { namespace iu9cawebchat {
json::JSON poll_update_chat_list_resp(SqliteConnection& conn, int64_t userId, int64_t LocalHistoryId) { json::JSON poll_update_chat_list_resp(SqliteConnection& conn, int64_t userId, int64_t LocalHistoryId) {
printf("Userid: %ld\n", userId);
json::JSON chatListUpdResp; json::JSON chatListUpdResp;
SqliteStatement my_membership_changes(conn, SqliteStatement my_membership_changes(conn,
"SELECT `chatId`, `role` FROM `user_chat_membership` WHERE `userId` = ?1 " "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) { void poll_update_chat_list(SqliteConnection& conn, int64_t userId, const json::JSON& Sent, json::JSON& Recv) {
Recv["status"].asInteger() = json::Integer(0l); Recv["status"].asInteger() = json::Integer(0l);
// todo: in libjsonincpp: get rid of Integer // 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) { 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 internalapi_chatListPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
json::JSON Recv; json::JSON Recv;
poll_update_chat_list(conn, uid, Sent, Recv); poll_update_chat_list(conn, uid, Sent, Recv);
printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str());
return Recv; return Recv;
} }

View File

@ -144,7 +144,6 @@ namespace iu9cawebchat {
/* All the api calls processing is done in dedicated files. /* All the api calls processing is done in dedicated files.
* All functions related to polling are defined in api_pollevents.cpp */ * 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) { 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}}, {}); SqliteStatement req(conn, "SELECT `it_HistoryId` FROM `chat` WHERE `id` = ?1", {{1, chatId}}, {});
fsql_integer_or_null HistoryId; fsql_integer_or_null HistoryId;

View File

@ -36,8 +36,7 @@ int main(int argc, char** argv){
iu9cawebchat::run_website(config); iu9cawebchat::run_website(config);
} else } else
een9_THROW("unknown action (known are 'run', 'initialize')"); een9_THROW("unknown action (known are 'run', 'initialize')");
// todo: put back execption after debug } catch (std::exception& e) {
} catch (std::bad_weak_ptr& e) {
printf("System failure\n%s\n", e.what()); printf("System failure\n%s\n", e.what());
} }
return 0; return 0;