Compare commits

...

16 Commits

29 changed files with 743 additions and 415 deletions

View File

@ -68,7 +68,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Install Dependencies - name: Install Dependencies
run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static pkgconf:x64-windows
# setup vs env # setup vs env
- uses: ilammy/msvc-dev-cmd@v1 - uses: ilammy/msvc-dev-cmd@v1
@ -79,7 +79,7 @@ jobs:
#- uses: ilammy/setup-nasm@v1 #- uses: ilammy/setup-nasm@v1
- name: Configure CMake - name: Configure CMake
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DSDL3IMAGE_VENDORED=ON -DSDL3IMAGE_DEPS_SHARED=ON -DSDL3IMAGE_JXL=OFF -DSDL3IMAGE_AVIF=OFF run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DSDL3IMAGE_VENDORED=ON -DSDL3IMAGE_DEPS_SHARED=ON -DSDL3IMAGE_JXL=OFF -DSDL3IMAGE_AVIF=OFF -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
@ -122,7 +122,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Install Dependencies - name: Install Dependencies
run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static pkgconf:x64-windows
# setup vs env # setup vs env
- uses: ilammy/msvc-dev-cmd@v1 - uses: ilammy/msvc-dev-cmd@v1
@ -133,7 +133,7 @@ jobs:
#- uses: ilammy/setup-nasm@v1 #- uses: ilammy/setup-nasm@v1
- name: Configure CMake - name: Configure CMake
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DTOMATO_ASAN=ON -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DSDL3IMAGE_VENDORED=ON -DSDL3IMAGE_DEPS_SHARED=ON -DSDL3IMAGE_JXL=OFF -DSDL3IMAGE_AVIF=OFF run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DTOMATO_ASAN=ON -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DSDL3IMAGE_VENDORED=ON -DSDL3IMAGE_DEPS_SHARED=ON -DSDL3IMAGE_JXL=OFF -DSDL3IMAGE_AVIF=OFF -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4

View File

@ -59,7 +59,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Install Dependencies - name: Install Dependencies
run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static pkgconf:x64-windows
# setup vs env # setup vs env
- uses: ilammy/msvc-dev-cmd@v1 - uses: ilammy/msvc-dev-cmd@v1
@ -70,7 +70,7 @@ jobs:
#- uses: ilammy/setup-nasm@v1 #- uses: ilammy/setup-nasm@v1
- name: Configure CMake - name: Configure CMake
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DSDL3IMAGE_VENDORED=ON -DSDL3IMAGE_DEPS_SHARED=ON -DSDL3IMAGE_JXL=OFF -DSDL3IMAGE_AVIF=OFF run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DSDL3IMAGE_VENDORED=ON -DSDL3IMAGE_DEPS_SHARED=ON -DSDL3IMAGE_JXL=OFF -DSDL3IMAGE_AVIF=OFF -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4

View File

