Compare commits

..

No commits in common. "2219653f404f1e36fa1409db541d6a61a974065b" and "632d4069ac6dffd2294a019ff9bc80df3720c1c4" have entirely different histories.

56 changed files with 1226 additions and 1975 deletions

View File

@ -38,11 +38,11 @@ regexis024_build_system.sh
Помимо самого бинарника нужен файл с настройками сервиса. Формат настроек: JSON. Помимо самого бинарника нужен файл с настройками сервиса. Формат настроек: JSON.
Комментарии не поддерживаются. Пример такого файла находится в example/config.json. Комментарии не поддерживаются. Пример такого файла находится в example/config.json.
Вместе с бинарным фалом так же распространяются ассеты, необходимые для работы сайта. Вместе с бинарным фалом так же распространяются ассеты, необъходимые для работы сайта.
Их можно найти в папке assets. В настроках (поле `["assets"]`) указывается путь до Их можно найти в папке assets. В настроках (поле `["assets"]`) указывается путь до
папки с ассетами. Путь может быть как абсолютным, так и относительным к рабочей директории. папки с ассетами. Путь может быть как абсолютным, так и относительным к рабочей директории.
Поле настроек `["database"]` указывает как соединиться с базой данных. Поле настроек `["database"]` указывает как соединиться с базой данных.
Поддерживается только база данных sqlite3. Поддерживается только хранение в файле. Поддерживается только база данных sqlite. Поддерживается только хранение в файле.
Поле `["database"]["file"]` указывает путь где хранится sqlite база данных. Поле `["database"]["file"]` указывает путь где хранится sqlite база данных.
Перед тем как использовать сервис нужно его проинициализировать (а точнее проинициализировать Перед тем как использовать сервис нужно его проинициализировать (а точнее проинициализировать

View File

@ -4,56 +4,26 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="/assets/img/favicon.png"> <title>Веб-Чат Members</title>
<link rel="stylesheet" href="/assets/css/common.css"> <link rel="stylesheet" href="/assets/css/chat.css">
<link rel="stylesheet" href="/assets/css/common-popup.css">
<link rel="stylesheet" href="/assets/css/chat-members.css">
<title>Chat Members</title>
</head> </head>
<body> <body>
{% PUT chat.pass pres userinfo openedchat initial_chatUpdResp %} {% PUT chat.pass pres userinfo openedchat initial_chatUpdResp %}
<!--TODO: ADD SOMETHING WRITE SOMETHING AAAAAA -->
<div id="user-summoning-win" class="popup-window"> <div class="overlay" id="overlay">
<h1 class="popup-window-msg">Nickname for summoned user</h1> <div class="members-list" id="members-list">
<input class="one-line-input" id="summoned-user-nickname"> <div class="members-list-header">
<input type="checkbox" id="summoned-user-is-read-only"> <span class="close" onclick="closeMembersList()">&times;</span>
<label>Make read only</label><br> <h2 class="all-members">Все участники</h2>
<button class="popup-window-btn-yes" id="user-summoning-yes">Yes, summon</button>
<button class="popup-window-btn-no" id="user-summoning-no">No, cancel</button>
</div>
<div id="user-deletion-win" class="popup-window">
<!-- header will actually be rewritten before showing the window to include user nickname -->
<h1 id="user-deletion-win-title" class="popup-window-msg">Are you sure you want to ban user?</h1>
<button class="popup-window-btn-yes" id="user-deletion-yes">Yes, delete</button>
<button class="popup-window-btn-no" id="user-deletion-no">No, cancel</button>
</div>
<div class="document-container resp-container">
<div id="navigation-panel" class="panel">
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
</a>
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
</a>
<p class="panel-thing panel-header-txt"> Members list of {% WRITE openedchat.name %} ({% WRITE openedchat.nickname %})</p>
<a href="/chat/{% WRITE openedchat.nickname %}" id="go-to-chat-settings" class="panel-thing">
<img alt="Back to chat" src="/assets/img/return.svg" width="32px">
</a>
</div> </div>
<div class="members-list-body">
<div class="dynamic-block-list"> <ul id="members-list-body">
<img id="CM-btn-add" class="button-add centered-block-el" alt="New chat" src="/assets/img/add.svg"> <!-- Список участников будет добавлен динамически -->
<div class="dynamic-block-list-el-container" id="CM-list"> </ul>
</div>
</div> </div>
</div> </div>
</div>
<script src="/assets/js/common.js"></script> <script src="/assets/js/chat-members.js"></script>
<script src="/assets/js/common-popup.js"></script>
<script src="/assets/js/chat-members.js"></script>
</body> </body>
</html> </html>
{% ENDELDEF %} {% ENDELDEF %}

View File

@ -9,60 +9,30 @@
{% ELDEF main JSON pres JSON userinfo JSON openedchat JSON initial_chatUpdResp %} {% ELDEF main JSON pres JSON userinfo JSON openedchat JSON initial_chatUpdResp %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="ru">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="/assets/img/favicon.png"> <title>Веб-Чат</title>
<link rel="stylesheet" href="/assets/css/debug.css">
<link rel="stylesheet" href="/assets/css/common.css">
<link rel="stylesheet" href="/assets/css/common-popup.css">
<link rel="stylesheet" href="/assets/css/chat.css"> <link rel="stylesheet" href="/assets/css/chat.css">
<title>Chat</title>
</head> </head>
<body> <body>
{% PUT chat.pass pres userinfo openedchat initial_chatUpdResp %} {% PUT chat.pass pres userinfo openedchat initial_chatUpdResp %}
<!-- TODO AAAAA-->
<div id="msg-deletion-win" class="popup-window"> <div class="chat-container">
<h1 class="popup-window-msg">Are you sure you want to delete this message?</h1> <div class="chat-header">
<!-- message preview will be actually rewritten before each window activation--> <span class="room-name">Веб чат</span>
<p class="message-in-popup-preview" id="win-deletion-msg-preview">Lorem ipsum dolor</p> <button class="members" onclick="openMembersList()">Показать участников</button>
<button class="popup-window-btn-yes" id="msg-deletion-yes">Yes, delete</button>
<button class="popup-window-btn-no" id="msg-deletion-no">No, cancel</button>
</div> </div>
<div class="chat-messages" id="chat-messages">
<div class="fullscreen-container resp-container"> <!-- Сообщения чата будут здесь -->
<div class="panel" id="navigation-info-panel">
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
</a>
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
</a>
<p class="panel-thing panel-header-txt"> {% WRITE openedchat.name %} ({% WRITE openedchat.nickname %})</p>
<a href="/chat-members/{% WRITE openedchat.nickname %}" id="go-to-chat-settings" class="panel-thing">
<img alt="Settings of chat. List of members" src="/assets/img/settings-iron.svg" width="32px">
</a>
</div>
<div id="chat-widget">
<div class="chat-debug-rect" id="debug-line-highest" style="background-color: #8600d3"></div>
<div class="chat-debug-rect" id="debug-line-top-padding" style="background-color: #ff00ae"></div>
<div class="chat-debug-rect" id="debug-line-bottom-padding" style="background-color: #ff0062"></div>
<div class="chat-debug-rect" id="debug-line-lowest" style="background-color: #ff2f00"></div>
<div id="top-loading" class="message-supercontainer">
<img class="loading-spinner" alt="Loading backward..." src="/assets/gif/loading.gif">
</div>
<div id="bottom-loading" class="message-supercontainer">
<img class="loading-spinner" alt="Loading forward..." src="/assets/gif/loading.gif">
</div>
</div>
<div class="panel" id="input-panel">
<div contentEditable id="message-input" class="panel-thing"></div>
</div>
<script src="/assets/js/common.js"></script>
<script src="/assets/js/common-popup.js"></script>
<script src="/assets/js/chat.js"></script>
</div> </div>
<div class="chat-footer">
<input type="text" class="chat-input" id="chat-input" placeholder="Введите сообщение...">
<button class="chat-send-button" onclick="sendMessage()">Отправить</button>
</div>
</div>
<script src="/assets/js/chat.js"></script>
</body> </body>
</html> </html>
{% ENDELDEF %} {% ENDELDEF %}

View File

@ -1,69 +1,42 @@
{% ELDEF main JSON pres JSON userinfo JSON alienprofile JSON errors %} {% ELDEF main JSON pres JSON userprofile JSON errors %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="/assets/css/profile.css">
<link rel="icon" type="image/png" href="/assets/img/favicon.png"> <title>Профиль</title>
<link rel="stylesheet" href="/assets/css/common.css">
<link rel="stylesheet" href="/assets/css/edit-profile.css">
<title>Edit user Profile</title>
</head> </head>
<body> <body>
<div class="main-container">
<div class="document-container">
<div id="navigation-panel" class="panel">
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
</a>
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
</a>
</div>
{% FOR error IN errors %} {% FOR error IN errors %}
<div class="server-notif-error-msg-box"> <div>
{% WRITE error.text %} <p>{% WRITE error.text %}</p>
</div> </div>
{% ENDFOR %} {% ENDFOR %}
<div class="profile-header">
<div class="profile-container"> <h1>Редактирование профиля</h1>
<h2 class="profile-name-text">{% WRITE alienprofile.name %}</h2>
<h3 class="profile-nickname-text">Nickname: {% WRITE alienprofile.nickname %}</h3>
<p class="profile-bio-text">
{% WRITE alienprofile.bio %}
</p>
</div>
<div class="profile-container resp-container">
<h1 class="wide-centered-header">Change user attributes</h1>
<form action = "/user/{% WRITE alienprofile.nickname %}" method="post" enctype="application/x-www-form-urlencoded">
<table class="logins-input-table">
<tr>
<td class="logins-input-td1">
<label for="new-name-input">Enter new name:</label>
</td>
<td class="logins-input-td2">
<input name="name" id="new-name-input" type="text" placeholder="New name" class="one-line-input">
</td>
</tr>
<tr>
<td class="logins-input-td1">
<label for="new-password-input">Enter new password: </label>
</td>
<td class="logins-input-td2">
<input name="password" id="new-password-input" type="password" placeholder="New password" class="one-line-input">
</td>
</tr>
</table>
<label for="input-change-bio">Change description:</label>
<br>
<textarea name="bio" id="input-change-bio" class="multiline-input"></textarea>
<button class="action-button centered-block-el" type="submit">Submit changes</button>
</form>
</div> </div>
<form method="post" action="/user/{% WRITE userprofile.nickname %}">
<div class="columns">
<div class="column">
<p>{% WRITE userprofile.name %} ( {% WRITE userprofile.nickname %} )</p>
<label for="name"> Изменить имя </label>
<input type="text" name="name" id="name">
<br>
<label for="password"> Изменить пароль </label>
<input type="password" name="password" id="password">
<br>
</div>
</div>
<div class="additional-info">
<p>О себе</p>
<p>{% WRITE userprofile.bio %}</p><br>
<label for="bio">Изменить</label>
<textarea name="bio" id="bio"></textarea>
</div>
<button class="save" type="submit">Сохранить изменения</button>
</form>
</div> </div>
</body> </body>
</html> </html>
{% ENDELDEF%} {% ENDELDEF%}

View File

@ -2,7 +2,6 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" type="image/png" href="/assets/img/favicon.png">
<title>Not found</title> <title>Not found</title>
</head> </head>
<body> <body>

View File

@ -4,71 +4,69 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>List of chat rooms</title> <title>Список Чат-Комнат</title>
<link rel="icon" type="image/png" href="/assets/img/favicon.png">
<link rel="stylesheet" href="/assets/css/common.css">
<link rel="stylesheet" href="/assets/css/common-popup.css">
<link rel="stylesheet" href="/assets/css/list-rooms.css"> <link rel="stylesheet" href="/assets/css/list-rooms.css">
</head> </head>
<body> <body>
<script> <script>
let pres = {% PUT jsinsert pres %}; let pres = {% PUT jsinsert pres %};
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="chat-creation-win" class="popup-window"> <div class="container">
<h1 class="popup-window-msg">Input identifying information for your new chat</h1> <h1 style="color: white;">Выберите Чат-Комнату</h1>
<table class="id-str-input-table"> <ul class="room-list">
<tr> <!-- Здесь будет список комнат -->
<td class="id-str-input-td1"> </ul>
<label for="chat-nickname-input">Enter nickname for new chat:</label> <button class="create-room-button" onclick="openCreateRoomModal()">Создать Комнату</button>
</td> </div>
<td class="id-str-input-td2">
<input 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 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 id="chat-renunciation-win" class="popup-window"> <!-- Модальное окно для создания комнаты -->
<!-- header will actually be rewritten before showing the window to include chat nickname --> <!--<div id="createRoomModal" class="modal">-->
<h1 id="chat-renunciation-win-title" class="popup-window-msg">Are you sure you want to leave chat?</h1> <!-- <div class="modal-content">-->
<button class="popup-window-btn-yes" id="chat-renunciation-win-yes">Yes, leave</button> <!-- <div class="modal-header">-->
<button class="popup-window-btn-no" id="chat-renunciation-win-no">No, cancel</button> <!-- <span class="close" onclick="closeCreateRoomModal()">&times;</span>-->
</div> <!-- <h2>Создать Комнату</h2>-->
<!-- </div>-->
<!-- <div class="modal-body">-->
<!-- <input type="text" id="newRoomName" placeholder="Название комнаты">-->
<!-- <input type="password" id="newRoomNickname" placeholder="Никнейм комнаты">-->
<!-- </div>-->
<!-- <div id="error"></div>-->
<!-- <div class="modal-footer">-->
<!-- <button class="join-button" onclick="createRoom()">Создать</button>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
x <!--&lt;!&ndash; Модальное окно для добавления участников &ndash;&gt;-->
<div class="document-container resp-container"> <!--<div class="overlay" id="add_members">-->
<div id="navigation-panel" class="panel"> <!-- <div class="add-members">-->
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing"> <!-- <div class="add-members-header">-->
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px"> <!-- <span class="close" onclick="closeAdd()">&times;</span>-->
</a> <!-- <h2>Добавить участников</h2>-->
<p class="panel-thing panel-header-txt"> <!-- </div>-->
List of available rooms <!-- <div class="add-members-body">-->
</p> <!-- <input type="text" id="newMemberLogin" placeholder="Логин пользователя">-->
</div> <!-- </div>-->
<!-- <div class="add-members-footer">-->
<div class="dynamic-block-list"> <!-- <button class="add-member-button" onclick="addMember()">Добавить</button>-->
<img id="CL-bacbe" class="button-add centered-block-el" alt="New chat" src="/assets/img/add.svg"> <!-- </div>-->
<div class="dynamic-block-list-el-container" id="CL-dblec"> <!-- </div>-->
<!--</div>-->
</div> <!--<div class="overlay" id="delete-chat">-->
</div> <!-- <div class="delete-chat">-->
</div> <!-- <div class="delete-chat-header">-->
<script src="/assets/js/common.js"></script> <!-- <span class="delete-close" onclick="closeConfirm()">&times;</span>-->
<script src="/assets/js/common-popup.js"></script> <!-- <h2>Вы уверены, что хотите удалить чат?</h2>-->
<script src="/assets/js/list-rooms.js"></script> <!-- </div>-->
<!-- <div class="delete-chat-body">-->
<!-- <button class="confirm" onclick="deleteChat()">Да</button>-->
<!-- <button class="cancel" onclick="closeConfirm()">Нет</button>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
<script src="/assets/js/list-rooms.js"></script>
</body> </body>
</html> </html>
{% ENDELDEF %} {% ENDELDEF %}

View File

@ -1,41 +1,29 @@
{% ELDEF main JSON pres JSON userinfo JSON errors %} {% ELDEF main JSON pres JSON userinfo JSON errors %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{% WRITE pres.lang %}">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="/assets/img/favicon.png"> <title>{% WRITE pres.phr.decl.page-login %}</title>
<link rel="stylesheet" href="/assets/css/common.css">
<link rel="stylesheet" href="/assets/css/login.css"> <link rel="stylesheet" href="/assets/css/login.css">
<title>Login Page</title>
</head> </head>
<body>
{% FOR error IN errors %}
<div class="server-notif-error-msg-box">
{% WRITE error.text %}
</div>
{% ENDFOR %}
<div class="form-container"> <body>
<h1 class="wide-centered-header">Login</h1> {% FOR msg IN errors %}
<form action="/login" method="post" enctype="application/x-www-form-urlencoded"> <div class="error-msg">
<table class="logins-input-table"> {% WRITE msg.text %}
<tr>
<td class="logins-input-td1"><label for="input-nickname">Enter user nickname:</label></td>
<td class="logins-input-td2">
<input name="nickname" id="input-nickname" type="text" placeholder="Nickname" class="one-line-input" required>
</td>
</tr>
<tr>
<td class="logins-input-td1"><label for="input-password">Enter password:</label></td>
<td class="logins-input-td2">
<input name="password" id="input-password" type="password" placeholder="Password" class="one-line-input" required>
</td>
</tr>
</table>
<button class="action-button centered-block-el" type="submit">Login</button>
</form>
</div> </div>
{% ENDFOR %}
<div class="form-container">
<h1 class="hide-cursor no-select">{% WRITE pres.phr.decl.enter %}</h1>
<form action="/login" method="post" enctype="application/x-www-form-urlencoded">
<label for="nickname">{% WRITE pres.phr.decl.nickname %}</label>
<input type="text" name="nickname" id="nickname"><br>
<label for="password">{% WRITE pres.phr.decl.password %}</label>
<input type="password" name="password" id="password"><br>
<button type="submit" class="hide-cursor no-select">{% WRITE pres.phr.act.enter %}</button>
</form>
</div>
</body> </body>
</html> </html>

View File

@ -1,36 +1,26 @@
{% ELDEF main JSON pres JSON userinfo JSON alienprofile %} {% ELDEF main JSON pres JSON userprofile %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="/assets/css/profile.css">
<link rel="icon" type="image/png" href="/assets/img/favicon.png"> <title>Профиль</title>
<link rel="stylesheet" href="/assets/css/common.css">
<!-- This page is so simple, that it does not even have it's separate css file -->
<title>User Profile</title>
</head> </head>
<body> <body>
<div class="main-container">
<div class="document-container resp-container"> <div class="profile-header">
<div id="navigation-panel" class="panel"> <h1>Профиль пользователя</h1>
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
</a>
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
</a>
</div> </div>
<div class="columns">
<div class="profile-container"> <div class="column">
<h2 class="profile-name-text">{% WRITE alienprofile.name %}</h2> <p>{% WRITE userprofile.name %} ( {% WRITE userprofile.nickname %} )</p>
<h3 class="profile-nickname-text">Nickname: {% WRITE alienprofile.nickname %}</h3> </div>
<p class="profile-bio-text"> </div>
{% WRITE alienprofile.bio %} <div class="additional-info">
</p> <p>О себе</p>
<p>{% WRITE userprofile.bio %}</p><br>
</div> </div>
</div> </div>
</body> </body>
</html> </html>
{% ENDELDEF%} {% ENDELDEF%}

View File

@ -1,33 +0,0 @@
#CM-btn-add {
margin-top: 6px;
margin-bottom: 4px;
display: none;
}
.CM-member-box {
display: flex;
flex-direction: row;
}
.CL-member-box-nickname {
margin-left: 8px;
justify-self: flex-start;
}
.CM-member-box-name {
margin-left: 14px;
justify-self: flex-start;
}
.CM-member-box-role {
margin-left: auto;
justify-self: flex-end;
}
.CM-member-box-leave-btn {
margin-left: 10px;
margin-right: 8px;
justify-self: flex-end;
width: 16px;
cursor: pointer;
}

View File

@ -1,142 +1,202 @@
body, html { body {
height: 100%; font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #e5e5e5;
} }
#chat-widget { .chat-container {
position: relative; width: 100%;
flex: 1; max-width: 800px;
background-color: #f1f1f1; height: 90vh;
background-color: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
border-radius: 8px;
overflow: hidden; overflow: hidden;
} }
.message-supercontainer{ .chat-header {
position: absolute; background-color: #007bb5;
width: 100%;
left: 0;
background-color: rgba(150, 0, 100, 50);
/*display: flex;*/
/*flex-direction: row;*/
/*justify-content: center;*/
}
.message-box{
/*display: inline-block;*/
padding: 5px;
}
.message-box-mine {
margin-right: 5px;
margin-left: auto;
max-width: 400px;
border: 2px solid #82a173;
padding: 5px;
background-color: #cdff9b;
color: black;
/*justify-self: flex-end;*/
}
.message-box-alien {
margin-left: 5px;
margin-right: auto;
max-width: 400px;
border: 2px solid dimgrey;
padding: 5px;
background-color: white;
color: black;
/*justify-self: flex-start;*/
}
/* Only non-system messages can be deleted. Deleted messages do not have delete button
This class should be used with (and, ofcourse, after) class message-box-my/message-box-alien */
.message-box-deleted {
border: 2px solid #cb0005;
background-color: #ffc1bc;
}
.message-box-deleted .message-box-msg{
font-weight: bold;
}
.message-box-system {
margin-left: auto;
margin-right: auto;
max-width: 500px;
padding: 4px;
background-color: #2d2d2d;
color: white; color: white;
font-weight: bold; padding: 25px;
justify-self: center; display: flex;
justify-content: center;
align-items: center;
position: relative;
} }
.room-name {
/* in #chat-widget .message-box */ position: absolute;
.message-box-top{ left: 50%;
/* You see, each message contains a 20+2+2 px high icon that HAS TO BE LOADED FIRST. font-size: 24px;
This happens after window.onload, so I added a crutch: loading won't update height in
unpredictable moment. cause it will be already high enough. BUGA-GA-GA!! */
min-height: 30px;
display: block;
} }
.members {
.message-box-sender-name{ border: none;
color: black; position: absolute;
text-decoration: none; left: 80%;
padding: 2px; border-radius: 10px;
display: inline;
font-size: 0.8em;
}
/* Additional to message-box-sender-name */
.message-box-sender-shortname {
font-weight: bold;
padding-left: 3px;
font-size: 0.94em;
}
.message-box-sender-name:hover{
color: #1060ff
}
.message-box-button{
width: 20px;
padding: 2px;
cursor: pointer; cursor: pointer;
display: inline; width: 150px;
background-color: #f7f7f7;
height: 25px;
transition: background-color 0.3s ease;
} }
.members:hover {
.message-box-msg{ background-color: #218838;
word-wrap: break-word;
} }
.chat-messages {
#input-panel { flex: 1;
min-height: 20px;
}
#message-input {
padding: 15px; padding: 15px;
height: auto; overflow-y: auto;
width: 100%; background-color: #f7f7f7;
display: inline-block;
background-color: white;
border: 1px solid #1000d0;
border-radius : 7px;
font-size: .9rem;
margin: 10px;
} }
.message-in-popup-preview{ .chat-message {
border: 4px solid red; display: flex;
width: 80%; align-items: flex-start;
max-width: 200px; margin-bottom: 15px;
margin-left: auto; }
margin-right: auto;
max-height: 20%; .chat-message .avatar {
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 10px;
}
.chat-message .avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.chat-message .message-content {
max-width: 70%;
background-color: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.chat-message .message-content .username {
font-weight: bold;
margin-bottom: 5px;
}
.chat-message .message-content .text {
word-wrap: break-word; word-wrap: break-word;
} }
.loading-spinner{ .chat-footer {
margin-left: auto; display: flex;
margin-right: auto; padding: 15px;
background-color: rgba(0, 0, 0, 0); padding-left: 50px;
width: 25px; border-top: 1px solid #ddd;
display: block; }
.chat-input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 20px;
margin-right: 10px;
outline: none;
}
.chat-send-button {
padding: 10px 20px;
border: none;
background-color: #0088cc;
color: white;
border-radius: 20px;
cursor: pointer;
outline: none;
}
.members-list {
display: none;
position: fixed;
background-color: #fff;
margin: 10% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 400px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
.members-list-header {
display: flex;
}
.all-members {
position: absolute;
left: 32%;
top: 0%;
margin-bottom: 30px;
font-family: Arial, sans-serif;
}
.close {
position: absolute;
right: 5%;
font-size: 24px;
font-weight: bold;
}
.members-list span {
cursor: pointer;
}
.members-list-body ul {
list-style-type: none;
left: 0%;
}
.members-list-body img {
margin-top: 10px;
left: 0%;
height: 30px;
width: 30px;
border-radius: 50%;
}
.members-list-body a {
margin-left: 5px;
margin-top: 10px;
text-decoration: none;
color: black;
}
.members-list-body a:hover {
text-decoration: underline;
color: #0088cc;
}
.members-list-body button {
padding: 5px 10px;
border: none;
background-color: #dc2e45;
color: white;
border-radius: 20px;
position: absolute;
left: 300px;
margin-top: 20px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.members-list-body button:hover {
background-color: #881527;
}
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
z-index: 1000;
}
.chat-send-button:hover {
background-color: #007bb5;
} }

View File

@ -1,50 +0,0 @@
.popup-overlay-veil {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
z-index: 99;
display: none; /* Hidden by default */
}
.popup-window {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 100;
display: none;
}
.popup-btn {
display: inline;
padding: 5px;
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

@ -1,206 +0,0 @@
/* Profile view elements */
.profile-container {
background: white;
border-radius: 5px;
padding: 20px;
margin-top: 60px; /* Space below the fixed panel */
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3);
}
.profile-name-text {
color: black;
}
.profile-nickname-text{
color: #444;
text-align: left;
}
.profile-bio-text {
padding-top: 40px;
text-align: left;
line-height: 1.6;
color: black;
}
/* Panels */
.panel {
width: 100%;
border: 2px solid blue;
background-color: #54b3ff;
display: flex;
flex-direction: row;
align-items: center;
}
.panel-thing {
padding: 6px;
}
.panel-header-txt{
color: white;
font-size: 1.9em;
flex: 1;
text-align: center;
}
/* Containers for the whole document */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Arial, sans-serif;
}
.document-container {
width: 80%; /* Full width of the viewport */
margin: 0 auto; /* Center the container horizontally */
}
.fullscreen-container {
width: 80%; /* Full width of the viewport */
height: 100vh; /* Full height of the viewport */
display: flex;
flex-direction: column; /* Stack children vertically */
margin: 0 auto; /* Center the container horizontally */
}
@media (orientation: landscape) {
.resp-container{
width: 80%;
}
}
@media (orientation: portrait){
.resp-container{
width: 100%;
}
}
body {
background-color: #f000f0;
background-image: url("/assets/img/clavicle-transparent.png"), url("/assets/img/broken-clavicle.png");
background-repeat: revert;
background-size: 10%, 25%;
}
/* Notifications, returned from server and embedded into html page at render-time */
.server-notif-error-msg-box{
font-size: 1.3em;
text-align: center;
padding: 10px;
border: 2px solid red;
border-radius: 30px;
background-color: #ff5050;
max-width: 40%;
margin: 15px auto;
}
/* Centered headers */
.wide-centered-header {
width: 100%;
text-align: center;
font-size: 1.4em;
}
/* Cool buttons with text */
.action-button {
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
.action-button:hover {
background-color: #0056b3; /* Darker blue on hover */
}
/* This is for centering non-100%wide block */
.centered-block-el {
display: block;
margin-left: auto;
margin-right: auto;
}
/* Beautiful text input */
.one-line-input {
width: 100%;
padding: 8px;
margin: 8px 0;
border: 1px solid #ccc;
border-radius: 4px;
}
.multiline-input {
width: 100%;
/*max-width: 600px;*/
height: 200px;
padding: 10px;
font-size: 1.15em;
border: 2px solid #ccc;
border-radius: 5px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); /* Subtle shadow */
outline: none; /* Remove default outline on focus */
resize: vertical; /* Allow resizing vertically */
transition: border-color 0.15s, box-shadow 0.15s; /* Smooth transition for border color and shadow */
}
.multiline-input:focus {
border-color: #007bff; /* Change border color on focus */
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); /* Shadow on focus */
}
/* Handles the case of list of elements with dickanme, name, role and delete button
For list of chats and list of users in chat */
.dynamic-block-list {
margin-top:12px;
display: flex;
flex-direction: column;
background-color: white;
border: 1px solid #c7c7c7;
align-items: stretch;
padding-left: 8px;
padding-right: 8px;
padding-bottom: 8px;
}
.dynamic-block-list-el {
margin-top: 8px;
background-color: white;
border: 1px solid #c7c7c7;
color: black;
padding: 5px;
}
.button-add{
width: 50px;
cursor: pointer;
}
.dynamic-block-list-el-container{
width: 100%;
}
.entity-nickname-txt {
font-weight: bold;
color: black;
text-decoration: none;
font-size: 1.5em;
}
.entity-reg-field-txt {
/* For name and role */
color: #242424;
text-decoration: none;
font-size: 1.5em;
}

