Line data Source code
1 : /* 2 : * Copyright (C) 2024 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software; you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation; either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program is distributed in the hope that it will be useful, 10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program; if not, write to the Free Software 16 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 : */ 18 : 19 : #include "typers.h" 20 : #include "manager.h" 21 : #include "jamidht/jamiaccount.h" 22 : 23 : static constexpr std::chrono::steady_clock::duration COMPOSING_TIMEOUT {std::chrono::seconds(12)}; 24 : 25 : namespace jami { 26 : 27 279 : Typers::Typers(const std::shared_ptr<JamiAccount>& acc, const std::string& convId) 28 279 : : ioContext_(Manager::instance().ioContext()) 29 279 : , acc_(acc) 30 279 : , accountId_(acc->getAccountID()) 31 279 : , convId_(convId) 32 837 : , selfUri_(acc->getUsername()) 33 : { 34 : 35 279 : } 36 : 37 216 : Typers::~Typers() 38 : { 39 217 : for (auto& watcher : watcher_) { 40 1 : watcher.second.cancel(); 41 : } 42 216 : watcher_.clear(); 43 216 : } 44 : 45 : std::string 46 4 : getIsComposing(const std::string& conversationId, bool isWriting) 47 : { 48 : // implementing https://tools.ietf.org/rfc/rfc3994.txt 49 : return fmt::format("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" 50 : "<isComposing><state>{}</state>{}</isComposing>", 51 4 : isWriting ? "active"sv : "idle"sv, 52 4 : conversationId.empty() 53 12 : ? "" 54 12 : : "<conversation>" + conversationId + "</conversation>"); 55 : } 56 : 57 : void 58 7 : Typers::addTyper(const std::string &typer, bool sendMessage) 59 : { 60 7 : auto acc = acc_.lock(); 61 7 : if (!acc || !acc->isComposingEnabled()) 62 1 : return; 63 6 : auto [it, res] = watcher_.emplace(typer, asio::steady_timer {*ioContext_}); 64 6 : if (res) { 65 6 : auto& watcher = it->second; 66 : // Check next member 67 6 : watcher.expires_at(std::chrono::steady_clock::now() + COMPOSING_TIMEOUT); 68 6 : watcher.async_wait( 69 12 : std::bind(&Typers::onTyperTimeout, 70 12 : shared_from_this(), 71 : std::placeholders::_1, 72 : typer)); 73 : 74 6 : if (typer != selfUri_) 75 6 : emitSignal<libjami::ConfigurationSignal::ComposingStatusChanged>(accountId_, 76 3 : convId_, 77 : typer, 78 : 1); 79 : } 80 6 : if (sendMessage) { 81 : // In this case we should emit for remote to update the timer 82 9 : acc->sendInstantMessage(convId_, 83 6 : {{MIME_TYPE_IM_COMPOSING, getIsComposing(convId_, true)}}); 84 : } 85 7 : } 86 : 87 : void 88 24 : Typers::removeTyper(const std::string &typer, bool sendMessage) 89 : { 90 24 : auto acc = acc_.lock(); 91 24 : if (!acc || !acc->isComposingEnabled()) 92 1 : return; 93 23 : if (watcher_.erase(typer)) { 94 5 : if (sendMessage) { 95 3 : acc->sendInstantMessage(convId_, 96 2 : {{MIME_TYPE_IM_COMPOSING, getIsComposing(convId_, false)}}); 97 : } 98 5 : if (typer != selfUri_) { 99 6 : emitSignal<libjami::ConfigurationSignal::ComposingStatusChanged>(accountId_, 100 3 : convId_, 101 : typer, 102 : 0); 103 : } 104 : } 105 24 : } 106 : 107 : void 108 6 : Typers::onTyperTimeout(const asio::error_code& ec, const std::string &typer) 109 : { 110 6 : if (ec == asio::error::operation_aborted) 111 3 : return; 112 3 : removeTyper(typer); 113 : } 114 : 115 : } // namespace jami