2019-12-19 22:46:16 +11:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
|
2019-07-13 19:39:01 +10:00
|
|
|
import QtQuick 2.12
|
2020-03-08 02:11:32 +11:00
|
|
|
import QtQuick.Layouts 1.12
|
2020-03-13 04:30:46 +11:00
|
|
|
import QtQuick.Window 2.12
|
2020-03-27 12:23:43 +11:00
|
|
|
import Clipboard 0.1
|
2019-12-03 07:29:29 +11:00
|
|
|
import "../../.."
|
2019-12-18 19:53:08 +11:00
|
|
|
import "../../../Base"
|
2020-03-28 22:18:00 +11:00
|
|
|
import "../../../ShortcutBundles"
|
2019-03-22 14:28:14 +11:00
|
|
|
|
2019-08-28 12:46:31 +10:00
|
|
|
Rectangle {
|
2020-03-25 02:26:17 +11:00
|
|
|
color: theme.chat.eventList.background
|
|
|
|
|
|
|
|
|
2019-09-01 20:56:03 +10:00
|
|
|
property alias eventList: eventList
|
2019-07-07 07:29:32 +10:00
|
|
|
|
2019-04-29 01:01:38 +10:00
|
|
|
|
2020-03-26 14:06:51 +11:00
|
|
|
HShortcut {
|
2020-03-27 22:06:38 +11:00
|
|
|
sequences: window.settings.keys.unfocusOrDeselectAllMessages
|
2020-03-27 19:49:01 +11:00
|
|
|
onActivated: {
|
2020-04-03 22:48:41 +11:00
|
|
|
eventList.selectedCount ?
|
|
|
|
eventList.checked = {} :
|
|
|
|
eventList.currentIndex = -1
|
2020-03-27 19:49:01 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HShortcut {
|
|
|
|
sequences: window.settings.keys.focusPreviousMessage
|
|
|
|
onActivated: eventList.incrementCurrentIndex()
|
|
|
|
}
|
|
|
|
|
|
|
|
HShortcut {
|
|
|
|
sequences: window.settings.keys.focusNextMessage
|
|
|
|
onActivated:
|
|
|
|
eventList.currentIndex === 0 ?
|
|
|
|
eventList.currentIndex = -1 :
|
|
|
|
eventList.decrementCurrentIndex()
|
|
|
|
}
|
|
|
|
|
|
|
|
HShortcut {
|
2020-04-01 23:01:13 +11:00
|
|
|
active: eventList.currentItem
|
2020-03-27 19:49:01 +11:00
|
|
|
sequences: window.settings.keys.toggleSelectMessage
|
|
|
|
onActivated: eventList.toggleCheck(eventList.currentIndex)
|
|
|
|
}
|
|
|
|
|
|
|
|
HShortcut {
|
2020-04-01 23:01:13 +11:00
|
|
|
active: eventList.currentItem
|
2020-03-27 19:49:01 +11:00
|
|
|
sequences: window.settings.keys.selectMessagesUntilHere
|
|
|
|
onActivated:
|
2020-03-27 20:05:25 +11:00
|
|
|
eventList.checkFromLastToHere(eventList.currentIndex)
|
2020-03-27 19:49:01 +11:00
|
|
|
}
|
|
|
|
|
2020-04-03 01:19:43 +11:00
|
|
|
HShortcut {
|
|
|
|
sequences: window.settings.keys.removeFocusedOrSelectedMessages
|
|
|
|
onActivated: utils.makePopup(
|
|
|
|
"Popups/RedactPopup.qml",
|
|
|
|
chat,
|
|
|
|
{
|
2020-04-03 21:13:45 +11:00
|
|
|
preferUserId: chat.userId,
|
2020-04-03 01:19:43 +11:00
|
|
|
roomId: chat.roomId,
|
2020-04-03 21:13:45 +11:00
|
|
|
|
|
|
|
eventSenderAndIds:
|
2020-04-03 01:19:43 +11:00
|
|
|
(events || findLastRemovableDelegate()).map(
|
2020-04-03 21:50:24 +11:00
|
|
|
ev => [ev.sender_id, ev.id],
|
2020-04-03 01:19:43 +11:00
|
|
|
),
|
2020-04-03 21:13:45 +11:00
|
|
|
|
2020-04-03 01:19:43 +11:00
|
|
|
isLast: ! events,
|
2020-04-03 21:13:45 +11:00
|
|
|
|
2020-04-03 01:19:43 +11:00
|
|
|
onlyOwnMessageWarning:
|
|
|
|
! chat.roomInfo.can_redact_all &&
|
|
|
|
events &&
|
|
|
|
events.length < eventList.selectedCount
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
readonly property var events:
|
|
|
|
eventList.selectedCount ?
|
|
|
|
eventList.redactableCheckedEvents :
|
|
|
|
|
|
|
|
eventList.currentItem &&
|
|
|
|
eventList.canRedact(eventList.currentItem.currentModel) ?
|
|
|
|
[eventList.currentItem.currentModel] :
|
|
|
|
|
|
|
|
null
|
|
|
|
|
|
|
|
function findLastRemovableDelegate() {
|
|
|
|
for (let i = 0; i < eventList.model.count && i <= 1000; i++) {
|
|
|
|
const event = eventList.model.get(i)
|
2020-04-03 03:23:14 +11:00
|
|
|
if (eventList.canRedact(event) &&
|
2020-04-03 21:13:45 +11:00
|
|
|
mainUI.accountIds.includes(event.sender_id)) return [event]
|
2020-04-03 01:19:43 +11:00
|
|
|
}
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 19:49:01 +11:00
|
|
|
HShortcut {
|
2020-04-01 23:01:13 +11:00
|
|
|
active: eventList.currentItem
|
2020-03-27 19:49:01 +11:00
|
|
|
sequences: window.settings.keys.debugFocusedMessage
|
|
|
|
onActivated:
|
|
|
|
eventList.currentItem.eventContent.debugConsoleLoader.toggle()
|
2020-03-26 14:06:51 +11:00
|
|
|
}
|
|
|
|
|
2020-03-28 22:18:00 +11:00
|
|
|
HShortcut {
|
|
|
|
sequences: window.settings.keys.clearRoomMessages
|
|
|
|
onActivated: utils.makePopup(
|
|
|
|
"Popups/ClearMessagesPopup.qml",
|
|
|
|
mainUI,
|
|
|
|
{
|
|
|
|
userId: window.uiState.pageProperties.userId,
|
|
|
|
roomId: window.uiState.pageProperties.roomId,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
FlickShortcuts {
|
2020-04-01 23:01:13 +11:00
|
|
|
active: ! mainUI.debugConsole.visible
|
2020-03-28 22:18:00 +11:00
|
|
|
flickable: eventList
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
HListView {
|
|
|
|
id: eventList
|
|
|
|
clip: true
|
2020-03-27 19:49:01 +11:00
|
|
|
keyNavigationWraps: false
|
2020-03-25 02:26:17 +11:00
|
|
|
|
2019-08-31 01:17:13 +10:00
|
|
|
anchors.fill: parent
|
2020-03-25 02:26:17 +11:00
|
|
|
anchors.leftMargin: theme.spacing
|
|
|
|
anchors.rightMargin: theme.spacing
|
|
|
|
|
|
|
|
topMargin: theme.spacing
|
|
|
|
bottomMargin: theme.spacing
|
|
|
|
verticalLayoutDirection: ListView.BottomToTop
|
|
|
|
|
|
|
|
// Keep x scroll pages cached, to limit images having to be
|
|
|
|
// reloaded from network.
|
|
|
|
cacheBuffer: Screen.desktopAvailableHeight * 2
|
|
|
|
|
|
|
|
model: ModelStore.get(chat.userId, chat.roomId, "events")
|
|
|
|
delegate: EventDelegate {}
|
|
|
|
|
2020-03-27 20:09:49 +11:00
|
|
|
highlight: Rectangle {
|
|
|
|
color: theme.chat.message.focusedHighlight
|
|
|
|
opacity: theme.chat.message.focusedHighlightOpacity
|
|
|
|
}
|
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
// Since the list is BottomToTop, this is actually a header
|
|
|
|
footer: Item {
|
|
|
|
width: eventList.width
|
|
|
|
height: (button.height + theme.spacing * 2) * opacity
|
|
|
|
opacity: eventList.loading ? 1 : 0
|
|
|
|
visible: opacity > 0
|
|
|
|
|
|
|
|
Behavior on opacity { HNumberAnimation {} }
|
|
|
|
|
|
|
|
HButton {
|
|
|
|
id: button
|
|
|
|
width: Math.min(parent.width, implicitWidth)
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
|
|
|
loading: true
|
|
|
|
text: qsTr("Loading previous messages...")
|
|
|
|
enableRadius: true
|
|
|
|
iconItem.small: true
|
2019-09-12 05:25:57 +10:00
|
|
|
}
|
2019-07-20 13:07:26 +10:00
|
|
|
}
|
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
onYPosChanged:
|
|
|
|
if (canLoad && yPos < 0.1) Qt.callLater(loadPastEvents)
|
2019-07-20 13:07:26 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
// When an invited room becomes joined, we should now be able to
|
|
|
|
// fetch past events.
|
|
|
|
onInviterChanged: canLoad = true
|
2020-03-08 02:11:32 +11:00
|
|
|
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
property string inviter: chat.roomInfo.inviter || ""
|
|
|
|
property real yPos: visibleArea.yPosition
|
|
|
|
property bool canLoad: true
|
|
|
|
property bool loading: false
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
property bool ownEventsOnRight:
|
|
|
|
width < theme.chat.eventList.ownEventsOnRightUnderWidth
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-27 11:31:19 +11:00
|
|
|
property string delegateWithSelectedText: ""
|
2020-03-27 12:23:43 +11:00
|
|
|
property string selectedText: ""
|
2020-03-27 11:31:19 +11:00
|
|
|
|
2020-04-03 01:19:43 +11:00
|
|
|
readonly property var redactableCheckedEvents:
|
|
|
|
getSortedChecked().filter(ev => eventList.canRedact(ev))
|
|
|
|
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-27 12:23:43 +11:00
|
|
|
function copySelectedDelegates() {
|
|
|
|
if (eventList.selectedText) {
|
|
|
|
Clipboard.text = eventList.selectedText
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-03-27 19:49:01 +11:00
|
|
|
if (! eventList.selectedCount && eventList.currentIndex !== -1) {
|
2020-03-27 19:58:49 +11:00
|
|
|
const model = eventList.model.get(eventList.currentIndex)
|
|
|
|
const source = JSON.parse(model.source)
|
|
|
|
|
|
|
|
Clipboard.text =
|
|
|
|
"body" in source ?
|
|
|
|
source.body :
|
|
|
|
utils.stripHtmlTags(utils.processedEventText(model))
|
|
|
|
|
2020-03-27 19:49:01 +11:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-03-27 12:23:43 +11:00
|
|
|
const contents = []
|
|
|
|
|
2020-03-27 20:05:25 +11:00
|
|
|
for (const model of eventList.getSortedChecked()) {
|
2020-03-27 20:38:08 +11:00
|
|
|
const source = JSON.parse(model.source)
|
|
|
|
|
|
|
|
contents.push(
|
|
|
|
"body" in source ?
|
|
|
|
source.body :
|
|
|
|
utils.stripHtmlTags(utils.processedEventText(model))
|
|
|
|
)
|
2020-03-27 12:23:43 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
Clipboard.text = contents.join("\n\n")
|
|
|
|
}
|
|
|
|
|
2020-04-03 01:19:43 +11:00
|
|
|
function canRedact(eventModel) {
|
|
|
|
return eventModel.event_type !== "RedactedEvent" &&
|
|
|
|
(chat.roomInfo.can_redact_all ||
|
2020-04-03 21:13:45 +11:00
|
|
|
mainUI.accountIds.includes(eventModel.sender_id))
|
2020-04-03 01:19:43 +11:00
|
|
|
}
|
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
function canCombine(item, itemAfter) {
|
|
|
|
if (! item || ! itemAfter) return false
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
return Boolean(
|
|
|
|
! canTalkBreak(item, itemAfter) &&
|
|
|
|
! canDayBreak(item, itemAfter) &&
|
|
|
|
item.sender_id === itemAfter.sender_id &&
|
|
|
|
utils.minutesBetween(item.date, itemAfter.date) <= 5
|
|
|
|
)
|
|
|
|
}
|
2019-08-16 01:46:40 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
function canTalkBreak(item, itemAfter) {
|
|
|
|
if (! item || ! itemAfter) return false
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
return Boolean(
|
|
|
|
! canDayBreak(item, itemAfter) &&
|
|
|
|
utils.minutesBetween(item.date, itemAfter.date) >= 20
|
|
|
|
)
|
|
|
|
}
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
function canDayBreak(item, itemAfter) {
|
|
|
|
if (itemAfter && itemAfter.event_type === "RoomCreateEvent")
|
|
|
|
return true
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
if (! item || ! itemAfter || ! item.date || ! itemAfter.date)
|
|
|
|
return false
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
return item.date.getDate() !== itemAfter.date.getDate()
|
|
|
|
}
|
2019-08-31 01:17:13 +10:00
|
|
|
|
2020-03-25 02:26:17 +11:00
|
|
|
function loadPastEvents() {
|
|
|
|
// try/catch blocks to hide pyotherside error when the
|
|
|
|
// component is destroyed but func is still running
|
|
|
|
|
|
|
|
try {
|
|
|
|
eventList.canLoad = false
|
|
|
|
eventList.loading = true
|
|
|
|
|
|
|
|
py.callClientCoro(
|
|
|
|
chat.userId,
|
|
|
|
"load_past_events",
|
|
|
|
[chat.roomId],
|
|
|
|
moreToLoad => {
|
|
|
|
try {
|
|
|
|
eventList.canLoad = moreToLoad
|
|
|
|
|
|
|
|
// Call yPosChanged() to run this func again
|
|
|
|
// if the loaded messages aren't enough to fill
|
|
|
|
// the screen.
|
|
|
|
if (moreToLoad) yPosChanged()
|
|
|
|
|
|
|
|
eventList.loading = false
|
|
|
|
} catch (err) {
|
|
|
|
return
|
2019-08-31 01:17:13 +10:00
|
|
|
}
|
2020-03-25 02:26:17 +11:00
|
|
|
}
|
|
|
|
)
|
|
|
|
} catch (err) {
|
|
|
|
return
|
2019-08-31 01:17:13 +10:00
|
|
|
}
|
|
|
|
}
|
2019-03-22 14:28:14 +11:00
|
|
|
}
|
2019-04-23 01:31:06 +10:00
|
|
|
|
2019-05-03 04:20:21 +10:00
|
|
|
HNoticePage {
|
2019-12-05 00:32:07 +11:00
|
|
|
text: qsTr("No messages to show yet")
|
2019-04-29 04:20:30 +10:00
|
|
|
|
2019-07-14 10:15:20 +10:00
|
|
|
visible: eventList.model.count < 1
|
2019-05-03 04:20:21 +10:00
|
|
|
anchors.fill: parent
|
2019-04-23 01:31:06 +10:00
|
|
|
}
|
2019-03-22 14:28:14 +11:00
|
|
|
}
|