@ -1,214 +1,62 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR) cmake_minimum_required(VERSION 3.9...3.16 FATAL_ERROR)
set(EXPERIMENTAL_API ON CACHE BOOL "" FORCE)
set(UNITTEST OFF CACHE BOOL "" FORCE)
set(BOOTSTRAP_DAEMON OFF CACHE BOOL "" FORCE)
add_subdirectory(./c-toxcore)
# the ideal case
#add_library(toxcore ALIAS toxcore_static)
# the sad case
add_library(toxcore INTERFACE)
target_link_libraries(toxcore INTERFACE toxcore_static)
# HACK: "install" api headers into binary dir
configure_file(
./c-toxcore/toxcore/tox.h
${CMAKE_CURRENT_BINARY_DIR}/include/tox/tox.h
@ONLY
)
configure_file(
./c-toxcore/toxcore/tox_events.h
${CMAKE_CURRENT_BINARY_DIR}/include/tox/tox_events.h
@ONLY
)
configure_file(
./c-toxcore/toxcore/tox_private.h
${CMAKE_CURRENT_BINARY_DIR}/include/tox/tox_private.h
@ONLY
)
configure_file(
./c-toxcore/toxencryptsave/toxencryptsave.h
${CMAKE_CURRENT_BINARY_DIR}/include/tox/toxencryptsave.h
@ONLY
)
configure_file(
./c-toxcore/toxav/toxav.h
${CMAKE_CURRENT_BINARY_DIR}/include/tox/toxav.h
@ONLY
)
target_include_directories(toxcore INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/)
# HACK: support old libsodium find
# libs should handle this case themselfs
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
set(TOX_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c-toxcore/")
# TODO: shared
add_library(toxcore STATIC
${TOX_DIR}third_party/cmp/cmp.c
${TOX_DIR}third_party/cmp/cmp.h
${TOX_DIR}toxcore/announce.c
${TOX_DIR}toxcore/announce.h
${TOX_DIR}toxcore/bin_pack.c
${TOX_DIR}toxcore/bin_pack.h
${TOX_DIR}toxcore/bin_unpack.c
${TOX_DIR}toxcore/bin_unpack.h
${TOX_DIR}toxcore/ccompat.c
${TOX_DIR}toxcore/ccompat.h
${TOX_DIR}toxcore/crypto_core.c
${TOX_DIR}toxcore/crypto_core.h
${TOX_DIR}toxcore/crypto_core_pack.c
${TOX_DIR}toxcore/crypto_core_pack.h
${TOX_DIR}toxcore/DHT.c
${TOX_DIR}toxcore/DHT.h
${TOX_DIR}toxcore/events/conference_connected.c
${TOX_DIR}toxcore/events/conference_invite.c
${TOX_DIR}toxcore/events/conference_message.c
${TOX_DIR}toxcore/events/conference_peer_list_changed.c
${TOX_DIR}toxcore/events/conference_peer_name.c
${TOX_DIR}toxcore/events/conference_title.c
${TOX_DIR}toxcore/events/dht_get_nodes_response.c
${TOX_DIR}toxcore/events/events_alloc.c
${TOX_DIR}toxcore/events/events_alloc.h
${TOX_DIR}toxcore/events/file_chunk_request.c
${TOX_DIR}toxcore/events/file_recv.c
${TOX_DIR}toxcore/events/file_recv_chunk.c
${TOX_DIR}toxcore/events/file_recv_control.c
${TOX_DIR}toxcore/events/friend_connection_status.c
${TOX_DIR}toxcore/events/friend_lossless_packet.c
${TOX_DIR}toxcore/events/friend_lossy_packet.c
${TOX_DIR}toxcore/events/friend_message.c
${TOX_DIR}toxcore/events/friend_name.c
${TOX_DIR}toxcore/events/friend_read_receipt.c
${TOX_DIR}toxcore/events/friend_request.c
${TOX_DIR}toxcore/events/friend_status.c
${TOX_DIR}toxcore/events/friend_status_message.c
${TOX_DIR}toxcore/events/friend_typing.c
${TOX_DIR}toxcore/events/self_connection_status.c
${TOX_DIR}toxcore/events/group_custom_packet.c
${TOX_DIR}toxcore/events/group_custom_private_packet.c
${TOX_DIR}toxcore/events/group_invite.c
${TOX_DIR}toxcore/events/group_join_fail.c
${TOX_DIR}toxcore/events/group_message.c
${TOX_DIR}toxcore/events/group_moderation.c
${TOX_DIR}toxcore/events/group_password.c
${TOX_DIR}toxcore/events/group_peer_exit.c
${TOX_DIR}toxcore/events/group_peer_join.c
${TOX_DIR}toxcore/events/group_peer_limit.c
${TOX_DIR}toxcore/events/group_peer_name.c
${TOX_DIR}toxcore/events/group_peer_status.c
${TOX_DIR}toxcore/events/group_privacy_state.c
${TOX_DIR}toxcore/events/group_private_message.c
${TOX_DIR}toxcore/events/group_self_join.c
${TOX_DIR}toxcore/events/group_topic.c
${TOX_DIR}toxcore/events/group_topic_lock.c
${TOX_DIR}toxcore/events/group_voice_state.c
${TOX_DIR}toxcore/forwarding.c
${TOX_DIR}toxcore/forwarding.h
${TOX_DIR}toxcore/friend_connection.c
${TOX_DIR}toxcore/friend_connection.h
${TOX_DIR}toxcore/friend_requests.c
${TOX_DIR}toxcore/friend_requests.h
${TOX_DIR}toxcore/group.c
${TOX_DIR}toxcore/group.h
${TOX_DIR}toxcore/group_announce.c
${TOX_DIR}toxcore/group_announce.h
${TOX_DIR}toxcore/group_moderation.c
${TOX_DIR}toxcore/group_moderation.h
${TOX_DIR}toxcore/group_chats.c
${TOX_DIR}toxcore/group_chats.h
${TOX_DIR}toxcore/group_common.h
${TOX_DIR}toxcore/group_connection.c
${TOX_DIR}toxcore/group_connection.h
${TOX_DIR}toxcore/group_onion_announce.c
${TOX_DIR}toxcore/group_onion_announce.h
${TOX_DIR}toxcore/group_pack.c
${TOX_DIR}toxcore/group_pack.h
${TOX_DIR}toxcore/LAN_discovery.c
${TOX_DIR}toxcore/LAN_discovery.h
${TOX_DIR}toxcore/list.c
${TOX_DIR}toxcore/list.h
${TOX_DIR}toxcore/logger.c
${TOX_DIR}toxcore/logger.h
${TOX_DIR}toxcore/Messenger.c
${TOX_DIR}toxcore/Messenger.h
${TOX_DIR}toxcore/mem.c
${TOX_DIR}toxcore/mem.h
${TOX_DIR}toxcore/mono_time.c
${TOX_DIR}toxcore/mono_time.h
${TOX_DIR}toxcore/net_crypto.c
${TOX_DIR}toxcore/net_crypto.h
${TOX_DIR}toxcore/network.c
${TOX_DIR}toxcore/network.h
${TOX_DIR}toxcore/onion_announce.c
${TOX_DIR}toxcore/onion_announce.h
${TOX_DIR}toxcore/onion.c
${TOX_DIR}toxcore/onion_client.c
${TOX_DIR}toxcore/onion_client.h
${TOX_DIR}toxcore/onion.h
${TOX_DIR}toxcore/ping_array.c
${TOX_DIR}toxcore/ping_array.h
${TOX_DIR}toxcore/ping.c
${TOX_DIR}toxcore/ping.h
${TOX_DIR}toxcore/shared_key_cache.c
${TOX_DIR}toxcore/shared_key_cache.h
${TOX_DIR}toxcore/state.c
${TOX_DIR}toxcore/state.h
${TOX_DIR}toxcore/TCP_client.c
${TOX_DIR}toxcore/TCP_client.h
${TOX_DIR}toxcore/TCP_common.c
${TOX_DIR}toxcore/TCP_common.h
${TOX_DIR}toxcore/TCP_connection.c
${TOX_DIR}toxcore/TCP_connection.h
${TOX_DIR}toxcore/TCP_server.c
${TOX_DIR}toxcore/TCP_server.h
${TOX_DIR}toxcore/timed_auth.c
${TOX_DIR}toxcore/timed_auth.h
${TOX_DIR}toxcore/tox_api.c
${TOX_DIR}toxcore/tox.c
${TOX_DIR}toxcore/tox_dispatch.c
${TOX_DIR}toxcore/tox_dispatch.h
${TOX_DIR}toxcore/tox_event.c
${TOX_DIR}toxcore/tox_event.h
${TOX_DIR}toxcore/tox_events.c
${TOX_DIR}toxcore/tox_events.h
${TOX_DIR}toxcore/tox.h
${TOX_DIR}toxcore/tox_private.c
${TOX_DIR}toxcore/tox_private.h
${TOX_DIR}toxcore/tox_pack.h
${TOX_DIR}toxcore/tox_pack.c
${TOX_DIR}toxcore/tox_unpack.c
${TOX_DIR}toxcore/tox_unpack.h
${TOX_DIR}toxcore/util.c
${TOX_DIR}toxcore/util.h
${TOX_DIR}toxencryptsave/defines.h
${TOX_DIR}toxencryptsave/toxencryptsave.c
${TOX_DIR}toxencryptsave/toxencryptsave.h
)
# HACK: "install" api headers into self
# this is dirty, should be binary dir
# TODO: add the others
configure_file(
${TOX_DIR}toxcore/tox.h
${TOX_DIR}tox/tox.h
@ONLY
)
configure_file(
${TOX_DIR}toxcore/tox_events.h
${TOX_DIR}tox/tox_events.h
@ONLY
)
configure_file(
${TOX_DIR}toxcore/tox_private.h
${TOX_DIR}tox/tox_private.h
@ONLY
)
target_include_directories(toxcore PRIVATE "${TOX_DIR}toxcore")
target_include_directories(toxcore PUBLIC "${TOX_DIR}")
target_compile_definitions(toxcore PUBLIC USE_IPV6=1)
#target_compile_definitions(toxcore PUBLIC MIN_LOGGER_LEVEL=LOGGER_LEVEL_DEBUG)
target_compile_definitions(toxcore PUBLIC MIN_LOGGER_LEVEL=LOGGER_LEVEL_INFO)
find_package(unofficial-sodium CONFIG QUIET)
find_package(sodium QUIET) find_package(sodium QUIET)
if(unofficial-sodium_FOUND) # vcpkg if(unofficial-sodium_FOUND) # vcpkg
if(TARGET unofficial-sodium::sodium) if(TARGET unofficial-sodium::sodium)
target_link_libraries(toxcore unofficial-sodium::sodium) target_link_libraries(toxcore INTERFACE unofficial-sodium::sodium)
endif() endif()
if(TARGET unofficial-sodium::sodium_config_public) if(TARGET unofficial-sodium::sodium_config_public)
target_link_libraries(toxcore unofficial-sodium::sodium_config_public) target_link_libraries(toxcore INTERFACE unofficial-sodium::sodium_config_public)
endif() endif()
elseif(sodium_FOUND) elseif(sodium_FOUND)
target_link_libraries(toxcore sodium) target_link_libraries(toxcore INTERFACE sodium)
else() else()
message(SEND_ERROR "missing libsodium") message(SEND_ERROR "missing libsodium")
endif() endif()
if(WIN32)
target_link_libraries(toxcore ws2_32 iphlpapi)
endif()
find_package(pthreads QUIET)
if(TARGET PThreads4W::PThreads4W)
target_link_libraries(toxcore PThreads4W::PThreads4W)
else()
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(toxcore Threads::Threads)
endif()
add_executable(DHT_Bootstrap EXCLUDE_FROM_ALL
${TOX_DIR}other/DHT_bootstrap.c
${TOX_DIR}other/bootstrap_node_packets.h
${TOX_DIR}other/bootstrap_node_packets.c
${TOX_DIR}testing/misc_tools.h
${TOX_DIR}testing/misc_tools.c
)
target_link_libraries(DHT_Bootstrap toxcore)

View File

@ -52,14 +52,16 @@ add_executable(tomato
./sdl_clipboard_utils.hpp ./sdl_clipboard_utils.hpp
./sdl_clipboard_utils.cpp ./sdl_clipboard_utils.cpp
./file_selector.hpp ./chat_gui/theme.hpp
./file_selector.cpp ./chat_gui/theme.cpp
./chat_gui/contact_list.hpp
./send_image_popup.hpp ./chat_gui/contact_list.cpp
./send_image_popup.cpp ./chat_gui/file_selector.hpp
./chat_gui/file_selector.cpp
./settings_window.hpp ./chat_gui/send_image_popup.hpp
./settings_window.cpp ./chat_gui/send_image_popup.cpp
./chat_gui/settings_window.hpp
./chat_gui/settings_window.cpp
./tox_ui_utils.hpp ./tox_ui_utils.hpp
./tox_ui_utils.cpp ./tox_ui_utils.cpp

View File

@ -0,0 +1,314 @@
#include "./contact_list.hpp"
#include <solanaceae/contact/components.hpp>
#include <imgui/imgui.h>
//#include <imgui/imgui_internal.h>
#include <array>
static void drawIconDirectLines(
const ImVec2 p0,
const ImVec2 p1_o,
const ImU32 col,
const float thickness
) {
#define PLINE(x0, y0, x1, y1) \
ImGui::GetWindowDrawList()->AddLine( \
{p0.x + p1_o.x*(x0), p0.y + p1_o.y*(y0)}, \
{p0.x + p1_o.x*(x1), p0.y + p1_o.y*(y1)}, \
col, \
thickness \
);
// arrow 1
// (3,1) -> (9,7)
PLINE(0.3f, 0.1f, 0.9f, 0.7f)
// (9,7) -> (9,5)
PLINE(0.9f, 0.7f, 0.9f, 0.5f)
// (9,7) -> (7,7)
PLINE(0.9f, 0.7f, 0.7f, 0.7f)
// arrow 2
// (7,9) -> (1,3)
PLINE(0.7f, 0.9f, 0.1f, 0.3f)
// (1,3) -> (3,3)
PLINE(0.1f, 0.3f, 0.3f, 0.3f)
// (1,3) -> (1,5)
PLINE(0.1f, 0.3f, 0.1f, 0.5f)
#undef PLINE
}
static void drawIconDirect(
const ImVec2 p0,
const ImVec2 p1_o,
const ImU32 col_main,
const ImU32 col_back
) {
// dark background
// the circle looks bad in light mode
//ImGui::GetWindowDrawList()->AddCircleFilled({p0.x + p1_o.x*0.5f, p0.y + p1_o.y*0.5f}, p1_o.x*0.5f, col_back);
drawIconDirectLines(p0, p1_o, col_back, 4.0f);
drawIconDirectLines(p0, p1_o, col_main, 1.5f);
}
static void drawIconCloud(
const ImVec2 p0,
const ImVec2 p1_o,
const ImU32 col_main,
const ImU32 col_back
) {
std::array<ImVec2, 19> points {{
{0.2f, 0.9f},
{0.8f, 0.9f},
{0.9f, 0.8f},
{0.9f, 0.7f},
{0.7f, 0.7f},
{0.9f, 0.5f},
{0.9f, 0.4f},
{0.8f, 0.2f},
{0.6f, 0.2f},
{0.5f, 0.3f},
{0.5f, 0.5f},
{0.4f, 0.4f},
{0.3f, 0.4f},
{0.2f, 0.5f},
{0.2f, 0.6f},
{0.3f, 0.7f},
{0.1f, 0.7f},
{0.1f, 0.8f},
{0.2f, 0.9f},
}};
for (auto& v : points) {
v.y -= 0.1f;
v = {p0.x + p1_o.x*v.x, p0.y + p1_o.y*v.y};
}
// the circle looks bad in light mode
//ImGui::GetWindowDrawList()->AddCircleFilled({p0.x + p1_o.x*0.5f, p0.y + p1_o.y*0.5f}, p1_o.x*0.5f, col_back);
ImGui::GetWindowDrawList()->AddPolyline(points.data(), points.size(), col_back, ImDrawFlags_None, 4.f);
ImGui::GetWindowDrawList()->AddPolyline(points.data(), points.size(), col_main, ImDrawFlags_None, 1.5f);
}
void renderAvatar(
const Theme& th,
ContactTextureCache& contact_tc,
const Contact3Handle c,
ImVec2 box
) {
// deploy dummy of same size and check visibility
const auto orig_curser_pos = ImGui::GetCursorPos();
ImGui::Dummy(box);
if (ImGui::IsItemVisible()) {
ImGui::SetCursorPos(orig_curser_pos); // reset for actual img
ImVec4 color_current = th.getColor<ThemeCol_Contact::avatar_offline>();
if (c.all_of<Contact::Components::ConnectionState>()) {
const auto c_state = c.get<Contact::Components::ConnectionState>().state;
if (c_state == Contact::Components::ConnectionState::State::direct) {
color_current = th.getColor<ThemeCol_Contact::avatar_online_direct>();
} else if (c_state == Contact::Components::ConnectionState::State::cloud) {
color_current = th.getColor<ThemeCol_Contact::avatar_online_cloud>();
}
}
// avatar
const auto [id, width, height] = contact_tc.get(c);
ImGui::Image(
id,
box,
{0, 0},
{1, 1},
{1, 1, 1, 1},
color_current
);
}
}
bool renderContactBig(
const Theme& th,
ContactTextureCache& contact_tc,
const Contact3Handle c,
int line_height,
const bool unread,
const bool selectable,
const bool selected
) {
ImGui::BeginGroup();
if (line_height < 1) {
line_height = 1;
}
// we dont need ### bc there is no named prefix
auto label = "##" + std::to_string(entt::to_integral(c.entity()));
const bool request_incoming = c.all_of<Contact::Components::RequestIncoming>();
const bool request_outgoing = c.all_of<Contact::Components::TagRequestOutgoing>();
ImVec2 orig_curser_pos = ImGui::GetCursorPos();
// HACK: fake selected to make it draw a box for us
const bool show_selected = request_incoming || request_outgoing || selected;
if (request_incoming) {
ImGui::PushStyleColor(
ImGuiCol_Header,
th.getColor<ThemeCol_Contact::request_incoming>()
);
} else if (request_outgoing) {
ImGui::PushStyleColor(
ImGuiCol_Header,
th.getColor<ThemeCol_Contact::request_outgoing>()
);
}
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
bool got_selected = false;
if (selectable) {
got_selected = ImGui::Selectable(label.c_str(), show_selected, ImGuiSelectableFlags_None, {0, line_height*TEXT_BASE_HEIGHT});
} else {
got_selected = ImGui::InvisibleButton(label.c_str(), {-FLT_MIN, line_height*TEXT_BASE_HEIGHT});
}
if (request_incoming || request_outgoing) {
ImGui::PopStyleColor();
}
const auto* cstate = c.try_get<Contact::Components::ConnectionState>();
if (ImGui::BeginItemTooltip()) {
if (cstate != nullptr) {
ImGui::Text("Connection state: %s",
(cstate->state == Contact::Components::ConnectionState::disconnected)
? "offline"
: (cstate->state == Contact::Components::ConnectionState::direct)
? "online (direct)"
: "online (cloud)"
);
} else {
ImGui::TextUnformatted("Connection state: unknown");
}
if (
const auto* slt = c.try_get<Contact::Components::StatusText>();
slt != nullptr &&
!slt->text.empty()
) {
ImGui::SeparatorText("Status Text");
//ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
ImGui::TextUnformatted(slt->text.c_str());
//ImGui::PopStyleColor();
}
// TODO: add a whole bunch more info
ImGui::EndTooltip();
}
ImVec2 post_curser_pos = ImGui::GetCursorPos();
ImVec2 img_curser {
orig_curser_pos.x + ImGui::GetStyle().FramePadding.x,
orig_curser_pos.y + ImGui::GetStyle().FramePadding.y
};
float img_y {TEXT_BASE_HEIGHT*line_height - ImGui::GetStyle().FramePadding.y*2};
ImGui::SetCursorPos(img_curser);
renderAvatar(th, contact_tc, c, {img_y, img_y});
const float same_line_spacing = ImGui::GetStyle().ItemSpacing.x*0.5f;
ImGui::SameLine(0.f, same_line_spacing);
ImGui::BeginGroup();
{
{ // line 1
if (line_height == 1 && cstate != nullptr) {
// icon pos
auto p0 = ImGui::GetCursorScreenPos();
p0.y += ImGui::GetStyle().FramePadding.y;
ImVec2 p1_o = {img_y, img_y}; // img_y is 1 line_height in this case
const ImU32 col_back = ImGui::GetColorU32(th.getColor<ThemeCol_Contact::icon_backdrop>());
if (cstate->state == Contact::Components::ConnectionState::direct) { // direct icon
drawIconDirect(
p0,
p1_o,
ImGui::GetColorU32(th.getColor<ThemeCol_Contact::avatar_online_direct>()),
col_back
);
} else if (cstate->state == Contact::Components::ConnectionState::cloud) { // cloud icon
drawIconCloud(
p0,
p1_o,
ImGui::GetColorU32(th.getColor<ThemeCol_Contact::avatar_online_cloud>()),
col_back
);
}
ImGui::Dummy(p1_o);
ImGui::SameLine(0.f, same_line_spacing);
}
ImGui::Text("%s%s", unread?"* ":"", (c.all_of<Contact::Components::Name>() ? c.get<Contact::Components::Name>().name.c_str() : "<unk>"));
}
// line 2
if (line_height >= 2) {
if (request_incoming) {
ImGui::TextUnformatted("Incoming request/invite");
} else if (request_outgoing) {
ImGui::TextUnformatted("Outgoing request/invite");
} else {
if (cstate != nullptr) {
// icon pos
auto p0 = ImGui::GetCursorScreenPos();
p0.y += ImGui::GetStyle().FramePadding.y;
const float box_hight = TEXT_BASE_HEIGHT - ImGui::GetStyle().FramePadding.y*2;
ImVec2 p1_o = {box_hight, box_hight};
const ImU32 col_back = ImGui::GetColorU32(th.getColor<ThemeCol_Contact::icon_backdrop>());
if (cstate->state == Contact::Components::ConnectionState::direct) { // direct icon
drawIconDirect(
p0,
p1_o,
ImGui::GetColorU32(th.getColor<ThemeCol_Contact::avatar_online_direct>()),
col_back
);
} else if (cstate->state == Contact::Components::ConnectionState::cloud) { // cloud icon
drawIconCloud(
p0,
p1_o,
ImGui::GetColorU32(th.getColor<ThemeCol_Contact::avatar_online_cloud>()),
col_back
);
}
ImGui::Dummy(p1_o);
ImGui::SameLine(0.f, same_line_spacing);
}
if (
const auto* slt = c.try_get<Contact::Components::StatusText>();
slt != nullptr &&
!slt->text.empty() &&
slt->first_line_length > 0 &&
slt->first_line_length <= slt->text.size()
) {
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
ImGui::TextUnformatted(slt->text.c_str(), slt->text.c_str() + slt->first_line_length);
ImGui::PopStyleColor();
} else {
ImGui::TextDisabled(""); // or dummy?
}
}
// line 3
//if (line_height >= 3) {
// constexpr std::string_view test_text{"text"};
// ImGui::RenderTextEllipsis(ImGui::GetWindowDrawList(), ImVec2{}, ImVec2{}, 1.f, 1.f, test_text.data(), test_text.data()+test_text.size(), nullptr);
//}
}
}
ImGui::EndGroup();
ImGui::SetCursorPos(post_curser_pos);
ImGui::EndGroup();
return got_selected;
}

View File

@ -0,0 +1,43 @@
#pragma once
#include "./texture_cache_defs.hpp"
#include "./theme.hpp"
#include <solanaceae/contact/contact_model3.hpp>
enum class ThemeCol_Contact {
request_incoming,
request_outgoing,
avatar_online_direct,
avatar_online_cloud,
avatar_offline,
icon_backdrop,
};
void renderAvatar(
const Theme& th,
ContactTextureCache& contact_tc,
const Contact3Handle c,
ImVec2 box
);
// returns true if clicked, if selectable, will highlight on hover and respect selected
// TODO: refine
// +------+
// | | *Name (Alias?)
// |Avatar| Satus Message <-- richpresence interface?
// | | user status (online/away/busy)-direct/relayed / offline <-- last text?
// +------+
bool renderContactBig(
const Theme& th,
ContactTextureCache& contact_tc,
const Contact3Handle c,
int line_height = 3,
const bool unread = false,
const bool selectable = false,
const bool selected = false
);

View File

@ -1,10 +1,10 @@
#include "./send_image_popup.hpp" #include "./send_image_popup.hpp"
#include "./image_loader_sdl_bmp.hpp" #include "../image_loader_sdl_bmp.hpp"
#include "./image_loader_stb.hpp" #include "../image_loader_stb.hpp"
#include "./image_loader_webp.hpp" #include "../image_loader_webp.hpp"
#include "./image_loader_qoi.hpp" #include "../image_loader_qoi.hpp"
#include "./image_loader_sdl_image.hpp" #include "../image_loader_sdl_image.hpp"
#include <imgui/imgui.h> #include <imgui/imgui.h>

View File

@ -5,8 +5,8 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "./image_loader.hpp" #include "../image_loader.hpp"
#include "./texture_cache.hpp" #include "../texture_cache.hpp"
struct SendImagePopup { struct SendImagePopup {
TextureUploaderI& _tu; TextureUploaderI& _tu;

View File

@ -0,0 +1,12 @@
#pragma once
#include <solanaceae/message3/registry_message_model.hpp>
#include "../texture_cache.hpp"
#include "../tox_avatar_loader.hpp"
#include "../message_image_loader.hpp"
using ContactTextureCache = TextureCache<void*, Contact3, ToxAvatarLoader>;
using MessageTextureCache = TextureCache<void*, Message3Handle, MessageImageLoader>;

62
src/chat_gui/theme.cpp Normal file
View File

@ -0,0 +1,62 @@
#include "./theme.hpp"
// HACK: includes everything and sets theme defaults
#include "./contact_list.hpp"
//#include <iostream>
//enum class TestThemeSet {
//Value1,
//};
//// specialization
////template<>
////std::string typeValueName(TestThemeSet v) {
////switch (v) {
////case TestThemeSet::Value1: return "Value1";
////default: return "unk";
////}
////}
Theme::Theme(void) {
load();
}
void Theme::update(void) {
}
bool Theme::load(void) {
name = "Default";
//setColor<TestThemeSet::Value1>(ImVec4{});
//std::cout << "test value name: " << getColorName<TestThemeSet::Value1>() << "\n";
return true;
}
bool Theme::store(void) {
return true;
}
Theme getDefaultThemeDark(void) {
Theme t;
t.setColor<ThemeCol_Contact::request_incoming >({0.98f, 0.41f, 0.26f, 0.52f});
t.setColor<ThemeCol_Contact::request_outgoing >({0.98f, 0.26f, 0.41f, 0.52f});
t.setColor<ThemeCol_Contact::avatar_online_direct >({0.3f, 1.0f, 0.0f, 1.0f});
t.setColor<ThemeCol_Contact::avatar_online_cloud >({0.0f, 1.0f, 0.8f, 1.0f});
t.setColor<ThemeCol_Contact::avatar_offline >({0.4f, 0.4f, 0.4f, 1.0f});
t.setColor<ThemeCol_Contact::icon_backdrop >({0.0f, 0.0f, 0.0f, 0.4f});
return t;
}
Theme getDefaultThemeLight(void) {
// HACK: inherit dark and only diff
Theme t = getDefaultThemeDark();
return t;
}

76
src/chat_gui/theme.hpp Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include <imgui/imgui.h>
#include <entt/container/dense_map.hpp>
#include <entt/core/type_info.hpp>
#include <string>
#include <type_traits>
// default is resolving to its value
template<typename T>
std::string typeValueName(T V) {
return std::to_string(static_cast<std::underlying_type_t<T>>(V));
}
// stores theming values and colors not expressed by imgui directly
struct Theme {
using key_type = entt::id_type;
entt::dense_map<key_type, ImVec4> colors;
entt::dense_map<key_type, std::string> colors_name;
// TODO: spec out dependencies
// TODO: what for
entt::dense_map<key_type, float> single_values;
std::string name; // theme name
Theme(void);
// call when any color changed, so dependencies can be resolved
void update(void);
template<auto V>
void setColor(ImVec4 color) {
constexpr auto key = entt::type_hash<entt::tag<static_cast<entt::id_type>(V)>>::value();
colors[key] = color;
if (!colors_name.contains(key)) {
std::string key_name = static_cast<std::string>(
entt::type_name<decltype(V)>::value()
) + ":" + typeValueName(V);
colors_name[key] = key_name;
}
}
template<auto V>
ImVec4 getColor(void) const {
constexpr auto key = entt::type_hash<entt::tag<static_cast<entt::id_type>(V)>>::value();
const auto it = colors.find(key);
if (it != colors.end()) {
return it->second;
} else {
return {}; // TODO: pink as default?
}
}
template<auto V>
std::string_view getColorName(void) const {
constexpr auto key = entt::type_hash<entt::tag<static_cast<entt::id_type>(V)>>::value();
if (colors_name.contains(key)) {
return colors_name.at(key);
} else {
return "unk";
}
}
// TODO: actually serialize from config?
bool load(void);
bool store(void);
};
Theme getDefaultThemeDark(void);
Theme getDefaultThemeLight(void);

View File

@ -1,7 +1,5 @@
#include "./chat_gui4.hpp" #include "./chat_gui4.hpp"
#include "./file_selector.hpp"
#include <solanaceae/message3/components.hpp> #include <solanaceae/message3/components.hpp>
#include <solanaceae/tox_messages/components.hpp> #include <solanaceae/tox_messages/components.hpp>
#include <solanaceae/contact/components.hpp> #include <solanaceae/contact/components.hpp>
@ -15,6 +13,8 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include "./chat_gui/contact_list.hpp"
#include "./media_meta_info_loader.hpp" #include "./media_meta_info_loader.hpp"
#include "./sdl_clipboard_utils.hpp" #include "./sdl_clipboard_utils.hpp"
@ -145,8 +145,9 @@ ChatGui4::ChatGui4(
Contact3Registry& cr, Contact3Registry& cr,
TextureUploaderI& tu, TextureUploaderI& tu,
ContactTextureCache& contact_tc, ContactTextureCache& contact_tc,
MessageTextureCache& msg_tc MessageTextureCache& msg_tc,
) : _conf(conf), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _sip(tu) { Theme& theme
) : _conf(conf), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _theme(theme), _sip(tu) {
} }
ChatGui4::~ChatGui4(void) { ChatGui4::~ChatGui4(void) {
@ -197,16 +198,42 @@ float ChatGui4::render(float time_delta) {
sub_contacts = &_cr.get<Contact::Components::ParentOf>(*_selected_contact).subs; sub_contacts = &_cr.get<Contact::Components::ParentOf>(*_selected_contact).subs;
} }
if (ImGui::BeginChild(chat_label.c_str(), {0, 0}, true)) { const bool highlight_private {!_cr.all_of<Contact::Components::TagPrivate>(*_selected_contact)};
//if (_cr.all_of<Contact::Components::ParentOf>(*_selected_contact)) {
if (sub_contacts != nullptr) { if (ImGui::BeginChild(chat_label.c_str(), {0, 0}, ImGuiChildFlags_Border, ImGuiWindowFlags_MenuBar)) {
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("debug")) {
ImGui::Checkbox("show extra info", &_show_chat_extra_info);
ImGui::Checkbox("show avatar transfers", &_show_chat_avatar_tf);
ImGui::SeparatorText("tox");
// TODO: cheese it and rename to copy id?
if (_cr.all_of<Contact::Components::ToxGroupPersistent>(*_selected_contact)) {
if (ImGui::MenuItem("copy ngc chatid")) {
const auto& chat_id = _cr.get<Contact::Components::ToxGroupPersistent>(*_selected_contact).chat_id.data;
const auto chat_id_str = bin2hex(std::vector<uint8_t>{chat_id.begin(), chat_id.end()});
ImGui::SetClipboardText(chat_id_str.c_str());
}
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
renderContactBig(_theme, _contact_tc, {_cr, *_selected_contact}, 3, false, false, false);
ImGui::Separator();
if (sub_contacts != nullptr && !_cr.all_of<Contact::Components::TagPrivate>(*_selected_contact) && _cr.all_of<Contact::Components::TagGroup>(*_selected_contact)) {
if (!sub_contacts->empty()) { if (!sub_contacts->empty()) {
if (ImGui::BeginChild("subcontacts", {150, -100}, true)) { if (ImGui::BeginChild("subcontacts", {TEXT_BASE_WIDTH * 18.f, -100.f}, true)) {
ImGui::Text("subs: %zu", sub_contacts->size()); ImGui::Text("subs: %zu", sub_contacts->size());
ImGui::Separator(); ImGui::Separator();
for (const auto& c : *sub_contacts) { for (const auto& c : *sub_contacts) {
// TODO: can a sub be selected? no // TODO: can a sub be selected? no
if (renderSubContactListContact(c, _selected_contact.has_value() && *_selected_contact == c)) { //if (renderSubContactListContact(c, _selected_contact.has_value() && *_selected_contact == c)) {
if (renderContactBig(_theme, _contact_tc, {_cr, c}, 1)) {
_text_input_buffer.insert(0, (_cr.all_of<Contact::Components::Name>(c) ? _cr.get<Contact::Components::Name>(c).name : "<unk>") + ": "); _text_input_buffer.insert(0, (_cr.all_of<Contact::Components::Name>(c) ? _cr.get<Contact::Components::Name>(c).name : "<unk>") + ": ");
} }
} }
@ -257,27 +284,11 @@ float ChatGui4::render(float time_delta) {
ImGui::EndChild(); ImGui::EndChild();
} }
if (ImGui::BeginChild("message_log", {0, -100}, false, ImGuiWindowFlags_MenuBar)) { if (ImGui::BeginChild("message_log", {0, -100}, ImGuiChildFlags_None)) {
if (ImGui::BeginMenuBar()) { // TODO: background image?
if (ImGui::BeginMenu("debug")) { //auto p_min = ImGui::GetCursorScreenPos();
ImGui::Checkbox("show extra info", &_show_chat_extra_info); //auto a_max = ImGui::GetContentRegionAvail();
ImGui::Checkbox("show avatar transfers", &_show_chat_avatar_tf); //ImGui::GetWindowDrawList()->AddImage(0, p_min, {p_min.x+a_max.x, p_min.y+a_max.y});
ImGui::SeparatorText("tox");
// TODO: cheese it and rename to copy id?
if (_cr.all_of<Contact::Components::ToxGroupPersistent>(*_selected_contact)) {
if (ImGui::MenuItem("copy ngc chatid")) {
const auto& chat_id = _cr.get<Contact::Components::ToxGroupPersistent>(*_selected_contact).chat_id.data;
const auto chat_id_str = bin2hex(std::vector<uint8_t>{chat_id.begin(), chat_id.end()});
ImGui::SetClipboardText(chat_id_str.c_str());
}
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
auto* msg_reg_ptr = _rmm.get(*_selected_contact); auto* msg_reg_ptr = _rmm.get(*_selected_contact);
@ -287,7 +298,7 @@ float ChatGui4::render(float time_delta) {
ImGuiTableFlags_SizingFixedFit ImGuiTableFlags_SizingFixedFit
; ;
if (msg_reg_ptr != nullptr && ImGui::BeginTable("chat_table", 5, table_flags)) { if (msg_reg_ptr != nullptr && ImGui::BeginTable("chat_table", 5, table_flags)) {
ImGui::TableSetupColumn("name", 0, TEXT_BASE_WIDTH * 15.f); ImGui::TableSetupColumn("name", 0, TEXT_BASE_WIDTH * 16.f);
ImGui::TableSetupColumn("message", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("message", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("delivered/read"); ImGui::TableSetupColumn("delivered/read");
ImGui::TableSetupColumn("timestamp"); ImGui::TableSetupColumn("timestamp");
@ -315,7 +326,7 @@ float ChatGui4::render(float time_delta) {
msg_reg.view<Components::UnreadFade>().each([&to_remove, time_delta](const Message3 e, Components::UnreadFade& fade) { msg_reg.view<Components::UnreadFade>().each([&to_remove, time_delta](const Message3 e, Components::UnreadFade& fade) {
// TODO: configurable // TODO: configurable
const float fade_duration = 7.5f; const float fade_duration = 7.5f;
fade.fade -= 1.f/fade_duration * std::min<float>(time_delta, 1.f/10.f); // fps but not below 10 for smooth fade fade.fade -= 1.f/fade_duration * std::min<float>(time_delta, 1.f/8.f); // fps but not below 8 for smooth-ish fade
if (fade.fade <= 0.f) { if (fade.fade <= 0.f) {
to_remove.push_back(e); to_remove.push_back(e);
} }
@ -375,6 +386,10 @@ float ChatGui4::render(float time_delta) {
// name // name
if (ImGui::TableNextColumn()) { if (ImGui::TableNextColumn()) {
const float img_y {TEXT_BASE_HEIGHT - ImGui::GetStyle().FramePadding.y*2};
renderAvatar(_theme, _contact_tc, {_cr, c_from.c}, {img_y, img_y});
ImGui::SameLine(0.f, ImGui::GetStyle().ItemSpacing.x*0.5f);
if (_cr.all_of<Contact::Components::Name>(c_from.c)) { if (_cr.all_of<Contact::Components::Name>(c_from.c)) {
ImGui::TextUnformatted(_cr.get<Contact::Components::Name>(c_from.c).name.c_str()); ImGui::TextUnformatted(_cr.get<Contact::Components::Name>(c_from.c).name.c_str());
} else { } else {
@ -417,7 +432,7 @@ float ChatGui4::render(float time_delta) {
std::optional<ImVec4> row_bg; std::optional<ImVec4> row_bg;
// private group message // private group message
if (_cr.any_of<Contact::Components::TagSelfWeak, Contact::Components::TagSelfStrong>(c_to.c)) { if (highlight_private && _cr.any_of<Contact::Components::TagSelfWeak, Contact::Components::TagSelfStrong>(c_to.c)) {
const ImVec4 priv_msg_hi_col = ImVec4(0.5f, 0.2f, 0.5f, 0.35f); const ImVec4 priv_msg_hi_col = ImVec4(0.5f, 0.2f, 0.5f, 0.35f);
ImU32 row_bg_color = ImGui::GetColorU32(priv_msg_hi_col); ImU32 row_bg_color = ImGui::GetColorU32(priv_msg_hi_col);
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, row_bg_color); ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, row_bg_color);
@ -456,8 +471,11 @@ float ChatGui4::render(float time_delta) {
if (ImGui::TableNextColumn()) { if (ImGui::TableNextColumn()) {
// TODO: theming for hardcoded values // TODO: theming for hardcoded values
if (msg_reg.all_of<Message::Components::Remote::TimestampReceived>(e)) { if (!msg_reg.all_of<Message::Components::ReceivedBy>(e)) {
const auto list = msg_reg.get<Message::Components::Remote::TimestampReceived>(e).ts; // TODO: dedup?
ImGui::TextDisabled("_");
} else {
const auto list = msg_reg.get<Message::Components::ReceivedBy>(e).ts;
// wrongly assumes contacts never get removed from a group // wrongly assumes contacts never get removed from a group
if (sub_contacts != nullptr && list.size() < sub_contacts->size()) { if (sub_contacts != nullptr && list.size() < sub_contacts->size()) {
// if partically delivered // if partically delivered
@ -471,6 +489,7 @@ float ChatGui4::render(float time_delta) {
std::string synced_by_text {"delivery confirmed by:"}; std::string synced_by_text {"delivery confirmed by:"};
const int64_t now_ts_s = int64_t(Message::getTimeMS() / 1000u); const int64_t now_ts_s = int64_t(Message::getTimeMS() / 1000u);
size_t other_contacts {0};
for (const auto& [c, syned_ts] : list) { for (const auto& [c, syned_ts] : list) {
if (_cr.all_of<Contact::Components::TagSelfStrong>(c)) { if (_cr.all_of<Contact::Components::TagSelfStrong>(c)) {
//synced_by_text += "\n sself(!)"; // makes no sense //synced_by_text += "\n sself(!)"; // makes no sense
@ -480,23 +499,26 @@ float ChatGui4::render(float time_delta) {
} else { } else {
synced_by_text += "\n >" + (_cr.all_of<Contact::Components::Name>(c) ? _cr.get<Contact::Components::Name>(c).name : "<unk>"); synced_by_text += "\n >" + (_cr.all_of<Contact::Components::Name>(c) ? _cr.get<Contact::Components::Name>(c).name : "<unk>");
} }
other_contacts += 1;
const int64_t seconds_ago = (int64_t(syned_ts / 1000u) - now_ts_s) * -1; const int64_t seconds_ago = (int64_t(syned_ts / 1000u) - now_ts_s) * -1;
synced_by_text += " (" + std::to_string(seconds_ago) + "sec ago)"; synced_by_text += " (" + std::to_string(seconds_ago) + "sec ago)";
} }
ImGui::Text("%s", synced_by_text.c_str()); if (other_contacts > 0) {
ImGui::Text("%s", synced_by_text.c_str());
} else {
ImGui::TextUnformatted("no delivery confirmation");
}
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
} else {
ImGui::TextDisabled("_");
} }
ImGui::SameLine(); ImGui::SameLine();
// TODO: dedup // TODO: dedup
if (msg_reg.all_of<Message::Components::Remote::TimestampRead>(e)) { if (msg_reg.all_of<Message::Components::ReadBy>(e)) {
const auto list = msg_reg.get<Message::Components::Remote::TimestampRead>(e).ts; const auto list = msg_reg.get<Message::Components::ReadBy>(e).ts;
// wrongly assumes contacts never get removed from a group // wrongly assumes contacts never get removed from a group
if (sub_contacts != nullptr && list.size() < sub_contacts->size()) { if (sub_contacts != nullptr && list.size() < sub_contacts->size()) {
// if partially read // if partially read
@ -1045,13 +1067,13 @@ void ChatGui4::renderMessageExtra(Message3Registry& reg, const Message3 e) {
} }
// TODO: remove? // TODO: remove?
if (reg.all_of<Message::Components::Remote::TimestampReceived>(e)) { if (reg.all_of<Message::Components::ReceivedBy>(e)) {
std::string synced_by_text {"receivedBy:"}; std::string synced_by_text {"receivedBy:"};
const int64_t now_ts_s = int64_t(Message::getTimeMS() / 1000u); const int64_t now_ts_s = int64_t(Message::getTimeMS() / 1000u);
for (const auto& [c, syned_ts] : reg.get<Message::Components::Remote::TimestampReceived>(e).ts) { for (const auto& [c, syned_ts] : reg.get<Message::Components::ReceivedBy>(e).ts) {
if (_cr.all_of<Contact::Components::TagSelfStrong>(c)) { if (_cr.all_of<Contact::Components::TagSelfStrong>(c)) {
synced_by_text += "\n sself(!)"; // makes no sense synced_by_text += "\n sself"; // required (except when synced externally)
} else if (_cr.all_of<Contact::Components::TagSelfWeak>(c)) { } else if (_cr.all_of<Contact::Components::TagSelfWeak>(c)) {
synced_by_text += "\n wself"; synced_by_text += "\n wself";
} else { } else {
@ -1069,7 +1091,18 @@ void ChatGui4::renderContactList(void) {
if (ImGui::BeginChild("contacts", {TEXT_BASE_WIDTH*35, 0})) { if (ImGui::BeginChild("contacts", {TEXT_BASE_WIDTH*35, 0})) {
//for (const auto& c : _cm.getBigContacts()) { //for (const auto& c : _cm.getBigContacts()) {
for (const auto& c : _cr.view<Contact::Components::TagBig>()) { for (const auto& c : _cr.view<Contact::Components::TagBig>()) {
if (renderContactListContactBig(c, _selected_contact.has_value() && *_selected_contact == c)) { const bool selected = _selected_contact.has_value() && *_selected_contact == c;
// TODO: is there a better way?
// maybe cache mm?
bool has_unread = false;
if (const auto* mm = _rmm.get(c); mm != nullptr) {
if (const auto* unread_storage = mm->storage<Message::Components::TagUnread>(); unread_storage != nullptr && !unread_storage->empty()) {
has_unread = true;
}
}
if (renderContactBig(_theme, _contact_tc, {_cr, c}, 2, has_unread, true, selected)) {
_selected_contact = c; _selected_contact = c;
} }
} }
@ -1077,121 +1110,6 @@ void ChatGui4::renderContactList(void) {
ImGui::EndChild(); ImGui::EndChild();
} }
bool ChatGui4::renderContactListContactBig(const Contact3 c, const bool selected) {
// TODO:
// - unread message
// - avatar img
// - connection status
// - user status
// - status message
// - context menu n shit?
// +------+
// | | *Name (Alias?)
// |Avatar| Satus Message
// | | user status (online/away/busy)-direct/relayed / offline
// +------+
auto label = "###" + std::to_string(entt::to_integral(c));
const bool request_incoming = _cr.all_of<Contact::Components::RequestIncoming>(c);
const bool request_outgoing = _cr.all_of<Contact::Components::TagRequestOutgoing>(c);
ImVec2 orig_curser_pos = ImGui::GetCursorPos();
// HACK: fake selected to make it draw a box for us
const bool show_selected = request_incoming || request_outgoing || selected;
if (request_incoming) {
// TODO: theming
ImGui::PushStyleColor(ImGuiCol_Header, {0.98f, 0.41f, 0.26f, 0.52f});
} else if (request_outgoing) {
// TODO: theming
ImGui::PushStyleColor(ImGuiCol_Header, {0.98f, 0.26f, 0.41f, 0.52f});
}
const bool got_selected = ImGui::Selectable(label.c_str(), show_selected, 0, {0,3*TEXT_BASE_HEIGHT});
if (request_incoming || request_outgoing) {
ImGui::PopStyleColor();
}
ImVec2 post_curser_pos = ImGui::GetCursorPos();
ImVec2 img_curser {
orig_curser_pos.x + ImGui::GetStyle().FramePadding.x,
orig_curser_pos.y + ImGui::GetStyle().FramePadding.y
};
float img_y {
//(post_curser_pos.y - orig_curser_pos.y) - ImGui::GetStyle().FramePadding.y*2
TEXT_BASE_HEIGHT*3 - ImGui::GetStyle().FramePadding.y*2
};
ImGui::SetCursorPos(img_curser);
const ImVec4 color_online_direct{0.3, 1, 0, 1};
const ImVec4 color_online_cloud{0, 1, 0.8, 1};
const ImVec4 color_offline{0.4, 0.4, 0.4, 1};
ImVec4 color_current = color_offline;
if (_cr.all_of<Contact::Components::ConnectionState>(c)) {
const auto c_state = _cr.get<Contact::Components::ConnectionState>(c).state;
if (c_state == Contact::Components::ConnectionState::State::direct) {
color_current = color_online_direct;
} else if (c_state == Contact::Components::ConnectionState::State::cloud) {
color_current = color_online_cloud;
}
}
// avatar
const auto [id, width, height] = _contact_tc.get(c);
ImGui::Image(
id,
ImVec2{img_y, img_y},
{0, 0},
{1, 1},
{1, 1, 1, 1},
color_current
);
// TODO: move this out of chat gui
any_unread = false;
ImGui::SameLine();
ImGui::BeginGroup();
{
// TODO: is there a better way?
// maybe cache mm?
bool has_unread = false;
if (const auto* mm = _rmm.get(c); mm != nullptr) {
if (const auto* unread_storage = mm->storage<Message::Components::TagUnread>(); unread_storage != nullptr && !unread_storage->empty()) {
#if 0
assert(unread_storage.size() == 0);
assert(unread_storage.cbegin() == unread_storage.cend());
std::cout << "UNREAD ";
for (const auto e : mm->view<Message::Components::TagUnread>()) {
std::cout << entt::to_integral(e) << " ";
}
std::cout << "\n";
#endif
has_unread = true;
any_unread = true;
}
}
ImGui::Text("%s%s", has_unread?"* ":"", (_cr.all_of<Contact::Components::Name>(c) ? _cr.get<Contact::Components::Name>(c).name.c_str() : "<unk>"));
if (request_incoming) {
ImGui::TextUnformatted("Incoming request/invite");
} else if (request_outgoing) {
ImGui::TextUnformatted("Outgoing request/invite");
}
//ImGui::Text("status message...");
//ImGui::TextDisabled("hi");
//ImGui::RenderTextEllipsis
}
ImGui::EndGroup();
ImGui::SetCursorPos(post_curser_pos);
return got_selected;
}
bool ChatGui4::renderContactListContactSmall(const Contact3 c, const bool selected) const { bool ChatGui4::renderContactListContactSmall(const Contact3 c, const bool selected) const {
std::string label; std::string label;
@ -1202,6 +1120,7 @@ bool ChatGui4::renderContactListContactSmall(const Contact3 c, const bool select
return ImGui::Selectable(label.c_str(), selected); return ImGui::Selectable(label.c_str(), selected);
} }
#if 0
bool ChatGui4::renderSubContactListContact(const Contact3 c, const bool selected) const { bool ChatGui4::renderSubContactListContact(const Contact3 c, const bool selected) const {
std::string label; std::string label;
@ -1224,6 +1143,7 @@ bool ChatGui4::renderSubContactListContact(const Contact3 c, const bool selected
return ImGui::Selectable(label.c_str(), selected); return ImGui::Selectable(label.c_str(), selected);
} }
#endif
void ChatGui4::pasteFile(const char* mime_type) { void ChatGui4::pasteFile(const char* mime_type) {
size_t data_size = 0; size_t data_size = 0;

View File

@ -3,12 +3,14 @@
#include <solanaceae/message3/registry_message_model.hpp> #include <solanaceae/message3/registry_message_model.hpp>
#include <solanaceae/util/config_model.hpp> #include <solanaceae/util/config_model.hpp>
#include "./chat_gui/theme.hpp"
#include "./texture_uploader.hpp" #include "./texture_uploader.hpp"
#include "./texture_cache.hpp" #include "./texture_cache.hpp"
#include "./tox_avatar_loader.hpp" #include "./tox_avatar_loader.hpp"
#include "./message_image_loader.hpp" #include "./message_image_loader.hpp"
#include "./file_selector.hpp" #include "./chat_gui/file_selector.hpp"
#include "./send_image_popup.hpp" #include "./chat_gui/send_image_popup.hpp"
#include <entt/container/dense_map.hpp> #include <entt/container/dense_map.hpp>
@ -29,6 +31,8 @@ class ChatGui4 {
ContactTextureCache& _contact_tc; ContactTextureCache& _contact_tc;
MessageTextureCache& _msg_tc; MessageTextureCache& _msg_tc;
Theme& _theme;
FileSelector _fss; FileSelector _fss;
SendImagePopup _sip; SendImagePopup _sip;
@ -57,7 +61,8 @@ class ChatGui4 {
Contact3Registry& cr, Contact3Registry& cr,
TextureUploaderI& tu, TextureUploaderI& tu,
ContactTextureCache& contact_tc, ContactTextureCache& contact_tc,
MessageTextureCache& msg_tc MessageTextureCache& msg_tc,
Theme& theme
); );
~ChatGui4(void); ~ChatGui4(void);
@ -65,8 +70,6 @@ class ChatGui4 {
float render(float time_delta); float render(float time_delta);
public: public:
bool any_unread {false};
void sendFilePath(const char* file_path); void sendFilePath(const char* file_path);
private: private:
@ -75,9 +78,8 @@ class ChatGui4 {
void renderMessageExtra(Message3Registry& reg, const Message3 e); void renderMessageExtra(Message3Registry& reg, const Message3 e);
void renderContactList(void); void renderContactList(void);
bool renderContactListContactBig(const Contact3 c, const bool selected);
bool renderContactListContactSmall(const Contact3 c, const bool selected) const; bool renderContactListContactSmall(const Contact3 c, const bool selected) const;
bool renderSubContactListContact(const Contact3 c, const bool selected) const; //bool renderSubContactListContact(const Contact3 c, const bool selected) const;
void pasteFile(const char* mime_type); void pasteFile(const char* mime_type);
}; };

View File

@ -6,6 +6,7 @@
#include <imgui/backends/imgui_impl_sdlrenderer3.h> #include <imgui/backends/imgui_impl_sdlrenderer3.h>
#include "./theme.hpp" #include "./theme.hpp"
#include "./chat_gui/theme.hpp"
#include "./start_screen.hpp" #include "./start_screen.hpp"
@ -58,11 +59,14 @@ int main(int argc, char** argv) {
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
Theme theme;
if (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_LIGHT) { if (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_LIGHT) {
ImGui::StyleColorsLight(); ImGui::StyleColorsLight();
theme = getDefaultThemeLight();
} else { } else {
//ImGui::StyleColorsDark(); //ImGui::StyleColorsDark();
setThemeGreen(); setThemeGreen();
theme = getDefaultThemeDark();
} }
{ {
@ -85,7 +89,7 @@ int main(int argc, char** argv) {
ImGui_ImplSDL3_InitForSDLRenderer(window.get(), renderer.get()); ImGui_ImplSDL3_InitForSDLRenderer(window.get(), renderer.get());
ImGui_ImplSDLRenderer3_Init(renderer.get()); ImGui_ImplSDLRenderer3_Init(renderer.get());
std::unique_ptr<Screen> screen = std::make_unique<StartScreen>(renderer.get()); std::unique_ptr<Screen> screen = std::make_unique<StartScreen>(renderer.get(), theme);
bool quit = false; bool quit = false;

View File

@ -12,7 +12,7 @@
#include <memory> #include <memory>
#include <cmath> #include <cmath>
MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins) : MainScreen::MainScreen(SDL_Renderer* renderer_, Theme& theme_, std::string save_path, std::string save_password, std::string new_username, std::vector<std::string> plugins) :
renderer(renderer_), renderer(renderer_),
rmm(cr), rmm(cr),
msnj{cr, {}, {}}, msnj{cr, {}, {}},
@ -24,6 +24,7 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
tmm(rmm, cr, tcm, tc, tc), tmm(rmm, cr, tcm, tc, tc),
ttm(rmm, cr, tcm, tc, tc), ttm(rmm, cr, tcm, tc, tc),
tffom(cr, rmm, tcm, tc, tc), tffom(cr, rmm, tcm, tc, tc),
theme(theme_),
mmil(rmm), mmil(rmm),
tam(rmm, cr, conf), tam(rmm, cr, conf),
sdlrtu(renderer_), sdlrtu(renderer_),
@ -31,7 +32,7 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
contact_tc(tal, sdlrtu), contact_tc(tal, sdlrtu),
mil(), mil(),
msg_tc(mil, sdlrtu), msg_tc(mil, sdlrtu),
cg(conf, rmm, cr, sdlrtu, contact_tc, msg_tc), cg(conf, rmm, cr, sdlrtu, contact_tc, msg_tc, theme),
sw(conf), sw(conf),
tuiu(tc, conf), tuiu(tc, conf),
tdch(tpi) tdch(tpi)
@ -44,9 +45,10 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
conf.set("tox", "save_file_path", save_path); conf.set("tox", "save_file_path", save_path);
{ // name stuff { // name stuff
// a new profile will not have this set
auto name = tc.toxSelfGetName(); auto name = tc.toxSelfGetName();
if (name.empty()) { if (name.empty()) {
name = "tomato"; name = new_username;
} }
conf.set("tox", "name", name); conf.set("tox", "name", name);
tc.setSelfName(name); // TODO: this is ugly tc.setSelfName(name); // TODO: this is ugly

View File

@ -28,7 +28,7 @@
#include "./message_image_loader.hpp" #include "./message_image_loader.hpp"
#include "./chat_gui4.hpp" #include "./chat_gui4.hpp"
#include "./settings_window.hpp" #include "./chat_gui/settings_window.hpp"
#include "./tox_ui_utils.hpp" #include "./tox_ui_utils.hpp"
#include "./tox_dht_cap_histo.hpp" #include "./tox_dht_cap_histo.hpp"
#include "./tox_friend_faux_offline_messaging.hpp" #include "./tox_friend_faux_offline_messaging.hpp"
@ -62,6 +62,8 @@ struct MainScreen final : public Screen {
ToxTransferManager ttm; ToxTransferManager ttm;
ToxFriendFauxOfflineMessaging tffom; ToxFriendFauxOfflineMessaging tffom;
Theme& theme;
MediaMetaInfoLoader mmil; MediaMetaInfoLoader mmil;
ToxAvatarManager tam; ToxAvatarManager tam;
@ -89,7 +91,7 @@ struct MainScreen final : public Screen {
uint64_t _window_hidden_ts {0}; uint64_t _window_hidden_ts {0};
float _time_since_event {0.f}; float _time_since_event {0.f};
MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins); MainScreen(SDL_Renderer* renderer_, Theme& theme_, std::string save_path, std::string save_password, std::string new_username, std::vector<std::string> plugins);
~MainScreen(void); ~MainScreen(void);
bool handleEvent(SDL_Event& e) override; bool handleEvent(SDL_Event& e) override;

View File

@ -5,10 +5,11 @@
#include <imgui/imgui.h> #include <imgui/imgui.h>
#include <imgui/misc/cpp/imgui_stdlib.h> #include <imgui/misc/cpp/imgui_stdlib.h>
#include <cctype>
#include <memory> #include <memory>
#include <filesystem> #include <filesystem>
StartScreen::StartScreen(SDL_Renderer* renderer) : _renderer(renderer) { StartScreen::StartScreen(SDL_Renderer* renderer, Theme& theme) : _renderer(renderer), _theme(theme) {
} }
Screen* StartScreen::render(float, bool&) { Screen* StartScreen::render(float, bool&) {
@ -33,13 +34,13 @@ Screen* StartScreen::render(float, bool&) {
_fss.requestFile( _fss.requestFile(
[](const auto& path) -> bool { return std::filesystem::is_regular_file(path); }, [](const auto& path) -> bool { return std::filesystem::is_regular_file(path); },
[this](const auto& path) { [this](const auto& path) {
tox_profile_path = path.string(); _tox_profile_path = path.string();
}, },
[](){} [](){}
); );
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::TextUnformatted(tox_profile_path.c_str()); ImGui::TextUnformatted(_tox_profile_path.c_str());
ImGui::TextUnformatted("password:"); ImGui::TextUnformatted("password:");
ImGui::SameLine(); ImGui::SameLine();
@ -56,8 +57,22 @@ Screen* StartScreen::render(float, bool&) {
if (ImGui::BeginTabItem("create profile")) { if (ImGui::BeginTabItem("create profile")) {
_new_save = true; _new_save = true;
ImGui::TextUnformatted("TODO: profile path"); ImGui::TextUnformatted("username:");
ImGui::TextUnformatted("TODO: profile name"); ImGui::SameLine();
if (ImGui::InputText("##user_name", &_user_name)) {
std::string tmp_copy = _user_name;
for (auto& c : tmp_copy) {
if (!std::isalnum(static_cast<unsigned char>(c)) && c != '-' && c != '.') {
c = '_';
}
}
if (tmp_copy.empty()) {
tmp_copy = "unnamed-tomato";
}
_tox_profile_path = tmp_copy + ".tox";
}
ImGui::TextUnformatted("password:"); ImGui::TextUnformatted("password:");
ImGui::SameLine(); ImGui::SameLine();
@ -69,6 +84,8 @@ Screen* StartScreen::render(float, bool&) {
ImGui::SameLine(); ImGui::SameLine();
ImGui::Checkbox("show password", &_show_password); ImGui::Checkbox("show password", &_show_password);
ImGui::TextUnformatted("TODO: profile path (current path for now)");
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (ImGui::BeginTabItem("plugins")) { if (ImGui::BeginTabItem("plugins")) {
@ -81,7 +98,6 @@ Screen* StartScreen::render(float, bool&) {
continue; continue;
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::TextUnformatted(it->c_str()); ImGui::TextUnformatted(it->c_str());
ImGui::PopID(); ImGui::PopID();
@ -105,7 +121,7 @@ Screen* StartScreen::render(float, bool&) {
ImGui::Separator(); ImGui::Separator();
if (!_new_save && !std::filesystem::is_regular_file(tox_profile_path)) { if (!_new_save && !std::filesystem::is_regular_file(_tox_profile_path)) {
// load but file missing // load but file missing
ImGui::BeginDisabled(); ImGui::BeginDisabled();
@ -115,7 +131,7 @@ Screen* StartScreen::render(float, bool&) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_AllowWhenDisabled)) { if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("file does not exist"); ImGui::SetTooltip("file does not exist");
} }
} else if (_new_save && std::filesystem::exists(tox_profile_path)) { } else if (_new_save && std::filesystem::exists(_tox_profile_path)) {
// new but file exists // new but file exists
ImGui::BeginDisabled(); ImGui::BeginDisabled();
@ -127,7 +143,7 @@ Screen* StartScreen::render(float, bool&) {
} }
} else { } else {
if (ImGui::Button("load", {60, 25})) { if (ImGui::Button("load", {60, 25})) {
auto new_screen = std::make_unique<MainScreen>(_renderer, tox_profile_path, _password, queued_plugin_paths); auto new_screen = std::make_unique<MainScreen>(_renderer, _theme, _tox_profile_path, _password, _user_name, queued_plugin_paths);
return new_screen.release(); return new_screen.release();
} }
} }

View File

@ -2,7 +2,8 @@
#include "./screen.hpp" #include "./screen.hpp"
#include "./file_selector.hpp" #include "./chat_gui/theme.hpp"
#include "./chat_gui/file_selector.hpp"
#include <vector> #include <vector>
#include <string> #include <string>
@ -14,18 +15,20 @@ extern "C" {
struct StartScreen final : public Screen { struct StartScreen final : public Screen {
SDL_Renderer* _renderer; SDL_Renderer* _renderer;
Theme& _theme;
FileSelector _fss; FileSelector _fss;
bool _new_save {false}; bool _new_save {false};
std::string _user_name {"unnamed-tomato"};
bool _show_password {false}; bool _show_password {false};
std::string _password; std::string _password;
std::string tox_profile_path {"tomato.tox"}; std::string _tox_profile_path {"unnamed-tomato.tox"};
std::vector<std::string> queued_plugin_paths; std::vector<std::string> queued_plugin_paths;
StartScreen(void) = delete; StartScreen(void) = delete;
StartScreen(SDL_Renderer* renderer); StartScreen(SDL_Renderer* renderer, Theme& theme);
~StartScreen(void) = default; ~StartScreen(void) = default;
// return nullptr if not next // return nullptr if not next

View File

@ -187,7 +187,8 @@ std::optional<TextureEntry> ToxAvatarLoader::load(TextureUploaderI& tu, Contact3
if (!_cr.any_of< if (!_cr.any_of<
Contact::Components::ToxFriendPersistent, Contact::Components::ToxFriendPersistent,
Contact::Components::ToxGroupPersistent, Contact::Components::ToxGroupPersistent,
Contact::Components::ToxGroupPeerPersistent Contact::Components::ToxGroupPeerPersistent,
Contact::Components::ID
>(c)) { >(c)) {
return std::nullopt; return std::nullopt;
} }
@ -199,6 +200,12 @@ std::optional<TextureEntry> ToxAvatarLoader::load(TextureUploaderI& tu, Contact3
pixels = generateToxIdenticon(_cr.get<Contact::Components::ToxGroupPersistent>(c).chat_id); pixels = generateToxIdenticon(_cr.get<Contact::Components::ToxGroupPersistent>(c).chat_id);
} else if (_cr.all_of<Contact::Components::ToxGroupPeerPersistent>(c)) { } else if (_cr.all_of<Contact::Components::ToxGroupPeerPersistent>(c)) {
pixels = generateToxIdenticon(_cr.get<Contact::Components::ToxGroupPeerPersistent>(c).peer_key); pixels = generateToxIdenticon(_cr.get<Contact::Components::ToxGroupPeerPersistent>(c).peer_key);
} else if (_cr.all_of<Contact::Components::ID>(c)) {
// TODO: should we really use toxidenticons for other protocols?
// (this is required for self)
auto id_copy = _cr.get<Contact::Components::ID>(c).data;
id_copy.resize(32);
pixels = generateToxIdenticon(id_copy);
} }
TextureEntry new_entry; TextureEntry new_entry;

View File

@ -3,7 +3,7 @@
// meh, change this // meh, change this
#include <exception> #include <exception>
#include <system_error> #include <system_error>
#include <toxencryptsave/toxencryptsave.h> #include <tox/toxencryptsave.h>
#include <sodium.h> #include <sodium.h>

View File

@ -107,6 +107,12 @@ ToxFriendFauxOfflineMessaging::dfmc_Ret ToxFriendFauxOfflineMessaging::doFriendM
const uint64_t ts_now = Message::getTimeMS(); const uint64_t ts_now = Message::getTimeMS();
if (!_cr.all_of<Contact::Components::Self>(c)) {
return dfmc_Ret::NO_MSG; // error
}
const auto self_c = _cr.get<Contact::Components::Self>(c).self;
// filter for unconfirmed messages // filter for unconfirmed messages
// we assume sorted // we assume sorted
@ -127,9 +133,8 @@ ToxFriendFauxOfflineMessaging::dfmc_Ret ToxFriendFauxOfflineMessaging::doFriendM
continue; // skip continue; // skip
} }
// exclude if (!mr->any_of<
if (mr->any_of< Message::Components::ReceivedBy
Message::Components::Remote::TimestampReceived // this acts like a tag, which is wrong in groups
>(msg) >(msg)
) { ) {
continue; // skip continue; // skip
@ -139,6 +144,16 @@ ToxFriendFauxOfflineMessaging::dfmc_Ret ToxFriendFauxOfflineMessaging::doFriendM
continue; // not outbound (in private) continue; // not outbound (in private)
} }
const auto& ts_received = mr->get<Message::Components::ReceivedBy>(msg).ts;
// not target
if (ts_received.contains(c)) {
continue;
}
// needs to contain self
if (!ts_received.contains(self_c)) {
continue;
}
valid_unsent = true; valid_unsent = true;
uint64_t msg_ts = msg_view.get<Message::Components::Timestamp>(msg).ts; uint64_t msg_ts = msg_view.get<Message::Components::Timestamp>(msg).ts;