1 /**************************************************************************
2 * *
3 * Copyright (C) 2016 Felix Rohrbach <kde@fxrh.de> *
4 * *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 3 *
8 * of the License, or (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 * *
18 **************************************************************************/
19
20 #include "quaternionroom.h"
21
22 #include <user.h>
23 #include <events/roommessageevent.h>
24 #include <QtCore/QRegularExpression>
25
26 using namespace Quotient;
27
QuaternionRoom(Connection * connection,QString roomId,JoinState joinState)28 QuaternionRoom::QuaternionRoom(Connection* connection, QString roomId,
29 JoinState joinState)
30 : Room(connection, std::move(roomId), joinState)
31 {
32 connect(this, &QuaternionRoom::notificationCountChanged,
33 this, &QuaternionRoom::countChanged);
34 connect(this, &QuaternionRoom::highlightCountChanged,
35 this, &QuaternionRoom::countChanged);
36 connect(this, &Room::namesChanged,
37 this, &QuaternionRoom::htmlSafeDisplayNameChanged);
38 }
39
cachedUserFilter() const40 const QString& QuaternionRoom::cachedUserFilter() const
41 {
42 return m_cachedUserFilter;
43 }
44
setCachedUserFilter(const QString & input)45 void QuaternionRoom::setCachedUserFilter(const QString& input)
46 {
47 m_cachedUserFilter = input;
48 }
49
isEventHighlighted(const RoomEvent * e) const50 bool QuaternionRoom::isEventHighlighted(const RoomEvent* e) const
51 {
52 return highlights.contains(e);
53 }
54
savedTopVisibleIndex() const55 int QuaternionRoom::savedTopVisibleIndex() const
56 {
57 return firstDisplayedMarker() == timelineEdge() ? 0 :
58 firstDisplayedMarker() - messageEvents().rbegin();
59 }
60
savedBottomVisibleIndex() const61 int QuaternionRoom::savedBottomVisibleIndex() const
62 {
63 return lastDisplayedMarker() == timelineEdge() ? 0 :
64 lastDisplayedMarker() - messageEvents().rbegin();
65 }
66
saveViewport(int topIndex,int bottomIndex)67 void QuaternionRoom::saveViewport(int topIndex, int bottomIndex)
68 {
69 if (topIndex == -1 || bottomIndex == -1
70 || (bottomIndex == savedBottomVisibleIndex()
71 && (bottomIndex == 0 || topIndex == savedTopVisibleIndex())))
72 return;
73 if (bottomIndex == 0) {
74 qDebug() << "Saving viewport as the latest available";
75 setFirstDisplayedEventId({});
76 setLastDisplayedEventId({});
77 return;
78 }
79 qDebug() << "Saving viewport:" << topIndex << "thru" << bottomIndex;
80 setFirstDisplayedEvent(maxTimelineIndex() - topIndex);
81 setLastDisplayedEvent(maxTimelineIndex() - bottomIndex);
82 }
83
htmlSafeDisplayName() const84 QString QuaternionRoom::htmlSafeDisplayName() const
85 {
86 return displayName().toHtmlEscaped();
87 }
88
countChanged()89 void QuaternionRoom::countChanged()
90 {
91 if (displayed() && !hasUnreadMessages()) {
92 resetNotificationCount();
93 resetHighlightCount();
94 }
95 }
96
onAddNewTimelineEvents(timeline_iter_t from)97 void QuaternionRoom::onAddNewTimelineEvents(timeline_iter_t from)
98 {
99 std::for_each(from, messageEvents().cend(),
100 [this](const TimelineItem& ti) { checkForHighlights(ti); });
101 }
102
onAddHistoricalTimelineEvents(rev_iter_t from)103 void QuaternionRoom::onAddHistoricalTimelineEvents(rev_iter_t from)
104 {
105 std::for_each(from, messageEvents().crend(),
106 [this](const TimelineItem& ti) { checkForHighlights(ti); });
107 }
108
checkForHighlights(const Quotient::TimelineItem & ti)109 void QuaternionRoom::checkForHighlights(const Quotient::TimelineItem& ti)
110 {
111 auto localUserId = localUser()->id();
112 if (ti->senderId() == localUserId)
113 return;
114 if (auto* e = ti.viewAs<RoomMessageEvent>()) {
115 constexpr auto ReOpt = QRegularExpression::MultilineOption
116 | QRegularExpression::CaseInsensitiveOption;
117 constexpr auto MatchOpt = QRegularExpression::PartialPreferFirstMatch;
118 const QRegularExpression localUserRe(
119 "(\\W|^)" + localUserId + "(\\W|$)", ReOpt);
120 // FIXME: unravels if the room member name contains characters special
121 // to regexp($, e.g.)
122 const QRegularExpression roomMembernameRe(
123 "(\\W|^)" + roomMembername(localUserId) + "(\\W|$)", ReOpt);
124 const auto& text = e->plainBody();
125 const auto& localMatch = localUserRe.match(text, 0, MatchOpt);
126 const auto& roomMemberMatch = roomMembernameRe.match(text, 0, MatchOpt);
127 if (localMatch.hasMatch() || roomMemberMatch.hasMatch())
128 highlights.insert(e);
129 }
130 }
131