// In-memory platform composition for the native website preview. // Chat/contact models and services remain the production implementations. #include "app/app_config.h" #include "app/app_facade_access.h" #include "chat/domain/chat_model.h" #include "chat/infra/mesh_peer_directory_core.h" #include "chat/infra/store/ram_store.h" #include "chat/usecase/chat_service.h" #include "chat/usecase/contact_service.h" #include #include #include namespace { class PreviewRadio final : public chat::IMeshAdapter { public: bool sendText(chat::ChannelId, const std::string&, chat::MessageId* id, chat::NodeId) override { if (id) *id = ++next_id; return true; } bool pollIncomingText(chat::MeshIncomingText*) override { return false; } bool sendAppData(chat::ChannelId, uint32_t, const uint8_t*, size_t, chat::NodeId, bool, chat::MessageId, bool) override { return true; } bool pollIncomingData(chat::MeshIncomingData*) override { return false; } void applyConfig(const chat::MeshConfig&) override {} bool isReady() const override { return true; } bool pollIncomingRawPacket(uint8_t*, size_t&, size_t) override { return false; } chat::NodeId getNodeId() const override { return 0xa1b35b7c; } chat::MeshCapabilities getCapabilities() const override { chat::MeshCapabilities c; c.supports_unicast_text = true; c.supports_node_info = true; return c; } private: uint32_t next_id = 100; }; class MemoryBlob final : public chat::IMeshPeerDirectoryBlobStore { public: chat::MeshPeerDirectoryBlobLoadResult loadBlob(std::vector& out) override { out = data; return data.empty() ? chat::MeshPeerDirectoryBlobLoadResult::Missing : chat::MeshPeerDirectoryBlobLoadResult::Loaded; } bool saveBlob(const uint8_t* bytes, std::size_t size) override { data.assign(bytes, bytes + size); return true; } void clearBlob() override { data.clear(); } private: std::vector data; }; class PreviewApp final : public app::IAppFacade { public: PreviewApp() : directory(blob), contacts(directory), chat(model, radio, store) { directory.beginEmpty(); contacts.begin(); contacts.updateNodeInfo(0x12345678, "ALEX", "Alex", 8, -94, 1789100000); contacts.addContact(0x12345678, "Alex"); contacts.updateNodeInfo(0x23456789, "MORG", "Morgan", 6, -102, 1789100000); contacts.addContact(0x23456789, "Morgan"); std::snprintf(config.node_name, sizeof(config.node_name), "Trail Mate"); std::snprintf(config.short_name, sizeof(config.short_name), "TM"); } app::AppConfigEdit beginConfigEdit() override { return {&config, nullptr, nullptr, nullptr}; } void saveConfig() override {} void saveConfig(app::AppConfigChangeSet) override {} void applyMeshConfig() override {} void applyUserInfo() override {} void applyPositionConfig() override {} void applyNetworkLimits() override {} void applyPrivacyConfig() override {} void applyChatDefaults() override {} chat::MeshProtocol getMeshProtocol() const override { return static_cast(config.mesh_protocol); } void getEffectiveUserInfo(char* a, std::size_t n, char* b, std::size_t m) const override { std::snprintf(a, n, "%s", config.node_name); std::snprintf(b, m, "%s", config.short_name); } bool switchMeshProtocol(chat::MeshProtocol p, bool) override { config.mesh_protocol = p; contacts.setActiveProtocol(p); return true; } chat::ChatService& getChatService() override { return chat; } chat::contacts::ContactService& getContactService() override { return contacts; } chat::IMeshAdapter* getMeshAdapter() override { return &radio; } const chat::IMeshAdapter* getMeshAdapter() const override { return &radio; } chat::NodeId getSelfNodeId() const override { return radio.getNodeId(); } team::TeamController* getTeamController() override { return nullptr; } team::TeamPairingService* getTeamPairing() override { return nullptr; } team::TeamService* getTeamService() override { return nullptr; } const team::TeamService* getTeamService() const override { return nullptr; } team::TeamTrackSampler* getTeamTrackSampler() override { return nullptr; } void setTeamModeActive(bool) override {} void broadcastNodeInfo() override {} void clearNodeDb() override { directory.clear(); } void clearMessageDb() override { store.clearAll(); model.clearAll(); } ble::BleManager* getBleManager() override { return nullptr; } const ble::BleManager* getBleManager() const override { return nullptr; } bool isBleEnabled() const override { return ble_enabled; } void setBleEnabled(bool enabled) override { ble_enabled = enabled; } void restartDevice() override {} chat::ui::IChatUiRuntime* getChatUiRuntime() override { return ui_runtime; } void setChatUiRuntime(chat::ui::IChatUiRuntime* value) override { ui_runtime = value; } BoardBase* getBoard() override { return nullptr; } const BoardBase* getBoard() const override { return nullptr; } void updateCoreServices() override {} void tickEventRuntime() override {} void dispatchPendingEvents(std::size_t) override {} protected: const app::AppConfig& getConfig() const override { return config; } private: app::AppConfig config{}; bool ble_enabled = false; chat::ui::IChatUiRuntime* ui_runtime = nullptr; MemoryBlob blob; chat::MeshPeerDirectoryCore directory; chat::contacts::ContactService contacts; PreviewRadio radio; chat::ChatModel model; chat::RamStore store; chat::ChatService chat; }; } // namespace void preview_bind_app() { static PreviewApp app; app::bindAppFacade(app); }