This repository has been archived on 2021-04-23. You can view files and clone it, but cannot push or open issues or pull requests.
brainlet-client/main.cpp
2021-04-23 17:32:13 +03:00

169 lines
5.7 KiB
C++

// Copyright (c) 2021 hiimgoodpack
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "http.h"
#include "websocket.h"
#include "brainlet.h"
#include <cstring> // std::strcpy
#include <memory> // std::unique_ptr
#include <algorithm> // std::for_each
#include <gtkmm.h>
#include <unordered_map>
#include <vector>
#include <string>
static std::unique_ptr<Brainlet::Client> client = nullptr;
static std::unordered_map<Gtk::ListBoxRow*, Brainlet::Channel> channelRowToChannel;
static std::unordered_map<std::string, std::vector<Brainlet::Message>> messages;
static std::string currentChannelId;
static Gtk::ApplicationWindow* loginWindow;
static Gtk::ApplicationWindow* chatWindow;
static Gtk::HeaderBar* chatHeader;
static Gtk::Widget* loginInterface;
static Gtk::Entry* domainEntry;
static Gtk::Entry* usernameEntry;
static Gtk::Entry* passwordEntry;
static Gtk::Button* loginButton;
static Gtk::Widget* chatInterface;
static Gtk::ListBox* channelList;
static Gtk::ListBox* messageList;
static Gtk::Entry* messageEntry;
static Gtk::Label* usernameLabel;
gboolean update(gpointer) {
client->processNextEvent();
return G_SOURCE_CONTINUE;
}
void addChannel(const Brainlet::Channel channel) {
auto row = Gtk::make_managed<Gtk::ListBoxRow>();
auto name = Gtk::make_managed<Gtk::Label>();
name->set_halign(Gtk::ALIGN_START);
name->set_line_wrap(true);
name->set_text(channel.name);
row->add(*name);
channelList->add(*row);
row->show_all_children();
row->show();
channelRowToChannel.emplace(row, channel);
messages.emplace(
std::piecewise_construct,
std::forward_as_tuple(channel.id),
std::forward_as_tuple()
);
}
void addMessage(const Brainlet::Message message) {
auto row = Gtk::make_managed<Gtk::ListBoxRow>();
auto text = Gtk::make_managed<Gtk::Label>();
text->set_halign(Gtk::ALIGN_START);
text->set_line_wrap(true);
std::string messageFormatted = message.author.username + ": " + message.message;
text->set_markup(messageFormatted.c_str());
row->add(*text);
messageList->add(*row);
std::string mention = "@" + client->id;
if (message.message.find(mention) != std::string::npos)
messageList->select_row(*row);
row->show_all_children();
row->show();
}
int main(int argc, char* argv[]) {
curl_global_init(CURL_GLOBAL_ALL);
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create("org.hiimgoodpack.brainlet_client");
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("./design.glade");
builder->get_widget("LoginWindow", loginWindow);
builder->get_widget("ChatWindow", chatWindow);
builder->get_widget("ChatHeader", chatHeader);
builder->get_widget("Login", loginInterface);
builder->get_widget("Domain", domainEntry);
builder->get_widget("Username", usernameEntry);
builder->get_widget("Password", passwordEntry);
builder->get_widget("LoginButton", loginButton);
builder->get_widget("Chat", chatInterface);
builder->get_widget("Channels", channelList);
builder->get_widget("Messages", messageList);
builder->get_widget("Message", messageEntry);
builder->get_widget("UsernameLabel", usernameLabel);
auto login = [&]() {
if (client.get() != nullptr) return;
const std::string username = usernameEntry->get_text();
const std::string password = passwordEntry->get_text();
usernameLabel->set_text(username.c_str());
app->add_window(*chatWindow);
chatWindow->show_all();
loginWindow->close();
client.reset(new Brainlet::Client(domainEntry->get_text()));
client->login(username, password);
client->onNewChannel = addChannel;
client->onNewMessage = [&](const Brainlet::Message message, const Brainlet::Channel channel) {
messages.at(channel.id).push_back(message);
if (channel.id == currentChannelId)
addMessage(message);
};
g_timeout_add((1.0f/5.0f)*1000.0f, update, nullptr);
};
passwordEntry->signal_activate().connect(login);
loginButton->signal_clicked().connect(login);
channelList->signal_row_activated().connect([&](Gtk::ListBoxRow* row) {
// Clear messages in messageList
messageList->foreach(std::bind(std::mem_fn(&Gtk::ListBox::remove), messageList, std::placeholders::_1));
// Add messages in the channel selected
currentChannelId = channelRowToChannel.at(row).id;
// Set the title in the HeaderBar to the currently selected channel's name
chatHeader->set_title(channelRowToChannel.at(row).name);
const std::vector<Brainlet::Message>& channelMessages = messages.at(currentChannelId);
std::for_each(channelMessages.begin(), channelMessages.end(), addMessage);
});
messageEntry->signal_activate().connect([&]() {
// TODO: Show message locally first to avoid latency
// Also show some sort of unread indicator
client->sendMessage(messageEntry->get_text(), currentChannelId);
messageEntry->set_text("");
});
app->run(*loginWindow);
}