Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
0c674e0137 | |||
7948d820c3 | |||
5aac3422aa |
@ -61,6 +61,9 @@ add_executable(tomato
|
||||
./tox_dht_cap_histo.hpp
|
||||
./tox_dht_cap_histo.cpp
|
||||
|
||||
./tox_friend_faux_offline_messaging.hpp
|
||||
./tox_friend_faux_offline_messaging.cpp
|
||||
|
||||
./chat_gui4.hpp
|
||||
./chat_gui4.cpp
|
||||
)
|
||||
|
@ -17,10 +17,8 @@
|
||||
|
||||
#include "./media_meta_info_loader.hpp"
|
||||
#include "./sdl_clipboard_utils.hpp"
|
||||
#include "SDL_clipboard.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <cstdio>
|
||||
#include <chrono>
|
||||
@ -28,10 +26,7 @@
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Components {
|
||||
|
||||
@ -72,6 +67,56 @@ static std::string file_path_url_escape(const std::string&& value) {
|
||||
return escaped.str();
|
||||
}
|
||||
|
||||
const void* clipboard_callback(void* userdata, const char* mime_type, size_t* size) {
|
||||
if (mime_type == nullptr) {
|
||||
// cleared or new data is set
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (userdata == nullptr) {
|
||||
// error
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* cg = static_cast<ChatGui4*>(userdata);
|
||||
std::lock_guard lg{cg->_set_clipboard_data_mutex};
|
||||
if (!cg->_set_clipboard_data.count(mime_type)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto& sh_vec = cg->_set_clipboard_data.at(mime_type);
|
||||
if (!static_cast<bool>(sh_vec)) {
|
||||
// error, empty shared pointer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*size = sh_vec->size();
|
||||
|
||||
return sh_vec->data();
|
||||
}
|
||||
|
||||
void ChatGui4::setClipboardData(std::vector<std::string> mime_types, std::shared_ptr<std::vector<uint8_t>>&& data) {
|
||||
if (!static_cast<bool>(data)) {
|
||||
std::cerr << "CG error: tried to set clipboard with empty shp\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->empty()) {
|
||||
std::cerr << "CG error: tried to set clipboard with empty data\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> tmp_mimetype_list;
|
||||
|
||||
std::lock_guard lg{_set_clipboard_data_mutex};
|
||||
for (const auto& mime_type : mime_types) {
|
||||
tmp_mimetype_list.push_back(mime_type.data());
|
||||
_set_clipboard_data[mime_type] = data;
|
||||
}
|
||||
|
||||
SDL_SetClipboardData(clipboard_callback, nullptr, this, tmp_mimetype_list.data(), tmp_mimetype_list.size());
|
||||
}
|
||||
|
||||
ChatGui4::ChatGui4(
|
||||
ConfigModelI& conf,
|
||||
RegistryMessageModel& rmm,
|
||||
@ -80,6 +125,17 @@ ChatGui4::ChatGui4(
|
||||
) : _conf(conf), _rmm(rmm), _cr(cr), _tal(_cr), _contact_tc(_tal, tu), _msg_tc(_mil, tu), _sip(tu) {
|
||||
}
|
||||
|
||||
ChatGui4::~ChatGui4(void) {
|
||||
// TODO: this is bs
|
||||
SDL_ClearClipboardData();
|
||||
|
||||
// this might be better, need to see if this works (docs needs improving)
|
||||
//for (const auto& [k, _] : _set_clipboard_data) {
|
||||
//const auto* tmp_mime_type = k.c_str();
|
||||
//SDL_SetClipboardData(nullptr, nullptr, nullptr, &tmp_mime_type, 1);
|
||||
//}
|
||||
}
|
||||
|
||||
void ChatGui4::render(float time_delta) {
|
||||
if (!_cr.storage<Contact::Components::TagAvatarInvalidate>().empty()) { // handle force-reloads for avatars
|
||||
std::vector<Contact3> to_purge;
|
||||
@ -514,7 +570,7 @@ void ChatGui4::render(float time_delta) {
|
||||
_fss.requestFile(
|
||||
[](const auto& path) -> bool { return std::filesystem::is_regular_file(path); },
|
||||
[this](const auto& path){
|
||||
_rmm.sendFilePath(*_selected_contact, path.filename().u8string(), path.u8string());
|
||||
_rmm.sendFilePath(*_selected_contact, path.filename().generic_u8string(), path.generic_u8string());
|
||||
},
|
||||
[](){}
|
||||
);
|
||||
@ -577,7 +633,7 @@ void ChatGui4::render(float time_delta) {
|
||||
|
||||
void ChatGui4::sendFilePath(const char* file_path) {
|
||||
if (_selected_contact && std::filesystem::is_regular_file(file_path)) {
|
||||
_rmm.sendFilePath(*_selected_contact, std::filesystem::path(file_path).filename().u8string(), file_path);
|
||||
_rmm.sendFilePath(*_selected_contact, std::filesystem::path(file_path).filename().generic_u8string(), file_path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -671,10 +727,10 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
||||
//for (const auto& c : _cr.view<entt::get_t<Contact::Components::TagBig>, entt::exclude_t<Contact::Components::RequestIncoming, Contact::Components::TagRequestOutgoing>>()) {
|
||||
for (const auto& c : _cr.view<Contact::Components::TagBig>()) {
|
||||
if (renderContactListContactSmall(c, false)) {
|
||||
//_rmm.sendFilePath(*_selected_contact, path.filename().u8string(), path.u8string());
|
||||
//_rmm.sendFilePath(*_selected_contact, path.filename().generic_u8string(), path.generic_u8string());
|
||||
const auto& fil = reg.get<Message::Components::Transfer::FileInfoLocal>(e);
|
||||
for (const auto& path : fil.file_list) {
|
||||
_rmm.sendFilePath(c, std::filesystem::path{path}.filename().u8string(), path);
|
||||
_rmm.sendFilePath(c, std::filesystem::path{path}.filename().generic_u8string(), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -761,10 +817,19 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
||||
const auto& local_info = reg.get<Message::Components::Transfer::FileInfoLocal>(e);
|
||||
if (local_info.file_list.size() > i && ImGui::BeginPopupContextItem("##file_c")) {
|
||||
if (ImGui::MenuItem("open")) {
|
||||
std::string url{"file://" + file_path_url_escape(std::filesystem::canonical(local_info.file_list.at(i)).u8string())};
|
||||
const std::string url{"file://" + file_path_url_escape(std::filesystem::canonical(local_info.file_list.at(i)).generic_u8string())};
|
||||
std::cout << "opening file '" << url << "'\n";
|
||||
SDL_OpenURL(url.c_str());
|
||||
}
|
||||
if (ImGui::MenuItem("copy file")) {
|
||||
const std::string url{"file://" + file_path_url_escape(std::filesystem::canonical(local_info.file_list.at(i)).generic_u8string())};
|
||||
//ImGui::SetClipboardText(url.c_str());
|
||||
setClipboardData({"text/uri-list", "text/x-moz-url"}, std::make_shared<std::vector<uint8_t>>(url.begin(), url.end()));
|
||||
}
|
||||
if (ImGui::MenuItem("copy filepath")) {
|
||||
const auto file_path = std::filesystem::canonical(local_info.file_list.at(i)).u8string(); //TODO: use generic over native?
|
||||
ImGui::SetClipboardText(file_path.c_str());
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
@ -1065,7 +1130,7 @@ void ChatGui4::pasteFile(const char* mime_type) {
|
||||
std::ofstream(tmp_file_path, std::ios_base::out | std::ios_base::binary)
|
||||
.write(reinterpret_cast<const char*>(img_data.data()), img_data.size());
|
||||
|
||||
_rmm.sendFilePath(*_selected_contact, tmp_file_name.str(), tmp_file_path.u8string());
|
||||
_rmm.sendFilePath(*_selected_contact, tmp_file_name.str(), tmp_file_path.generic_u8string());
|
||||
},
|
||||
[](){}
|
||||
);
|
||||
|
@ -9,9 +9,13 @@
|
||||
#include "./message_image_loader.hpp"
|
||||
#include "./file_selector.hpp"
|
||||
#include "./send_image_popup.hpp"
|
||||
#include "entt/container/dense_map.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
class ChatGui4 {
|
||||
ConfigModelI& _conf;
|
||||
@ -37,6 +41,12 @@ class ChatGui4 {
|
||||
float TEXT_BASE_WIDTH {1};
|
||||
float TEXT_BASE_HEIGHT {1};
|
||||
|
||||
// mimetype -> data
|
||||
entt::dense_map<std::string, std::shared_ptr<std::vector<uint8_t>>> _set_clipboard_data;
|
||||
std::mutex _set_clipboard_data_mutex; // might be called out of order
|
||||
friend const void* clipboard_callback(void* userdata, const char* mime_type, size_t* size);
|
||||
void setClipboardData(std::vector<std::string> mime_types, std::shared_ptr<std::vector<uint8_t>>&& data);
|
||||
|
||||
public:
|
||||
ChatGui4(
|
||||
ConfigModelI& conf,
|
||||
@ -44,6 +54,7 @@ class ChatGui4 {
|
||||
Contact3Registry& cr,
|
||||
TextureUploaderI& tu
|
||||
);
|
||||
~ChatGui4(void);
|
||||
|
||||
public:
|
||||
void render(float time_delta);
|
||||
|
@ -47,7 +47,7 @@ void FileSelector::render(void) {
|
||||
std::filesystem::path current_path = _current_file_path;
|
||||
current_path.remove_filename();
|
||||
|
||||
ImGui::Text("path: %s", _current_file_path.u8string().c_str());
|
||||
ImGui::Text("path: %s", _current_file_path.generic_u8string().c_str());
|
||||
|
||||
// begin table with selectables
|
||||
constexpr ImGuiTableFlags table_flags =
|
||||
@ -175,7 +175,7 @@ void FileSelector::render(void) {
|
||||
}
|
||||
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted((dir_entry.path().filename().u8string() + "/").c_str());
|
||||
ImGui::TextUnformatted((dir_entry.path().filename().generic_u8string() + "/").c_str());
|
||||
}
|
||||
|
||||
if (ImGui::TableNextColumn()) {
|
||||
@ -206,7 +206,7 @@ void FileSelector::render(void) {
|
||||
}
|
||||
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted(dir_entry.path().filename().u8string().c_str());
|
||||
ImGui::TextUnformatted(dir_entry.path().filename().generic_u8string().c_str());
|
||||
}
|
||||
|
||||
if (ImGui::TableNextColumn()) {
|
||||
|
@ -16,6 +16,7 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
|
||||
tcm(cr, tc, tc),
|
||||
tmm(rmm, cr, tcm, tc, tc),
|
||||
ttm(rmm, cr, tcm, tc, tc),
|
||||
tffom(cr, rmm, tcm, tc, tc),
|
||||
mmil(rmm),
|
||||
tam(rmm, cr, conf),
|
||||
sdlrtu(renderer_),
|
||||
@ -241,6 +242,8 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
|
||||
|
||||
tcm.iterate(time_delta); // compute
|
||||
|
||||
const float fo_interval = tffom.tick(time_delta);
|
||||
|
||||
tam.iterate(); // compute
|
||||
|
||||
const float pm_interval = pm.tick(time_delta); // compute
|
||||
@ -253,6 +256,10 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
|
||||
tc.toxIterationInterval()/1000.f,
|
||||
pm_interval
|
||||
);
|
||||
_min_tick_interval = std::min<float>(
|
||||
_min_tick_interval,
|
||||
fo_interval
|
||||
);
|
||||
|
||||
switch (_compute_perf_mode) {
|
||||
// normal 1ms lower bound
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "./settings_window.hpp"
|
||||
#include "./tox_ui_utils.hpp"
|
||||
#include "./tox_dht_cap_histo.hpp"
|
||||
#include "./tox_friend_faux_offline_messaging.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
@ -52,6 +53,7 @@ struct MainScreen final : public Screen {
|
||||
ToxContactModel2 tcm;
|
||||
ToxMessageManager tmm;
|
||||
ToxTransferManager ttm;
|
||||
ToxFriendFauxOfflineMessaging tffom;
|
||||
|
||||
MediaMetaInfoLoader mmil;
|
||||
ToxAvatarManager tam;
|
||||
|
@ -83,7 +83,7 @@ std::string ToxAvatarManager::getAvatarPath(const ToxKey& key) const {
|
||||
const std::string_view avatar_save_path {_conf.get_string("ToxAvatarManager", "save_path").value()};
|
||||
const auto pub_key_string = bin2hex({key.data.cbegin(), key.data.cend()});
|
||||
const auto file_path = std::filesystem::path(avatar_save_path) / (pub_key_string + ".png");
|
||||
return file_path.u8string();
|
||||
return file_path.generic_u8string();
|
||||
}
|
||||
|
||||
void ToxAvatarManager::addAvatarFileToContact(const Contact3 c, const ToxKey& key) {
|
||||
|
188
src/tox_friend_faux_offline_messaging.cpp
Normal file
188
src/tox_friend_faux_offline_messaging.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
#include "./tox_friend_faux_offline_messaging.hpp"
|
||||
|
||||
#include <solanaceae/toxcore/tox_interface.hpp>
|
||||
|
||||
#include <solanaceae/contact/components.hpp>
|
||||
#include <solanaceae/tox_contacts/components.hpp>
|
||||
#include <solanaceae/message3/components.hpp>
|
||||
#include <solanaceae/tox_messages/components.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Message::Components {
|
||||
struct LastSendAttempt {
|
||||
uint64_t ts {0};
|
||||
};
|
||||
} // Message::Components
|
||||
|
||||
namespace Contact::Components {
|
||||
struct NextSendAttempt {
|
||||
uint64_t ts {0};
|
||||
};
|
||||
} // Contact::Components
|
||||
|
||||
ToxFriendFauxOfflineMessaging::ToxFriendFauxOfflineMessaging(
|
||||
Contact3Registry& cr,
|
||||
RegistryMessageModel& rmm,
|
||||
ToxContactModel2& tcm,
|
||||
ToxI& t,
|
||||
ToxEventProviderI& tep
|
||||
) : _cr(cr), _rmm(rmm), _tcm(tcm), _t(t), _tep(tep) {
|
||||
}
|
||||
|
||||
float ToxFriendFauxOfflineMessaging::tick(float time_delta) {
|
||||
// hard limit interval to once per minute
|
||||
_interval_timer += time_delta;
|
||||
if (_interval_timer < 1.f * 60.f) {
|
||||
return std::max(60.f - _interval_timer, 0.001f); // TODO: min next timer
|
||||
}
|
||||
_interval_timer = 0.f;
|
||||
|
||||
const uint64_t ts_now = Message::getTimeMS();
|
||||
|
||||
// check ALL
|
||||
// for each online tox friend
|
||||
uint64_t min_next_attempt_ts {std::numeric_limits<uint64_t>::max()};
|
||||
_cr.view<Contact::Components::ToxFriendEphemeral, Contact::Components::ConnectionState>()
|
||||
.each([this, &min_next_attempt_ts, ts_now](const Contact3 c, const auto& tfe, const auto& cs) {
|
||||
if (cs.state == Contact::Components::ConnectionState::disconnected) {
|
||||
// cleanup
|
||||
if (_cr.all_of<Contact::Components::NextSendAttempt>(c)) {
|
||||
_cr.remove<Contact::Components::NextSendAttempt>(c);
|
||||
}
|
||||
} else {
|
||||
if (!_cr.all_of<Contact::Components::NextSendAttempt>(c)) {
|
||||
const auto& nsa = _cr.emplace<Contact::Components::NextSendAttempt>(c, ts_now + uint64_t(_delay_after_cc*1000)); // wait before first message is sent
|
||||
min_next_attempt_ts = std::min(min_next_attempt_ts, nsa.ts);
|
||||
} else {
|
||||
auto& next_attempt = _cr.get<Contact::Components::NextSendAttempt>(c).ts;
|
||||
|
||||
if (doFriendMessageCheck(c, tfe)) {
|
||||
next_attempt = ts_now + uint64_t(_delay_inbetween*1000);
|
||||
}
|
||||
|
||||
min_next_attempt_ts = std::min(min_next_attempt_ts, next_attempt);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (min_next_attempt_ts <= ts_now) {
|
||||
// we (probably) sent this iterate
|
||||
_interval_timer = 60.f - 0.1f; // TODO: ugly magic
|
||||
return 0.1f;
|
||||
} else if (min_next_attempt_ts == std::numeric_limits<uint64_t>::max()) {
|
||||
// nothing to sync or all offline that need syncing
|
||||
return 60.f; // TODO: ugly magic
|
||||
} else {
|
||||
// TODO: ugly magic
|
||||
return _interval_timer = 60.f - std::min(60.f, (min_next_attempt_ts - ts_now) / 1000.f);
|
||||
}
|
||||
}
|
||||
|
||||
bool ToxFriendFauxOfflineMessaging::doFriendMessageCheck(const Contact3 c, const Contact::Components::ToxFriendEphemeral& tfe) {
|
||||
// walk all messages and check if
|
||||
// unacked message
|
||||
// timeouts for exising unacked messages expired (send)
|
||||
|
||||
auto* mr = static_cast<const RegistryMessageModel&>(_rmm).get(c);
|
||||
if (mr == nullptr) {
|
||||
// no messages
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t ts_now = Message::getTimeMS();
|
||||
|
||||
// filter for unconfirmed messages
|
||||
|
||||
// we assume sorted
|
||||
// ("reverse" iteration <.<)
|
||||
auto msg_view = mr->view<Message::Components::Timestamp>();
|
||||
// we search for the oldest, not too recently sent, unconfirmed message
|
||||
for (auto it = msg_view.rbegin(), view_end = msg_view.rend(); it != view_end; it++) {
|
||||
const Message3 msg = *it;
|
||||
|
||||
// require
|
||||
if (!mr->all_of<
|
||||
Message::Components::MessageText, // text only for now
|
||||
Message::Components::ContactTo
|
||||
>(msg)
|
||||
) {
|
||||
continue; // skip
|
||||
}
|
||||
|
||||
// exclude
|
||||
if (mr->any_of<
|
||||
Message::Components::Remote::TimestampReceived // this acts like a tag, which is wrong in groups
|
||||
>(msg)
|
||||
) {
|
||||
continue; // skip
|
||||
}
|
||||
|
||||
uint64_t msg_ts = msg_view.get<Message::Components::Timestamp>(msg).ts;
|
||||
if (mr->all_of<Message::Components::TimestampWritten>(msg)) {
|
||||
msg_ts = mr->get<Message::Components::TimestampWritten>(msg).ts;
|
||||
}
|
||||
if (mr->all_of<Message::Components::LastSendAttempt>(msg)) {
|
||||
const auto lsa = mr->get<Message::Components::LastSendAttempt>(msg).ts;
|
||||
if (lsa > msg_ts) {
|
||||
msg_ts = lsa;
|
||||
}
|
||||
}
|
||||
|
||||
if (ts_now < (msg_ts + uint64_t(_delay_retry * 1000))) {
|
||||
// not time yet
|
||||
continue;
|
||||
}
|
||||
|
||||
// it is time
|
||||
const auto [msg_id, _] = _t.toxFriendSendMessage(
|
||||
tfe.friend_number,
|
||||
(
|
||||
mr->all_of<Message::Components::TagMessageIsAction>(msg)
|
||||
? Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION
|
||||
: Tox_Message_Type::TOX_MESSAGE_TYPE_NORMAL
|
||||
),
|
||||
mr->get<Message::Components::MessageText>(msg).text
|
||||
);
|
||||
|
||||
// TODO: this is ugly
|
||||
mr->emplace_or_replace<Message::Components::LastSendAttempt>(msg, ts_now);
|
||||
|
||||
if (msg_id.has_value()) {
|
||||
// tmm will pick this up for us
|
||||
mr->emplace_or_replace<Message::Components::ToxFriendMessageID>(msg, msg_id.value());
|
||||
} // else error
|
||||
|
||||
// we sent our message, no point further iterating
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: somehow cleanup lsa
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ToxFriendFauxOfflineMessaging::onToxEvent(const Tox_Event_Friend_Connection_Status* e) {
|
||||
const auto friend_number = tox_event_friend_connection_status_get_friend_number(e);
|
||||
const auto friend_status = tox_event_friend_connection_status_get_connection_status(e);
|
||||
|
||||
if (friend_status == Tox_Connection::TOX_CONNECTION_NONE) {
|
||||
return false; // skip
|
||||
// maybe cleanup?
|
||||
}
|
||||
|
||||
auto c = _tcm.getContactFriend(friend_number);
|
||||
if (!static_cast<bool>(c) || !c.all_of<Contact::Components::ToxFriendEphemeral, Contact::Components::ConnectionState>()) {
|
||||
// UH error??
|
||||
return false;
|
||||
}
|
||||
|
||||
_cr.emplace_or_replace<Contact::Components::NextSendAttempt>(c, Message::getTimeMS() + uint64_t(_delay_after_cc*1000)); // wait before first message is sent
|
||||
|
||||
// TODO: ugly magic
|
||||
_interval_timer = 60.f - 0.1f;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
50
src/tox_friend_faux_offline_messaging.hpp
Normal file
50
src/tox_friend_faux_offline_messaging.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
||||
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
|
||||
#include <solanaceae/contact/contact_model3.hpp>
|
||||
#include <solanaceae/message3/registry_message_model.hpp>
|
||||
|
||||
// fwd
|
||||
struct ToxI;
|
||||
namespace Contact::Components {
|
||||
struct ToxFriendEphemeral;
|
||||
}
|
||||
|
||||
// resends unconfirmed messages.
|
||||
// timers get reset on connection changes, and send order is preserved.
|
||||
class ToxFriendFauxOfflineMessaging : public ToxEventI {
|
||||
Contact3Registry& _cr;
|
||||
RegistryMessageModel& _rmm;
|
||||
ToxContactModel2& _tcm;
|
||||
ToxI& _t;
|
||||
ToxEventProviderI& _tep;
|
||||
|
||||
float _interval_timer{0.f};
|
||||
|
||||
// TODO: increase timer?
|
||||
const float _delay_after_cc {4.5f};
|
||||
const float _delay_inbetween {0.3f};
|
||||
const float _delay_retry {10.f}; // retry sending after 10s
|
||||
|
||||
public:
|
||||
ToxFriendFauxOfflineMessaging(
|
||||
Contact3Registry& cr,
|
||||
RegistryMessageModel& rmm,
|
||||
ToxContactModel2& tcm,
|
||||
ToxI& t,
|
||||
ToxEventProviderI& tep
|
||||
);
|
||||
|
||||
float tick(float time_delta);
|
||||
|
||||
private:
|
||||
// only called for online friends
|
||||
// returns true if a message was sent
|
||||
// dont call this too often
|
||||
bool doFriendMessageCheck(const Contact3 c, const Contact::Components::ToxFriendEphemeral& tfe);
|
||||
|
||||
protected:
|
||||
bool onToxEvent(const Tox_Event_Friend_Connection_Status* e) override;
|
||||
};
|
||||
|
Reference in New Issue
Block a user