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