From 8089e8e88fb145b4b954c310a0eda2ec647259cd Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 27 Oct 2018 01:47:08 +0200 Subject: Add users, nickname change, messages now have author --- css/style.css | 38 +++++++++++++++++++++ include/ChatMessage.hpp | 6 +++- include/ChatWindow.hpp | 19 +++++++++-- include/User.hpp | 13 ++++++++ include/Window.hpp | 2 +- src/ChatMessage.cpp | 6 ++-- src/ChatWindow.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++++----- src/Window.cpp | 39 ++++++++++++++++++---- 8 files changed, 189 insertions(+), 23 deletions(-) create mode 100644 include/User.hpp diff --git a/css/style.css b/css/style.css index c024481..bb40474 100644 --- a/css/style.css +++ b/css/style.css @@ -20,10 +20,38 @@ button { background-color: #2f3136; color: #f7f7f7; border-style: none; + outline-style: none; + box-shadow: 0px 0px 0px transparent; + text-shadow: 0px 0px 0px transparent; +} + +menu { + background-color: #36393e; + color: #f7f7f7; + border: 1px solid black; +} + +.emoji { + background-color: #36393e; + color: #f7f7f7; + border: 1px solid black; +} + +.emoji-picker { + background-color: #36393e; + color: #f7f7f7; + border: 1px solid black; +} + +.emoji-picker entry { + background-color: #36393e; + color: #f7f7f7; + border: 1px solid black; } #side-panels separator { border: 1px solid #2f3136; + background-color: #2f3136; } #top-bar { @@ -48,6 +76,11 @@ button { */ } +#left-panel separator { + border: 1px solid #36393e; + background-color: #36393e; +} + #channels-title { color: #f7f7f7; font-weight: bold; @@ -124,4 +157,9 @@ textview text { .window-notification { background-color: #aa3030; +} + +.username-list-username { + color: #0fc0fc; + font-weight: bold; } \ No newline at end of file diff --git a/include/ChatMessage.hpp b/include/ChatMessage.hpp index 0547557..271aa5f 100644 --- a/include/ChatMessage.hpp +++ b/include/ChatMessage.hpp @@ -1,16 +1,20 @@ #pragma once +#include "types.hpp" #include #include namespace dchat { + class User; class ChatMessage : public Gtk::Grid { public: - ChatMessage(const Glib::ustring &username, const Glib::ustring &text); + ChatMessage(const Glib::ustring &username, const Glib::ustring &text, uint32_t timestampSeconds, const User *user); Gtk::Label username; Gtk::Label text; + uint32_t timestampSeconds; + const User *user; }; } \ No newline at end of file diff --git a/include/ChatWindow.hpp b/include/ChatWindow.hpp index b13046e..b10af2b 100644 --- a/include/ChatWindow.hpp +++ b/include/ChatWindow.hpp @@ -1,7 +1,8 @@ #pragma once +#include "User.hpp" #include -#include +#include #include #include #include @@ -9,15 +10,23 @@ #include #include #include +#include namespace dchat { + class ChatMessage; + class ChatWindow : public Gtk::Grid { public: ChatWindow(); void addChannel(const odhtdb::Hash &nodeHash); - void addLocalMessage(const odhtdb::Hash &channelId, Glib::ustring msg); + void addLocalMessage(const odhtdb::Hash &channelId, const odhtdb::Signature::PublicKey &userPublicKey, uint32_t timestampSeconds, Glib::ustring msg); + void addUser(const odhtdb::Signature::PublicKey &userPublicKey); + void setUserNickname(const odhtdb::Signature::PublicKey &userPublicKey, const Glib::ustring &name); + + // Returns nullptr if user with @publicKey is not found + User* getUserByPublicKey(const odhtdb::Signature::PublicKey &publicKey) const; private: void setupTopBar(); void setupLeftPanel(Gtk::Paned *sidePanels); @@ -27,6 +36,7 @@ namespace dchat Gtk::Grid topbar; Gtk::Entry topbarSearchBar; Gtk::Grid leftPanelChannels; + Gtk::Grid leftPanelUsers; Gtk::Label currentChannelTitle; Gtk::ScrolledWindow chatArea; Gtk::Grid chatAreaLayout; @@ -34,11 +44,14 @@ namespace dchat struct ChannelData { - Gtk::Button *button; + Gtk::ToggleButton *button; int messageCount; }; odhtdb::MapHash channelDataById; int channelCount; + std::vector users; + + ChatMessage *lastMessage; }; } \ No newline at end of file diff --git a/include/User.hpp b/include/User.hpp new file mode 100644 index 0000000..471a5cc --- /dev/null +++ b/include/User.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace dchat +{ + struct User + { + const odhtdb::Signature::PublicKey publicKey; + Glib::ustring name; + }; +} \ No newline at end of file diff --git a/include/Window.hpp b/include/Window.hpp index 96197c6..29ec285 100644 --- a/include/Window.hpp +++ b/include/Window.hpp @@ -14,7 +14,7 @@ namespace dchat public: Window(); virtual ~Window(); - protected: + private: std::unique_ptr database; std::mutex databaseCallbackMutex; Gtk::Stack stack; diff --git a/src/ChatMessage.cpp b/src/ChatMessage.cpp index 048bfca..db5fad2 100644 --- a/src/ChatMessage.cpp +++ b/src/ChatMessage.cpp @@ -2,9 +2,11 @@ namespace dchat { - ChatMessage::ChatMessage(const Glib::ustring &_username, const Glib::ustring &_text) : + ChatMessage::ChatMessage(const Glib::ustring &_username, const Glib::ustring &_text, uint32_t _timestampSeconds, const User *_user) : username(_username), - text(_text) + text(_text), + timestampSeconds(_timestampSeconds), + user(_user) { username.set_selectable(true); username.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START); diff --git a/src/ChatWindow.cpp b/src/ChatWindow.cpp index bebc644..b0bb696 100644 --- a/src/ChatWindow.cpp +++ b/src/ChatWindow.cpp @@ -5,8 +5,12 @@ namespace dchat { + // Merge all messages that are written by the same user without interrupt within a timeframe + const int MERGE_MESSAGE_TIMESTAMP_DIFF_SEC = 60; + ChatWindow::ChatWindow() : - channelCount(0) + channelCount(0), + lastMessage(nullptr) { setupTopBar(); @@ -66,15 +70,14 @@ namespace dchat leftPanelChannels.attach(*channelsTitle, 0, 0, 1, 1); //// - Gtk::Grid *leftPanelUsers = Gtk::manage(new Gtk::Grid()); - leftPanelUsers->set_vexpand(true); - leftPanel->add2(*leftPanelUsers); + leftPanelUsers.set_vexpand(true); + leftPanel->add2(leftPanelUsers); Gtk::Label *usersTitle = Gtk::manage(new Gtk::Label()); usersTitle->set_name("users-title"); usersTitle->set_text("Users"); usersTitle->set_halign(Gtk::ALIGN_START); - leftPanelUsers->attach(*usersTitle, 0, 0, 1, 1); + leftPanelUsers.attach(*usersTitle, 0, 0, 1, 1); } void ChatWindow::setupMessageArea(Gtk::Grid *rightPanel) @@ -102,8 +105,9 @@ namespace dchat void ChatWindow::addChannel(const odhtdb::Hash &nodeHash) { assert(channelDataById.find(nodeHash) == channelDataById.end()); - printf("Added channel\n"); - Gtk::Button *channelButton = Gtk::manage(new Gtk::Button("Channel name")); + fprintf(stderr, "Added channel %s\n", nodeHash.toString().c_str()); + Gtk::ToggleButton *channelButton = Gtk::manage(new Gtk::ToggleButton("Channel name")); + channelButton->set_active(true); channelButton->get_style_context()->add_class("channel-button"); channelButton->set_hexpand(true); channelButton->get_child()->set_halign(Gtk::ALIGN_START); @@ -113,15 +117,82 @@ namespace dchat channelDataById[nodeHash] = { channelButton, 0 }; } - void ChatWindow::addLocalMessage(const odhtdb::Hash &channelId, Glib::ustring msg) + void ChatWindow::addLocalMessage(const odhtdb::Hash &channelId, const odhtdb::Signature::PublicKey &userPublicKey, uint32_t timestampSeconds, Glib::ustring msg) { auto it = channelDataById.find(channelId); assert(it != channelDataById.end()); - ChatMessage *message = Gtk::manage(new ChatMessage("Arezu", msg)); + User *user = getUserByPublicKey(userPublicKey); + if(!user) + { + fprintf(stderr, "Unable to add message to channel because the user %s doesn't exist in it\n", userPublicKey.toString().c_str()); + return; + } + + if(lastMessage && lastMessage->user->publicKey == userPublicKey) + { + int64_t msgTimeDiff = (int64_t)timestampSeconds - (int64_t)lastMessage->timestampSeconds; + if(msgTimeDiff <= MERGE_MESSAGE_TIMESTAMP_DIFF_SEC) + { + lastMessage->text.set_text(lastMessage->text.get_text() + "\n" + msg); + return; + } + } + + ChatMessage *message = Gtk::manage(new ChatMessage(user->name, msg, timestampSeconds, user)); + lastMessage = message; message->set_valign(Gtk::Align::ALIGN_START); message->set_hexpand(true); message->show_all(); chatAreaLayout.attach(*message, 0, it->second.messageCount, 1, 1); ++it->second.messageCount; } + + void ChatWindow::addUser(const odhtdb::Signature::PublicKey &userPublicKey) + { + User *existingUser = getUserByPublicKey(userPublicKey); + if(existingUser) + { + fprintf(stderr, "ChatWindow::addUser: user %s was not added because the user has already been added\n", userPublicKey.toString().c_str()); + return; + } + + Gtk::Label *username = Gtk::manage(new Gtk::Label("NoName")); + username->set_halign(Gtk::ALIGN_START); + username->show(); + username->get_style_context()->add_class("username-list-username"); + leftPanelUsers.attach(*username, 0, 1 + users.size(), 1, 1); + users.push_back(new User { userPublicKey, "NoName" }); + fprintf(stderr, "Added user %s\n", userPublicKey.toString().c_str()); + } + + void ChatWindow::setUserNickname(const odhtdb::Signature::PublicKey &userPublicKey, const Glib::ustring &name) + { + for(size_t i = 0; i < users.size(); ++i) + { + User *user = users[i]; + if(user->publicKey == userPublicKey) + { + user->name = name; + Gtk::Widget *usernameLabel = leftPanelUsers.get_child_at(0, 1 + i); + assert(usernameLabel); + //assert(usernameLabel->get_type() == Gtk::Label::get_type()); + static_cast(usernameLabel)->set_text(name); + fprintf(stderr, "Set nickname for user %s to %s\n", userPublicKey.toString().c_str(), name.c_str()); + return; + } + } + fprintf(stderr, "ChatWindow::setUserNickname: user %s doesn't exist\n", userPublicKey.toString().c_str()); + } + + User* ChatWindow::getUserByPublicKey(const odhtdb::Signature::PublicKey &publicKey) const + { + for(User *user : users) + { + if(user->publicKey == publicKey) + { + return user; + } + } + return nullptr; + } } \ No newline at end of file diff --git a/src/Window.cpp b/src/Window.cpp index 38c187e..72a3d9d 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -2,6 +2,7 @@ #include "../include/Cache.hpp" #include "../include/ChannelDataType.hpp" #include "../include/WindowNotification.hpp" +#include namespace dchat { @@ -11,6 +12,7 @@ namespace dchat Gtk::Overlay *overlay = Gtk::manage(new Gtk::Overlay()); WindowNotification *windowNotification = Gtk::manage(new WindowNotification()); overlay->add_overlay(*windowNotification); + overlay->set_overlay_pass_through(*windowNotification); overlay->add(stack); add(*overlay); @@ -54,6 +56,7 @@ namespace dchat { std::lock_guard lock(databaseCallbackMutex); chatWindow.addChannel(*request.nodeHash); + chatWindow.addUser(*request.creatorPublicKey); }; callbackFuncs.addNodeCallbackFunc = [this](const odhtdb::DatabaseAddNodeRequest &request) @@ -63,22 +66,44 @@ namespace dchat return; ChannelDataType channelDataType = (ChannelDataType)static_cast(request.decryptedData.data)[0]; - switch(channelDataType) + try { - case ChannelDataType::ADD_MESSAGE: + switch(channelDataType) { - Glib::ustring msg((const char*)request.decryptedData.data + 1, request.decryptedData.size - 1); - chatWindow.addLocalMessage(*request.nodeHash, std::move(msg)); - break; + case ChannelDataType::ADD_MESSAGE: + { + Glib::ustring msg((const char*)request.decryptedData.data + 1, request.decryptedData.size - 1); + uint32_t timestampSeconds = ntp::NtpTimestamp::fromCombined(request.timestamp).seconds; + chatWindow.addLocalMessage(*request.nodeHash, *request.creatorPublicKey, timestampSeconds, std::move(msg)); + break; + } + case ChannelDataType::NICKNAME_CHANGE: + { + sibs::SafeDeserializer deserializer((const u8*)request.decryptedData.data + 1, request.decryptedData.size - 1); + u8 nameLength = deserializer.extract(); + if(nameLength > 0) + { + std::string nickname; + nickname.resize(nameLength); + deserializer.extract((u8*)&nickname[0], nameLength); + chatWindow.setUserNickname(*request.creatorPublicKey, nickname); + } + break; + } + default: + break; } - default: - break; + } + catch(std::exception &e) + { + fprintf(stderr, "Failed to process add node request, reason: %s\n", e.what()); } }; callbackFuncs.addUserCallbackFunc = [this](const odhtdb::DatabaseAddUserRequest &request) { std::lock_guard lock(databaseCallbackMutex); + chatWindow.addUser(*request.userToAddPublicKey); }; fprintf(stderr, "Connecting...\n"); -- cgit v1.2.3