Reorganize TimelineView to prepare porting the room list
This commit is contained in:
parent
5658be5215
commit
39a43ad4ab
48
resources/qml/ChatPage.qml
Normal file
48
resources/qml/ChatPage.qml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import im.nheko 1.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: chatPage
|
||||||
|
|
||||||
|
color: Nheko.colors.window
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
SplitView.minimumWidth: Nheko.avatarSize + Nheko.paddingSmall * 2
|
||||||
|
SplitView.preferredWidth: Nheko.avatarSize + Nheko.paddingSmall * 2
|
||||||
|
SplitView.maximumWidth: Nheko.avatarSize + Nheko.paddingSmall * 2
|
||||||
|
color: "blue"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
SplitView.minimumWidth: Nheko.avatarSize * 3 + Nheko.paddingSmall * 2
|
||||||
|
SplitView.preferredWidth: Nheko.avatarSize * 3 + Nheko.paddingSmall * 2
|
||||||
|
SplitView.maximumWidth: Nheko.avatarSize * 7 + Nheko.paddingSmall * 2
|
||||||
|
color: "red"
|
||||||
|
}
|
||||||
|
|
||||||
|
TimelineView {
|
||||||
|
id: timeline
|
||||||
|
|
||||||
|
SplitView.fillWidth: true
|
||||||
|
SplitView.minimumWidth: 400
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivacyScreen {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: Settings.privacyScreen
|
||||||
|
screenTimeout: Settings.privacyScreenTimeout
|
||||||
|
timelineRoot: timeline
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -21,7 +21,7 @@ Popup {
|
|||||||
modal: true
|
modal: true
|
||||||
palette: Nheko.colors
|
palette: Nheko.colors
|
||||||
parent: Overlay.overlay
|
parent: Overlay.overlay
|
||||||
width: implicitWidth >= (timelineRoot.width * 0.8) ? implicitWidth : (timelineRoot.width * 0.8)
|
width: implicitWidth >= (timelineView.width * 0.8) ? implicitWidth : (timelineView.width * 0.8)
|
||||||
height: implicitHeight + completerPopup.height + padding * 2
|
height: implicitHeight + completerPopup.height + padding * 2
|
||||||
leftPadding: 10
|
leftPadding: 10
|
||||||
rightPadding: 10
|
rightPadding: 10
|
||||||
|
260
resources/qml/Root.qml
Normal file
260
resources/qml/Root.qml
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import "./delegates"
|
||||||
|
import "./device-verification"
|
||||||
|
import "./emoji"
|
||||||
|
import "./voip"
|
||||||
|
import Qt.labs.platform 1.1 as Platform
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import im.nheko 1.0
|
||||||
|
import im.nheko.EmojiModel 1.0
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: timelineRoot
|
||||||
|
|
||||||
|
palette: Nheko.colors
|
||||||
|
|
||||||
|
FontMetrics {
|
||||||
|
id: fontMetrics
|
||||||
|
}
|
||||||
|
|
||||||
|
EmojiPicker {
|
||||||
|
id: emojiPopup
|
||||||
|
|
||||||
|
colors: palette
|
||||||
|
model: TimelineManager.completerFor("allemoji", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: userProfileComponent
|
||||||
|
|
||||||
|
UserProfile {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: roomSettingsComponent
|
||||||
|
|
||||||
|
RoomSettings {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: mobileCallInviteDialog
|
||||||
|
|
||||||
|
CallInvite {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: quickSwitcherComponent
|
||||||
|
|
||||||
|
QuickSwitcher {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: forwardCompleterComponent
|
||||||
|
|
||||||
|
ForwardCompleter {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: "Ctrl+K"
|
||||||
|
onActivated: {
|
||||||
|
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
|
||||||
|
TimelineManager.focusTimeline();
|
||||||
|
quickSwitch.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.Menu {
|
||||||
|
id: messageContextMenu
|
||||||
|
|
||||||
|
property string eventId
|
||||||
|
property string link
|
||||||
|
property string text
|
||||||
|
property int eventType
|
||||||
|
property bool isEncrypted
|
||||||
|
property bool isEditable
|
||||||
|
property bool isSender
|
||||||
|
|
||||||
|
function show(eventId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) {
|
||||||
|
eventId = eventId_;
|
||||||
|
eventType = eventType_;
|
||||||
|
isEncrypted = isEncrypted_;
|
||||||
|
isEditable = isEditable_;
|
||||||
|
isSender = isSender_;
|
||||||
|
if (text_)
|
||||||
|
text = text_;
|
||||||
|
else
|
||||||
|
text = "";
|
||||||
|
if (link_)
|
||||||
|
link = link_;
|
||||||
|
else
|
||||||
|
link = "";
|
||||||
|
if (showAt_)
|
||||||
|
open(showAt_);
|
||||||
|
else
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: messageContextMenu.text
|
||||||
|
enabled: visible
|
||||||
|
text: qsTr("Copy")
|
||||||
|
onTriggered: Clipboard.text = messageContextMenu.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: messageContextMenu.link
|
||||||
|
enabled: visible
|
||||||
|
text: qsTr("Copy link location")
|
||||||
|
onTriggered: Clipboard.text = messageContextMenu.link
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
id: reactionOption
|
||||||
|
|
||||||
|
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.Reaction) : false
|
||||||
|
text: qsTr("React")
|
||||||
|
onTriggered: emojiPopup.show(null, function(emoji) {
|
||||||
|
TimelineManager.queueReactionMessage(messageContextMenu.eventId, emoji);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false
|
||||||
|
text: qsTr("Reply")
|
||||||
|
onTriggered: TimelineManager.timeline.replyAction(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: messageContextMenu.isEditable && (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false)
|
||||||
|
enabled: visible
|
||||||
|
text: qsTr("Edit")
|
||||||
|
onTriggered: TimelineManager.timeline.editAction(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
text: qsTr("Read receipts")
|
||||||
|
onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage
|
||||||
|
text: qsTr("Forward")
|
||||||
|
onTriggered: {
|
||||||
|
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
|
||||||
|
forwardMess.setMessageEventId(messageContextMenu.eventId);
|
||||||
|
forwardMess.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
text: qsTr("Mark as read")
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
text: qsTr("View raw message")
|
||||||
|
onTriggered: TimelineManager.timeline.viewRawMessage(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
|
||||||
|
visible: messageContextMenu.isEncrypted
|
||||||
|
enabled: visible
|
||||||
|
text: qsTr("View decrypted raw message")
|
||||||
|
onTriggered: TimelineManager.timeline.viewDecryptedRawMessage(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canRedact() : false) || messageContextMenu.isSender
|
||||||
|
text: qsTr("Remove message")
|
||||||
|
onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
|
||||||
|
enabled: visible
|
||||||
|
text: qsTr("Save as")
|
||||||
|
onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
|
||||||
|
enabled: visible
|
||||||
|
text: qsTr("Open in external program")
|
||||||
|
onTriggered: TimelineManager.timeline.openMedia(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.MenuItem {
|
||||||
|
visible: messageContextMenu.eventId
|
||||||
|
enabled: visible
|
||||||
|
text: qsTr("Copy link to event")
|
||||||
|
onTriggered: TimelineManager.timeline.copyLinkToEvent(messageContextMenu.eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: deviceVerificationDialog
|
||||||
|
|
||||||
|
DeviceVerification {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: TimelineManager
|
||||||
|
onNewDeviceVerificationRequest: {
|
||||||
|
var dialog = deviceVerificationDialog.createObject(timelineRoot, {
|
||||||
|
"flow": flow
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
onOpenProfile: {
|
||||||
|
var userProfile = userProfileComponent.createObject(timelineRoot, {
|
||||||
|
"profile": profile
|
||||||
|
});
|
||||||
|
userProfile.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: TimelineManager.timeline
|
||||||
|
onOpenRoomSettingsDialog: {
|
||||||
|
var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
|
||||||
|
"roomSettings": settings
|
||||||
|
});
|
||||||
|
roomSettings.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: CallManager
|
||||||
|
onNewInviteState: {
|
||||||
|
if (CallManager.haveCallInvite && Settings.mobileMode) {
|
||||||
|
var dialog = mobileCallInviteDialog.createObject(msgView);
|
||||||
|
dialog.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatPage {
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -9,370 +9,123 @@ import "./voip"
|
|||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.13
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
import im.nheko.EmojiModel 1.0
|
import im.nheko.EmojiModel 1.0
|
||||||
|
|
||||||
Page {
|
Item {
|
||||||
id: timelineRoot
|
id: timelineView
|
||||||
|
|
||||||
palette: Nheko.colors
|
Label {
|
||||||
|
visible: !TimelineManager.timeline && !TimelineManager.isInitialSync
|
||||||
FontMetrics {
|
anchors.centerIn: parent
|
||||||
id: fontMetrics
|
text: qsTr("No room open")
|
||||||
|
font.pointSize: 24
|
||||||
|
color: Nheko.colors.text
|
||||||
}
|
}
|
||||||
|
|
||||||
EmojiPicker {
|
BusyIndicator {
|
||||||
id: emojiPopup
|
visible: running
|
||||||
|
anchors.centerIn: parent
|
||||||
colors: palette
|
running: TimelineManager.isInitialSync
|
||||||
model: TimelineManager.completerFor("allemoji", "")
|
height: 200
|
||||||
|
width: 200
|
||||||
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
ColumnLayout {
|
||||||
id: userProfileComponent
|
id: timelineLayout
|
||||||
|
|
||||||
UserProfile {
|
visible: TimelineManager.timeline != null
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: roomSettingsComponent
|
|
||||||
|
|
||||||
RoomSettings {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: mobileCallInviteDialog
|
|
||||||
|
|
||||||
CallInvite {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: quickSwitcherComponent
|
|
||||||
|
|
||||||
QuickSwitcher {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: forwardCompleterComponent
|
|
||||||
|
|
||||||
ForwardCompleter {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Shortcut {
|
|
||||||
sequence: "Ctrl+K"
|
|
||||||
onActivated: {
|
|
||||||
var quickSwitch = quickSwitcherComponent.createObject(timelineRoot);
|
|
||||||
TimelineManager.focusTimeline();
|
|
||||||
quickSwitch.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.Menu {
|
|
||||||
id: messageContextMenu
|
|
||||||
|
|
||||||
property string eventId
|
|
||||||
property string link
|
|
||||||
property string text
|
|
||||||
property int eventType
|
|
||||||
property bool isEncrypted
|
|
||||||
property bool isEditable
|
|
||||||
property bool isSender
|
|
||||||
|
|
||||||
function show(eventId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) {
|
|
||||||
eventId = eventId_;
|
|
||||||
eventType = eventType_;
|
|
||||||
isEncrypted = isEncrypted_;
|
|
||||||
isEditable = isEditable_;
|
|
||||||
isSender = isSender_;
|
|
||||||
if (text_)
|
|
||||||
text = text_;
|
|
||||||
else
|
|
||||||
text = "";
|
|
||||||
if (link_)
|
|
||||||
link = link_;
|
|
||||||
else
|
|
||||||
link = "";
|
|
||||||
if (showAt_)
|
|
||||||
open(showAt_);
|
|
||||||
else
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: messageContextMenu.text
|
|
||||||
enabled: visible
|
|
||||||
text: qsTr("Copy")
|
|
||||||
onTriggered: Clipboard.text = messageContextMenu.text
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: messageContextMenu.link
|
|
||||||
enabled: visible
|
|
||||||
text: qsTr("Copy link location")
|
|
||||||
onTriggered: Clipboard.text = messageContextMenu.link
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
id: reactionOption
|
|
||||||
|
|
||||||
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.Reaction) : false
|
|
||||||
text: qsTr("React")
|
|
||||||
onTriggered: emojiPopup.show(null, function(emoji) {
|
|
||||||
TimelineManager.queueReactionMessage(messageContextMenu.eventId, emoji);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false
|
|
||||||
text: qsTr("Reply")
|
|
||||||
onTriggered: TimelineManager.timeline.replyAction(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: messageContextMenu.isEditable && (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false)
|
|
||||||
enabled: visible
|
|
||||||
text: qsTr("Edit")
|
|
||||||
onTriggered: TimelineManager.timeline.editAction(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
text: qsTr("Read receipts")
|
|
||||||
onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage
|
|
||||||
text: qsTr("Forward")
|
|
||||||
onTriggered: {
|
|
||||||
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
|
|
||||||
forwardMess.setMessageEventId(messageContextMenu.eventId);
|
|
||||||
forwardMess.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
text: qsTr("Mark as read")
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
text: qsTr("View raw message")
|
|
||||||
onTriggered: TimelineManager.timeline.viewRawMessage(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
|
|
||||||
visible: messageContextMenu.isEncrypted
|
|
||||||
enabled: visible
|
|
||||||
text: qsTr("View decrypted raw message")
|
|
||||||
onTriggered: TimelineManager.timeline.viewDecryptedRawMessage(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canRedact() : false) || messageContextMenu.isSender
|
|
||||||
text: qsTr("Remove message")
|
|
||||||
onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
|
|
||||||
enabled: visible
|
|
||||||
text: qsTr("Save as")
|
|
||||||
onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
|
|
||||||
enabled: visible
|
|
||||||
text: qsTr("Open in external program")
|
|
||||||
onTriggered: TimelineManager.timeline.openMedia(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.MenuItem {
|
|
||||||
visible: messageContextMenu.eventId
|
|
||||||
enabled: visible
|
|
||||||
text: qsTr("Copy link to event")
|
|
||||||
onTriggered: TimelineManager.timeline.copyLinkToEvent(messageContextMenu.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Nheko.colors.window
|
spacing: 0
|
||||||
|
|
||||||
Component {
|
|
||||||
id: deviceVerificationDialog
|
|
||||||
|
|
||||||
DeviceVerification {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
TopBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Rectangle {
|
||||||
target: TimelineManager
|
Layout.fillWidth: true
|
||||||
onNewDeviceVerificationRequest: {
|
height: 1
|
||||||
var dialog = deviceVerificationDialog.createObject(timelineRoot, {
|
z: 3
|
||||||
"flow": flow
|
color: Nheko.colors.mid
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
onOpenProfile: {
|
|
||||||
var userProfile = userProfileComponent.createObject(timelineRoot, {
|
|
||||||
"profile": profile
|
|
||||||
});
|
|
||||||
userProfile.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Rectangle {
|
||||||
target: TimelineManager.timeline
|
id: msgView
|
||||||
onOpenRoomSettingsDialog: {
|
|
||||||
var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
|
Layout.fillWidth: true
|
||||||
"roomSettings": settings
|
Layout.fillHeight: true
|
||||||
});
|
color: Nheko.colors.base
|
||||||
roomSettings.show();
|
|
||||||
}
|
ColumnLayout {
|
||||||
}
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
id: stackLayout
|
||||||
|
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onActiveTimelineChanged() {
|
||||||
|
stackLayout.currentIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
target: TimelineManager
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: msgView.height - typingIndicator.height
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
source: CallManager.isOnCall && CallManager.callType != CallType.VOICE ? "voip/VideoCall.qml" : ""
|
||||||
|
onLoaded: TimelineManager.setVideoCallItem()
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: CallManager
|
|
||||||
onNewInviteState: {
|
|
||||||
if (CallManager.haveCallInvite && Settings.mobileMode) {
|
|
||||||
var dialog = mobileCallInviteDialog.createObject(msgView);
|
|
||||||
dialog.open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypingIndicator {
|
||||||
|
id: typingIndicator
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
CallInviteBar {
|
||||||
visible: !TimelineManager.timeline && !TimelineManager.isInitialSync
|
id: callInviteBar
|
||||||
anchors.centerIn: parent
|
|
||||||
text: qsTr("No room open")
|
|
||||||
font.pointSize: 24
|
|
||||||
color: Nheko.colors.text
|
|
||||||
}
|
|
||||||
|
|
||||||
BusyIndicator {
|
Layout.fillWidth: true
|
||||||
visible: running
|
|
||||||
anchors.centerIn: parent
|
|
||||||
running: TimelineManager.isInitialSync
|
|
||||||
height: 200
|
|
||||||
width: 200
|
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ActiveCallBar {
|
||||||
id: timelineLayout
|
Layout.fillWidth: true
|
||||||
|
z: 3
|
||||||
visible: TimelineManager.timeline != null
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
TopBar {
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 1
|
|
||||||
z: 3
|
|
||||||
color: Nheko.colors.mid
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: msgView
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
color: Nheko.colors.base
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
StackLayout {
|
|
||||||
id: stackLayout
|
|
||||||
|
|
||||||
currentIndex: 0
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onActiveTimelineChanged() {
|
|
||||||
stackLayout.currentIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
target: TimelineManager
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
source: CallManager.isOnCall && CallManager.callType != CallType.VOICE ? "voip/VideoCall.qml" : ""
|
|
||||||
onLoaded: TimelineManager.setVideoCallItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TypingIndicator {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
CallInviteBar {
|
|
||||||
id: callInviteBar
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
z: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
ActiveCallBar {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
z: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
z: 3
|
|
||||||
height: 1
|
|
||||||
color: Nheko.colors.mid
|
|
||||||
}
|
|
||||||
|
|
||||||
ReplyPopup {
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageInput {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoDropArea {
|
Rectangle {
|
||||||
anchors.fill: parent
|
Layout.fillWidth: true
|
||||||
roomid: TimelineManager.timeline ? TimelineManager.timeline.roomId() : ""
|
z: 3
|
||||||
|
height: 1
|
||||||
|
color: Nheko.colors.mid
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplyPopup {
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivacyScreen {
|
NhekoDropArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: Settings.privacyScreen
|
roomid: TimelineManager.timeline ? TimelineManager.timeline.roomId() : ""
|
||||||
screenTimeout: Settings.privacyScreenTimeout
|
|
||||||
timelineRoot: timelineLayout
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@ Item {
|
|||||||
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width)
|
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? parent.width : model.data.width)
|
||||||
property double tempHeight: tempWidth * model.data.proportionalHeight
|
property double tempHeight: tempWidth * model.data.proportionalHeight
|
||||||
property double divisor: model.isReply ? 5 : 3
|
property double divisor: model.isReply ? 5 : 3
|
||||||
property bool tooHigh: tempHeight > timelineRoot.height / divisor
|
property bool tooHigh: tempHeight > timelineView.height / divisor
|
||||||
|
|
||||||
height: Math.round(tooHigh ? timelineRoot.height / divisor : tempHeight)
|
height: Math.round(tooHigh ? timelineView.height / divisor : tempHeight)
|
||||||
width: Math.round(tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth)
|
width: Math.round(tooHigh ? (timelineView.height / divisor) / model.data.proportionalHeight : tempWidth)
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: blurhash
|
id: blurhash
|
||||||
|
@ -29,11 +29,11 @@ Rectangle {
|
|||||||
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? 400 : model.data.width)
|
property double tempWidth: Math.min(parent ? parent.width : undefined, model.data.width < 1 ? 400 : model.data.width)
|
||||||
property double tempHeight: tempWidth * model.data.proportionalHeight
|
property double tempHeight: tempWidth * model.data.proportionalHeight
|
||||||
property double divisor: model.isReply ? 4 : 2
|
property double divisor: model.isReply ? 4 : 2
|
||||||
property bool tooHigh: tempHeight > timelineRoot.height / divisor
|
property bool tooHigh: tempHeight > timelineView.height / divisor
|
||||||
|
|
||||||
visible: model.data.type == MtxEvent.VideoMessage
|
visible: model.data.type == MtxEvent.VideoMessage
|
||||||
height: tooHigh ? timelineRoot.height / divisor : tempHeight
|
height: tooHigh ? timelineView.height / divisor : tempHeight
|
||||||
width: tooHigh ? (timelineRoot.height / divisor) / model.data.proportionalHeight : tempWidth
|
width: tooHigh ? (timelineView.height / divisor) / model.data.proportionalHeight : tempWidth
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -11,7 +11,7 @@ MatrixText {
|
|||||||
|
|
||||||
text: "<style type=\"text/css\">a { color:" + Nheko.colors.link + ";}\ncode { background-color: " + Nheko.colors.alternateBase + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + Nheko.colors.alternateBase + "'>")
|
text: "<style type=\"text/css\">a { color:" + Nheko.colors.link + ";}\ncode { background-color: " + Nheko.colors.alternateBase + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + Nheko.colors.alternateBase + "'>")
|
||||||
width: parent ? parent.width : undefined
|
width: parent ? parent.width : undefined
|
||||||
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
|
height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : undefined
|
||||||
clip: isReply
|
clip: isReply
|
||||||
selectByMouse: !Settings.mobileMode && !isReply
|
selectByMouse: !Settings.mobileMode && !isReply
|
||||||
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
||||||
|
@ -123,6 +123,8 @@
|
|||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>qtquickcontrols2.conf</file>
|
<file>qtquickcontrols2.conf</file>
|
||||||
|
|
||||||
|
<file>qml/Root.qml</file>
|
||||||
|
<file>qml/ChatPage.qml</file>
|
||||||
<file>qml/TimelineView.qml</file>
|
<file>qml/TimelineView.qml</file>
|
||||||
<file>qml/Avatar.qml</file>
|
<file>qml/Avatar.qml</file>
|
||||||
<file>qml/Completer.qml</file>
|
<file>qml/Completer.qml</file>
|
||||||
|
@ -257,7 +257,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
|
|||||||
view->engine()->addImageProvider("MxcImage", imgProvider);
|
view->engine()->addImageProvider("MxcImage", imgProvider);
|
||||||
view->engine()->addImageProvider("colorimage", colorImgProvider);
|
view->engine()->addImageProvider("colorimage", colorImgProvider);
|
||||||
view->engine()->addImageProvider("blurhash", blurhashProvider);
|
view->engine()->addImageProvider("blurhash", blurhashProvider);
|
||||||
view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
|
view->setSource(QUrl("qrc:///qml/Root.qml"));
|
||||||
|
|
||||||
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
|
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
|
||||||
connect(parent,
|
connect(parent,
|
||||||
|
@ -14,6 +14,9 @@ class Nheko : public QObject
|
|||||||
Q_PROPERTY(QPalette colors READ colors NOTIFY colorsChanged)
|
Q_PROPERTY(QPalette colors READ colors NOTIFY colorsChanged)
|
||||||
Q_PROPERTY(QPalette inactiveColors READ inactiveColors NOTIFY colorsChanged)
|
Q_PROPERTY(QPalette inactiveColors READ inactiveColors NOTIFY colorsChanged)
|
||||||
Q_PROPERTY(int avatarSize READ avatarSize CONSTANT)
|
Q_PROPERTY(int avatarSize READ avatarSize CONSTANT)
|
||||||
|
Q_PROPERTY(int paddingSmall READ paddingSmall CONSTANT)
|
||||||
|
Q_PROPERTY(int paddingMedium READ paddingMedium CONSTANT)
|
||||||
|
Q_PROPERTY(int paddingLarge READ paddingLarge CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Nheko();
|
Nheko();
|
||||||
@ -23,6 +26,10 @@ public:
|
|||||||
|
|
||||||
int avatarSize() const { return 40; }
|
int avatarSize() const { return 40; }
|
||||||
|
|
||||||
|
int paddingSmall() const { return 4; }
|
||||||
|
int paddingMedium() const { return 8; }
|
||||||
|
int paddingLarge() const { return 20; }
|
||||||
|
|
||||||
Q_INVOKABLE void openLink(QString link) const;
|
Q_INVOKABLE void openLink(QString link) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
Loading…
Reference in New Issue
Block a user