View File

@ -1,8 +0,0 @@
.chat-debug-rect{
width: 100%;
position: absolute;
left: 0;
opacity: 0.3;
height: 3px;
z-index: 2;
}

View File

@ -1,23 +0,0 @@
/* The morbid thing */
table.logins-input-table {
width: 100%;
border-collapse: collapse; /* Combine borders */
}
.logins-input-td1, .logins-input-td2 {
border: none;
}
.logins-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 */
}
.logins-input-td2 {
width: 100%;
}
#input-change-bio{
margin-top: 5px;
margin-bottom: 5px;
}

View File

@ -1,51 +1,253 @@
#CL-bacbe { body {
margin-top: 6px; font-family: Arial, sans-serif;
margin-bottom: 4px; background-color: #f0f0f0;
margin: 0;
padding: 0;
} }
.CL-my-chat-box { .container {
max-width: 800px;
margin: 30px auto;
padding: 20px;
background-color: #007bff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
h1 {
text-align: center;
color: #fff;
}
.room-list {
list-style-type: none;
padding: 0;
}
.room-item {
display: flex; display: flex;
flex-direction: row; justify-content: space-between;
align-items: center;
padding: 15px;
margin: 10px 0;
background-color: #fafafa;
border: 1px solid #ddd;
border-radius: 5px;
transition: background-color 0.3s ease;
} }
.CL-my-chat-box-nickname { .room-item:hover {
margin-left: 8px; background-color: #eaeaea;
justify-self: flex-start;
} }
.CL-my-chat-box-name { .room-name {
margin-left: 14px; font-size: 18px;
justify-self: flex-start; color: #555;
} }
.CL-my-chat-box-my-role { .join-button {
margin-left: auto; padding: 10px 15px;
justify-self: flex-end; font-size: 16px;
color: white;
background-color: #007bff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.add-members-header {
text-align: center;
}
.add-members-footer {
text-align: right;
margin-top: 5px;
}
.add-members-button {
background-color: #218838;
padding: 10px 15px;
font-size: 16px;
color: white;
border: none;
border-radius: 5px;
position: absolute;
margin-left: 502px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.add-members-button:hover {
background-color: #006509
}
.delete-chat-button {
background-color: #dc2e45;
border: none;
color: white;
font-size: 16px;
border-radius: 5px;
position: absolute;
cursor: pointer;
transition: background-color 0.3s ease;
padding: 10px 15px;
margin-left: 380px;
}
.delete-chat-button:hover {
background-color: #881527;
}
#newMemberLogin {
width: 93.5%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 5px;
}
.add-member-button {
background-color: #218838;
padding: 10px 15px;
font-size: 16px;
color: white;
border: none;
border-radius: 5px;
position: absolute;
margin-left: -105px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.join-button:hover {
background-color: #0056b3;
} }
.CL-my-chat-box-leave-btn { .modal {
margin-left: 10px; display: none;
margin-right: 8px; position: fixed;
justify-self: flex-end; z-index: 1;
width: 16px; left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fff;
margin: 10% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 400px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
.modal-header, .modal-footer {
padding: 10px;
color: #333;
}
.modal-header {
text-align: center;
}
.modal-footer {
text-align: right;
}
.modal input {
width: 93.5%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 5px;
}
.create-room-button {
display: block;
width: 100%;
padding: 10px;
font-size: 16px;
color: white;
background-color: #1609ab;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 20px;
}
.create-room-button:hover {
background-color: #218838;
}
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
z-index: 1000;
}
.overlay .add-members {
background-color: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
max-width: 400px;
width: 100%;
height: 18%;
position: fixed;
}
.overlay .delete-chat {
background-color: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
max-width: 400px;
width: 100%;
height: 18%;
position: fixed;
}
.delete-close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer; cursor: pointer;
} }
.delete-chat-header {
/* The morbid thing */ text-align: center;
table.id-str-input-table {
width: 100%;
border-collapse: collapse; /* Combine borders */
} }
.id-str-input-td1, .id-str-input-td2 { .confirm {
background-color: #1609ab;
padding: 20px 70px;
font-size: 16px;
color: white;
border: none; border: none;
border-radius: 5px;
position: absolute;
margin-left: 20px;
cursor: pointer;
transition: background-color 0.3s ease;
} }
.id-str-input-td1 { .cancel {
text-align: left; background-color: #1609ab;
padding-right: 5px; padding: 20px 70px;
white-space: nowrap; /* Prevent text wrap, keeping it in one line */ font-size: 16px;
overflow: hidden; /* Hide overflow content */ color: white;
text-overflow: ellipsis; /* Show ellipsis for overflowing text */ border: none;
border-radius: 5px;
position: absolute;
margin-left: 220px;
cursor: pointer;
transition: background-color 0.3s ease;
} }
.id-str-input-td2 { .close {
width: 100%; color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
} }

View File

@ -1,34 +1,59 @@
/* I have no idea what is going on here */
body { body {
font-family: Arial, sans-serif;
display: flex; display: flex;
flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100vh; /* Full viewport height */ height: 100vh;
margin: 0; margin: 0;
background-color: #e5e5e5;
} }
.form-container { .form-container {
background-color: #ffffff; /* Brighter box color */ width: 100%;
padding: 20px; max-width: 400px;
border-radius: 10px; background-color: white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 50%; /* Set width of the form */ display: flex;
flex-direction: column;
border-radius: 8px;
padding: 40px;
text-align: center;
} }
/* The morbid thing */ h1 {
table.logins-input-table { margin-bottom: 20px;
width: 100%; color: #2F4F4F;
border-collapse: collapse; /* Combine borders */
} }
.logins-input-td1, .logins-input-td2 {
input {
width: 100%;
background: #f7f7f7;
font-size: 16px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 20px;
margin-bottom: 15px;
outline: none;
}
button {
width: 100%;
padding: 15px;
border: none; border: none;
background-color: #0088cc;
color: white;
border-radius: 20px;
cursor: pointer;
outline: none;
font-size: 16px;
font-weight: bold;
transition: background-color 0.3s;
} }
.logins-input-td1 {
padding-right: 5px; .error-msg {
white-space: nowrap; /* Prevent text wrap, keeping it in one line */ color: red;
overflow: hidden; /* Hide overflow content */ background-color: #ffc0c0;
text-overflow: ellipsis; /* Show ellipsis for overflowing text */ border-color: red;
} border-radius: 5px;
.logins-input-td2 {
width: 100%;
} }

129
assets/css/profile.css Normal file
View File

@ -0,0 +1,129 @@
body {
display: flex;
justify-content: center;
align-items: center;
height: 90vh;
background-color: #e5e5e5;
font-family: Arial, sans-serif;
}
.main-container {
width: 700px;
height: 700px;
border-color: antiquewhite;
background-color: white;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
border-radius: 10px;
}
.profile-header {
width: 700px;
height: 160px;
border-color: antiquewhite;
background-color: #0088cc;
border-radius: 10px;
position: relative;
}
.return {
background-color: #f0f0f0;
cursor: pointer;
width: 100px;
text-decoration: none;
color: black;
display: flex;
justify-content: center;
align-items: center;
height: 30px;
border-radius: 10px;
position: absolute;
left: 20px;
top: 25px;
border: none;
}
.return:hover{
text-decoration: underline;
color: #0088cc;
}
form {
display: flex;
flex-direction: column;
align-items: center;
}
.columns {
display: flex;
justify-content: center;
align-items: flex-start;
gap: 20px;
margin-bottom: 20px;
}
.column {
display: flex;
flex-direction: column;
align-items: center;
}
.add {
width: 100px;
height: 40px;
border-width: 2px;
cursor: pointer;
font-size: 16px;
border-radius: 10px;
}
.add:hover {
background-color: #007bb5;
}
.image-button:hover {
opacity: 0.8;
}
.image-button:active {
transform: scale(0.95);
}
#login {
font-family: Arial, sans-serif;
font-size:16px;
width: 150px;
height: 20px;
border-radius: 10px;
border-color: #2F4F4F;
}
#username {
font-family: Arial, sans-serif;
font-size:16px;
width: 150px;
height: 20px;
margin-bottom: 1px;
margin-top: 50px;
border-radius: 10px;
border-color: #2F4F4F;
}
#bio {
height: 150px;
width: 500px;
padding: 10px;
box-sizing: border-box;
font-family: Arial, sans-serif;
font-size:14px;
text-align: left;
vertical-align: top;
margin-bottom: 5px;
}
.save {
cursor:pointer;
font-size: 16px;
border-radius: 15px;
border-color: #2F4F4F;
height: 40px;
width: 150px;
}
.save:hover {
background-color: #007bb5;
}
.avatar {
border-radius: 50%;
object-fit: cover;
}

