Line data Source code
1 : /* 2 : * Copyright (C) 2004-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, see <https://www.gnu.org/licenses/>. 16 : */ 17 : 18 : #include "typers.h" 19 : #include "manager.h" 20 : #include "jamidht/jamiaccount.h" 21 : 22 : static constexpr std::chrono::steady_clock::duration COMPOSING_TIMEOUT {std::chrono::seconds(12)}; 23 : 24 : namespace jami { 25 : 26 385 : Typers::Typers(const std::shared_ptr<JamiAccount>& acc, const std::string& convId) 27 385 : : ioContext_(Manager::instance().ioContext()) 28 385 : , acc_(acc) 29 385 : , accountId_(acc->getAccountID()) 30 385 : , convId_(convId) 31 1155 : , selfUri_(acc->getUsername()) 32 : { 33 : 34 385 : } 35 : 36 385 : Typers::~Typers() 37 : { 38 386 : for (auto& watcher : watcher_) { 39 1 : watcher.second.cancel(); 40 : } 41 385 : watcher_.clear(); 42 385 : } 43 : 44 : std::string 45 4 : getIsComposing(const std::string& conversationId, bool isWriting) 46 : { 47 : // implementing https://tools.ietf.org/rfc/rfc3994.txt 48 : return fmt::format("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" 49 : "<isComposing><state>{}</state>{}</isComposing>", 50 4 : isWriting ? "active"sv : "idle"sv, 51 4 : conversationId.empty() 52 12 : ? "" 53 12 : : "<conversation>" + conversationId + "</conversation>"); 54 : } 55 : 56 : void 57 7 : Typers::addTyper(const std::string &typer, bool sendMessage) 58 : { 59 7 : auto acc = acc_.lock(); 60 7 : if (!acc || !acc->isComposingEnabled()) 61 1 : return; 62 6 : auto [it, res] = watcher_.emplace(typer, asio::steady_timer {*ioContext_}); 63 6 : if (res) { 64 6 : auto& watcher = it->second; 65 : // Check next member 66 6 : watcher.expires_at(std::chrono::steady_clock::now() + COMPOSING_TIMEOUT); 67 6 : watcher.async_wait( 68 12 : std::bind(&Typers::onTyperTimeout, 69 12 : shared_from_this(), 70 : std::placeholders::_1, 71 : typer)); 72 : 73 6 : if (typer != selfUri_) 74 6 : emitSignal<libjami::ConfigurationSignal::ComposingStatusChanged>(accountId_, 75 3 : convId_, 76 : typer, 77 : 1); 78 : } 79 6 : if (sendMessage) { 80 : // In this case we should emit for remote to update the timer 81 9 : acc->sendInstantMessage(convId_, 82 6 : {{MIME_TYPE_IM_COMPOSING, getIsComposing(convId_, true)}}); 83 : } 84 7 : } 85 : 86 : void 87 26 : Typers::removeTyper(const std::string &typer, bool sendMessage) 88 : { 89 26 : auto acc = acc_.lock(); 90 26 : if (!acc || !acc->isComposingEnabled()) 91 1 : return; 92 25 : if (watcher_.erase(typer)) { 93 5 : if (sendMessage) { 94 3 : acc->sendInstantMessage(convId_, 95 2 : {{MIME_TYPE_IM_COMPOSING, getIsComposing(convId_, false)}}); 96 : } 97 5 : if (typer != selfUri_) { 98 6 : emitSignal<libjami::ConfigurationSignal::ComposingStatusChanged>(accountId_, 99 3 : convId_, 100 : typer, 101 : 0); 102 : } 103 : } 104 26 : } 105 : 106 : void 107 6 : Typers::onTyperTimeout(const asio::error_code& ec, const std::string &typer) 108 : { 109 6 : if (ec == asio::error::operation_aborted) 110 3 : return; 111 3 : removeTyper(typer); 112 : } 113 : 114 : } // namespace jami