From 3175b646b9fc6ed517b27a20d100fef905c27b23 Mon Sep 17 00:00:00 2001 From: Lasan Mahaliyana Date: Sun, 21 Jun 2026 14:55:49 +0530 Subject: [PATCH] added IPC cilent for tui --- sharing/linux/tui/BUILD | 26 ++++ sharing/linux/tui/app.cc | 22 ++- sharing/linux/tui/app.h | 7 +- sharing/linux/tui/components/BUILD | 16 ++ .../tui/components/incoming_share_card.cc | 44 ++++++ .../tui/components/incoming_share_card.h | 21 +++ sharing/linux/tui/components/share_target.cc | 4 +- sharing/linux/tui/ipc_client.cc | 145 ++++++++++++++++++ sharing/linux/tui/ipc_client.h | 43 ++++++ sharing/linux/tui/ipc_client_test.cc | 135 ++++++++++++++++ sharing/linux/tui/page.h | 1 + sharing/linux/tui/palette.h | 41 +++-- sharing/linux/tui/ui/BUILD | 1 + sharing/linux/tui/ui/file_selected_screen.cc | 30 ++-- sharing/linux/tui/ui/home_screen.cc | 25 ++- sharing/linux/tui/ui/home_screen.h | 5 + 16 files changed, 533 insertions(+), 33 deletions(-) create mode 100644 sharing/linux/tui/components/incoming_share_card.cc create mode 100644 sharing/linux/tui/components/incoming_share_card.h create mode 100644 sharing/linux/tui/ipc_client.cc create mode 100644 sharing/linux/tui/ipc_client.h create mode 100644 sharing/linux/tui/ipc_client_test.cc diff --git a/sharing/linux/tui/BUILD b/sharing/linux/tui/BUILD index cda98e14..15d55bb3 100644 --- a/sharing/linux/tui/BUILD +++ b/sharing/linux/tui/BUILD @@ -1,5 +1,6 @@ load("@rules_cc//cc:cc_binary.bzl", "cc_binary") load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") @@ -51,6 +52,31 @@ cc_library( ], ) +cc_library( + name = "ipc_client", + srcs = [ + "ipc_client.cc", + ], + hdrs = [ + "ipc_client.h", + ], + deps = [ + "@nlohmann_json//:json", + ], +) + +cc_test( + name = "ipc_client_test", + srcs = [ + "ipc_client_test.cc", + ], + deps = [ + ":ipc_client", + "@com_google_googletest//:gtest_main", + "@nlohmann_json//:json", + ], +) + cc_library( name = "file_picker", srcs = [ diff --git a/sharing/linux/tui/app.cc b/sharing/linux/tui/app.cc index 4f7b63a7..160e8ceb 100644 --- a/sharing/linux/tui/app.cc +++ b/sharing/linux/tui/app.cc @@ -2,6 +2,8 @@ #include +#include + #include "ftxui/component/component.hpp" #include "sharing/linux/tui/ui/home_screen.h" @@ -33,6 +35,12 @@ int TuiApp::Run() { selected_file_ = path; current_page_ = Page::Sharing; }, + .incoming_share_device_name = incoming_share_device_name_, + .incoming_share_device_type = incoming_share_device_type_, + .on_incoming_share_accept = + [this]() { current_page_ = Page::FilePicker; }, + .on_incoming_share_decline = + [this]() { current_page_ = Page::FilePicker; }, }); auto app = @@ -48,10 +56,16 @@ bool TuiApp::HandleEvent(Event event) { return true; } - if (event == Event::Backspace && current_page_ == Page::Sharing) { - current_page_ = Page::FilePicker; - selected_file_.clear(); - return true; + if (event == Event::Backspace) { + if (current_page_ == Page::Sharing) { + current_page_ = Page::FilePicker; + selected_file_.clear(); + return true; + } + if (current_page_ == Page::IncomingShare) { + current_page_ = Page::FilePicker; + return true; + } } return false; diff --git a/sharing/linux/tui/app.h b/sharing/linux/tui/app.h index 8a2a35c5..4e5a1415 100644 --- a/sharing/linux/tui/app.h +++ b/sharing/linux/tui/app.h @@ -1,11 +1,12 @@ #pragma once +#include + #include "ftxui/component/screen_interactive.hpp" +#include "sharing/linux/tui/components/share_target.h" #include "sharing/linux/tui/file_picker.h" #include "sharing/linux/tui/page.h" -#include - namespace nearby::sharing::linux_tui { using namespace ftxui; @@ -23,6 +24,8 @@ class TuiApp { Page current_page_ = Page::FilePicker; std::string hostname_; std::string selected_file_; + std::string incoming_share_device_name_ = "Lasan's A55"; + ShareTargetType incoming_share_device_type_ = ShareTargetType::kPhone; }; } // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/BUILD b/sharing/linux/tui/components/BUILD index a12bc7d9..e396d7b1 100644 --- a/sharing/linux/tui/components/BUILD +++ b/sharing/linux/tui/components/BUILD @@ -35,6 +35,22 @@ cc_library( ":icons", ], ) + +cc_library( + name = "incoming_share_card", + srcs = [ + "incoming_share_card.cc", + ], + hdrs = [ + "incoming_share_card.h", + ], + deps = [ + ":share_target", + "//sharing/linux/tui:palette", + "@ftxui//:ftxui", + ], +) + cc_library( name = "file_picker_card", srcs = [ diff --git a/sharing/linux/tui/components/incoming_share_card.cc b/sharing/linux/tui/components/incoming_share_card.cc new file mode 100644 index 00000000..af97934d --- /dev/null +++ b/sharing/linux/tui/components/incoming_share_card.cc @@ -0,0 +1,44 @@ +#include "sharing/linux/tui/components/incoming_share_card.h" +#include "ftxui/component/component.hpp" +#include "ftxui/dom/elements.hpp" +#include "sharing/linux/tui/components/share_target.h" +#include "sharing/linux/tui/palette.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Component IncomingShareCard(IncomingShareCardOptions options) { + auto accept_button = Button("Accept", [options] { + if (options.on_accept) { + options.on_accept(); + } + }); + auto decline_button = Button("Decline", [options] { + if (options.on_decline) { + options.on_decline(); + } + }); + auto controls = Container::Horizontal({accept_button, decline_button}); + + return Renderer(controls, [accept_button, decline_button, options] { + return vbox({ + window(text(" Incoming share ") | center, + vbox({ + paragraph("Do you want to accept this share?") | + bold | center, + separator(), + ShareTarget(options.device_name, options.device_type), + separator(), + hbox({ + accept_button->Render() | flex, + separator(), + decline_button->Render() | flex, + }), + })), + }) | + borderStyled(Palette::border) | bgcolor(Palette::surface) | + size(WIDTH, GREATER_THAN, 40); + }); +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/incoming_share_card.h b/sharing/linux/tui/components/incoming_share_card.h new file mode 100644 index 00000000..912768ba --- /dev/null +++ b/sharing/linux/tui/components/incoming_share_card.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "ftxui/component/component.hpp" +#include "sharing/linux/tui/components/share_target.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +struct IncomingShareCardOptions { + std::string device_name; + ShareTargetType device_type = ShareTargetType::kUnknown; + std::function on_accept; + std::function on_decline; +}; + +Component IncomingShareCard(IncomingShareCardOptions options); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/share_target.cc b/sharing/linux/tui/components/share_target.cc index 147c6f23..065dce45 100644 --- a/sharing/linux/tui/components/share_target.cc +++ b/sharing/linux/tui/components/share_target.cc @@ -25,8 +25,8 @@ Element ShareTarget(std::string device_name, ShareTargetType device_type) { {icon, separatorLight() | dim, paragraph(device_name) | color(Palette::secondary) | bold | center - }) | - size(WIDTH, EQUAL, 14) | border; + }) |bgcolor(Palette::surface)| + size(WIDTH, EQUAL, 14) | borderStyled(Palette::border); }; } // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ipc_client.cc b/sharing/linux/tui/ipc_client.cc new file mode 100644 index 00000000..51092670 --- /dev/null +++ b/sharing/linux/tui/ipc_client.cc @@ -0,0 +1,145 @@ +#include "sharing/linux/tui/ipc_client.h" + +#include +#include +#include +#include + +#include +#include + +namespace nearby::sharing::linux_tui { + +IpcClient::IpcClient(std::string socket_path) + : socket_path_(std::move(socket_path)) {} + +IpcClient::~IpcClient() { + Stop(); +} + +bool IpcClient::Start(EventHandler event_handler) { + Stop(); + event_handler_ = std::move(event_handler); + + socket_fd_ = socket(AF_UNIX, SOCK_STREAM, 0); + if (socket_fd_ < 0) { + Emit({{"event", "ipc_disconnected"}, + {"message", "failed to create socket"}}); + return false; + } + + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socket_path_.c_str(), sizeof(addr.sun_path) - 1); + + if (connect(socket_fd_, reinterpret_cast(&addr), sizeof(addr)) < + 0) { + close(socket_fd_); + socket_fd_ = -1; + Emit({{"event", "ipc_disconnected"}, + {"message", "failed to connect to daemon"}}); + return false; + } + + running_.store(true); + connected_.store(true); + read_thread_ = std::thread([this] { ReadLoop(); }); + Emit({{"event", "ipc_connected"}}); + return true; +} + +void IpcClient::Stop() { + running_.store(false); + connected_.store(false); + + { + std::lock_guard lock(write_mutex_); + if (socket_fd_ >= 0) { + shutdown(socket_fd_, SHUT_RDWR); + close(socket_fd_); + socket_fd_ = -1; + } + } + + if (read_thread_.joinable()) { + read_thread_.join(); + } +} + +bool IpcClient::Send(const nlohmann::json& message) { + return SendLine(message.dump()); +} + +void IpcClient::ReadLoop() { + std::string buffer; + char chunk[1024]{}; + + while (running_.load()) { + ssize_t received = recv(socket_fd_, chunk, sizeof(chunk), 0); + if (received > 0) { + buffer.append(chunk, static_cast(received)); + size_t newline = std::string::npos; + while ((newline = buffer.find('\n')) != std::string::npos) { + std::string line = buffer.substr(0, newline); + buffer.erase(0, newline + 1); + if (line.empty()) { + continue; + } + try { + Emit(nlohmann::json::parse(line)); + } catch (const nlohmann::json::exception& error) { + Emit({{"event", "ipc_error"}, + {"message", + std::string("malformed daemon JSON: ") + error.what()}}); + } + } + continue; + } + + if (received < 0 && errno == EINTR) { + continue; + } + break; + } + + connected_.store(false); + if (running_.load()) { + Emit({{"event", "ipc_disconnected"}, {"message", "daemon disconnected"}}); + } +} + +void IpcClient::Emit(nlohmann::json event) { + if (event_handler_) { + event_handler_(event); + } +} + +bool IpcClient::SendLine(std::string_view line) { + std::lock_guard lock(write_mutex_); + if (socket_fd_ < 0) { + return false; + } + + std::string data(line); + if (data.empty() || data.back() != '\n') { + data.push_back('\n'); + } + + size_t total_sent = 0; + while (total_sent < data.size()) { + ssize_t sent = send(socket_fd_, data.data() + total_sent, + data.size() - total_sent, MSG_NOSIGNAL); + if (sent > 0) { + total_sent += static_cast(sent); + continue; + } + if (sent < 0 && errno == EINTR) { + continue; + } + return false; + } + + return true; +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ipc_client.h b/sharing/linux/tui/ipc_client.h new file mode 100644 index 00000000..18f94d5f --- /dev/null +++ b/sharing/linux/tui/ipc_client.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "nlohmann/json.hpp" + +namespace nearby::sharing::linux_tui { + +class IpcClient { + public: + using EventHandler = std::function; + + explicit IpcClient(std::string socket_path = "/tmp/nearby_sharing_sock"); + ~IpcClient(); + + IpcClient(const IpcClient&) = delete; + IpcClient& operator=(const IpcClient&) = delete; + + bool Start(EventHandler event_handler); + void Stop(); + bool Send(const nlohmann::json& message); + bool connected() const { return connected_.load(); } + + private: + void ReadLoop(); + void Emit(nlohmann::json event); + bool SendLine(std::string_view line); + + std::string socket_path_; + EventHandler event_handler_; + std::thread read_thread_; + mutable std::mutex write_mutex_; + std::atomic running_{false}; + std::atomic connected_{false}; + int socket_fd_ = -1; +}; + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ipc_client_test.cc b/sharing/linux/tui/ipc_client_test.cc new file mode 100644 index 00000000..af57ed33 --- /dev/null +++ b/sharing/linux/tui/ipc_client_test.cc @@ -0,0 +1,135 @@ +#include "sharing/linux/tui/ipc_client.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "nlohmann/json.hpp" + +namespace nearby::sharing::linux_tui { +namespace { + +std::string TestSocketPath() { + return "/tmp/nearby_tui_ipc_client_test_" + std::to_string(getpid()); +} + +int CreateServerSocket(const std::string& socket_path) { + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + EXPECT_GE(fd, 0); + + unlink(socket_path.c_str()); + + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1); + + EXPECT_EQ(bind(fd, reinterpret_cast(&addr), sizeof(addr)), 0); + EXPECT_EQ(listen(fd, 1), 0); + return fd; +} + +std::string ReadLine(int fd) { + std::string line; + char byte = '\0'; + while (recv(fd, &byte, 1, 0) == 1) { + if (byte == '\n') { + return line; + } + line.push_back(byte); + } + return line; +} + +void SendAll(int fd, const std::string& data) { + size_t sent_total = 0; + while (sent_total < data.size()) { + ssize_t sent = send(fd, data.data() + sent_total, data.size() - sent_total, + MSG_NOSIGNAL); + ASSERT_GT(sent, 0); + sent_total += static_cast(sent); + } +} + +} // namespace + +TEST(IpcClientTest, SendsJsonCommandWithNewline) { + std::string socket_path = TestSocketPath(); + int server_fd = CreateServerSocket(socket_path); + std::string received; + + std::thread server_thread([&] { + int client_fd = accept(server_fd, nullptr, nullptr); + ASSERT_GE(client_fd, 0); + received = ReadLine(client_fd); + close(client_fd); + }); + + IpcClient client(socket_path); + ASSERT_TRUE(client.Start([](const nlohmann::json&) {})); + EXPECT_TRUE(client.Send({{"command", "start_receive"}})); + client.Stop(); + + server_thread.join(); + close(server_fd); + unlink(socket_path.c_str()); + + nlohmann::json command = nlohmann::json::parse(received); + EXPECT_EQ(command["command"], "start_receive"); +} + +TEST(IpcClientTest, ParsesSplitJsonEvents) { + std::string socket_path = TestSocketPath(); + int server_fd = CreateServerSocket(socket_path); + + std::mutex mutex; + std::condition_variable cv; + std::vector events; + + std::thread server_thread([&] { + int client_fd = accept(server_fd, nullptr, nullptr); + ASSERT_GE(client_fd, 0); + SendAll(client_fd, + R"({"event":"target_discovered","share_target":{"id":1}})" + "\n" + R"({"event":"incoming)"); + SendAll(client_fd, R"(_transfer","share_target":{"id":2},"transfer":{}})" + "\n"); + close(client_fd); + }); + + IpcClient client(socket_path); + ASSERT_TRUE(client.Start([&](const nlohmann::json& event) { + if (event.value("event", std::string()).rfind("ipc_", 0) == 0) { + return; + } + std::lock_guard lock(mutex); + events.push_back(event); + cv.notify_one(); + })); + + { + std::unique_lock lock(mutex); + cv.wait_for(lock, std::chrono::seconds(2), + [&] { return events.size() >= 2; }); + } + client.Stop(); + + server_thread.join(); + close(server_fd); + unlink(socket_path.c_str()); + + ASSERT_GE(events.size(), 2u); + EXPECT_EQ(events[0]["event"], "target_discovered"); + EXPECT_EQ(events[1]["event"], "incoming_transfer"); +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/page.h b/sharing/linux/tui/page.h index eeeb381e..9a04bb4c 100644 --- a/sharing/linux/tui/page.h +++ b/sharing/linux/tui/page.h @@ -5,6 +5,7 @@ namespace nearby::sharing::linux_tui { enum class Page { FilePicker, Sharing, + IncomingShare, }; } // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/palette.h b/sharing/linux/tui/palette.h index 5820ddbd..8ed13daf 100644 --- a/sharing/linux/tui/palette.h +++ b/sharing/linux/tui/palette.h @@ -6,20 +6,41 @@ namespace nearby::sharing::linux_tui { using namespace ftxui; struct Palette { - inline static const Color primary = - Color::RGB(0xF1, 0xF9, 0xF6); // Off-white mint tint + // === Core Typography & Brand === + // Primary light text for high-contrast readability against dark backgrounds + inline static const Color primary = Color::RGB(0xF1, 0xF9, 0xF6); // Off-white mint tint - inline static const Color secondary = - Color::RGB(0x8A, 0xAF, 0xA4); // Muted sage gray + // Secondary text, inactive states, or subtle accents + inline static const Color secondary = Color::RGB(0x8A, 0xAF, 0xA4); // Muted sage gray - inline static const Color accent = - Color::RGB(0xA3, 0xD9, 0xC9); // Original Mint Leaf + // Main brand accent, focused element backgrounds, or prominent icons (Unchanged) + inline static const Color accent = Color::RGB(0xA3, 0xD9, 0xC9); // Original Mint Leaf - inline static const Color active = - Color::RGB(0x23, 0x3D, 0x34); // Deep forest green tint + // === Surface & Structure === + // Active menu item highlights, selection cards, or focused component backgrounds + inline static const Color active = Color::RGB(0x23, 0x3D, 0x34); // Deep forest green tint - inline static const Color base = - Color::RGB(0x12, 0x1D, 0x1A); // Near-black deep mint/charcoal + // Base application window background + inline static const Color base = Color::RGB(0x12, 0x1D, 0x1A); // Near-black deep mint/charcoal + + // Explicit component surface background (e.g., sidebars, modals, or inactive cards) + inline static const Color surface = Color::RGB(0x19, 0x2A, 0x25); // Mid-tone dark green + + // Standard UI borders, dividers, and subtle grid lines + inline static const Color border = Color::RGB(0x32, 0x52, 0x47); // Defined slate green + + // Extremely muted text, placeholders, or disabled options + inline static const Color disabled = Color::RGB(0x56, 0x73, 0x6B); // Ghostly sage green + + // === Functional / Status Elements === + // Critical errors, destructive actions, or alerts (toned down for dark mode) + inline static const Color error = Color::RGB(0xE0, 0x7A, 0x7A); // Soft desaturated coral/red + + // Warnings, pending indicators, or high-priority notifications + inline static const Color warning = Color::RGB(0xE6, 0xC2, 0x80); // Soft amber gold + + // Success messages, online badges, or completed operations + inline static const Color success = Color::RGB(0x81, 0xC7, 0x9D); // Vibrant spring mint }; } // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/BUILD b/sharing/linux/tui/ui/BUILD index f5301a5a..018810c1 100644 --- a/sharing/linux/tui/ui/BUILD +++ b/sharing/linux/tui/ui/BUILD @@ -56,6 +56,7 @@ cc_library( ":file_selected_screen", ":home_header", ":sidebar", + "//sharing/linux/tui/components:incoming_share_card", "//sharing/linux/tui:file_picker", "//sharing/linux/tui:page", "//sharing/linux/tui:palette", diff --git a/sharing/linux/tui/ui/file_selected_screen.cc b/sharing/linux/tui/ui/file_selected_screen.cc index f1a947fa..df77a620 100644 --- a/sharing/linux/tui/ui/file_selected_screen.cc +++ b/sharing/linux/tui/ui/file_selected_screen.cc @@ -1,6 +1,6 @@ #include "sharing/linux/tui/ui/file_selected_screen.h" -#include "sharing/linux/tui/components/share_target.h" +#include "sharing/linux/tui/components/share_target.h" #include "sharing/linux/tui/palette.h" namespace nearby::sharing::linux_tui { @@ -10,20 +10,28 @@ Element FileSelectedScreen(const std::string& selected_file) { FlexboxConfig config; config.direction = FlexboxConfig::Direction::Row; config.wrap = FlexboxConfig::Wrap::Wrap; - config.gap_x = 2; + config.gap_x = 1; config.gap_y = 1; return vbox({ - flexbox({ - ShareTarget("Lasan's A55", ShareTargetType::kPhone), - ShareTarget("lasan-laptop", ShareTargetType::kLaptop), - ShareTarget("lasan-laptop", ShareTargetType::kLaptop), - ShareTarget("lasan-laptop", ShareTargetType::kLaptop), - ShareTarget("Lasan's S9+", ShareTargetType::kTablet), - ShareTarget("Lasan's S9+", ShareTargetType::kTablet), + hbox( + {filler(), + flexbox( + { + ShareTarget("Lasan's A55", ShareTargetType::kPhone), + ShareTarget("lasan-laptop", ShareTargetType::kLaptop), + ShareTarget("lasan-laptop", ShareTargetType::kLaptop), + ShareTarget("lasan-laptop", ShareTargetType::kLaptop), + ShareTarget("Lasan's S9+", ShareTargetType::kTablet), + ShareTarget("Lasan's S9+", ShareTargetType::kTablet), - }, config) | flex + }, + config) | + flex + + }), + filler(), }) | - borderStyled(Palette::primary) | flex; + flex; } } // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/home_screen.cc b/sharing/linux/tui/ui/home_screen.cc index dd4dff92..96a3061d 100644 --- a/sharing/linux/tui/ui/home_screen.cc +++ b/sharing/linux/tui/ui/home_screen.cc @@ -2,6 +2,7 @@ #include "ftxui/dom/elements.hpp" #include "sharing/linux/tui/components/file_picker_card.h" +#include "sharing/linux/tui/components/incoming_share_card.h" #include "sharing/linux/tui/palette.h" #include "sharing/linux/tui/ui/file_selected_screen.h" #include "sharing/linux/tui/ui/home_header.h" @@ -14,17 +15,32 @@ Component HomeScreen(HomeScreenOptions options) { auto file_picker_card = FilePickerCard({.file_picker = options.file_picker, .on_file_selected = options.on_file_selected}); + auto incoming_share_card = + IncomingShareCard({.device_name = options.incoming_share_device_name, + .device_type = options.incoming_share_device_type, + .on_accept = options.on_incoming_share_accept, + .on_decline = options.on_incoming_share_decline}); + auto content = Container::Vertical({file_picker_card, incoming_share_card}); - return Renderer(file_picker_card, [file_picker_card, options] { + return Renderer(content, [file_picker_card, incoming_share_card, options] { const std::string selected_file = options.selected_file == nullptr ? "" : *options.selected_file; const Page current_page = options.current_page == nullptr ? Page::FilePicker : *options.current_page; - auto main_panel = current_page == Page::FilePicker - ? file_picker_card->Render() - : FileSelectedScreen(selected_file); + Element main_panel; + switch (current_page) { + case Page::FilePicker: + main_panel = file_picker_card->Render(); + break; + case Page::IncomingShare: + main_panel = incoming_share_card->Render(); + break; + case Page::Sharing: + main_panel = FileSelectedScreen(selected_file); + break; + } return vbox({ HomeHeader() | color(Palette::accent), @@ -32,6 +48,7 @@ Component HomeScreen(HomeScreenOptions options) { hbox({ Sidebar({.hostname = options.hostname, .selected_file = selected_file}), + separator() | color(Palette::border), main_panel | flex, }) | flex, }) | diff --git a/sharing/linux/tui/ui/home_screen.h b/sharing/linux/tui/ui/home_screen.h index 2c2eb61f..867aaa65 100644 --- a/sharing/linux/tui/ui/home_screen.h +++ b/sharing/linux/tui/ui/home_screen.h @@ -4,6 +4,7 @@ #include #include "ftxui/component/component.hpp" +#include "sharing/linux/tui/components/share_target.h" #include "sharing/linux/tui/file_picker.h" #include "sharing/linux/tui/page.h" @@ -16,6 +17,10 @@ struct HomeScreenOptions { const std::string* selected_file = nullptr; FilePicker* file_picker = nullptr; std::function on_file_selected; + std::string incoming_share_device_name; + ShareTargetType incoming_share_device_type = ShareTargetType::kUnknown; + std::function on_incoming_share_accept; + std::function on_incoming_share_decline; }; Component HomeScreen(HomeScreenOptions options);