File indexing completed on 2024-12-01 04:37:02

0001 /*
0002    SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "roomwidget.h"
0008 #include "accountroomsettings.h"
0009 #include "conferencecalldialog/conferencecalldialog.h"
0010 #include "conferencecalldialog/conferencedirectcalldialog.h"
0011 #include "connection.h"
0012 #include "dialogs/addusersinroomdialog.h"
0013 #include "dialogs/autotranslateconfiguredialog.h"
0014 #include "dialogs/channelinfodialog.h"
0015 #include "dialogs/configurenotificationdialog.h"
0016 #include "dialogs/directchannelinfodialog.h"
0017 #include "dialogs/inviteusersdialog.h"
0018 #include "dialogs/searchmessagedialog.h"
0019 #include "dialogs/showattachmentdialog.h"
0020 #include "dialogs/showmentionsmessagesdialog.h"
0021 #include "dialogs/showpinnedmessagesdialog.h"
0022 #include "dialogs/showstarredmessagesdialog.h"
0023 #include "dialogs/showthreadsdialog.h"
0024 #include "discussions/showdiscussionsdialog.h"
0025 #include "exportmessages/exportmessagesdialog.h"
0026 #include "messagelinewidget.h"
0027 #include "messagelistview.h"
0028 #include "plugintextmessagewidget.h"
0029 #include "prunemessages/prunemessagesdialog.h"
0030 #include "rocketchataccount.h"
0031 #include "rocketchatbackend.h"
0032 #include "roomutil.h"
0033 #include "ruqolawidgets_debug.h"
0034 #include "usersinroomflowwidget.h"
0035 
0036 #include "offlinewidget/offlinewidget.h"
0037 
0038 #include "otr/otrwidget.h"
0039 #include "reconnectinfowidget.h"
0040 #include "roomcounterinfowidget.h"
0041 #include "roomwidgetbase.h"
0042 #include "ruqola_thread_message_widgets_debug.h"
0043 #include "teams/teamchannelsdialog.h"
0044 #include "teams/teaminfo.h"
0045 #include "threadwidget/threadmessagedialog.h"
0046 #include "video-conference/videoconferencejoinjob.h"
0047 #include "video-conference/videoconferencestartjob.h"
0048 #include "videoconference/videoconferencemessageinfomanager.h"
0049 
0050 #include <KLocalizedString>
0051 #include <KMessageBox>
0052 
0053 #include <QDesktopServices>
0054 #include <QDragEnterEvent>
0055 #include <QDropEvent>
0056 #include <QMimeData>
0057 #include <QPushButton>
0058 #include <QScrollBar>
0059 
0060 #include "config-ruqola.h"
0061 #include "video-conference/videoconferencecapabilitiesjob.h"
0062 
0063 #if HAVE_TEXT_TO_SPEECH
0064 #include <TextEditTextToSpeech/TextToSpeechContainerWidget>
0065 #endif
0066 
0067 RoomWidget::RoomWidget(QWidget *parent)
0068     : QWidget(parent)
0069     , mRoomWidgetBase(new RoomWidgetBase(MessageListView::Mode::Editing, this))
0070     , mRoomHeaderWidget(new RoomHeaderWidget(this))
0071     , mUsersInRoomFlowWidget(new UsersInRoomFlowWidget(this))
0072     , mRoomCounterInfoWidget(new RoomCounterInfoWidget(this))
0073 
0074 #if HAVE_TEXT_TO_SPEECH
0075     , mTextToSpeechWidget(new TextEditTextToSpeech::TextToSpeechContainerWidget(this))
0076 #endif
0077 {
0078     auto mainLayout = new QVBoxLayout(this);
0079     mainLayout->setObjectName(QStringLiteral("mainLayout"));
0080     mainLayout->setContentsMargins({});
0081 
0082     mRoomHeaderWidget->setObjectName(QStringLiteral("mRoomHeaderWidget"));
0083     mainLayout->addWidget(mRoomHeaderWidget);
0084 
0085     auto roomWidget = new QWidget(this);
0086     mainLayout->addWidget(roomWidget);
0087     mRoomWidgetLayout = new QVBoxLayout(roomWidget);
0088     mRoomWidgetLayout->setObjectName(QStringLiteral("roomWidgetLayout"));
0089     mRoomWidgetLayout->setContentsMargins({});
0090     mRoomWidgetLayout->setSpacing(0);
0091 
0092     mUsersInRoomFlowWidget->setObjectName(QStringLiteral("mUsersInRoomFlowWidget"));
0093     mRoomWidgetLayout->addWidget(mUsersInRoomFlowWidget);
0094     mUsersInRoomFlowWidget->setVisible(false);
0095 
0096     mRoomCounterInfoWidget->setObjectName(QStringLiteral("mRoomCounterInfoWidget"));
0097 
0098     connect(mRoomWidgetBase, &RoomWidgetBase::errorMessage, this, [this](const QString &message) {
0099         if (!mPluginTextMessageWidget) {
0100             createPluginTextMessateWidget();
0101         }
0102         mPluginTextMessageWidget->slotShareError(message);
0103     });
0104     connect(mRoomWidgetBase, &RoomWidgetBase::successMessage, this, [this](const QString &message) {
0105         if (!mPluginTextMessageWidget) {
0106             createPluginTextMessateWidget();
0107         }
0108         mPluginTextMessageWidget->slotShareSuccess(message);
0109     });
0110 
0111     mRoomWidgetLayout->addWidget(mRoomCounterInfoWidget);
0112 
0113 #if HAVE_TEXT_TO_SPEECH
0114     mTextToSpeechWidget->setObjectName(QStringLiteral("mTextToSpeechWidget"));
0115     mRoomWidgetLayout->addWidget(mTextToSpeechWidget);
0116     connect(mRoomWidgetBase, &RoomWidgetBase::textToSpeech, mTextToSpeechWidget, &TextEditTextToSpeech::TextToSpeechContainerWidget::say);
0117 #endif
0118 
0119     mRoomWidgetLayout->addWidget(mRoomWidgetBase);
0120     connect(mRoomCounterInfoWidget, &RoomCounterInfoWidget::markAsRead, this, &RoomWidget::slotClearNotification);
0121     connect(mRoomCounterInfoWidget, &RoomCounterInfoWidget::jumpToUnreadMessage, this, &RoomWidget::slotJumpToUnreadMessage);
0122     connect(mRoomHeaderWidget, &RoomHeaderWidget::favoriteChanged, this, &RoomWidget::slotChangeFavorite);
0123     connect(mRoomHeaderWidget, &RoomHeaderWidget::encryptedChanged, this, &RoomWidget::slotEncryptedChanged);
0124     connect(mRoomHeaderWidget, &RoomHeaderWidget::goBackToRoom, this, &RoomWidget::slotGoBackToRoom);
0125     connect(mRoomHeaderWidget, &RoomHeaderWidget::listOfUsersChanged, this, &RoomWidget::slotShowListOfUsersInRoom);
0126     connect(mRoomHeaderWidget, &RoomHeaderWidget::searchMessageRequested, this, &RoomWidget::slotSearchMessages);
0127     connect(mRoomHeaderWidget, &RoomHeaderWidget::actionRequested, this, &RoomWidget::slotActionRequested);
0128     connect(mRoomHeaderWidget, &RoomHeaderWidget::channelInfoRequested, this, &RoomWidget::slotChannelInfoRequested);
0129     connect(mRoomWidgetBase, &RoomWidgetBase::loadHistory, this, &RoomWidget::slotLoadHistory);
0130     connect(mRoomWidgetBase, &RoomWidgetBase::createNewDiscussion, this, &RoomWidget::slotCreateNewDiscussion);
0131     connect(mRoomHeaderWidget, &RoomHeaderWidget::teamChannelsRequested, this, &RoomWidget::slotTeamChannelsRequested);
0132     connect(mRoomHeaderWidget, &RoomHeaderWidget::openTeam, this, &RoomWidget::slotOpenTeamRequested);
0133     connect(mRoomHeaderWidget, &RoomHeaderWidget::callRequested, this, &RoomWidget::slotCallRequested);
0134     setAcceptDrops(true);
0135 }
0136 
0137 RoomWidget::~RoomWidget()
0138 {
0139     delete mRoom;
0140 }
0141 
0142 void RoomWidget::createPluginTextMessateWidget()
0143 {
0144     mPluginTextMessageWidget = new PluginTextMessageWidget(this);
0145     mPluginTextMessageWidget->setObjectName(QStringLiteral("mPluginTextMessageWidget"));
0146     mRoomWidgetLayout->insertWidget(1, mPluginTextMessageWidget);
0147 }
0148 
0149 // TODO using it
0150 void RoomWidget::createRoomReconnectInfoWidget()
0151 {
0152     mRoomReconnectInfoWidget = new ReconnectInfoWidget(this);
0153     mRoomReconnectInfoWidget->setObjectName(QStringLiteral("mRoomReconnectInfoWidget"));
0154     connect(mRoomReconnectInfoWidget, &ReconnectInfoWidget::tryReconnect, this, &RoomWidget::slotTryReconnect);
0155     // After mUsersInRoomFlowWidget
0156     mRoomWidgetLayout->insertWidget(1, mRoomReconnectInfoWidget);
0157 }
0158 
0159 // TODO using it.
0160 void RoomWidget::createOffLineWidget()
0161 {
0162     mOffLineWidget = new OffLineWidget(this);
0163     mOffLineWidget->setObjectName(QStringLiteral("mOffLineWidget"));
0164     // After mUsersInRoomFlowWidget
0165     mRoomWidgetLayout->insertWidget(1, mOffLineWidget);
0166 }
0167 
0168 // TODO using it.
0169 void RoomWidget::createOtrWidget()
0170 {
0171     mOtrWidget = new OtrWidget(this);
0172     mOtrWidget->setObjectName(QStringLiteral("mOtrWidget"));
0173     connect(mOtrWidget, &OtrWidget::closeOtr, this, &RoomWidget::slotCloseOtr);
0174     connect(mOtrWidget, &OtrWidget::refreshKeys, this, &RoomWidget::slotRefreshOtrKeys);
0175     // After mUsersInRoomFlowWidget
0176     mRoomWidgetLayout->insertWidget(1, mOtrWidget);
0177 }
0178 
0179 void RoomWidget::slotLoadHistory()
0180 {
0181     mCurrentRocketChatAccount->loadHistory(mRoomWidgetBase->roomId());
0182 }
0183 
0184 void RoomWidget::slotChannelInfoRequested()
0185 {
0186     if (!mRoom) {
0187         return;
0188     }
0189     if (mRoomType == Room::RoomType::Direct) {
0190         DirectChannelInfoDialog dlg(mCurrentRocketChatAccount, this);
0191         dlg.setUserName(mRoom->name());
0192         dlg.exec();
0193     } else {
0194         QPointer<ChannelInfoDialog> dlg = new ChannelInfoDialog(mRoom, mCurrentRocketChatAccount, this);
0195         if (dlg->exec()) {
0196             const RocketChatRestApi::SaveRoomSettingsJob::SaveRoomSettingsInfo info = dlg->saveRoomSettingsInfo();
0197             if (info.isValid()) {
0198                 auto saveRoomSettingsJob = new RocketChatRestApi::SaveRoomSettingsJob(this);
0199                 saveRoomSettingsJob->setSaveRoomSettingsInfo(info);
0200                 mCurrentRocketChatAccount->restApi()->initializeRestApiJob(saveRoomSettingsJob);
0201                 if (!saveRoomSettingsJob->start()) {
0202                     qCDebug(RUQOLAWIDGETS_LOG) << "Impossible to start saveRoomSettingsJob";
0203                 }
0204             }
0205         }
0206         delete dlg;
0207     }
0208 }
0209 
0210 void RoomWidget::slotActionRequested(RoomHeaderWidget::ChannelActionType type)
0211 {
0212     switch (type) {
0213     case RoomHeaderWidget::ShowMentions:
0214         slotShowMentions();
0215         break;
0216     case RoomHeaderWidget::ShowPinned:
0217         slotPinnedMessages();
0218         break;
0219     case RoomHeaderWidget::ShowStarred:
0220         slotStarredMessages();
0221         break;
0222     case RoomHeaderWidget::ShowDiscussions:
0223         slotShowDiscussions();
0224         break;
0225     case RoomHeaderWidget::ShowThreads:
0226         slotShowThreads();
0227         break;
0228     case RoomHeaderWidget::ShowAttachment:
0229         slotShowFileAttachments();
0230         break;
0231     case RoomHeaderWidget::Notification:
0232         slotConfigureNotification();
0233         break;
0234     case RoomHeaderWidget::AutoTranslate:
0235         slotConfigureAutoTranslate();
0236         break;
0237     case RoomHeaderWidget::InviteUsers:
0238         slotInviteUsers();
0239         break;
0240     case RoomHeaderWidget::AddUsersInRoom:
0241         slotAddUsersInRoom();
0242         break;
0243     case RoomHeaderWidget::VideoChat:
0244         slotVideoChat();
0245         break;
0246     case RoomHeaderWidget::PruneMessages:
0247         slotPruneMessages();
0248         break;
0249     case RoomHeaderWidget::ExportMessages:
0250         slotExportMessages();
0251         break;
0252     case RoomHeaderWidget::OtrMessages:
0253         // TODO
0254         break;
0255     case RoomHeaderWidget::EncryptMessages:
0256         // TODO
0257         break;
0258     }
0259 }
0260 
0261 void RoomWidget::slotPruneMessages()
0262 {
0263     if (!mRoom) {
0264         return;
0265     }
0266     QPointer<PruneMessagesDialog> dlg = new PruneMessagesDialog(mCurrentRocketChatAccount, this);
0267     dlg->setRoomName(mRoom->name());
0268     if (dlg->exec()) {
0269         RocketChatRestApi::RoomsCleanHistoryJob::CleanHistoryInfo info = dlg->cleanHistoryInfo();
0270         info.roomId = mRoomWidgetBase->roomId();
0271         if (KMessageBox::ButtonCode::PrimaryAction
0272             == KMessageBox::questionTwoActions(this,
0273                                                i18n("Do you want really remove history?"),
0274                                                i18nc("@title:window", "Remove History"),
0275                                                KStandardGuiItem::remove(),
0276                                                KStandardGuiItem::cancel())) {
0277             mCurrentRocketChatAccount->cleanChannelHistory(info);
0278         }
0279     }
0280     delete dlg;
0281 }
0282 
0283 void RoomWidget::slotExportMessages()
0284 {
0285     QPointer<ExportMessagesDialog> dlg = new ExportMessagesDialog(this);
0286     if (dlg->exec()) {
0287         RocketChatRestApi::RoomsExportJob::RoomsExportInfo info = dlg->roomExportInfo();
0288         info.roomId = mRoomWidgetBase->roomId();
0289         mCurrentRocketChatAccount->exportMessages(info);
0290     }
0291     delete dlg;
0292 }
0293 
0294 void RoomWidget::slotVideoChat()
0295 {
0296     mCurrentRocketChatAccount->createJitsiConfCall(mRoomWidgetBase->roomId());
0297 }
0298 
0299 void RoomWidget::slotAddUsersInRoom()
0300 {
0301     QPointer<AddUsersInRoomDialog> dlg = new AddUsersInRoomDialog(mCurrentRocketChatAccount, this);
0302     if (dlg->exec()) {
0303         const QStringList users = dlg->userIds();
0304         for (const QString &user : users) {
0305             mCurrentRocketChatAccount->addUserToRoom(user, mRoomWidgetBase->roomId(), mRoomType);
0306         }
0307     }
0308     delete dlg;
0309 }
0310 
0311 void RoomWidget::slotInviteUsers()
0312 {
0313     InviteUsersDialog dlg(mCurrentRocketChatAccount, this);
0314     dlg.setRoomId(mRoomWidgetBase->roomId());
0315     dlg.generateLink();
0316     dlg.exec();
0317 }
0318 
0319 void RoomWidget::updateListView()
0320 {
0321     mRoomWidgetBase->updateListView();
0322 }
0323 
0324 void RoomWidget::slotConfigureAutoTranslate()
0325 {
0326     if (!mRoom) {
0327         return;
0328     }
0329     AutoTranslateConfigureDialog dlg(mCurrentRocketChatAccount, this);
0330     dlg.setRoom(mRoom);
0331     dlg.exec();
0332 }
0333 
0334 void RoomWidget::slotConfigureNotification()
0335 {
0336     if (!mRoom) {
0337         return;
0338     }
0339     ConfigureNotificationDialog dlg(mCurrentRocketChatAccount, this);
0340     dlg.setRoom(mRoom);
0341     dlg.exec();
0342 }
0343 
0344 void RoomWidget::slotStarredMessages()
0345 {
0346     if (!mRoom) {
0347         return;
0348     }
0349     QPointer<ShowStarredMessagesDialog> dlg = new ShowStarredMessagesDialog(mCurrentRocketChatAccount, this);
0350     dlg->setRoomId(mRoomWidgetBase->roomId());
0351     dlg->setModel(mCurrentRocketChatAccount->listMessagesFilterProxyModel());
0352     dlg->setRoom(mRoom);
0353     mCurrentRocketChatAccount->getListMessages(mRoomWidgetBase->roomId(), ListMessagesModel::StarredMessages);
0354     connect(dlg, &ShowListMessageBaseDialog::goToMessageRequested, this, &RoomWidget::slotGotoMessage);
0355     dlg->exec();
0356     delete dlg;
0357 }
0358 
0359 void RoomWidget::slotPinnedMessages()
0360 {
0361     if (!mRoom) {
0362         return;
0363     }
0364     QPointer<ShowPinnedMessagesDialog> dlg = new ShowPinnedMessagesDialog(mCurrentRocketChatAccount, this);
0365     dlg->setRoomId(mRoomWidgetBase->roomId());
0366     dlg->setRoom(mRoom);
0367     dlg->setModel(mCurrentRocketChatAccount->listMessagesFilterProxyModel());
0368     mCurrentRocketChatAccount->getListMessages(mRoomWidgetBase->roomId(), ListMessagesModel::PinnedMessages);
0369     connect(dlg, &ShowListMessageBaseDialog::goToMessageRequested, this, &RoomWidget::slotGotoMessage);
0370     dlg->exec();
0371     delete dlg;
0372 }
0373 
0374 void RoomWidget::slotShowMentions()
0375 {
0376     if (!mRoom) {
0377         return;
0378     }
0379     QPointer<ShowMentionsMessagesDialog> dlg = new ShowMentionsMessagesDialog(mCurrentRocketChatAccount, this);
0380     dlg->setRoomId(mRoomWidgetBase->roomId());
0381     dlg->setModel(mCurrentRocketChatAccount->listMessagesFilterProxyModel());
0382     dlg->setRoom(mRoom);
0383     mCurrentRocketChatAccount->getListMessages(mRoomWidgetBase->roomId(), ListMessagesModel::MentionsMessages);
0384     connect(dlg, &ShowListMessageBaseDialog::goToMessageRequested, this, &RoomWidget::slotGotoMessage);
0385     dlg->exec();
0386     delete dlg;
0387 }
0388 
0389 void RoomWidget::slotShowThreads()
0390 {
0391     if (!mRoom) {
0392         return;
0393     }
0394     QPointer<ShowThreadsDialog> dlg = new ShowThreadsDialog(mCurrentRocketChatAccount, this);
0395     dlg->setModel(mCurrentRocketChatAccount->listMessagesFilterProxyModel());
0396     dlg->setRoomId(mRoomWidgetBase->roomId());
0397     dlg->setRoom(mRoom);
0398     mCurrentRocketChatAccount->getListMessages(mRoomWidgetBase->roomId(), ListMessagesModel::ThreadsMessages);
0399     connect(dlg, &ShowListMessageBaseDialog::goToMessageRequested, this, &RoomWidget::slotGotoMessage);
0400     dlg->exec();
0401     delete dlg;
0402 }
0403 
0404 void RoomWidget::slotShowDiscussions()
0405 {
0406     auto dlg = new ShowDiscussionsDialog(mCurrentRocketChatAccount, this);
0407     dlg->setModel(mCurrentRocketChatAccount->discussionsFilterProxyModel());
0408     dlg->setRoomId(mRoomWidgetBase->roomId());
0409     mCurrentRocketChatAccount->discussionsInRoom(mRoomWidgetBase->roomId());
0410     dlg->show();
0411 }
0412 
0413 void RoomWidget::slotShowFileAttachments()
0414 {
0415     auto dlg = new ShowAttachmentDialog(mCurrentRocketChatAccount, this);
0416     mCurrentRocketChatAccount->roomFiles(mRoomWidgetBase->roomId(), mRoomType);
0417     dlg->setModel(mCurrentRocketChatAccount->filesForRoomFilterProxyModel());
0418     dlg->setRoomId(mRoomWidgetBase->roomId());
0419     dlg->setRoomType(mRoomType);
0420     dlg->show();
0421 }
0422 
0423 void RoomWidget::slotSearchMessages()
0424 {
0425     if (!mRoom) {
0426         return;
0427     }
0428     SearchMessageDialog dlg(mCurrentRocketChatAccount, this);
0429     dlg.setRoomId(mRoomWidgetBase->roomId());
0430     dlg.setRoom(mRoom);
0431     dlg.setModel(mCurrentRocketChatAccount->searchMessageFilterProxyModel());
0432     connect(&dlg, &SearchMessageDialog::goToMessageRequested, this, &RoomWidget::slotGotoMessage);
0433     dlg.exec();
0434 }
0435 
0436 void RoomWidget::slotCallRequested()
0437 {
0438     if (!mRoom) {
0439         return;
0440     }
0441     if (mRoom->channelType() == Room::RoomType::Direct && mRoom->userNames().count() == 2) {
0442         QPointer<ConferenceDirectCallDialog> dlg = new ConferenceDirectCallDialog(mCurrentRocketChatAccount, this);
0443         dlg->setRoomId(mRoomWidgetBase->roomId());
0444         dlg->setAllowRinging(mRoom->hasPermission(QStringLiteral("videoconf-ring-users")));
0445         if (dlg->exec()) {
0446 #if 0
0447             auto conferenceInfoJob = new RocketChatRestApi::VideoConferenceInfoJob(this);
0448             conferenceInfoJob->setCallId(obj[QLatin1String("callId")].toString());
0449             mCurrentRocketChatAccount->restApi()->initializeRestApiJob(conferenceInfoJob);
0450             connect(conferenceInfoJob, &RocketChatRestApi::VideoConferenceInfoJob::videoConferenceInfoDone, this, [this, callInfo](const QJsonObject &obj) {
0451                 qDebug() << " info " << obj;
0452                 VideoConferenceInfo info;
0453                 info.parse(obj);
0454             });
0455             if (!conferenceInfoJob->start()) {
0456                 qCWarning(RUQOLAWIDGETS_LOG) << "Impossible to start VideoConferenceInfoJob job";
0457             }
0458 #endif
0459             // TODO show conf call info
0460         }
0461         delete dlg;
0462     } else {
0463         auto job = new RocketChatRestApi::VideoConferenceCapabilitiesJob(this);
0464         mCurrentRocketChatAccount->restApi()->initializeRestApiJob(job);
0465         connect(job, &RocketChatRestApi::VideoConferenceCapabilitiesJob::noVideoConferenceProviderApps, this, [this] {
0466             KMessageBox::information(this, i18n("A workspace admin needs to install and configure a conference call apps."), i18n("Video Conference"));
0467         });
0468         connect(job, &RocketChatRestApi::VideoConferenceCapabilitiesJob::videoConferenceCapabilitiesDone, this, [this](const QJsonObject &obj) {
0469             // qDebug() << "obj  " << obj;
0470             // {"capabilities":{"cam":true,"mic":true,"title":true},"providerName":"jitsi","success":true}
0471             const QJsonObject capabilitiesObj = obj[QLatin1String("capabilities")].toObject();
0472             const bool useCam = capabilitiesObj[QLatin1String("cam")].toBool();
0473             const bool useMic = capabilitiesObj[QLatin1String("mic")].toBool();
0474             ConferenceCallWidget::ConferenceCallStart callInfo;
0475             callInfo.useCamera = useCam;
0476             callInfo.useMic = useMic;
0477 
0478             QPointer<ConferenceCallDialog> dlg = new ConferenceCallDialog(mCurrentRocketChatAccount, this);
0479             dlg->setConferenceCallInfo(callInfo);
0480             if (dlg->exec()) {
0481                 const ConferenceCallWidget::ConferenceCallStart conferenceCallInfo = dlg->conferenceCallInfo();
0482 
0483                 auto job = new RocketChatRestApi::VideoConferenceStartJob(this);
0484                 RocketChatRestApi::VideoConferenceStartJob::VideoConferenceStartInfo startInfo;
0485                 startInfo.roomId = mRoomWidgetBase->roomId();
0486                 startInfo.allowRinging = mRoom->hasPermission(QStringLiteral("videoconf-ring-users"));
0487                 job->setInfo(startInfo);
0488                 mCurrentRocketChatAccount->restApi()->initializeRestApiJob(job);
0489                 connect(job, &RocketChatRestApi::VideoConferenceStartJob::videoConferenceStartDone, this, [this, conferenceCallInfo](const QJsonObject &obj) {
0490                     // qDebug() << "obj  " << obj;
0491                     // {"data":{"callId":"63949ea24ef3f3baa9658f25","providerName":"jitsi","rid":"hE6RS3iv5ND5EGWC6","type":"videoconference"},"success":true}
0492                     const QString callId{obj[QLatin1String("callId")].toString()};
0493                     mCurrentRocketChatAccount->videoConferenceMessageInfoManager()->addCallId(callId);
0494                     auto conferenceJoinJob = new RocketChatRestApi::VideoConferenceJoinJob(this);
0495                     RocketChatRestApi::VideoConferenceJoinJob::VideoConferenceJoinInfo joinInfo;
0496                     joinInfo.callId = callId;
0497                     joinInfo.useCamera = conferenceCallInfo.useCamera;
0498                     joinInfo.useMicro = conferenceCallInfo.useMic;
0499                     conferenceJoinJob->setInfo(joinInfo);
0500                     mCurrentRocketChatAccount->restApi()->initializeRestApiJob(conferenceJoinJob);
0501                     connect(conferenceJoinJob, &RocketChatRestApi::VideoConferenceJoinJob::videoConferenceJoinDone, this, [](const QJsonObject &joinObject) {
0502                         // qDebug() << " join info " << obj;
0503                         QDesktopServices::openUrl(QUrl(joinObject[QLatin1String("url")].toString()));
0504                     });
0505                     if (!conferenceJoinJob->start()) {
0506                         qCWarning(RUQOLAWIDGETS_LOG) << "Impossible to start VideoConferenceJoinJob job";
0507                     }
0508                 });
0509                 if (!job->start()) {
0510                     qCWarning(RUQOLAWIDGETS_LOG) << "Impossible to start VideoConferenceCapabilitiesJob job";
0511                 }
0512             }
0513             delete dlg;
0514         });
0515         if (!job->start()) {
0516             qCWarning(RUQOLAWIDGETS_LOG) << "Impossible to start VideoConferenceCapabilitiesJob job";
0517         }
0518     }
0519 }
0520 
0521 void RoomWidget::slotOpenTeamRequested(const QString &teamId)
0522 {
0523     Q_EMIT mCurrentRocketChatAccount->openTeamNameRequested(teamId);
0524 }
0525 
0526 void RoomWidget::slotTeamChannelsRequested()
0527 {
0528     if (!mRoom) {
0529         return;
0530     }
0531     TeamChannelsDialog dlg(mCurrentRocketChatAccount, this);
0532     dlg.setRoom(mRoom);
0533     dlg.exec();
0534 }
0535 
0536 void RoomWidget::slotCreateNewDiscussion(const QString &messageId, const QString &originalMessage)
0537 {
0538     mRoomWidgetBase->slotCreateNewDiscussion(messageId, originalMessage, mRoomHeaderWidget->roomName());
0539 }
0540 
0541 void RoomWidget::slotCreatePrivateDiscussion(const QString &userName)
0542 {
0543     Q_EMIT mCurrentRocketChatAccount->openLinkRequested(RoomUtil::generateUserLink(userName));
0544 }
0545 
0546 void RoomWidget::dragEnterEvent(QDragEnterEvent *event)
0547 {
0548     // Don't allow to drop element when it's blocked
0549     if (mRoom && mRoom->roomIsBlocked()) {
0550         return;
0551     }
0552     const QMimeData *mimeData = event->mimeData();
0553     if (mimeData->hasUrls()) {
0554         event->accept();
0555     }
0556 }
0557 
0558 void RoomWidget::dropEvent(QDropEvent *event)
0559 {
0560     const QMimeData *mimeData = event->mimeData();
0561     if (mimeData->hasUrls()) {
0562         mRoomWidgetBase->messageLineWidget()->handleMimeData(mimeData);
0563     }
0564 }
0565 
0566 void RoomWidget::storeRoomSettings()
0567 {
0568     if (mCurrentRocketChatAccount) {
0569         if (mRoomWidgetBase->messageLineWidget()->text().isEmpty()) {
0570             auto *vbar = mRoomWidgetBase->messageListView()->verticalScrollBar();
0571             if (vbar->value() != vbar->maximum()) {
0572                 AccountRoomSettings::PendingTypedInfo info;
0573                 info.scrollbarPosition = mRoomWidgetBase->messageListView()->verticalScrollBar()->value();
0574                 mCurrentRocketChatAccount->accountRoomSettings()->add(mRoomWidgetBase->roomId(), info);
0575             } else {
0576                 mCurrentRocketChatAccount->accountRoomSettings()->remove(mRoomWidgetBase->roomId());
0577             }
0578         } else {
0579             AccountRoomSettings::PendingTypedInfo info;
0580             info.text = mRoomWidgetBase->messageLineWidget()->text();
0581             info.messageIdBeingEdited = mRoomWidgetBase->messageLineWidget()->messageIdBeingEdited();
0582             info.scrollbarPosition = mRoomWidgetBase->messageListView()->verticalScrollBar()->value();
0583             info.threadMessageId = mRoomWidgetBase->messageLineWidget()->threadMessageId();
0584             info.quotePermalink = mRoomWidgetBase->messageLineWidget()->quotePermalink();
0585             info.quoteText = mRoomWidgetBase->messageLineWidget()->quoteText();
0586             mCurrentRocketChatAccount->accountRoomSettings()->add(mRoomWidgetBase->roomId(), info);
0587         }
0588     }
0589 }
0590 
0591 void RoomWidget::clearBeforeSwitching()
0592 {
0593     mRoomWidgetBase->messageLineWidget()->setThreadMessageId({});
0594     mRoomWidgetBase->messageLineWidget()->setQuoteMessage({}, {});
0595 }
0596 
0597 void RoomWidget::forceLineEditFocus()
0598 {
0599     mRoomWidgetBase->messageLineWidget()->setFocus();
0600 }
0601 
0602 void RoomWidget::setChannelSelected(const QString &roomId, Room::RoomType roomType)
0603 {
0604     storeRoomSettings();
0605     setRoomId(roomId);
0606     setRoomType(roomType);
0607     clearBeforeSwitching();
0608     const AccountRoomSettings::PendingTypedInfo currentPendingInfo = mCurrentRocketChatAccount->accountRoomSettings()->value(roomId);
0609     if (currentPendingInfo.isValid()) {
0610         mRoomWidgetBase->messageLineWidget()->setQuoteMessage(currentPendingInfo.quotePermalink, currentPendingInfo.quoteText);
0611         mRoomWidgetBase->messageLineWidget()->setThreadMessageId(currentPendingInfo.threadMessageId);
0612         mRoomWidgetBase->messageLineWidget()->setText(currentPendingInfo.text);
0613         mRoomWidgetBase->messageLineWidget()->setMessageIdBeingEdited(currentPendingInfo.messageIdBeingEdited);
0614         if (currentPendingInfo.scrollbarPosition != -1) {
0615             mRoomWidgetBase->messageListView()->verticalScrollBar()->setValue(currentPendingInfo.scrollbarPosition);
0616         }
0617     } else {
0618         mRoomWidgetBase->messageLineWidget()->setText(QString());
0619     }
0620     mRoomWidgetBase->messageLineWidget()->setMode(mRoomWidgetBase->messageLineWidget()->messageIdBeingEdited().isEmpty()
0621                                                       ? MessageLineWidget::EditingMode::NewMessage
0622                                                       : MessageLineWidget::EditingMode::EditMessage);
0623 
0624     mRoomWidgetBase->messageLineWidget()->setFocus();
0625 }
0626 
0627 void RoomWidget::slotUpdateRoomCounterInfoWidget()
0628 {
0629     if (mRoom) {
0630         mRoomCounterInfoWidget->setChannelCounterInfo(mRoom->channelCounterInfo());
0631     }
0632 }
0633 
0634 void RoomWidget::updateRoomHeader()
0635 {
0636     if (mRoom) {
0637         mRoomHeaderWidget->setRoomName(mRoom->displayRoomName());
0638         mRoomHeaderWidget->setRoomAnnouncement(mRoom->displayAnnouncement());
0639         mRoomHeaderWidget->setRoomTopic(mRoom->displayTopic());
0640         mRoomHeaderWidget->setFavoriteStatus(mRoom->favorite());
0641         mRoomHeaderWidget->setEncypted(mRoom->encrypted() && mRoom->hasPermission(QStringLiteral("edit-room")));
0642         mRoomHeaderWidget->setIsDiscussion(mRoom->isDiscussionRoom());
0643         mRoomHeaderWidget->setIsMainTeam(mRoom->teamInfo().mainTeam());
0644         mRoomHeaderWidget->setTeamRoomInfo(mRoom->teamRoomInfo());
0645         mRoomHeaderWidget->setIsDirectGroup((mRoom->channelType() == Room::RoomType::Direct) && mRoom->userNames().count() > 2);
0646         if (mRoom->roomId() == QLatin1String("%1%1").arg(mCurrentRocketChatAccount->userId())) {
0647             mRoomHeaderWidget->setCallEnabled(false);
0648         } else {
0649             mRoomHeaderWidget->setCallEnabled(true);
0650         }
0651         // TODO Description ?
0652 
0653         mRoomWidgetBase->updateRoomReadOnly(mRoom);
0654         if (mRoom->channelCounterInfo().isValid() && mRoom->channelCounterInfo().unreadMessages() > 0) {
0655             mRoomCounterInfoWidget->animatedShow();
0656         } else {
0657             mRoomCounterInfoWidget->animatedHide();
0658         }
0659     }
0660 }
0661 
0662 QString RoomWidget::roomId() const
0663 {
0664     return mRoomWidgetBase->roomId();
0665 }
0666 
0667 void RoomWidget::setRoomType(Room::RoomType roomType)
0668 {
0669     mRoomType = roomType;
0670 }
0671 
0672 Room *RoomWidget::room() const
0673 {
0674     return mRoom;
0675 }
0676 
0677 void RoomWidget::slotShowListOfUsersInRoom(bool checked)
0678 {
0679     mUsersInRoomFlowWidget->setVisible(checked);
0680 }
0681 
0682 void RoomWidget::setRoomId(const QString &roomId)
0683 {
0684     mRoomWidgetBase->setRoomId(roomId);
0685     mRoom = mCurrentRocketChatAccount->room(mRoomWidgetBase->roomId());
0686     if (mRoom) {
0687         connectRoom();
0688         mRoomWidgetBase->messageLineWidget()->setRoomId(roomId);
0689         mRoomWidgetBase->messageListView()->setChannelSelected(mRoom);
0690         mUsersInRoomFlowWidget->setRoom(mRoom);
0691         mRoomHeaderWidget->setRoom(mRoom);
0692     } else {
0693         qCWarning(RUQOLAWIDGETS_LOG) << " Impossible to find room " << roomId;
0694     }
0695 }
0696 
0697 void RoomWidget::connectRoom()
0698 {
0699     if (mRoom) {
0700         connect(mRoom, &Room::announcementChanged, this, [this]() {
0701             mRoomHeaderWidget->setRoomAnnouncement(mRoom->displayAnnouncement());
0702         });
0703         connect(mRoom, &Room::topicChanged, this, [this]() {
0704             mRoomHeaderWidget->setRoomTopic(mRoom->displayTopic());
0705         });
0706         connect(mRoom, &Room::nameChanged, this, [this]() {
0707             mRoomHeaderWidget->setRoomName(mRoom->displayRoomName());
0708         });
0709         connect(mRoom, &Room::fnameChanged, this, [this]() {
0710             mRoomHeaderWidget->setRoomName(mRoom->displayRoomName());
0711         });
0712         connect(mRoom, &Room::favoriteChanged, this, [this]() {
0713             mRoomHeaderWidget->setFavoriteStatus(mRoom->favorite());
0714         });
0715         connect(mRoom, &Room::encryptedChanged, this, [this]() {
0716             mRoomHeaderWidget->setEncypted(mRoom->encrypted());
0717         });
0718         // TODO verify it.
0719         connect(mRoom, &Room::teamInfoChanged, this, [this]() {
0720             mRoomHeaderWidget->setIsMainTeam(mRoom->teamInfo().mainTeam());
0721         });
0722         connect(mRoom, &Room::autoTranslateLanguageChanged, this, &RoomWidget::updateListView);
0723         connect(mRoom, &Room::autoTranslateChanged, this, &RoomWidget::updateListView);
0724         connect(mRoom, &Room::ignoredUsersChanged, this, &RoomWidget::updateListView);
0725         connect(mRoom, &Room::channelCounterInfoChanged, this, &RoomWidget::slotUpdateRoomCounterInfoWidget);
0726         connect(mRoom, &Room::highlightsWordChanged, this, &RoomWidget::updateListView);
0727     }
0728     slotUpdateRoomCounterInfoWidget();
0729     updateRoomHeader();
0730 }
0731 
0732 void RoomWidget::slotJumpToUnreadMessage(qint64 numberOfMessage)
0733 {
0734     MessagesModel *roomMessageModel = mCurrentRocketChatAccount->messageModelForRoom(mRoomWidgetBase->roomId());
0735     if (roomMessageModel->rowCount() >= numberOfMessage) {
0736         const QString messageId = roomMessageModel->messageIdFromIndex(roomMessageModel->rowCount() - numberOfMessage);
0737         mRoomWidgetBase->messageListView()->goToMessage(messageId);
0738     } else {
0739         RocketChatRestApi::ChannelHistoryJob::ChannelHistoryInfo info;
0740         switch (mRoomType) {
0741         case Room::RoomType::Channel:
0742             info.channelType = RocketChatRestApi::ChannelHistoryJob::Channel;
0743             break;
0744         case Room::RoomType::Direct:
0745             info.channelType = RocketChatRestApi::ChannelHistoryJob::Direct;
0746             break;
0747         case Room::RoomType::Private:
0748             info.channelType = RocketChatRestApi::ChannelHistoryJob::Groups;
0749             break;
0750         case Room::RoomType::Unknown:
0751             qCWarning(RUQOLAWIDGETS_LOG) << " Problem with room type ";
0752             break;
0753         }
0754         if (mRoomType == Room::RoomType::Unknown) {
0755             return;
0756         }
0757         auto job = new RocketChatRestApi::ChannelHistoryJob(this);
0758         info.count = numberOfMessage - roomMessageModel->rowCount() + 1;
0759         info.roomId = mRoomWidgetBase->roomId();
0760         const qint64 endDateTime = roomMessageModel->lastTimestamp();
0761         info.latestMessage = QDateTime::fromMSecsSinceEpoch(endDateTime, Qt::UTC).toString(Qt::ISODateWithMs);
0762         // qDebug() << " info.latestMessage " << info.latestMessage;
0763         job->setChannelHistoryInfo(info);
0764         mCurrentRocketChatAccount->restApi()->initializeRestApiJob(job);
0765         connect(job, &RocketChatRestApi::ChannelHistoryJob::channelHistoryDone, this, [this, numberOfMessage, roomMessageModel](const QJsonObject &obj) {
0766             mCurrentRocketChatAccount->rocketChatBackend()->processIncomingMessages(obj.value(QLatin1String("messages")).toArray(), true, true);
0767             // qDebug() << " obj " << obj;
0768             //                qDebug() << " roomMessageModel->rowCount() " << roomMessageModel->rowCount();
0769             //                qDebug() << " numberOfMessage " << numberOfMessage;
0770             //                qDebug() << " initialRowCount " <<  (roomMessageModel->rowCount() - numberOfMessage);
0771 
0772             const QString messageId = roomMessageModel->messageIdFromIndex(roomMessageModel->rowCount() - numberOfMessage);
0773             mRoomWidgetBase->messageListView()->goToMessage(messageId);
0774         });
0775         if (!job->start()) {
0776             qCDebug(RUQOLAWIDGETS_LOG) << "Impossible to start ChannelHistoryJob";
0777         }
0778     }
0779 }
0780 
0781 void RoomWidget::scrollToMessageId(const QString &messageId)
0782 {
0783     slotGotoMessage(messageId, {});
0784 }
0785 
0786 void RoomWidget::slotGotoMessage(const QString &messageId, const QString &messageDateTimeUtc)
0787 {
0788     MessageListView *messageListView = mRoomWidgetBase->messageListView();
0789     auto messageModel = qobject_cast<MessagesModel *>(messageListView->model());
0790     Q_ASSERT(messageModel);
0791     const QModelIndex index = messageModel->indexForMessage(messageId);
0792     if (index.isValid()) {
0793         messageListView->scrollTo(index);
0794     } else /* if (!messageDateTimeUtc.isEmpty())*/ {
0795         RocketChatRestApi::ChannelHistoryJob::ChannelHistoryInfo info;
0796         switch (mRoomType) {
0797         case Room::RoomType::Channel:
0798             info.channelType = RocketChatRestApi::ChannelHistoryJob::Channel;
0799             break;
0800         case Room::RoomType::Direct:
0801             info.channelType = RocketChatRestApi::ChannelHistoryJob::Direct;
0802             break;
0803         case Room::RoomType::Private:
0804             info.channelType = RocketChatRestApi::ChannelHistoryJob::Groups;
0805             break;
0806         case Room::RoomType::Unknown:
0807             qCWarning(RUQOLAWIDGETS_LOG) << " Problem with room type ";
0808             break;
0809         }
0810         if (mRoomType == Room::RoomType::Unknown) {
0811             return;
0812         }
0813         auto job = new RocketChatRestApi::ChannelHistoryJob(this);
0814         info.roomId = mRoomWidgetBase->roomId();
0815         const qint64 endDateTime = messageModel->lastTimestamp();
0816         info.latestMessage = QDateTime::fromMSecsSinceEpoch(endDateTime, Qt::UTC).toString(Qt::ISODateWithMs);
0817         info.oldestMessage = messageDateTimeUtc;
0818         info.inclusive = true;
0819         info.count = 5000;
0820         // qDebug() << " info " << info;
0821         job->setChannelHistoryInfo(info);
0822         mCurrentRocketChatAccount->restApi()->initializeRestApiJob(job);
0823         connect(job, &RocketChatRestApi::ChannelHistoryJob::channelHistoryDone, this, [messageId, messageModel, messageListView, this](const QJsonObject &obj) {
0824             mCurrentRocketChatAccount->rocketChatBackend()->processIncomingMessages(obj.value(QLatin1String("messages")).toArray(), true, true);
0825             const QModelIndex index = messageModel->indexForMessage(messageId);
0826             if (index.isValid()) {
0827                 messageListView->scrollTo(index);
0828             } else {
0829                 qCWarning(RUQOLAWIDGETS_LOG) << "Message not found:" << messageId;
0830             }
0831         });
0832         if (!job->start()) {
0833             qCDebug(RUQOLAWIDGETS_LOG) << "Impossible to start ChannelHistoryJob";
0834         }
0835     }
0836 }
0837 
0838 void RoomWidget::slotClearNotification()
0839 {
0840     mCurrentRocketChatAccount->markRoomAsRead(mRoomWidgetBase->roomId());
0841 }
0842 
0843 void RoomWidget::slotEncryptedChanged(bool b)
0844 {
0845     RocketChatRestApi::SaveRoomSettingsJob::SaveRoomSettingsInfo info;
0846     info.encrypted = b;
0847     info.roomId = mRoomWidgetBase->roomId();
0848     info.roomType = mRoom ? mRoom->roomFromRoomType(mRoom->channelType()) : QString();
0849     info.mSettingsWillBeChanged |= RocketChatRestApi::SaveRoomSettingsJob::SaveRoomSettingsInfo::Encrypted;
0850     auto saveRoomSettingsJob = new RocketChatRestApi::SaveRoomSettingsJob(this);
0851     saveRoomSettingsJob->setSaveRoomSettingsInfo(info);
0852     mCurrentRocketChatAccount->restApi()->initializeRestApiJob(saveRoomSettingsJob);
0853     if (!saveRoomSettingsJob->start()) {
0854         qCDebug(RUQOLAWIDGETS_LOG) << "Impossible to start saveRoomSettingsJob";
0855     }
0856 }
0857 
0858 void RoomWidget::slotChangeFavorite(bool b)
0859 {
0860     mCurrentRocketChatAccount->changeFavorite(mRoomWidgetBase->roomId(), b);
0861 }
0862 
0863 Room::RoomType RoomWidget::roomType() const
0864 {
0865     return mRoomType;
0866 }
0867 
0868 void RoomWidget::setCurrentRocketChatAccount(RocketChatAccount *account)
0869 {
0870     if (mCurrentRocketChatAccount) {
0871         disconnect(mCurrentRocketChatAccount, &RocketChatAccount::openThreadRequested, this, &RoomWidget::slotOpenThreadRequested);
0872         disconnect(mCurrentRocketChatAccount, &RocketChatAccount::displayReconnectWidget, this, &RoomWidget::slotDisplayReconnectWidget);
0873         disconnect(mCurrentRocketChatAccount, &RocketChatAccount::loginStatusChanged, this, &RoomWidget::slotLoginStatusChanged);
0874         disconnect(mCurrentRocketChatAccount, &RocketChatAccount::needUpdateMessageView, this, &RoomWidget::updateListView);
0875     }
0876 
0877     mCurrentRocketChatAccount = account;
0878     mRoomWidgetBase->setCurrentRocketChatAccount(account);
0879     connect(mCurrentRocketChatAccount, &RocketChatAccount::openThreadRequested, this, &RoomWidget::slotOpenThreadRequested);
0880     connect(mCurrentRocketChatAccount, &RocketChatAccount::displayReconnectWidget, this, &RoomWidget::slotDisplayReconnectWidget);
0881     connect(mCurrentRocketChatAccount, &RocketChatAccount::loginStatusChanged, this, &RoomWidget::slotLoginStatusChanged);
0882     connect(mCurrentRocketChatAccount, &RocketChatAccount::needUpdateMessageView, this, &RoomWidget::updateListView);
0883     // TODO verify if we need to show or not reconnect widget
0884     mRoomHeaderWidget->setCurrentRocketChatAccount(account);
0885     mUsersInRoomFlowWidget->setCurrentRocketChatAccount(account);
0886 }
0887 
0888 void RoomWidget::slotLoginStatusChanged()
0889 {
0890     const auto loginStatus = mCurrentRocketChatAccount->loginStatus();
0891     if (loginStatus == DDPAuthenticationManager::LoggedIn) {
0892         if (mRoomReconnectInfoWidget) {
0893             mRoomReconnectInfoWidget->hide();
0894         }
0895     }
0896 }
0897 
0898 void RoomWidget::slotGoBackToRoom()
0899 {
0900     if (mRoom) {
0901         Q_EMIT selectChannelRequested(mRoom->parentRid());
0902     }
0903 }
0904 
0905 void RoomWidget::slotOpenThreadRequested(const QString &threadMessageId,
0906                                          const QString &threadMessagePreview,
0907                                          bool threadIsFollowing,
0908                                          const Message &threadMessage)
0909 {
0910     qCDebug(RUQOLA_THREAD_MESSAGE_WIDGETS_LOG) << "threadMessageId: " << threadMessageId;
0911     auto dlg = new ThreadMessageDialog(mCurrentRocketChatAccount, this);
0912     ThreadMessageWidget::ThreadMessageInfo info;
0913     info.threadMessageId = threadMessageId;
0914     info.threadMessagePreview = threadMessagePreview;
0915     info.threadIsFollowing = threadIsFollowing;
0916     info.room = mRoom;
0917     info.messageThread = threadMessage;
0918     dlg->setThreadMessageInfo(info);
0919     dlg->show();
0920 }
0921 
0922 void RoomWidget::setLayoutSpacing(int spacing)
0923 {
0924     mRoomWidgetBase->layout()->setSpacing(spacing);
0925 }
0926 
0927 void RoomWidget::slotTryReconnect()
0928 {
0929     mCurrentRocketChatAccount->reconnectToServer();
0930 }
0931 
0932 void RoomWidget::slotDisplayReconnectWidget(int seconds)
0933 {
0934     // Disable for the moment it seems to create some problems
0935     // FIXME mRoomReconnectInfoWidget->setReconnectSecondDelay(seconds);
0936 }
0937 
0938 void RoomWidget::slotCloseOtr()
0939 {
0940     mCurrentRocketChatAccount->ddp()->streamNotifyUserOtrEnd(roomId(), mCurrentRocketChatAccount->userId());
0941 }
0942 
0943 void RoomWidget::slotRefreshOtrKeys()
0944 {
0945     // TODO
0946 }
0947 
0948 #include "moc_roomwidget.cpp"