View File

@ -0,0 +1,77 @@
dy {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #e5e5e5;
}
.form-container {
width: 100%;
max-width: 400px;
background-color: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
border-radius: 8px;
padding: 40px;
text-align: center;
}
h1 {
margin-bottom: 20px;
color: #2F4F4F;
}
input {
width: 100%;
background: #f7f7f7;
font-size: 16px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 20px;
margin-bottom: 15px;
outline: none;
}
button {
width: 100%;
padding: 15px;
border: none;
background-color: #0088cc;
color: white;
border-radius: 20px;
cursor: pointer;
outline: none;
font-size: 16px;
font-weight: bold;
transition: background-color 0.3s;
}
button:hover,
button:focus-visible {
background-color: #007bb5;
}
.hide-cursor::placeholder {
color: #000;
}
.hide-cursor {
caret-color: transparent;
}
.no-select {
-webkit-user-select: none; /* Для Safari */
-moz-user-select: none; /* Для Firefox */
user-select: none; /* Для всех остальных браузеров */
}
div {
color: red;
font-size: 15px;
margin-top: 10px;
display: none;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="128"
height="128"
viewBox="0 0 3.84 3.84"
fill="none"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 1.9142674,3.7842088 c -0.8814997,0 -1.32224859,0 -1.59609402,-0.2738529 C 0.04432607,3.2365216 0.04432607,2.7957577 0.04432607,1.9142674 c 0,-0.8814997 0,-1.32224859 0.27384731,-1.59609402 C 0.59201881,0.04432607 1.0327677,0.04432607 1.9142674,0.04432607 c 0.8814903,0 1.3222542,0 1.5960885,0.27384731 0.2738529,0.27384543 0.2738529,0.71459432 0.2738529,1.59609402 0,0.8814903 0,1.3222542 -0.2738529,1.5960885 C 3.2365216,3.7842088 2.7957577,3.7842088 1.9142674,3.7842088 Z m 0,-2.5711694 c 0.077453,0 0.1402457,0.062791 0.1402457,0.1402456 v 0.4207368 h 0.4207367 c 0.077453,0 0.1402456,0.062793 0.1402456,0.1402456 0,0.077453 -0.062793,0.1402457 -0.1402456,0.1402457 H 2.0545131 v 0.4207367 c 0,0.077453 -0.062793,0.1402456 -0.1402457,0.1402456 -0.077453,0 -0.1402456,-0.062793 -0.1402456,-0.1402456 V 2.0545131 H 1.353285 c -0.077455,0 -0.1402456,-0.062793 -0.1402456,-0.1402457 0,-0.077453 0.062791,-0.1402456 0.1402456,-0.1402456 H 1.7740218 V 1.353285 c 0,-0.077455 0.062793,-0.1402456 0.1402456,-0.1402456 z"
fill="#1c274c"
id="path1"
style="stroke-width:0.186994" />
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
fill="#000000"
width="128"
height="128"
viewBox="-1.7 0 3.264 3.264"
class="cf-icon-svg"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<path
d="M 1.4376583,1.6137431 A 1.5211864,1.5211864 0 1 1 -0.08352807,0.09255663 1.5209944,1.5209944 0 0 1 1.4376583,1.6137431 Z m -1.30733258,0.00192 0.58257383,-0.582766 A 0.15217629,0.15217629 0 0 0 0.49770077,0.81769971 L -0.08468094,1.4004657 -0.66763909,0.81769971 A 0.15217629,0.15217629 0 0 0 -0.88283792,1.0328985 l 0.5829582,0.5827659 -0.58276597,0.5827661 a 0.15217629,0.15217629 0 0 0 0.21519874,0.2150066 l 0.58257388,-0.582766 0.58276608,0.582766 A 0.15217629,0.15217629 0 0 0 0.71309179,2.1982382 Z"
id="path1"
style="display:inline;fill:#ff0000;fill-opacity:1;stroke-width:0.192143" />
</svg>

Before

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 824 B

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
fill="#000000"
version="1.1"
id="Capa_1"
width="128"
height="128"
viewBox="0 0 70.75936 70.75936"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs3" />
<g
id="g3"
style="fill:#c8f5ff;fill-opacity:1;stroke:#6adeef;stroke-width:8.62702;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(0.1490509,0,0,0.14948634,3.4471925,2.5065977)">
<g
id="g2"
style="fill:#c8f5ff;fill-opacity:1;stroke:#6adeef;stroke-width:8.62702;stroke-dasharray:none;stroke-opacity:1">
<path
d="m 409.657,32.474 c -43.146,-43.146 -113.832,-43.146 -156.978,0 l -84.763,84.762 c 29.07,-8.262 60.589,-6.12 88.129,6.732 l 44.063,-44.064 c 17.136,-17.136 44.982,-17.136 62.118,0 17.136,17.136 17.136,44.982 0,62.118 l -55.386,55.386 -36.414,36.414 c -17.136,17.136 -44.982,17.136 -62.119,0 l -47.43,47.43 c 11.016,11.017 23.868,19.278 37.332,24.48 36.415,14.382 78.643,8.874 110.467,-16.219 3.06,-2.447 6.426,-5.201 9.18,-8.262 l 57.222,-57.222 34.578,-34.578 c 43.453,-43.145 43.453,-113.525 10e-4,-156.977 z"
id="path1"
style="fill:#c8f5ff;fill-opacity:1;stroke:#6adeef;stroke-width:8.62702;stroke-dasharray:none;stroke-opacity:1" />
<path
d="m 184.135,320.114 -42.228,42.228 c -17.136,17.137 -44.982,17.137 -62.118,0 -17.136,-17.136 -17.136,-44.981 0,-62.118 l 91.8,-91.799 c 17.136,-17.136 44.982,-17.136 62.119,0 l 47.43,-47.43 c -11.016,-11.016 -23.868,-19.278 -37.332,-24.48 -38.25,-15.3 -83.232,-8.262 -115.362,20.502 -1.53,1.224 -3.06,2.754 -4.284,3.978 l -91.8,91.799 c -43.146,43.146 -43.146,113.832 0,156.979 43.146,43.146 113.832,43.146 156.978,0 l 82.927,-83.845 c -42.23,9.791 -52.022,8.568 -88.13,-5.814 z"
id="path2"
style="fill:#c8f5ff;fill-opacity:1;stroke:#6adeef;stroke-width:8.62702;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="127.99999"
height="127.99999"
viewBox="0 0 33.866664 33.866664"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<path
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 6.2911505,3.4844006 V 6.6404405 H 3.0362141 V 28.776832 h 4.7418555 v 2.929792 H 0.58956039 V 3.438992 Z"
id="path63" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 27.557741,31.386555 v -3.15604 h 3.254937 V 6.0941238 h -4.741856 v -2.929792 h 7.188509 V 31.431963 Z"
id="path63-2" />
<path
id="rect63"
style="fill:#fdffff;fill-opacity:1;fill-rule:nonzero;stroke:#e1e1e1;stroke-width:0.529167;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 8.7183307,8.3901854 c -2.467802,0 -4.4545084,1.9867066 -4.4545084,4.4545086 v 5.357812 c 0,2.467802 1.9867064,4.454509 4.4545084,4.454509 0,0 6.6668103,0 10.2365843,0 1.184667,0 1.8523,1.573101 2.973462,1.946651 1.827305,0.608823 5.758305,0.474906 5.758305,0.474906 0,0 -1.204106,-0.984163 -2.336589,-2.042039 -0.307888,-0.287603 -0.182877,-1.488944 0.134028,-1.896921 0.674916,-0.868872 0.918918,-1.680982 0.918918,-2.937106 v -5.357812 c 0,-2.467802 -1.986706,-4.4545086 -4.454508,-4.4545086 z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/img/logo0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="128"
height="127.99999"
viewBox="0 0 33.866667 33.866664"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<path
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.294159;stroke-linejoin:round;stroke-opacity:1"
d="m 0.65443061,23.377766 10.20536539,-7.351693 -0.06427,3.749871 c 0,0 6.384296,0.115304 9.607291,0.115304 1.824632,0 4.010317,0.117998 5.361398,-1.065432 0.797635,-0.698657 1.041209,-1.943308 0.954461,-2.97946 -0.118465,-1.414973 -0.417733,-2.830021 -1.492491,-3.799298 -0.964892,-0.870193 -2.338758,-1.449022 -3.659301,-1.465666 -1.347581,-0.01698 -4.311633,-0.01476 -4.311633,-0.01476 l 0.02678,-6.0468658 c 0,0 5.209174,-0.1269759 7.836958,-0.1269759 2.090574,0 3.730335,0.2813604 5.374446,1.5274776 1.165995,0.8837399 2.874757,2.1212447 2.874757,3.8971142 0,3.5737799 -0.03341,6.8479459 -0.03341,10.6502529 0,3.155641 -2.159421,4.126978 -3.036427,4.888332 -1.783594,1.548393 -4.069763,1.553329 -6.468859,1.553329 -4.709142,0 -13.295569,0 -13.295569,0 v 3.244296 z"
id="path1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="127.99999"
height="127.99999"
viewBox="0 0 33.866664 33.866664"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<path
id="path11"
style="fill:#e6e6e6;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.792427;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 9.8376422,3.4995279 c -0.413454,0.04422 -0.9249931,0.2733392 -1.5859497,0.7720459 -2.15303,1.6245101 -1.0645387,2.7213368 -0.2780192,3.6576578 0.9643744,1.1480481 0.5272069,2.4297214 -0.2444295,3.5413854 -1.0846469,1.562605 -1.8094411,2.689497 -3.1812011,2.618445 -1.7185528,-0.08902 -3.2061983,0.950419 -3.1817179,2.618962 0.020151,1.373449 0.960489,2.763844 2.9987833,2.826184 1.3729571,0.04199 2.35275,1.613748 2.9993001,2.825667 0.6465497,1.211919 1.3490594,3.417259 0.6769613,4.064868 -0.989134,0.953094 -1.312826,2.263816 0.6769612,4.064868 1.2829023,1.161218 2.6693573,0.639536 3.7666953,-0.946195 1.179214,-1.70405 2.337505,-1.523233 4.127396,-1.422652 1.371435,0.07707 2.886284,-0.325958 3.858679,1.446423 0.660704,1.204262 1.722209,2.911554 3.85868,1.445906 1.132687,-0.77704 2.468135,-2.085182 0.947745,-4.010091 -0.610564,-0.773014 -0.532201,-2.186383 0.948263,-4.010607 1.255317,-1.546798 1.98091,-2.751433 3.181201,-2.618962 1.365308,0.150686 2.84107,-0.224934 3.181718,-2.618445 0.366273,-2.57357 -1.941907,-2.695322 -3.312976,-2.778641 C 28.1324,14.906856 28.128014,13.488893 26.590624,12.103137 25.053235,10.717381 25.023658,9.341954 26.190649,8.2930337 27.35764,7.2441129 27.633021,6.3291961 25.719877,4.5222045 24.596532,3.461188 23.254965,3.1719841 21.630204,4.9759236 20.911774,5.7735833 19.336054,7.1894888 17.196366,7.1086099 15.002057,7.0256665 13.838949,7.4611865 12.285555,5.8053303 11.434493,4.8981315 11.078004,3.3668691 9.8376422,3.4995279 Z m 6.7892498,6.5215661 a 7.4172139,7.0961218 0 0 1 7.417118,7.096207 7.4172139,7.0961218 0 0 1 -7.417118,7.096208 7.4172139,7.0961218 0 0 1 -7.4171184,-7.096208 7.4172139,7.0961218 0 0 1 7.4171184,-7.096207 z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="127.99999"
height="127.99999"
viewBox="0 0 33.866664 33.866664"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<path
style="fill:#000080;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.465;stroke-linejoin:round;stroke-dasharray:none"
d="M 9.3745975,32.81176 H 25.632941 c 0,0 0.08996,-6.044498 -0.03778,-10.988966 0.0321,-3.524476 -1.327193,-6.290541 -8.363226,-6.155275 -7.036034,0.135265 -7.7455423,2.805955 -7.7693511,5.331649 -0.034594,3.669826 -0.087981,11.812592 -0.087981,11.812592 z"
id="path7" />
<path
id="path8"
style="fill:#000080;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.965;stroke-linejoin:round;stroke-dasharray:none"
d="M 17.963761,1.3260172 A 11.612947,6.192831 0 0 0 6.3510334,7.5189208 11.612947,6.192831 0 0 0 17.963761,13.711824 11.612947,6.192831 0 0 0 29.576489,7.5189208 11.612947,6.192831 0 0 0 17.963761,1.3260172 Z m -3.947046,2.876827 a 1.5186517,3.4351659 0 0 1 1.518771,3.4349323 1.5186517,3.4351659 0 0 1 -1.518771,3.4354495 1.5186517,3.4351659 0 0 1 -1.518253,-3.4354495 1.5186517,3.4351659 0 0 1 1.518253,-3.4349323 z m 6.47299,0.2160074 a 1.5186517,3.4351659 0 0 1 1.518254,3.4349324 1.5186517,3.4351659 0 0 1 -1.518254,3.435449 1.5186517,3.4351659 0 0 1 -1.51877,-3.435449 1.5186517,3.4351659 0 0 1 1.51877,-3.4349324 z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,188 +0,0 @@
let LocalHistoryId = 0;
function genSentBase(){
return {
'chatUpdReq': {
'LocalHistoryId': LocalHistoryId,
'chatId': openedchat.id
}
};
}
let members = new Map();
let memberBoxes = new Map();
let myRoleHere = null; // Dung local state updates should be updated first
let userDeletionWinStoredUId = -1;
function shouldShowDeleteButton(memberSt){
return userinfo.uid !== memberSt.userId && myRoleHere === userChatRoleAdmin && memberSt.roleHere !== userChatRoleDeleted;
}
function updateBoxWithSt(box, memberSt){
let ID = memberSt.userId;
let roleP = box.querySelector(".CM-member-box-role");
roleP.innerText = memberSt.roleHere;
box.style.backgroundColor = roleToColor(memberSt.roleHere);
box.querySelector(".CM-member-box-leave-btn").style.display =
(shouldShowDeleteButton(memberSt) ? "block" : "none");
}
function convertMemberStToBox(memberSt){
let ID = memberSt.userId;
let userProfileURI = "/user/" + memberSt.nickname;
let box = document.createElement("div");
box.className = "dynamic-block-list-el CM-member-box";
box.style.backgroundColor = roleToColor(memberSt.roleHere);
let inBoxNickname = document.createElement("a");
box.appendChild(inBoxNickname);
inBoxNickname.className = "entity-nickname-txt CM-member-box-nickname";
inBoxNickname.innerText = memberSt.nickname;
inBoxNickname.href = userProfileURI;
let inBoxName = document.createElement("a");
box.appendChild(inBoxName);
inBoxName.className = "entity-reg-field-txt CM-member-box-name";
inBoxName.innerText = memberSt.name;
inBoxName.href = userProfileURI;
let inBoxUserRoleHere = document.createElement("p");
box.appendChild(inBoxUserRoleHere);
inBoxUserRoleHere.className = "entity-reg-field-txt CM-member-box-role";
inBoxUserRoleHere.innerText = memberSt.roleHere;
let inBoxLeaveBtn = document.createElement("img");
box.appendChild(inBoxLeaveBtn);
inBoxLeaveBtn.className = "CM-member-box-leave-btn";
inBoxLeaveBtn.src = "/assets/img/delete.svg";
inBoxLeaveBtn.onclick = function (ev) {
if (ev.button !== 0)
return;
userDeletionWinStoredUId = ID;
document.getElementById("user-deletion-win-title").innerText =
"Do you really want to kick user " + memberSt.nickname + "?";
activatePopupWindowById("user-deletion-win");
};
box.querySelector(".CM-member-box-leave-btn").style.display =
(shouldShowDeleteButton(memberSt) ? "block" : "none");
return box;
}
function updateLocalStateFromChatUpdResp(chatUpdResp){
LocalHistoryId = chatUpdResp.HistoryId;
// If my role is updated, we need to update all the boes of already set users (kick button can appear and disappear)
let literalMemberList = document.getElementById("CM-list");
// We ignore messages and everything related to them. Dang, I really should add an argument to disable message lookup here
for (let memberSt of chatUpdResp.members){
console.log([memberSt, userinfo.uid, myRoleHere]);
if (memberSt.userId === userinfo.uid && myRoleHere !== memberSt.roleHere){
myRoleHere = memberSt.roleHere;
for (let [id, memberSt] of members){
let box = memberBoxes.get(id);
updateBoxWithSt(box, memberSt);
}
document.getElementById("CM-btn-add").style.display =
(memberSt.roleHere === userChatRoleAdmin ? "block" : "none");
console.log("DEBUG " + (memberSt.roleHere === userChatRoleAdmin ? "block" : "none"));
break;
}
}
for (let memberSt of chatUpdResp.members){
let id = memberSt.userId;
if (members.has(id)){
updateBoxWithSt(memberBoxes.get(id), memberSt);
} else {
if (memberSt.roleHere !== userChatRoleDeleted){
members.set(id, memberSt);
let box = convertMemberStToBox(memberSt);
memberBoxes.set(id, box);
literalMemberList.appendChild(box);
}
}
}
}
function updateLocalStateFromRecv(Recv){
updateLocalStateFromChatUpdResp(Recv.chatUpdResp);
}
function configureSummonUserInterface(){
document.getElementById("user-summoning-yes").onclick = function(ev ){
if (ev.button !==0)
return;
let nickname = String(document.getElementById("summoned-user-nickname").value);
let isReadOnly = document.getElementById("summoned-user-is-read-only").checked;
deactivateActivePopup();
let Sent = genSentBase();
Sent.nickname = nickname;
Sent.makeReadOnly = Boolean(isReadOnly);
apiRequest("addMemberToChat", Sent).
then((Recv) => {
updateLocalStateFromRecv(Recv);
}).catch((e) => {
console.log(e);
alert("Failed to add user to chat");
});
};
document.getElementById("user-summoning-no").onclick = function (ev) {
if (ev.button !== 0)
return;
deactivateActivePopup();
};
document.getElementById("CM-btn-add").onclick = function(ev) {
if (ev.button !== 0)
return;
document.getElementById("summoned-user-nickname").value = "";
// read-only flag persists throughout user summoning sessions, and IT IS NOT A BUG
activatePopupWindowById("user-summoning-win");
};
}
/* Popup activation button is configured for each box separately */
function configureKickUserInterfaceWinPart(){
document.getElementById("user-deletion-yes").onclick = function (ev){
if (ev.button !== 0)
return;
deactivateActivePopup();
if (userDeletionWinStoredUId < 0)
throw new Error("Karaul");
let Sent = genSentBase();
Sent.userId = userDeletionWinStoredUId;
apiRequest("removeMemberFromChat", Sent).
then((Recv) => {
updateLocalStateFromRecv(Recv);
}).catch((e) => {
console.log(e);
alert("Failed to kick user from chat");
});
}
document.getElementById("user-deletion-no").onclick = function (ev) {
if (ev.button !== 0)
return;
deactivateActivePopup();
};
}
__mainloopDelayMS = 5000;
__guestMainloopPollerAction = function (){
console.log("Hello, world");
apiRequest("chatPollEvents", genSentBase()).
then((Recv) => {
console.log(Recv);
updateLocalStateFromRecv(Recv);
});
}
window.onload = function(){
console.log("Page loaded");
configureSummonUserInterface();
configureKickUserInterfaceWinPart();
updateLocalStateFromChatUpdResp(initial_chatUpdResp);
mainloopPoller();
}

View File

@ -1,449 +1,162 @@
let LocalHistoryId = 0; let members = [
{ username: 'Адель', nickname: 'cold_siemens52', avatar: 'https://sun9-59.userapi.com/impg/t8GhZ7FkynVifY1FQCnaf31tGprbV_rfauZzgg/fSq4lyc6V0U.jpg?size=1280x1280&quality=96&sign=e3c309a125cb570d2e18465eba65f940&type=album' },
{ username: 'Антон', nickname: 'antyak_01', avatar: 'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_1280.png' },
{ username: 'Владимир', nickname: 'kkrkk2006', avatar: 'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_1280.png' }
];
let currentHistoryId = 0;
let currentChatID = null;
function renderMembersList() {
const membersListBody = document.getElementById('members-list-body');
membersListBody.innerHTML = '';
let members = new Map(); members.forEach((member, index) => {
const memberItem = document.createElement('li');
let loadedMessages = new Map(); // messageSt objects memberItem.innerHTML = `
/* <img src="${member.avatar}" alt="${member.username}">
container: EL, box: EL, offset: number (msgPres) */ <a href="profile.html">${member.username}</a>
let visibleMessages = new Map(); // HTMLElement objects <button class="delete-member" onclick="deleteMember(${index})">Удалить из чата</button>
`;
let anchoredMsg = -1; membersListBody.appendChild(memberItem);
let visibleMsgSegStart = -1; });
let visibleMsgSegEnd = -2;
let offsetOfAnchor = 500;
let highestPoint = null;
let lowestPoint = null;
let lastMsgId = -1;
let myRoleHere = null; // Dung local state updates should be updated first
// Would start with true if opened `/chat/<>`
let bumpedAtTheBottom = false;
// Hidden variable. When deletion window popup is active
// Persists from popup activation until popup deactivation
let storeHiddenMsgIdForDeletionWin = -1;
// Positive in production, negative for debug
let softZoneSz = -150;
let chatPadding = 300;
function genSentBase(){
return {
'chatUpdReq': {
'LocalHistoryId': LocalHistoryId,
'chatId': openedchat.id
}
};
} }
function genSentBaseGMN(){ function deleteMember(index) {
let Sent = genSentBase(); members.splice(index, 1);
Sent.amount = 1; renderMembersList();
return Sent;
} }
function getChatWgSz(){ async function sendMessage() {
let chatWg = document.getElementById("chat-widget"); const chatMessages = document.getElementById('chat-messages');
return [chatWg.offsetWidth, chatWg.offsetHeight]; const chatInput = document.getElementById('chat-input');
} const message = chatInput.value;
function elSetOffsetInChat(el, offset){ if (message.trim() !== '') {
el.style.bottom = String(offset) + "px"; const request = {
} 'chatId': currentChatID,
'LocalHistoryId': currentHistoryId,
function isMissingPrimaryMsgHeap(){ 'content': {
return lastMsgId >= 0 && anchoredMsg < 0; 'text': message
}
function isMissingTopMsgHeap(){
let [W, H] = getChatWgSz();
return anchoredMsg >= 0 && (highestPoint < H + softZoneSz && visibleMsgSegStart > 0);
}
function isMissingBottomMsgHeap(){
return anchoredMsg >= 0 && (lowestPoint > - softZoneSz && visibleMsgSegEnd < lastMsgId);
}
function updateOffsetOfVisibleMsg(msgId, offset){
visibleMessages.get(msgId).container.style.bottom = String(offset) + "px";
}
function updateOffsetsUpToTop(){
let offset = offsetOfAnchor;
for (let curMsg = anchoredMsg; curMsg >= visibleMsgSegStart; curMsg--){
updateOffsetOfVisibleMsg(curMsg, offset);
let height = visibleMessages.get(curMsg).container.offsetHeight;
offset += height + 5;
}
return offset;
}
function updateOffsetsDown(){
let offset = offsetOfAnchor;
for (let curMsg = anchoredMsg + 1; curMsg <= visibleMsgSegEnd; curMsg++){
let height = visibleMessages.get(curMsg).container.offsetHeight;
offset -= (height + 5);
updateOffsetOfVisibleMsg(curMsg, offset);
}
return offset;
}
function updateOffsetsSane(){
if (anchoredMsg < 0)
return;
highestPoint = updateOffsetsUpToTop();
lowestPoint = updateOffsetsDown();
}
function heightOfPreloadGhost(){
let [W, H] = getChatWgSz();
return Math.min(H * 0.9, Math.max(H * 0.69, 30));
}
function updateOffsets(){
let spinnerTop = document.getElementById("top-loading");
let spinnerBottom = document.getElementById("bottom-loading");
let SbH = spinnerBottom.offsetHeight;
if (anchoredMsg < 0){
hideHTMLElement(spinnerBottom);
elSetOffsetInChat(spinnerTop, chatPadding);
setElementVisibility(spinnerTop, isMissingPrimaryMsgHeap());
} else {
updateOffsetsSane();
let [W, H] = getChatWgSz();
let lowestLowestPoint = isMissingBottomMsgHeap() ? lowestPoint - heightOfPreloadGhost(): lowestPoint;
let highestHighestPoint = isMissingTopMsgHeap() ? highestPoint + heightOfPreloadGhost() : highestPoint;
if (lowestLowestPoint > chatPadding || (highestHighestPoint - lowestLowestPoint) <= H - chatPadding * 2) {
bumpedAtTheBottom = true;
offsetOfAnchor += (-lowestLowestPoint + chatPadding);
updateOffsetsSane();
} else if (highestHighestPoint < H - chatPadding) {
offsetOfAnchor += (-highestHighestPoint + (H - chatPadding));
updateOffsetsSane();
}
/* Messages weere updated (and only them). They were talking with ghosts.
Now we are trying to show spinners of ghosts */
elSetOffsetInChat(spinnerTop, highestPoint);
setElementVisibility(spinnerTop, isMissingTopMsgHeap());
elSetOffsetInChat(spinnerBottom, lowestPoint - SbH);
setElementVisibility(spinnerBottom, isMissingBottomMsgHeap());
}
}
function shouldShowDeleteMesgBtn(messageSt){
return !messageSt.isSystem && messageSt.exists && (messageSt.myRoleHere !== userChatRoleReadOnly) &&(
messageSt.myRoleHere === userChatRoleAdmin || messageSt.senderUserId === userinfo.uid);
}
// todo: fix messageboxes
function getMsgTypeClassSenderBased(messageSt){
if (messageSt.isSystem)
return "message-box-system"
if (messageSt.senderUserId === userinfo.uid)
return "message-box-mine"
return "message-box-alien";
}
function getMsgFullTypeClassName(messageSt){
return getMsgTypeClassSenderBased(messageSt) + (messageSt.exists ? "" : " message-box-deleted");
}
/* Two things can be updated: messages existance and delete button visibility
* Supercontainer.container is persistent, Supercontainer.box can change it's class */
function updateMessageSupercontainer(supercontainer, messageSt){
let box = supercontainer.box;
setElementVisibility(box.querySelector(".message-box-button-delete"), shouldShowDeleteMesgBtn(messageSt), "inline");
box.className = getMsgFullTypeClassName(messageSt);
// Notice, that no check of previous state is performed. Double loading is a rare event, I can afford to be slow
if (!messageSt.exists)
box.querySelector(".message-box-msg").innerText = msgErased;
}
function decodeSystemMessage(text){
let [subject, verb, object] = text.split(',');
let subjectId = Number(subject);
let objectId = Number(object);
let subjectRef = members.has(subjectId) ? members.get(subjectId).nickname : "???";
let objectRef = members.has(objectId) ? members.get(objectId).nickname : "???";
if (verb === "kicked"){
return subjectRef + " kicked " + objectRef;
} else if (verb === "summoned"){
return subjectRef + " summoned " + objectId;
} else if (verb === "left"){
return subjectRef + " left chat";
} else if (verb === "joined"){
return subjectId + " joined chat";
} else if (verb === "created"){
return subjectId + " created this chat";
}
return "... Bad log ...";
}
function convertMessageStToSupercontainer(messageSt){
let container = document.createElement("div");
container.className = "message-supercontainer";
let box = document.createElement("div");
container.appendChild(box);
box.className = getMsgFullTypeClassName(messageSt);
let ID = messageSt.id;
let topPart = document.createElement("div");
box.appendChild(topPart);
topPart.className = "message-box-top";
if (!members.has(messageSt.senderUserId))
throw new Error("First - update members");
let senderMemberSt = members.get(messageSt.senderUserId);
let senderProfileURI = "/user/" + senderMemberSt.nickname;
let inTopPartSenderName = document.createElement("a");
topPart.appendChild(inTopPartSenderName);
inTopPartSenderName.className = "message-box-sender-name";
inTopPartSenderName.innerText = senderMemberSt.name;
inTopPartSenderName.href = senderProfileURI;
let inTopPartSenderNickname = document.createElement("a");
topPart.appendChild(inTopPartSenderNickname);
inTopPartSenderNickname.className = "message-box-sender-name message-box-sender-shortname"
inTopPartSenderNickname.innerText = senderMemberSt.nickname;
inTopPartSenderNickname.href = senderProfileURI;
let inTopPartButtonDelete = document.createElement("img");
topPart.appendChild(inTopPartButtonDelete);
inTopPartButtonDelete.className = "message-box-button message-box-button-delete";
inTopPartButtonDelete.src = "/assets/img/delete.svg";
inTopPartButtonDelete.onclick = (ev) => {
if (ev.button !== 0)
return;
let msgText = box.querySelector(".message-box-msg").innerText;
let previewText = senderMemberSt.nickname + ":\n" + msgText;
if (previewText.length > 1000)
previewText = previewText.substring(0, 1000 - 3);
document.getElementById("win-deletion-msg-preview").innerText = previewText;
storeHiddenMsgIdForDeletionWin = ID;
activatePopupWindowById("msg-deletion-win");
};
setElementVisibility(inTopPartButtonDelete, shouldShowDeleteMesgBtn(messageSt), "inline");
let inTopPartButtonGetLink = document.createElement("img");
topPart.appendChild(inTopPartButtonGetLink);
inTopPartButtonGetLink.className = "message-box-button";
inTopPartButtonGetLink.src = "/assets/img/link.svg";
inTopPartButtonGetLink.onclick = (ev) => {
if (ev.button !== 0)
return;
let URI = window.location.host + "/chat/" + openedchat.nickname + "/m/" + String(ID);
document.getElementById("message-input").innerText += (" " + URI + "");
console.log("Tried to get link on message " + ID);
};
let msgPart = document.createElement("p");
box.appendChild(msgPart);
msgPart.className = "message-box-msg";
if (messageSt.exists){
if (messageSt.isSystem)
msgPart.innerText = decodeSystemMessage(messageSt.text);
else
msgPart.innerText = messageSt.text;
} else
msgPart.innerText = msgErased;
return {'container': container, 'box': box, 'offset': 0};
}
function makeVisible(msgId){
let supercontainer = convertMessageStToSupercontainer(loadedMessages.get(msgId));
const chatWin = document.getElementById("chat-widget");
chatWin.appendChild(supercontainer.container);
visibleMessages.set(msgId, supercontainer);
}
function opaNewMessageSt(messageSt){
let msgId = messageSt.id;
if (loadedMessages.has(msgId)){
loadedMessages.set(msgId, messageSt);
if (visibleMessages.has(msgId)){
updateMessageSupercontainer(visibleMessages.get(msgId), messageSt);
}
} else {
loadedMessages.set(msgId, messageSt);
if (anchoredMsg < 0){
anchoredMsg = msgId;
visibleMsgSegStart = msgId;
visibleMsgSegEnd = msgId;
makeVisible(msgId);
} else if (msgId + 1 === visibleMsgSegStart) {
visibleMsgSegStart--;
makeVisible(msgId);
while (loadedMessages.has(visibleMsgSegStart - 1)){
visibleMsgSegStart--;
makeVisible(visibleMsgSegStart);
}
} else if (msgId - 1 === visibleMsgSegEnd){
visibleMsgSegEnd++;
makeVisible(msgId);
while (loadedMessages.has(visibleMsgSegEnd + 1)){
visibleMsgSegEnd++;
makeVisible(visibleMsgSegEnd);
} }
};
const response = await fetch("/internalapi/sendMessage", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
});
const res = await response.json();
if (res.update) {
const update = res.update[0];
currentHistoryId = update.HistoryId;
const messageElement = document.createElement('div');
messageElement.classList.add('chat-message');
const avatarElement = document.createElement('div');
avatarElement.classList.add('avatar');
const avatarImage = document.createElement('img');
avatarImage.src = 'https://sun9-59.userapi.com/impg/t8GhZ7FkynVifY1FQCnaf31tGprbV_rfauZzgg/fSq4lyc6V0U.jpg?size=1280x1280&quality=96&sign=e3c309a125cb570d2e18465eba65f940&type=album';
avatarElement.appendChild(avatarImage);
const messageContentElement = document.createElement('div');
messageContentElement.classList.add('message-content');
const usernameElement = document.createElement('div');
usernameElement.classList.add('username');
usernameElement.textContent = await getUserName();
const textElement = document.createElement('div');
textElement.classList.add('text');
textElement.textContent = message;
messageContentElement.appendChild(usernameElement);
messageContentElement.appendChild(textElement);
messageElement.appendChild(avatarElement);
messageElement.appendChild(messageContentElement);
chatMessages.appendChild(messageElement);
chatInput.value = '';
chatMessages.scrollTop = chatMessages.scrollHeight;
} }
} }
} }
function canISendMessages(){ function openMembersList() {
return myRoleHere === userChatRoleRegular || myRoleHere === userChatRoleAdmin; renderMembersList();
document.getElementById("members-list").style.display = "block";
document.getElementById("overlay").style.display = "flex";
} }
function updateLocalStateFromChatUpdRespBlind(chatUpdResp){ function closeMembersList() {
console.log(anchoredMsg, offsetOfAnchor, chatUpdResp); document.getElementById("members-list").style.display = "none";
LocalHistoryId = chatUpdResp.HistoryId; document.getElementById("overlay").style.display = "none";
for (let memberSt of chatUpdResp.members){ }
let id = memberSt.userId;
if (id === userinfo.uid && myRoleHere !== memberSt.roleHere) { document.getElementById('chat-input').addEventListener('keydown', function (event) {
myRoleHere = memberSt.roleHere; if (event.key === 'Enter') {
for (let [msgId, sc] of visibleMessages){ sendMessage();
updateMessageSupercontainer(sc, loadedMessages.get(msgId));
}
setElementVisibility(document.getElementById("message-input"), canISendMessages());
}
} }
for (let memberSt of chatUpdResp.members){ });
let id = memberSt.userId;
members.set(id, memberSt);
}
lastMsgId = chatUpdResp.lastMsgId;
for (let messageSt of chatUpdResp.messages){
opaNewMessageSt(messageSt);
}
updateOffsets();
}
function updateLocalStateFromRecvBlind(Recv){ async function getUserID() {
updateLocalStateFromChatUpdRespBlind(Recv.chatUpdResp); const response = await fetch('/internalapi/mirror', {
} method: 'POST',
headers: {
async function requestMessageNeighbours(fromMsg, direction){ 'Content-Type': 'application/json'
let Sent = genSentBaseGMN(); },
Sent.msgId = fromMsg; body: JSON.stringify({})
Sent.direction = direction;
let Recv = await apiRequest("getMessageNeighbours", Sent);
updateLocalStateFromRecvBlind(Recv); // Blind to non-loaded whitespaces
}
function needToLoadWhitespace(){
return isMissingPrimaryMsgHeap() || isMissingTopMsgHeap() || isMissingBottomMsgHeap();
}
async function tryLoadWhitespaceSingle(){
console.log('tryLoadWhitespaceSingle');
if (isMissingPrimaryMsgHeap()){
await requestMessageNeighbours(-1, "backward");
} else if (isMissingTopMsgHeap()){
await requestMessageNeighbours(visibleMsgSegStart, "backward");
} else if (isMissingBottomMsgHeap()){
await requestMessageNeighbours(visibleMsgSegEnd, "forward");
}
}
async function loadWhitespaceMultitry(){
if (needToLoadWhitespace()){
cancelMainloopTimeout();
do {
try {
await tryLoadWhitespaceSingle();
await sleep(100);
} catch (e) {
console.error(e);
await sleep(1500);
}
} while (needToLoadWhitespace());
setMainloopTimeout();
}
}
async function updateLocalStateFromRecv(Recv){
updateLocalStateFromRecvBlind(Recv);
await loadWhitespaceMultitry();
}
async function safeApiRequestWithLocalStUpdate(type, Sent, errMsg){
try {
let Recv = await apiRequest(type, Sent)
await updateLocalStateFromRecv(Recv);
} catch(e) {
console.error(e);
alert(errMsg);
}
}
function configureMsgDeletionPopupButtons(){
document.getElementById("msg-deletion-yes").onclick = function(ev){
if (ev.button !== 0)
return;
deactivateActivePopup();
let Sent = genSentBase();
Sent.id = storeHiddenMsgIdForDeletionWin;
safeApiRequestWithLocalStUpdate("deleteMessage", Sent, "Failed to delete message");
};
document.getElementById("msg-deletion-no").onclick = function (ev){
if (ev.button !== 0)
return;
deactivateActivePopup();
}
}
__mainloopDelayMs = 1000;
async function UPDATE(){
let Recv = await apiRequest("chatPollEvents", genSentBase());
await updateLocalStateFromRecv(Recv);
}
__guestMainloopPollerAction = UPDATE;
window.onload = function (){
console.log("Page was loaded");
document.body.addEventListener("wheel", function (event) {
// event.preventDefault();
bumpedAtTheBottom = false;
offsetOfAnchor += event.deltaY / 3;
updateOffsets();
loadWhitespaceMultitry().then(dopDopYesYes);
}); });
document.getElementById("message-input").addEventListener("keyup", function (event) { const res = await response.json();
if (event.ctrlKey && event.key === 'Enter'){ return res.id;
let textarea = document.getElementById("message-input"); }
let text = String(textarea.innerText); async function getChatID() {
console.log(text); const chatNickname = window.location.pathname.split('/').pop();
textarea.innerText = ""; const response = await fetch('/internalapi/getChatList', {
let Sent = genSentBase(); method: 'POST',
Sent.content = {}; headers: {
Sent.content.text = text; 'Content-Type': 'application/json'
safeApiRequestWithLocalStUpdate("sendMessage", Sent, "Failed to send message"); },
} body: JSON.stringify({})
}); });
let chatWg = document.getElementById("chat-widget"); const res = await response.json();
let chatWgDebugLinesFnc = function (){ for (const chat of res.chats) {
let H = chatWg.offsetHeight; if (chat.content.nickname === chatNickname) {
elSetOffsetInChat(document.getElementById("debug-line-lowest"), -softZoneSz); return chat.id;
elSetOffsetInChat(document.getElementById("debug-line-highest"), H + softZoneSz); }
elSetOffsetInChat(document.getElementById("debug-line-top-padding"), H - chatPadding); }
elSetOffsetInChat(document.getElementById("debug-line-bottom-padding"), chatPadding) return -1;
};
window.addEventListener("resize", chatWgDebugLinesFnc);
chatWgDebugLinesFnc();
configureMsgDeletionPopupButtons();
updateLocalStateFromChatUpdRespBlind(initial_chatUpdResp);
setMainloopTimeout();
loadWhitespaceMultitry();
} }
async function editMessage(new_message) {
const req = {
'chatId': currentChatID,
'LocalHistoryId': currentHistoryId,
'id': getUserID(),
'content': {
'text': new_message
}
};
const res = await fetch('/internalapi/editMessage', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(req)
});
const response = await res.json();
if (response.update) {
currentHistoryId = response.update[0].HistoryId;
}
}
document.addEventListener("DOMContentLoaded", async function() {
currentChatID = await getChatID();
});

View File

@ -1,26 +0,0 @@
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 = "";
}

View File

@ -1,73 +0,0 @@
let dopDopYesYes = (ign) => {};
function sleep(ms){
return new Promise(res => setTimeout(res, ms));
}
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 __guestMainloopPollerAction = null;
function setMainloopTimeout(){
mainloopTimeout = setTimeout(mainloopPoller, __mainloopDelayMs);
}
function cancelMainloopTimeout(){
clearTimeout(mainloopTimeout);
mainloopTimeout = null;
}
function mainloopPoller(){
try {
if (__guestMainloopPollerAction)
__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
}
// todo: replace it with translation
const msgErased = "[ ERASED ]";
function hideHTMLElement(el){
el.style.display = "none";
}
function showHTMLElement(el){
el.style.display = "block";
}
function setElementVisibility(el, isVisible, howVisible = "block"){
el.style.display = isVisible ? howVisible : "none";
}

View File

@ -1,188 +1,186 @@
let LocalHistoryId = 0; let rooms = {};
let roomToDelete = null;
let currentRoom = null;
let currentHistoryId = 0;
function genSentBase(){ function openRoom(currentRoom) {
return { alert('Вы вошли в комнату: ' + currentRoom);
'chatListUpdReq': { }
'LocalHistoryId': LocalHistoryId
function closeAdd() {
document.getElementById('add_members').style.display = 'none';
}
function openAdd() {
document.getElementById('add_members').style.display = 'flex';
}
function openConfirm(roomNickname) {
roomToDelete = roomNickname;
document.getElementById("delete-chat").style.display = "flex";
}
function closeConfirm() {
roomToDelete = null;
document.getElementById("delete-chat").style.display = "none";
}
function deleteChat() {
if (roomToDelete && rooms[roomToDelete]) {
delete rooms[roomToDelete];
removeRoomFromList(roomToDelete);
closeConfirm();
} else {
alert("Не удалось найти выбранную комнату.");
}
}
function addMember() {
const login = document.getElementById('newMemberLogin').value;
if (login) {
alert(`Участник с никнеймом '${login}' добавлен`);
closeAdd();
} else {
alert('Пожалуйста, введите логин участника');
}
}
function openCreateRoomModal() {
document.getElementById('createRoomModal').style.display = 'block';
}
function closeCreateRoomModal() {
document.getElementById('createRoomModal').style.display = 'none';
}
async function createRoom() {
const errorElement = document.getElementById('error');
const roomName = document.getElementById('newRoomName').value.trim();
const roomNickname = document.getElementById('newRoomNickname').value.trim();
errorElement.style.display = 'none';
errorElement.textContent = '';
if (roomName === '' || roomNickname === '') {
errorElement.textContent = 'Пожалуйста, заполните все поля';
errorElement.style.display = 'block';
return;
}
const request = {
LocalHistoryId: currentHistoryId,
content: {
name: roomName,
nickname: roomNickname
} }
}; };
}
let myChats = new Map(); try {
let chatBoxes = new Map(); const response = await fetch('/internalapi/createChat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
});
/* Generate text that is displayed on the right side of chat intro box */ const res = await response.json();
function youAreXHere(myRoleHere){
return "You are " + myRoleHere + " here";
}
if (res.status === 0) {
let chatRenunciationWinStoredId = -1; addRoomToList(roomName, roomNickname);
rooms[roomNickname] = true;
function shouldShowDeleteButton(myMembershipSt){ closeCreateRoomModal();
return myMembershipSt.myRoleHere === userChatRoleDeleted; currentHistoryId = res.update.LocalHistoryId;
} window.location.href = '/chat/' + roomNickname;
/* 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);
box.querySelector(".CL-my-chat-box-leave-btn").style.display =
(shouldShowDeleteButton(myMembershipSt) ? "none" : "block");
}
function convertMyMembershipStToBox(myMembershipSt){
let chatURI = "/chat/" + myMembershipSt.chatNickname;
let ID = myMembershipSt.chatId;
let box = document.createElement("div");
box.className = "dynamic-block-list-el CL-my-chat-box";
box.style.backgroundColor = roleToColor(myMembershipSt.myRoleHere);
let inBoxNickname = document.createElement("a");
box.appendChild(inBoxNickname);
inBoxNickname.className = "entity-nickname-txt CL-my-chat-box-nickname";
inBoxNickname.innerText = myMembershipSt.chatNickname;
inBoxNickname.href = chatURI;
let inBoxName = document.createElement("a");
box.appendChild(inBoxName);
inBoxName.className = "entity-reg-field-txt CL-my-chat-box-name";
inBoxName.innerText = myMembershipSt.chatName;
inBoxName.href = chatURI;
let inBoxMyRoleHere = document.createElement("p");
box.appendChild(inBoxMyRoleHere);
inBoxMyRoleHere.className = "entity-reg-field-txt CL-my-chat-box-my-role";
inBoxMyRoleHere.innerText = youAreXHere(myMembershipSt.myRoleHere);
let inBoxLeaveBtn = document.createElement("img");
box.appendChild(inBoxLeaveBtn);
inBoxLeaveBtn.className = "CL-my-chat-box-leave-btn";
inBoxLeaveBtn.src = "/assets/img/delete.svg";
inBoxLeaveBtn.onclick = function (ev) {
if (ev.button !== 0)
return;
chatRenunciationWinStoredId = ID;
document.getElementById("chat-renunciation-win-title").innerText =
"Do you really want to leave chat " + myMembershipSt.chatNickname + "?";
activatePopupWindowById("chat-renunciation-win");
};
box.querySelector(".CL-my-chat-box-leave-btn").style.display =
(shouldShowDeleteButton(myMembershipSt) ? "none" : "block");
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 { } else {
if (myMembershipSt.myRoleHere === userChatRoleDeleted) throw new Error(res.error || 'Ошибка');
continue; }
myChats.set(chatId, myMembershipSt); } catch (error) {
let box = convertMyMembershipStToBox(myMembershipSt) alert('Ошибка создания чата: ' + error.message);
chatBoxes.set(chatId, box); }
literalChatList.appendChild(box); }
function addRoomToList(roomName) {
const roomList = document.querySelector('.room-list');
const existingRoomItem = Array.from(roomList.children).find(item => item.querySelector('.room-name').textContent === roomName);
if (existingRoomItem) {
existingRoomItem.remove();
}
const roomItem = document.createElement('li');
roomItem.classList.add('room-item');
roomItem.innerHTML = `
<span class="room-name">${roomName}</span>
<button class="delete-chat-button" onclick="openConfirm('${roomNickname}')">Удалить чат</button>
<button class="add-members-button" onclick="openAdd()">Добавить участников</button>
<button class="join-button" onclick="window.location.href = '/chat/${roomNickname}'">Войти</button>
`;
roomList.appendChild(roomItem);
}
function removeRoomFromList(roomName, roomNickname) {
const roomList = document.querySelector('.room-list');
const roomItem = Array.from(roomList.children).find(item => item.querySelector('.room-name').textContent === roomName);
if (roomItem) {
roomList.removeChild(roomItem);
}
}
async function initializeRoomList() {
try {
const response = await fetch('/internalapi/getChatList', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
const res = await response.json();
if (res.status === 0) {
res.chats.forEach(chat => {
addRoomToList(chat.content.name, chat.content.nickname);
});
} else {
throw new Error(res.error || 'Неизвестная ошибка');
}
} catch (error) {
alert('Ошибка загрузки списка чатов: ' + error.message);
}
}
async function getChatID() {
const chatNickname = window.location.pathname.split('/').pop();
const response = await fetch('/internalapi/getChatList', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
const res = await response.json();
for (const chat of res.chats) {
if (chat.content.nickname === chatNickname) {
return chat.id;
} }
} }
return -1;
} }
window.onclick = function(event) {
/* Use it ONLY if `Recv` reported success */ if (event.target === document.getElementById('createRoomModal')) {
function updateLocalStateFromRecv(Recv){ closeCreateRoomModal();
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 = String(chatNicknameInput.value);
let name = String(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){
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; document.getElementById('newRoomName').addEventListener('keydown', function(event) {
__guestMainloopPollerAction = function(){ if (event.key === 'Enter') {
let Sent = genSentBase(); createRoom();
apiRequest("chatListPollEvents", Sent }
).then((Recv) => { });
console.log("Got a response"); document.addEventListener('DOMContentLoaded', initializeRoomList);
console.log(Recv);
updateLocalStateFromRecv(Recv);
});
}
window.onload = function () {
console.log("Loading complete");
updateLocalStateFromChatListUpdResp(initial_chatListUpdResp);
configureChatCreationInterface();
configureChatRenunciationInterfaceWinPart();
mainloopPoller();
};

View File

@ -112,10 +112,6 @@ namespace een9 {
return status; return status;
} }
res.body.reserve(std::min(100000ul, body_size)); res.body.reserve(std::min(100000ul, body_size));
if (body_size == 0) {
status = 1;
}
break;
} }
} }
if (!res.has_body) { if (!res.has_body) {

View File

@ -55,16 +55,13 @@ namespace nytl {
} }
char skip(ParsingContext& ctx) { char skip(ParsingContext& ctx) {
if (ctx.pos >= ctx.text.size()) ASSERT(ctx.pos < ctx.text.size(), "Unexpected EOF");
THROW("Unexpected EOF");
return advance(ctx); return advance(ctx);
} }
void skip(ParsingContext& ctx, char ch) { void skip(ParsingContext& ctx, char ch) {
if (ctx.pos >= ctx.text.size()) ASSERT(ctx.pos < ctx.text.size(), "Unexpected EOF");
THROW("Unexpected EOF"); ASSERT(ctx.text[ctx.pos] == ch, "Unexpected character");
if (ctx.text[ctx.pos] != ch)
THROW("Unexpected character");
advance(ctx); advance(ctx);
} }
@ -153,8 +150,7 @@ namespace nytl {
void parse_bare_file(const std::string& filename, const std::string& content, void parse_bare_file(const std::string& filename, const std::string& content,
global_elem_set_t& result) global_elem_set_t& result)
{ {
if (result.count(filename) != 0) ASSERT(result.count(filename) == 0, "Repeated element " + filename);
THROW("Repeated element " + filename);
std::string txt = clement_lstrip(content); std::string txt = clement_lstrip(content);
rstrip(txt); rstrip(txt);
size_t cut = 9999999999999; size_t cut = 9999999999999;
@ -174,15 +170,13 @@ namespace nytl {
uptr<TPFrame> toMe(bool returned, ParsingContext& ctx) { uptr<TPFrame> toMe(bool returned, ParsingContext& ctx) {
if (!returned) { if (!returned) {
std::string nm = readName(ctx); std::string nm = readName(ctx);
if (nm.empty()) ASSERT(!nm.empty(), "Type specification expected");
THROW("Type specification expected");
nm = make_uppercase(nm); nm = make_uppercase(nm);
if (nm == "JSON") { if (nm == "JSON") {
result = json::JSON(true); result = json::JSON(true);
return NULL; return NULL;
} }
if (nm != "EL") ASSERT(nm == "EL", "Type of argument variable is either JSON or EL(...signature)")
THROW("Type of argument variable is either JSON or EL(...signature)");
skip(ctx, '('); skip(ctx, '(');
result.asArray(); result.asArray();
assert(result.isArray()); assert(result.isArray());
@ -223,10 +217,8 @@ namespace nytl {
uptr<EPFrame> toMe(bool returned, ParsingContext& ctx, const arg_name_list_t& local_var_names) { uptr<EPFrame> toMe(bool returned, ParsingContext& ctx, const arg_name_list_t& local_var_names) {
if (!returned) { if (!returned) {
std::string first = readName(ctx); std::string first = readName(ctx);
if (first.empty()) ASSERT(!first.empty(), "Expression should start with 'root' name of global package or local variable");
THROW("Expression should start with 'root' name of global package or local variable"); ASSERT(first != "_", "_ ??? ARE YOU KIDDING???");
if (first == "_")
THROW("Expression root can't be _");
if (local_var_names.count(first) == 1) { if (local_var_names.count(first) == 1) {
result["V"].asInteger() = json::Integer((int64_t)local_var_names.at(first)); result["V"].asInteger() = json::Integer((int64_t)local_var_names.at(first));
} else { } else {
@ -251,8 +243,7 @@ namespace nytl {
t = readUint(ctx); t = readUint(ctx);
if (!t.empty()) { if (!t.empty()) {
size_t v = std::stoul(t); size_t v = std::stoul(t);
if (v >= INT64_MAX) ASSERT(v < INT64_MAX, "Index is too big");
THROW("Index is too big");
chain.back() = json::JSON((int64_t)v); chain.back() = json::JSON((int64_t)v);
continue; continue;
} }
@ -361,8 +352,7 @@ namespace nytl {
ElementPart::when_for_put_S& P = result.parts.back().when_for_put; ElementPart::when_for_put_S& P = result.parts.back().when_for_put;
skipWhitespace(ctx); skipWhitespace(ctx);
std::string V1 = readName(ctx); std::string V1 = readName(ctx);
if (V1.empty()) ASSERT(!V1.empty(), "Expected variable name");
THROW("Expected variable name");
skipWhitespace(ctx); skipWhitespace(ctx);
bool have_colon_and_2 = false; bool have_colon_and_2 = false;
std::string V2; std::string V2;
@ -374,8 +364,7 @@ namespace nytl {
skipWhitespace(ctx); skipWhitespace(ctx);
} }
op = make_uppercase(readName(ctx)); op = make_uppercase(readName(ctx));
if (op != "IN") ASSERT(op == "IN", "Expected IN");
THROW("Expected IN");
skipWhitespace(ctx); skipWhitespace(ctx);
P.ref_over = parse_expression(ctx, local_var_names); P.ref_over = parse_expression(ctx, local_var_names);
P.internal_element = el_name + ".~" + std::to_string(free_hidden++); P.internal_element = el_name + ".~" + std::to_string(free_hidden++);
@ -383,15 +372,13 @@ namespace nytl {
newborn.is_hidden = true; newborn.is_hidden = true;
arg_name_list_t local_var_names_of_nxt = local_var_names; arg_name_list_t local_var_names_of_nxt = local_var_names;
if (V1 != "_") { if (V1 != "_") {
if (local_var_names_of_nxt.count(V1) != 0) ASSERT(local_var_names_of_nxt.count(V1) == 0, "Repeated local variable");
THROW("Repeated local variable");
size_t k = local_var_names_of_nxt.size(); size_t k = local_var_names_of_nxt.size();
local_var_names_of_nxt.emplace(V1, k); local_var_names_of_nxt.emplace(V1, k);
(have_colon_and_2 ? P.where_key_var : P.where_value_var) = (ssize_t)k; (have_colon_and_2 ? P.where_key_var : P.where_value_var) = (ssize_t)k;
} }
if (have_colon_and_2 && V2 != "_") { if (have_colon_and_2 && V2 != "_") {
if (local_var_names_of_nxt.count(V2) != 0) ASSERT(local_var_names_of_nxt.count(V2) == 0, "Repeated local variable");
THROW("Repeated local variable");
size_t k = local_var_names_of_nxt.size(); size_t k = local_var_names_of_nxt.size();
local_var_names_of_nxt.emplace(V2, k); local_var_names_of_nxt.emplace(V2, k);
P.where_value_var = (ssize_t)k; P.where_value_var = (ssize_t)k;
@ -408,12 +395,11 @@ namespace nytl {
ElementPart::when_ref_put_S& P = result.parts.back().when_ref_put; ElementPart::when_ref_put_S& P = result.parts.back().when_ref_put;
skipWhitespace(ctx); skipWhitespace(ctx);
std::string Vn = readName(ctx); std::string Vn = readName(ctx);
if (Vn.empty() || Vn == "_") ASSERT(!Vn.empty(), "Expected variable name");
THROW("REF: expected variable name"); ASSERT(Vn != "_", "Are you kidding???");
skipWhitespace(ctx); skipWhitespace(ctx);
op = make_uppercase(readName(ctx)); op = make_uppercase(readName(ctx));
if (op != "AS") ASSERT(op == "AS", "Expected AS");
THROW("Expected AS");
skipWhitespace(ctx); skipWhitespace(ctx);
P.ref_over = parse_expression(ctx, local_var_names); P.ref_over = parse_expression(ctx, local_var_names);
P.internal_element = el_name + ".~" + std::to_string(free_hidden++); P.internal_element = el_name + ".~" + std::to_string(free_hidden++);
@ -481,15 +467,13 @@ namespace nytl {
} }
}; };
if (op == "ENDELDEF") { if (op == "ENDELDEF") {
if (myself != gone_for_nothing) ASSERT(myself == gone_for_nothing, "Unexpected end of element");
THROW("Unexpected ENDELDEF");
skip_magic_block_end(ctx, syntax); skip_magic_block_end(ctx, syntax);
prepare_to_depart_parts(); prepare_to_depart_parts();
return NULL; return NULL;
} }
if (op == "ENDFOR") { if (op == "ENDFOR") {
if (myself != gone_for_for) ASSERT(myself == gone_for_for, "Unexpected end of for cycle");
THROW("Unexpected ENDFOR");
skipWhitespace(ctx); skipWhitespace(ctx);
/* Here I am using ret_data_int to return info about NOLF(1)/LF(2) decision */ /* Here I am using ret_data_int to return info about NOLF(1)/LF(2) decision */
ret_data_int = 2; // Default is to do LF ret_data_int = 2; // Default is to do LF
@ -507,8 +491,7 @@ namespace nytl {
return NULL; return NULL;
} }
if (op == "ENDREF") { if (op == "ENDREF") {
if (myself != gone_for_ref) assert(myself == gone_for_ref);
THROW("Unexpected ENDREF");
skip_magic_block_end(ctx, syntax); skip_magic_block_end(ctx, syntax);
prepare_to_depart_parts(); prepare_to_depart_parts();
return NULL; return NULL;
@ -542,15 +525,12 @@ namespace nytl {
if (peep(ctx) == EOFVAL) if (peep(ctx) == EOFVAL)
break; break;
skip_magic_block_start(ctx, syntax); skip_magic_block_start(ctx, syntax);
if (make_uppercase(readName(ctx)) != "ELDEF") ASSERT(make_uppercase(readName(ctx)) == "ELDEF", "Expected ELDEF");
THROW("Expected ELDEF");
skipWhitespace(ctx); skipWhitespace(ctx);
std::string elname_postfix = readName(ctx); std::string elname_postfix = readName(ctx);
if (elname_postfix == "_") ASSERT(elname_postfix != "_", "please don't");
THROW("Can't use _ as element name");
std::string fullname = elname_postfix == "main" ? filename : filename + "." + elname_postfix; std::string fullname = elname_postfix == "main" ? filename : filename + "." + elname_postfix;
if (result.count(fullname) != 0) ASSERT(result.count(fullname) == 0, "Element " + fullname + " has been already defined");
THROW("Element " + fullname + " has been already defined");
Element& newborn = result[fullname]; Element& newborn = result[fullname];
arg_name_list_t arglist; arg_name_list_t arglist;
while (true) { while (true) {
@ -560,11 +540,9 @@ namespace nytl {
newborn.arguments.push_back(parse_type(ctx)); newborn.arguments.push_back(parse_type(ctx));
skipWhitespace(ctx); skipWhitespace(ctx);
std::string argname = readName(ctx); std::string argname = readName(ctx);
if (argname.empty()) ASSERT(!argname.empty(), "Expected argument name");
THROW("Expected argument name");
if (argname != "_") { if (argname != "_") {
if (arglist.count(argname) != 0) ASSERT(arglist.count(argname) == 0, "Repeated argument (" + argname + ")");
THROW("Repeated argument (" + argname + ")");
size_t k = arglist.size(); size_t k = arglist.size();
arglist[argname] = k; arglist[argname] = k;
} }

View File

@ -212,7 +212,7 @@ namespace nytl {
const std::function<std::string(std::string)> &escape) { const std::function<std::string(std::string)> &escape) {
if (!returned) if (!returned)
if (elem_ns.count(name) != 1) if (elem_ns.count(name) != 1)
THROW("No such element (" + name + ")"); THROW("No such element");
const Element& el = elem_ns.at(name); const Element& el = elem_ns.at(name);
if (!returned) { if (!returned) {
/* Continue to do checks */ /* Continue to do checks */
@ -227,9 +227,7 @@ namespace nytl {
// If not json is expected, element must be expected // If not json is expected, element must be expected
assert(el.arguments[i].isArray()); assert(el.arguments[i].isArray());
ASSERT(!passed_args[i].is_json, "Expected element element arguemnt, got json"); ASSERT(!passed_args[i].is_json, "Expected element element arguemnt, got json");
const std::string& passed_el_as_arg = passed_args[i].EL_name; ASSERT(elem_ns.count(passed_args[i].EL_name), "No such element, can't compare signatures of argument value");
if (elem_ns.count(passed_el_as_arg) != 1)
THROW("No such element, can't compare signatures of argument value (" + passed_el_as_arg + ")");
const Element& arg_element = elem_ns.at(passed_args[i].EL_name); const Element& arg_element = elem_ns.at(passed_args[i].EL_name);
// ASSERT(passed_args); // ASSERT(passed_args);
if(el.arguments[i].asArray() != arg_element.arguments) if(el.arguments[i].asArray() != arg_element.arguments)

View File

@ -3,32 +3,16 @@
#include <assert.h> #include <assert.h>
namespace iu9cawebchat { namespace iu9cawebchat {
bool is_membership_row_present(SqliteConnection& conn, int64_t chatId, int64_t alienUserId) { void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role) {
SqliteStatement req(conn, assert(role != user_chat_role_deleted);
"SELECT EXISTS(SELECT 1 FROM `user_chat_membership` WHERE `chatId` = ?1 AND `userId` = ?2)",
{{1, chatId}, {2, alienUserId}}, {});
fsql_integer_or_null r{true, 0};
int status = sqlite_stmt_step(req, {{0, &r}}, {});
return (bool)r.value;
}
void alter_user_chat_role(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role) {
int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId); int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId);
int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId); int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId);
if (!is_membership_row_present(conn, chatId, alienUserId)) { sqlite_nooutput(conn,
sqlite_nooutput(conn, "INSERT INTO `user_chat_membership` (`userId`, `chatId`, `user_chatList_IncHistoryId`,"
"INSERT INTO `user_chat_membership` (`userId`, `chatId`, `user_chatList_IncHistoryId`," "`chat_IncHistoryId`, `role`) VALUES (?1, ?2, ?3, ?4, ?5)",
"`chat_IncHistoryId`, `role`) VALUES (?1, ?2, ?3, ?4, ?5)", {{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1},
{{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1}, {4, chat_HistoryId_BEFORE_EV + 1}, {5, role}}, {});
{4, chat_HistoryId_BEFORE_EV + 1}, {5, role}}, {});
} else {
sqlite_nooutput(conn,
"UPDATE `user_chat_membership` SET `user_chatList_IncHistoryId` = ?3,`chat_IncHistoryId` = ?4,"
"`role` = ?5 WHERE `userId` = ?1 AND `chatId` = ?2",
{{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1},
{4, chat_HistoryId_BEFORE_EV + 1}, {5, role}}, {});
}
sqlite_nooutput(conn, sqlite_nooutput(conn,
"UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1}, "UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1},
{2, chatId}}, {}); {2, chatId}}, {});
@ -38,12 +22,6 @@ namespace iu9cawebchat {
{{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {}); {{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {});
} }
void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role) {
assert(role != user_chat_role_deleted);
// int64_t old_role = get_role_of_user_in_chat(conn, alienUserId, chatId);
alter_user_chat_role(conn, chatId, alienUserId, role);
}
json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int(); int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
@ -55,17 +33,12 @@ namespace iu9cawebchat {
try { try {
alien = lookup_user_content_by_nickname(conn, alien_nickname); alien = lookup_user_content_by_nickname(conn, alien_nickname);
} catch (std::exception& e) { } catch (std::exception& e) {
return at_api_error_gen_bad_recv(-1l); return json::JSON(json::jdict{{"status", json::JSON(-1l)}});
} }
bool makeReadOnly = Sent["makeReadOnly"].toBool();
int64_t aliens_old_role = get_role_of_user_in_chat(conn, alien.id, chatId); int64_t aliens_old_role = get_role_of_user_in_chat(conn, alien.id, chatId);
if (aliens_old_role == user_chat_role_deleted) { if (aliens_old_role == user_chat_role_deleted) {
make_her_a_member_of_the_midnight_crew(conn, chatId, alien.id, make_her_a_member_of_the_midnight_crew(conn, chatId, alien.id, user_chat_role_regular);
makeReadOnly ? user_chat_role_read_only : user_chat_role_regular);
} else {
return at_api_error_gen_bad_recv(-2l);
} }
json::JSON Recv; json::JSON Recv;

View File

@ -7,16 +7,13 @@ namespace iu9cawebchat {
std::string new_chat_name = Sent["content"]["name"].asString(); std::string new_chat_name = Sent["content"]["name"].asString();
std::string new_chat_nickname = Sent["content"]["nickname"].asString(); std::string new_chat_nickname = Sent["content"]["nickname"].asString();
if (!check_nickname(new_chat_nickname) || !check_name(new_chat_name)) if (!check_nickname(new_chat_nickname) || !check_name(new_chat_name))
return at_api_error_gen_bad_recv(-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 at_api_error_gen_bad_recv(-2l); return json::JSON(json::jdict{{"status", json::JSON(-2l)}});
if (is_nickname_taken(conn, new_chat_nickname))
return at_api_error_gen_bad_recv(-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

@ -1,11 +1,9 @@
#include "server_data_interact.h" #include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h> #include <engine_engine_number_9/baza_throw.h>
#include "../debug.h"
namespace iu9cawebchat { namespace iu9cawebchat {
json::JSON internalapi_deleteMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { json::JSON internalapi_deleteMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
// debug_print_json(Sent); int64_t chatId = Sent["chatUpdReq"].asInteger().get_int();
int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here == user_chat_role_deleted) if (my_role_here == user_chat_role_deleted)
een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); een9_THROW("Unauthorized user tries to access internalapi_getChatInfo");

View File

@ -6,7 +6,7 @@ namespace iu9cawebchat {
int64_t chatId = Sent["chatId"].asInteger().get_int(); int64_t chatId = Sent["chatId"].asInteger().get_int();
if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted)
een9_THROW("Not a member"); een9_THROW("Not a member");
kick_from_chat(conn, chatId, uid); kick_from_chat(conn, uid, chatId);
json::JSON Recv; json::JSON Recv;
poll_update_chat_list(conn, uid, Sent, Recv); poll_update_chat_list(conn, uid, Sent, Recv);
return Recv; return Recv;

View File

@ -2,17 +2,34 @@
#include <engine_engine_number_9/baza_throw.h> #include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat { namespace iu9cawebchat {
void kick_from_chat(SqliteConnection& conn, int64_t chatId, int64_t alienUserId) { void kick_from_chat(SqliteConnection& conn, int64_t alienUserId, int64_t chatId) {
alter_user_chat_role(conn, chatId, alienUserId, user_chat_role_deleted); if (get_role_of_user_in_chat(conn, alienUserId, chatId) == user_chat_role_deleted)
een9_THROW("Can't delete a deleted member");
int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId);
int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId);
sqlite_nooutput(conn,
"UPDATE `user_chat_membership` SET `user_chatList_IncHistoryId` = ?3,"
"`chat_IncHistoryId` = ?4, `role` = ?5 WHERE `userId` = ?1 AND `chatId` = ?2",
{{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1},
{4, chat_HistoryId_BEFORE_EV + 1}, {5, user_chat_role_deleted}}, {});
sqlite_nooutput(conn,
"UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1},
{2, chatId}}, {});
sqlite_nooutput(conn,
"UPDATE `user` SET `chatList_HistoryId` = ?1 WHERE `id` = ?2",
{{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {});
} }
json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int(); int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here != user_chat_role_admin) if (my_role_here != user_chat_role_deleted)
een9_THROW("Only admin can delete members of chat"); een9_THROW("Only admin can delete members of chat");
int64_t badAlienId = Sent["userId"].asInteger().get_int(); int64_t badAlienId = Sent["chatUpdReq"]["userId"].asInteger().get_int();
kick_from_chat(conn, chatId, badAlienId); kick_from_chat(conn, badAlienId, chatId);
json::JSON Recv; json::JSON Recv;
poll_update_chat(conn, Sent, Recv); poll_update_chat(conn, Sent, Recv);
return Recv; return Recv;

View File

@ -1,7 +1,6 @@
#include "server_data_interact.h" #include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h> #include <engine_engine_number_9/baza_throw.h>
#include "../str_fields.h" #include "../str_fields.h"
#include "../debug.h"
namespace iu9cawebchat { namespace iu9cawebchat {
/* No authorization check is performed /* No authorization check is performed
@ -14,19 +13,16 @@ namespace iu9cawebchat {
int64_t chat_lastMsgId = get_lastMsgId_of_chat(conn, chatId); int64_t chat_lastMsgId = get_lastMsgId_of_chat(conn, chatId);
SqliteStatement req(conn, SqliteStatement req(conn,
"INSERT INTO `message` (`chatId`, `id`, `senderUserId`, `exists`, `isSystem`, `chat_IncHistoryId`, " "INSERT INTO `message` (`chatId`, `id`, `senderUserId`, `exists`, `isSystem`, `chat_IncHistoryId`, "
"`text`) VALUES (?1, ?2, ?3, 1, ?4, ?5, ?6)", "`text`) VALUES (?1, ?2, ?3 1, ?4, ?5, ?6)",
{{1, chatId}, {2, chat_lastMsgId + 1}, {4, (int64_t)isSystem}, {5, chat_HistoryId_BEFORE_MSG + 1}}, {{6, text}}); {{1, chatId}, {2, chat_lastMsgId + 1}, {4, (int64_t)isSystem}, {5, chat_HistoryId_BEFORE_MSG + 1}}, {{6, text}});
if (!isSystem) if (!isSystem)
sqlite_stmt_bind_int64(req, 3, uid); sqlite_stmt_bind_int64(req, 3, uid);
if (sqlite_stmt_step(req, {}, {}) != SQLITE_DONE)
een9_THROW("There must be something wrong");
sqlite_nooutput(conn, "UPDATE `chat` SET `lastMsgId` = ?1, `it_HistoryId` = ?2 WHERE `id` = ?3", sqlite_nooutput(conn, "UPDATE `chat` SET `lastMsgId` = ?1, `it_HistoryId` = ?2 WHERE `id` = ?3",
{{1, chat_lastMsgId + 1}, {2, chat_HistoryId_BEFORE_MSG + 1}, {3, chatId}}, {}); {{1, chat_lastMsgId + 1}, {2, chat_HistoryId_BEFORE_MSG + 1}, {3, chatId}}, {});
} }
json::JSON internalapi_sendMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { json::JSON internalapi_sendMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
debug_print_json(Sent); int64_t chatId = Sent["chatUpdReq"].asInteger().get_int();
int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here == user_chat_role_deleted) if (my_role_here == user_chat_role_deleted)
een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); een9_THROW("Unauthorized user tries to access internalapi_getChatInfo");
@ -40,7 +36,6 @@ namespace iu9cawebchat {
json::JSON Recv; json::JSON Recv;
poll_update_chat(conn, Sent, Recv); poll_update_chat(conn, Sent, Recv);
debug_print_json(Recv);
return Recv; return Recv;
} }
} }

View File

@ -1,16 +1,14 @@
#include "server_data_interact.h" #include "server_data_interact.h"
#include <assert.h> #include <assert.h>
#include <engine_engine_number_9/baza_throw.h> #include <engine_engine_number_9/baza_throw.h>
#include "../debug.h"
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 "
"AND `user_chatList_IncHistoryId` > ?2", {{1, userId}, {2, LocalHistoryId}}); "AND `user_chatList_IncHistoryId` > ?2", {{1, userId}, {2, LocalHistoryId}});
json::jarr& myChats = chatListUpdResp["myChats"].asArray(); json::jarr myChats = chatListUpdResp["myChats"].asArray();
while (true) { while (true) {
fsql_integer_or_null ev_chatId, usersRoleHere; fsql_integer_or_null ev_chatId, usersRoleHere;
int status = sqlite_stmt_step(my_membership_changes, {{0, &ev_chatId}, {1, &usersRoleHere}}, {}); int status = sqlite_stmt_step(my_membership_changes, {{0, &ev_chatId}, {1, &usersRoleHere}}, {});
@ -34,7 +32,7 @@ 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
Recv["chatListUpdResp"] = poll_update_chat_list_resp(conn, userId, Sent["chatListUpdReq"]["LocalHistoryId"].asInteger().get_int()); 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) {
@ -54,7 +52,7 @@ namespace iu9cawebchat {
json::jarr messages; json::jarr messages;
SqliteStatement messages_changes(conn, SqliteStatement messages_changes(conn,
"SELECT `id`, `senderUserId`, `exists`, `isSystem`, `text` FROM `message` " "SELECT `id`, `senderUserId`, `exists`, `isSystem`, `text` FROM `messages` "
"WHERE `chatId` = ?1 AND ( `chat_IncHistoryId` > ?2 OR ( ?3 <= `id` AND `id` <= ?4 ) )", "WHERE `chatId` = ?1 AND ( `chat_IncHistoryId` > ?2 OR ( ?3 <= `id` AND `id` <= ?4 ) )",
{{1, chatId}, {2, LocalHistoryId}, {3, QSEG_A}, {4, QSEG_B}}, {}); {{1, chatId}, {2, LocalHistoryId}, {3, QSEG_A}, {4, QSEG_B}}, {});
while (true) { while (true) {
@ -75,7 +73,7 @@ namespace iu9cawebchat {
json::jarr members; json::jarr members;
SqliteStatement membership_changes(conn, SqliteStatement membership_changes(conn,
"SELECT `userId`, `role` FROM `user_chat_membership` WHERE `chatId` = ?1 " "SELECT `userId`, `role`, FROM `user_chat_membership` WHERE `chatId` = ?1 "
"AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {}); "AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {});
while (true) { while (true) {
fsql_integer_or_null alienUserId; fsql_integer_or_null alienUserId;
@ -103,7 +101,7 @@ namespace iu9cawebchat {
chatUpdResp["members"].asArray() = poll_update_chat_resp_members(conn, chatId, 0); chatUpdResp["members"].asArray() = poll_update_chat_resp_members(conn, chatId, 0);
json::jarr& messages = chatUpdResp["messages"].asArray(); json::jarr messages = chatUpdResp["messages"].asArray();
if (selectedMsg >= 0) { if (selectedMsg >= 0) {
RowMessage_Content msg = lookup_message_content(conn, chatId, selectedMsg); RowMessage_Content msg = lookup_message_content(conn, chatId, selectedMsg);
messages.push_back(make_messageSt_obj(msg.id, msg.senderUserId, msg.exists, msg.isSystem, msg.text)); messages.push_back(make_messageSt_obj(msg.id, msg.senderUserId, msg.exists, msg.isSystem, msg.text));
@ -139,7 +137,7 @@ namespace iu9cawebchat {
int64_t QSEG_A, int64_t QSEG_B) { int64_t QSEG_A, int64_t QSEG_B) {
Recv["status"].asInteger() = json::Integer(0l); Recv["status"].asInteger() = json::Integer(0l);
Recv["chatUpdResp"] = poll_update_chat_important_segment_resp(conn, Recv["charUpdResp"] = poll_update_chat_important_segment_resp(conn,
Sent["chatUpdReq"]["chatId"].asInteger().get_int(), Sent["chatUpdReq"]["chatId"].asInteger().get_int(),
Sent["chatUpdReq"]["LocalHistoryId"].asInteger().get_int(), QSEG_A, QSEG_B); Sent["chatUpdReq"]["LocalHistoryId"].asInteger().get_int(), QSEG_A, QSEG_B);
} }
@ -166,8 +164,7 @@ namespace iu9cawebchat {
/* Reznya */ /* Reznya */
json::JSON internalapi_getMessageNeighbours(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { json::JSON internalapi_getMessageNeighbours(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
debug_print_json(Sent); int64_t chatId = Sent["chatId"].asInteger().get_int();
int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int();
if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted)
een9_THROW("Authentication failure"); een9_THROW("Authentication failure");
int64_t lastMsgId = get_lastMsgId_of_chat(conn, chatId); int64_t lastMsgId = get_lastMsgId_of_chat(conn, chatId);
@ -194,7 +191,6 @@ namespace iu9cawebchat {
} }
} }
poll_update_chat_important_segment(conn, Sent, Recv, qBeg, qEnd); poll_update_chat_important_segment(conn, Sent, Recv, qBeg, qEnd);
debug_print_json(Recv);
return Recv; return Recv;
} }
} }

View File

@ -5,10 +5,6 @@
#include "../str_fields.h" #include "../str_fields.h"
namespace iu9cawebchat { namespace iu9cawebchat {
json::JSON at_api_error_gen_bad_recv(int64_t code) {
return json::JSON(json::jdict{{"status", json::JSON(code)}});
}
const char* stringify_user_chat_role(int64_t role) { const char* stringify_user_chat_role(int64_t role) {
if (role == user_chat_role_admin) if (role == user_chat_role_admin)
return "admin"; return "admin";
@ -113,6 +109,8 @@ namespace iu9cawebchat {
int status = sqlite_stmt_step(req, {{0, &senderUserId}, {1, &exists}, {2, &isSystem}}, int status = sqlite_stmt_step(req, {{0, &senderUserId}, {1, &exists}, {2, &isSystem}},
{{3, &msg_text}}); {{3, &msg_text}});
if (status == SQLITE_ROW) { if (status == SQLITE_ROW) {
if (!(bool)exists.value)
een9_THROW("Message existed, but now it does not");
return {msgId, senderUserId.exist ? senderUserId.value : -1, (bool)exists.value, return {msgId, senderUserId.exist ? senderUserId.value : -1, (bool)exists.value,
(bool)isSystem.value, msg_text.value}; (bool)isSystem.value, msg_text.value};
} }
@ -146,6 +144,7 @@ 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

@ -8,8 +8,6 @@
#include <jsonincpp/string_representation.h> #include <jsonincpp/string_representation.h>
namespace iu9cawebchat { namespace iu9cawebchat {
json::JSON at_api_error_gen_bad_recv(int64_t code = -1);
constexpr int64_t user_chat_role_admin = 1; constexpr int64_t user_chat_role_admin = 1;
constexpr int64_t user_chat_role_regular = 2; constexpr int64_t user_chat_role_regular = 2;
constexpr int64_t user_chat_role_read_only = 3; constexpr int64_t user_chat_role_read_only = 3;
@ -63,9 +61,8 @@ namespace iu9cawebchat {
json::JSON poll_update_chat_ONE_MSG_resp(SqliteConnection& conn, int64_t chatId, int64_t selectedMsg); json::JSON poll_update_chat_ONE_MSG_resp(SqliteConnection& conn, int64_t chatId, int64_t selectedMsg);
void poll_update_chat(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv); void poll_update_chat(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv);
void alter_user_chat_role(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role);
void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role); void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role);
void kick_from_chat(SqliteConnection& conn, int64_t chatId, int64_t alienUserId); void kick_from_chat(SqliteConnection& conn, int64_t alienUserId, int64_t chatId);
bool is_nickname_taken(SqliteConnection& conn, const std::string& nickname); bool is_nickname_taken(SqliteConnection& conn, const std::string& nickname);
void reserve_nickname(SqliteConnection& conn, const std::string& nickname); void reserve_nickname(SqliteConnection& conn, const std::string& nickname);

View File

@ -7,10 +7,8 @@ namespace iu9cawebchat {
std::string when_page_chat(WorkerGuestData& wgd, const json::JSON& config_presentation, std::string when_page_chat(WorkerGuestData& wgd, const json::JSON& config_presentation,
const een9::ClientRequest& req, const json::JSON& userinfo) { const een9::ClientRequest& req, const json::JSON& userinfo) {
std::vector<std::string> path_segs = {}; std::vector<std::string> path_segs = {""};
path_segs.reserve(4); path_segs.reserve(4);
if (req.uri_path.empty() || req.uri_path[0] != '/')
return page_E404(wgd);
for (char ch: req.uri_path) { for (char ch: req.uri_path) {
if (ch == '/') if (ch == '/')
path_segs.emplace_back(); path_segs.emplace_back();
@ -25,17 +23,16 @@ namespace iu9cawebchat {
} }
if (!check_nickname(chat_nickname)) if (!check_nickname(chat_nickname))
return page_E404(wgd); return page_E404(wgd);
bool show_chat_members = (path_segs[0] == "chat-members"); if (path_segs.size() == 2) {
if (path_segs.size() == 4 && !show_chat_members) { } else if (path_segs.size() == 4) {
if (path_segs[2] != "m") if (path_segs[2] != "m")
return page_E404(wgd); return page_E404(wgd);
selected_message_id = std::stoll(path_segs[3]); selected_message_id = std::stoll(path_segs[3]);
} else if (path_segs.size() != 2) {
return page_E404(wgd); return page_E404(wgd);
} } else
return page_E404(wgd);
if (userinfo.isNull()) bool chat_members = (path_segs[0] == "chat-members");
return een9::form_http_server_response_303("/");
RowChat_Content chatInfo; RowChat_Content chatInfo;
try { try {
@ -43,7 +40,7 @@ namespace iu9cawebchat {
} catch (const std::exception& e) { } catch (const std::exception& e) {
return page_E404(wgd); return page_E404(wgd);
} }
if (get_role_of_user_in_chat(*wgd.db, userinfo["uid"].asInteger().get_int(), chatInfo.id) == user_chat_role_deleted) { if (get_role_of_user_in_chat(*wgd.db, userinfo["id"].asInteger().get_int(), chatInfo.id) == user_chat_role_deleted) {
return page_E404(wgd); return page_E404(wgd);
} }
@ -54,8 +51,8 @@ namespace iu9cawebchat {
// -1 means that nothing was selected // -1 means that nothing was selected
openedchat["selectedMessageId"].asInteger() = json::Integer(selected_message_id); openedchat["selectedMessageId"].asInteger() = json::Integer(selected_message_id);
json::JSON initial_chatUpdResp = poll_update_chat_ONE_MSG_resp(*wgd.db, chatInfo.id, selected_message_id); json::JSON initial_chatUpdResp = poll_update_chat_ONE_MSG_resp(*wgd.db, chatInfo.id, selected_message_id);
if (show_chat_members) if (chat_members)
return http_R200("chat-members", wgd, {&config_presentation, &userinfo, &openedchat, &initial_chatUpdResp}); return http_R200("chat", wgd, {&config_presentation, &userinfo, &openedchat, &initial_chatUpdResp});
return http_R200("chat", wgd, {&config_presentation, &userinfo, &openedchat, &initial_chatUpdResp}); return http_R200("chat", wgd, {&config_presentation, &userinfo, &openedchat, &initial_chatUpdResp});
} }
} }

View File

@ -8,7 +8,6 @@ namespace iu9cawebchat {
} }
json::JSON initial_chatListUpdResp = poll_update_chat_list_resp(*wgd.db, json::JSON initial_chatListUpdResp = poll_update_chat_list_resp(*wgd.db,
userinfo["uid"].asInteger().get_int(), 0); userinfo["uid"].asInteger().get_int(), 0);
printf("%s\n", json::generate_str(initial_chatListUpdResp, json::print_pretty).c_str());
return http_R200("list-rooms", wgd, {&config_presentation, &userinfo, &initial_chatListUpdResp}); return http_R200("list-rooms", wgd, {&config_presentation, &userinfo, &initial_chatListUpdResp});
} }
} }

View File

@ -18,10 +18,8 @@ namespace iu9cawebchat {
if (cmp.first == "password") if (cmp.first == "password")
password = cmp.second; password = cmp.second;
} }
if (!check_nickname(nickname)) een9_ASSERT(check_nickname(nickname), "/login/accpet-data rejected impossible nickname");
een9_THROW("/login/accpet-data rejected impossible nickname"); een9_ASSERT(check_password(password), "/login/accpet-data rejected impossible password");
if (!check_password(password))
een9_THROW("/login/accpet-data rejected impossible password");
uid = find_user_by_credentials(*wgd.db, nickname, password); uid = find_user_by_credentials(*wgd.db, nickname, password);
} catch(const std::exception& e){} } catch(const std::exception& e){}

View File

@ -39,8 +39,6 @@ namespace iu9cawebchat {
std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation, std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation,
const een9::ClientRequest& req, const std::vector<LoginCookie>& login_cookies, const json::JSON& userinfo) { const een9::ClientRequest& req, const std::vector<LoginCookie>& login_cookies, const json::JSON& userinfo) {
if (userinfo.isNull())
return een9::form_http_server_response_303("/");
SqliteConnection& conn = *wgd.db; SqliteConnection& conn = *wgd.db;
if (!is_page_of_certain_user(req.uri_path)) if (!is_page_of_certain_user(req.uri_path))
return page_E404(wgd); return page_E404(wgd);
@ -106,7 +104,7 @@ namespace iu9cawebchat {
json::JSON msg_list = jsonify_html_message_list({{"", json::JSON msg_list = jsonify_html_message_list({{"",
config_presentation["phr"]["decl"]["incorrect-profile-data"].asString()}}); config_presentation["phr"]["decl"]["incorrect-profile-data"].asString()}});
json::JSON alien_userprofile = user_row_to_userprofile_obj(conn, alien); json::JSON alien_userprofile = user_row_to_userprofile_obj(conn, alien);
return http_R200("edit-profile", wgd, {&config_presentation, &userinfo, &alien_userprofile, &msg_list}); return http_R200("edit-profile", wgd, {&config_presentation, &alien_userprofile, &msg_list});
} }
return een9::form_http_server_response_303_spec_head("/user/" + alien_nickname, response_hlines); return een9::form_http_server_response_303_spec_head("/user/" + alien_nickname, response_hlines);
} }
@ -114,9 +112,9 @@ namespace iu9cawebchat {
json::JSON alien_userprofile = user_row_to_userprofile_obj(conn, alien); json::JSON alien_userprofile = user_row_to_userprofile_obj(conn, alien);
if (can_edit) { if (can_edit) {
json::JSON empty_msg_list = jsonify_html_message_list({}); json::JSON empty_msg_list = jsonify_html_message_list({});
return http_R200("edit-profile", wgd, {&config_presentation, &userinfo, &alien_userprofile, &empty_msg_list}); return http_R200("edit-profile", wgd, {&config_presentation, &alien_userprofile, &empty_msg_list});
} }
return http_R200("view-profile", wgd, {&config_presentation, &userinfo, &alien_userprofile}); return http_R200("view-profile", wgd, {&config_presentation, &alien_userprofile});
} }
een9_THROW("Bad method"); een9_THROW("Bad method");
} }

View File

@ -1,6 +0,0 @@
#ifndef IU9_CA_WEB_CHAT_LIB_DEBUG_H
#define IU9_CA_WEB_CHAT_LIB_DEBUG_H
#define debug_print_json(x) printf("%s\n", json::generate_str(x, json::print_pretty).c_str())
#endif

View File

@ -75,9 +75,6 @@ namespace iu9cawebchat {
"`chat_IncHistoryId` INTEGER NOT NULL," "`chat_IncHistoryId` INTEGER NOT NULL,"
"PRIMARY KEY (`chatId`, `id`)" "PRIMARY KEY (`chatId`, `id`)"
")"); ")");
std::vector<std::string> sus = {"unknown", "undefined", "null", "none", "None", "NaN"};
for (auto& s: sus)
reserve_nickname(conn, s);
add_user(conn, "root", "Rootov Root Rootovich", root_pw, "One admin to rule them all", 0); add_user(conn, "root", "Rootov Root Rootovich", root_pw, "One admin to rule them all", 0);
sqlite_nooutput(conn, "END"); sqlite_nooutput(conn, "END");
} catch (const std::exception& e) { } catch (const std::exception& e) {

View File

@ -41,7 +41,6 @@ namespace iu9cawebchat {
samI.update({ samI.update({
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} }, een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/javascript"}} }, een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/javascript"}} },
een9::StaticAssetManagerRule{assets_dir + "/gif", "/assets/gif", {{".gif", "image/gif"}} },
een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", { een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", {
{".jpg", "image/jpg"}, {".png", "image/png"}, {".svg", "image/svg+xml"} {".jpg", "image/jpg"}, {".png", "image/png"}, {".svg", "image/svg+xml"}
} }, } },