mirror of
https://github.com/celisej567/abaddon.git
synced 2026-09-13 20:18:50 +03:00
basic avatars
This commit is contained in:
@@ -126,6 +126,8 @@ add_executable(abaddon
|
||||
settings.hpp
|
||||
settings.cpp
|
||||
abaddon.cpp
|
||||
filecache.hpp
|
||||
filecache.cpp
|
||||
components/channels.hpp
|
||||
components/channels.cpp
|
||||
components/chatmessage.hpp
|
||||
|
||||
@@ -283,6 +283,10 @@ void Abaddon::ActionReloadCSS() {
|
||||
}
|
||||
}
|
||||
|
||||
Cache &Abaddon::GetCache() {
|
||||
return m_cache;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Gtk::Main::init_gtkmm_internals(); // why???
|
||||
return Abaddon::Get().StartGTK();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "discord/discord.hpp"
|
||||
#include "windows/mainwindow.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "filecache.hpp"
|
||||
|
||||
#define APP_TITLE "Abaddon"
|
||||
|
||||
@@ -42,6 +43,8 @@ public:
|
||||
|
||||
void ActionReloadCSS();
|
||||
|
||||
Cache &GetCache();
|
||||
|
||||
std::string GetDiscordToken() const;
|
||||
bool IsDiscordActive() const;
|
||||
|
||||
@@ -62,6 +65,8 @@ private:
|
||||
std::unordered_map<Snowflake, Snowflake> m_oldest_listed_message;
|
||||
std::unordered_set<Snowflake> m_channels_history_loading;
|
||||
|
||||
Cache m_cache;
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
Glib::RefPtr<Gtk::Application> m_gtk_app;
|
||||
Glib::RefPtr<Gtk::CssProvider> m_css_provider;
|
||||
|
||||
@@ -40,6 +40,7 @@ for:
|
||||
after_build:
|
||||
- cmd: |
|
||||
xcopy /E /I %APPVEYOR_BUILD_FOLDER%\css "%APPVEYOR_BUILD_FOLDER%\build\%CONFIGURATION%\css\"
|
||||
xcopy /E /I %APPVEYOR_BUILD_FOLDER%\res "%APPVEYOR_BUILD_FOLDER%\build\%CONFIGURATION%\res\"
|
||||
7z a %APPVEYOR_BUILD_FOLDER%\out\abaddon.zip "%APPVEYOR_BUILD_FOLDER%\build\%CONFIGURATION%"
|
||||
|
||||
artifacts:
|
||||
|
||||
@@ -14,6 +14,18 @@ ChatMessageContainer::ChatMessageContainer(const Message *data) {
|
||||
m_author = Gtk::manage(new Gtk::Label);
|
||||
m_timestamp = Gtk::manage(new Gtk::Label);
|
||||
|
||||
static Glib::RefPtr<Gdk::Pixbuf> test = Gdk::Pixbuf::create_from_file("res/decamarks.png", 32, 32);
|
||||
m_avatar = Gtk::manage(new Gtk::Image(test));
|
||||
m_avatar->set_valign(Gtk::ALIGN_START);
|
||||
m_avatar->set_margin_right(10);
|
||||
|
||||
if (data->Author.HasAvatar()) {
|
||||
Abaddon::Get().GetCache().GetFileFromURL(data->Author.GetAvatarURL(), [this](std::string filepath) {
|
||||
auto buf = Gdk::Pixbuf::create_from_file(filepath, 32, 32);
|
||||
m_avatar->property_pixbuf() = buf;
|
||||
});
|
||||
}
|
||||
|
||||
get_style_context()->add_class("message-container");
|
||||
m_author->get_style_context()->add_class("message-container-author");
|
||||
m_timestamp->get_style_context()->add_class("message-container-timestamp");
|
||||
@@ -47,6 +59,7 @@ ChatMessageContainer::ChatMessageContainer(const Message *data) {
|
||||
m_meta_box->add(*m_author);
|
||||
m_meta_box->add(*m_timestamp);
|
||||
m_content_box->add(*m_meta_box);
|
||||
m_main_box->add(*m_avatar);
|
||||
m_main_box->add(*m_content_box);
|
||||
add(*m_main_box);
|
||||
set_margin_bottom(8);
|
||||
|
||||
@@ -27,6 +27,7 @@ protected:
|
||||
Gtk::Box *m_meta_box;
|
||||
Gtk::Label *m_author;
|
||||
Gtk::Label *m_timestamp;
|
||||
Gtk::Image *m_avatar;
|
||||
};
|
||||
|
||||
class ChatMessageItem {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#include "user.hpp"
|
||||
|
||||
bool User::HasAvatar() const {
|
||||
return Avatar.size() > 0;
|
||||
}
|
||||
|
||||
std::string User::GetAvatarURL(std::string ext, std::string size) const {
|
||||
return "https://cdn.discordapp.com/avatars/" + std::to_string(ID) + "/" + Avatar + "." + ext + "?size=" + size;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, User &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("username", m.Username);
|
||||
|
||||
@@ -25,4 +25,7 @@ struct User {
|
||||
std::string Phone; // null?
|
||||
|
||||
friend void from_json(const nlohmann::json &j, User &m);
|
||||
|
||||
bool HasAvatar() const;
|
||||
std::string GetAvatarURL(std::string ext = "png", std::string size = "64") const;
|
||||
};
|
||||
|
||||
80
filecache.cpp
Normal file
80
filecache.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "filecache.hpp"
|
||||
|
||||
Cache::Cache() {
|
||||
m_tmp_path = std::filesystem::temp_directory_path() / "abaddon-cache";
|
||||
std::filesystem::create_directories(m_tmp_path);
|
||||
}
|
||||
|
||||
Cache::~Cache() {
|
||||
std::error_code err;
|
||||
if (!std::filesystem::remove_all(m_tmp_path, err))
|
||||
fprintf(stderr, "error removing tmp dir\n");
|
||||
}
|
||||
|
||||
std::string Cache::SanitizeString(std::string str) {
|
||||
std::string ret;
|
||||
for (const char c : str) {
|
||||
if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'))
|
||||
ret += c;
|
||||
else
|
||||
ret += '_';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Cache::RespondFromPath(std::filesystem::path path, callback_type cb) {
|
||||
/*if (!std::filesystem::exists(path)) return;
|
||||
FILE *fp = std::fopen(path.string().c_str(), "rb");
|
||||
if (fp == nullptr) return;
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
int len = std::ftell(fp);
|
||||
std::rewind(fp);
|
||||
std::vector<uint8_t> ret;
|
||||
ret.resize(len);
|
||||
std::fread(ret.data(), 1, len, fp);
|
||||
std::fclose(fp);*/
|
||||
cb(path.string());
|
||||
}
|
||||
|
||||
void Cache::GetFileFromURL(std::string url, callback_type cb) {
|
||||
auto cache_path = m_tmp_path / SanitizeString(url);
|
||||
if (std::filesystem::exists(cache_path)) {
|
||||
RespondFromPath(cache_path, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_callbacks.find(url) != m_callbacks.end()) {
|
||||
m_callbacks[url].push_back(cb);
|
||||
} else {
|
||||
m_futures.push_back(cpr::GetCallback(std::bind(&Cache::OnResponse, this, std::placeholders::_1), cpr::Url { url }));
|
||||
m_callbacks[url].push_back(cb);
|
||||
}
|
||||
}
|
||||
|
||||
// this just seems really yucky
|
||||
void Cache::CleanupFutures() {
|
||||
for (auto it = m_futures.begin(); it != m_futures.end();) {
|
||||
if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
|
||||
it = m_futures.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::OnResponse(const cpr::Response &r) {
|
||||
CleanupFutures(); // see above comment
|
||||
if (r.error || r.status_code > 300) return;
|
||||
|
||||
std::vector<uint8_t> data(r.text.begin(), r.text.end());
|
||||
auto path = m_tmp_path / SanitizeString(r.url);
|
||||
FILE *fp = std::fopen(path.string().c_str(), "wb");
|
||||
if (fp == nullptr)
|
||||
return;
|
||||
std::fwrite(data.data(), 1, data.size(), fp);
|
||||
std::fclose(fp);
|
||||
|
||||
for (const auto &cb : m_callbacks[r.url]) {
|
||||
cb(path.string());
|
||||
}
|
||||
m_callbacks.erase(r.url);
|
||||
}
|
||||
28
filecache.hpp
Normal file
28
filecache.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <cpr/cpr.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
// todo throttle requests and keep track of active requests to stop redundant requests
|
||||
|
||||
class Cache {
|
||||
public:
|
||||
Cache();
|
||||
~Cache();
|
||||
|
||||
using callback_type = std::function<void(std::string)>;
|
||||
void GetFileFromURL(std::string url, callback_type cb);
|
||||
|
||||
private:
|
||||
std::string SanitizeString(std::string str);
|
||||
void CleanupFutures();
|
||||
void RespondFromPath(std::filesystem::path path, callback_type cb);
|
||||
void OnResponse(const cpr::Response &r);
|
||||
|
||||
std::unordered_map<std::string, std::vector<callback_type>> m_callbacks;
|
||||
std::vector<std::future<void>> m_futures;
|
||||
std::filesystem::path m_tmp_path;
|
||||
};
|
||||
BIN
res/decamarks.png
Normal file
BIN
res/decamarks.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Reference in New Issue
Block a user