forked from hiimgoodpack/brainlet-client
163 lines
5.2 KiB
C++
163 lines
5.2 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>
|
|
|
|
std::unique_ptr<Brainlet::Client> client = nullptr;
|
|
|
|
std::unordered_map<Gtk::ListBoxRow*, std::string> channelRowToId;
|
|
std::unordered_map<std::string, std::vector<Brainlet::Message>> messages;
|
|
std::string currentChannelId;
|
|
|
|
Gtk::Window* window;
|
|
|
|
Gtk::Widget* loginInterface;
|
|
Gtk::Entry* domainEntry;
|
|
Gtk::Entry* usernameEntry;
|
|
Gtk::Entry* passwordEntry;
|
|
Gtk::Button* loginButton;
|
|
|
|
Gtk::Widget* chatInterface;
|
|
Gtk::ListBox* channelList;
|
|
Gtk::ListBox* messageList;
|
|
Gtk::Entry* messageEntry;
|
|
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();
|
|
|
|
channelRowToId.emplace(row, channel.id);
|
|
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.authorId + ": " + 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("Window", window);
|
|
|
|
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());
|
|
window->remove();
|
|
window->add(*chatInterface);
|
|
|
|
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 = channelRowToId.at(row);
|
|
|
|
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("");
|
|
});
|
|
|
|
window->add(*loginInterface);
|
|
|
|
app->run(*window);
|
|
}
|