2023-02-21 23:48:49 +01:00
// SPDX-FileCopyrightText: Nheko Contributors
2021-03-14 02:45:20 +01:00
//
2021-03-05 00:35:15 +01:00
// SPDX-License-Identifier: GPL-3.0-or-later
2021-07-09 09:36:33 +02:00
import "./components"
2020-10-08 21:11:21 +02:00
import "./delegates"
import "./device-verification"
import "./emoji"
2021-03-12 05:09:57 +01:00
import "./ui"
2021-07-10 17:21:15 +02:00
import "./voip"
2021-03-14 22:22:52 +01:00
import Qt . labs . platform 1.1 as Platform
2022-04-23 01:43:25 +02:00
import QtQuick 2.15
2021-06-13 01:48:11 +02:00
import QtQuick . Controls 2.5
2021-01-12 15:03:39 +01:00
import QtQuick . Layouts 1.3
2022-12-10 16:17:15 +01:00
import QtQuick . Particles 2.15
2021-08-04 02:27:50 +02:00
import QtQuick . Window 2.13
2019-11-30 01:43:39 +01:00
import im . nheko 1.0
2020-05-14 02:19:15 +02:00
import im . nheko . EmojiModel 1.0
2019-09-02 23:28:05 +02:00
2021-05-14 15:23:32 +02:00
Item {
id: timelineView
2021-04-27 11:08:21 +02:00
2021-05-28 22:14:59 +02:00
property var room: null
2021-07-09 09:36:33 +02:00
property var roomPreview: null
2021-06-08 22:18:51 +02:00
property bool showBackButton: false
2023-01-14 02:23:07 +01:00
property bool shouldEffectsRun: false
2023-02-14 02:44:42 +01:00
required property PrivacyScreen privacyScreen
2022-03-27 21:23:40 +02:00
clip: true
2021-05-28 22:14:59 +02:00
2022-12-10 16:17:15 +01:00
onRoomChanged: if ( room != null ) room . triggerSpecialEffects ( )
2022-12-14 19:04:08 +01:00
// focus message input on key press, but not on Ctrl-C and such.
2023-05-13 02:04:35 +02:00
Keys.onPressed: {
if ( event . text && ! topBar . searchHasFocus ) {
TimelineManager . focusMessageInput ( ) ;
room . input . setText ( room . input . text + event . text ) ;
}
}
2022-12-13 18:09:34 +01:00
2022-04-03 02:28:44 +02:00
Shortcut {
sequence: StandardKey . Close
onActivated: Rooms . resetCurrentRoom ( )
}
2021-05-14 15:23:32 +02:00
Label {
2021-11-03 22:35:54 +01:00
visible: ! room && ! TimelineManager . isInitialSync && ( ! roomPreview || ! roomPreview . roomid )
2021-05-14 15:23:32 +02:00
anchors.centerIn: parent
text: qsTr ( "No room open" )
font.pointSize: 24
color: Nheko . colors . text
2021-04-11 16:31:49 +02:00
}
2021-03-12 05:09:57 +01:00
Spinner {
2021-03-15 00:22:33 +01:00
visible: TimelineManager . isInitialSync
2021-05-14 15:23:32 +02:00
anchors.centerIn: parent
2021-06-10 01:01:49 +02:00
foreground: Nheko . colors . mid
2021-05-14 15:23:32 +02:00
running: TimelineManager . isInitialSync
2021-03-12 05:09:57 +01:00
// height is somewhat arbitrary here... don't set width because width scales w/ height
height: parent . height / 16
2021-05-14 15:23:32 +02:00
z: 3
2022-04-20 05:42:47 +02:00
opacity: hh . hovered ? 0.3 : 1
Behavior on opacity {
NumberAnimation { duration: 100 ; }
}
HoverHandler {
id: hh
}
2021-02-21 18:40:21 +01:00
}
2021-05-14 15:23:32 +02:00
ColumnLayout {
id: timelineLayout
2021-04-11 22:24:39 +02:00
2021-06-18 16:22:06 +02:00
visible: room != null && ! room . isSpace
enabled: visible
2020-10-08 21:11:21 +02:00
anchors.fill: parent
2021-05-14 15:23:32 +02:00
spacing: 0
2021-01-12 20:22:52 +01:00
2021-05-14 15:23:32 +02:00
TopBar {
2022-10-06 21:59:59 +02:00
id: topBar
2021-06-08 22:18:51 +02:00
showBackButton: timelineView . showBackButton
2020-10-08 21:11:21 +02:00
}
2021-05-14 15:23:32 +02:00
Rectangle {
Layout.fillWidth: true
height: 1
2020-10-08 21:11:21 +02:00
z: 3
2021-05-14 23:35:34 +02:00
color: Nheko . theme . separator
2020-10-08 21:11:21 +02:00
}
2021-05-14 15:23:32 +02:00
Rectangle {
id: msgView
2021-01-26 06:03:09 +01:00
2021-05-14 15:23:32 +02:00
Layout.fillWidth: true
Layout.fillHeight: true
color: Nheko . colors . base
2020-10-08 21:11:21 +02:00
2021-05-14 15:23:32 +02:00
ColumnLayout {
anchors.fill: parent
spacing: 0
2020-11-15 04:52:49 +01:00
2021-05-14 15:23:32 +02:00
StackLayout {
id: stackLayout
2020-10-08 21:11:21 +02:00
2021-05-14 15:23:32 +02:00
currentIndex: 0
2020-11-15 04:52:49 +01:00
2021-05-14 15:23:32 +02:00
Connections {
2021-05-28 22:14:59 +02:00
function onRoomChanged ( ) {
2021-05-14 15:23:32 +02:00
stackLayout . currentIndex = 0 ;
2020-10-08 21:11:21 +02:00
}
2021-05-28 22:14:59 +02:00
target: timelineView
2021-05-14 15:23:32 +02:00
}
2020-11-15 04:52:49 +01:00
2021-08-29 19:24:44 +02:00
MessageView {
implicitHeight: msgView . height - typingIndicator . height
2022-10-06 21:59:59 +02:00
searchString: topBar . searchString
2021-08-29 19:24:44 +02:00
Layout.fillWidth: true
}
2021-08-11 04:16:27 +02:00
2021-05-14 15:23:32 +02:00
Loader {
source: CallManager . isOnCall && CallManager . callType != CallType . VOICE ? "voip/VideoCall.qml" : ""
onLoaded: TimelineManager . setVideoCallItem ( )
2020-10-27 18:14:06 +01:00
}
2020-10-08 21:11:21 +02:00
}
2021-05-14 15:23:32 +02:00
TypingIndicator {
id: typingIndicator
}
2021-01-11 23:51:39 +01:00
2020-12-17 17:25:32 +01:00
}
2021-05-14 15:23:32 +02:00
}
2020-10-08 21:11:21 +02:00
2021-05-14 15:23:32 +02:00
CallInviteBar {
id: callInviteBar
2020-10-08 21:11:21 +02:00
2021-05-14 15:23:32 +02:00
Layout.fillWidth: true
z: 3
}
2020-10-26 13:50:44 +01:00
2021-05-14 15:23:32 +02:00
ActiveCallBar {
Layout.fillWidth: true
z: 3
}
Rectangle {
Layout.fillWidth: true
z: 3
height: 1
2021-05-14 23:35:34 +02:00
color: Nheko . theme . separator
2021-05-14 15:23:32 +02:00
}
2021-02-03 03:12:08 +01:00
2022-03-21 05:05:29 +01:00
2022-03-21 05:49:12 +01:00
UploadBox {
2022-03-20 22:49:33 +01:00
}
2023-02-24 03:57:53 +01:00
MessageInputWarning {
text: qsTr ( "You are about to notify the whole room" )
2023-02-28 00:33:27 +01:00
visible: ( room && room . permissions . canPingRoom ( ) && room . input . containsAtRoom )
2023-02-24 03:57:53 +01:00
}
MessageInputWarning {
2023-02-28 00:06:24 +01:00
text: qsTr ( "The command /%1 is not recognized and will be sent as part of your message" ) . arg ( room ? room.input.currentCommand : "" )
2023-03-08 01:10:42 +01:00
visible: room ? room . input . containsInvalidCommand && ! room.input.containsIncompleteCommand : false
}
MessageInputWarning {
text: qsTr ( "/%1 looks like an incomplete command. To send it anyway, add a space to the end of your message." ) . arg ( room ? room.input.currentCommand : "" )
visible: room ? room.input.containsIncompleteCommand : false
bubbleColor: Nheko . theme . orange
2021-09-02 03:15:07 +02:00
}
2021-05-14 15:23:32 +02:00
ReplyPopup {
2021-02-03 01:30:03 +01:00
}
2020-10-26 13:50:44 +01:00
2021-05-14 15:23:32 +02:00
MessageInput {
2020-10-08 21:11:21 +02:00
}
2021-02-03 03:12:08 +01:00
}
2021-01-26 05:46:55 +01:00
2021-06-18 16:22:06 +02:00
ColumnLayout {
2021-07-09 09:36:33 +02:00
id: preview
2021-11-03 22:35:54 +01:00
property string roomId: room ? room.roomId : ( roomPreview ? roomPreview.roomid : "" )
2021-07-09 09:36:33 +02:00
property string roomName: room ? room.roomName : ( roomPreview ? roomPreview.roomName : "" )
property string roomTopic: room ? room.roomTopic : ( roomPreview ? roomPreview.roomTopic : "" )
property string avatarUrl: room ? room.roomAvatarUrl : ( roomPreview ? roomPreview.roomAvatarUrl : "" )
2022-12-27 01:40:03 +01:00
property string reason: roomPreview ? roomPreview.reason : ""
2021-07-09 09:36:33 +02:00
visible: room != null && room . isSpace || roomPreview != null
2021-06-18 16:22:06 +02:00
enabled: visible
anchors.fill: parent
anchors.margins: Nheko . paddingLarge
spacing: Nheko . paddingLarge
2021-07-09 09:36:33 +02:00
Item {
Layout.fillHeight: true
}
2021-06-18 16:22:06 +02:00
Avatar {
2021-07-09 09:36:33 +02:00
url: parent . avatarUrl . replace ( "mxc://" , "image://MxcImage/" )
2021-09-08 02:38:39 +02:00
roomid: parent . roomId
2021-07-09 09:36:33 +02:00
displayName: parent . roomName
2021-06-18 16:22:06 +02:00
height: 130
width: 130
Layout.alignment: Qt . AlignHCenter
enabled: false
}
2021-07-17 19:31:38 +02:00
2022-04-23 01:43:25 +02:00
RowLayout {
spacing: Nheko . paddingMedium
2021-06-18 16:22:06 +02:00
Layout.alignment: Qt . AlignHCenter
2022-04-23 01:43:25 +02:00
MatrixText {
2023-03-18 12:46:44 +01:00
text: ! roomPreview . isFetched ? qsTr ( "No preview available" ) : preview . roomName
2022-04-23 01:43:25 +02:00
font.pixelSize: 24
}
ImageButton {
image: ":/icons/icons/ui/settings.svg"
visible: ! ! room
hoverEnabled: true
ToolTip.visible: hovered
ToolTip.text: qsTr ( "Settings" )
onClicked: TimelineManager . openRoomSettings ( room . roomId )
}
2021-06-18 16:22:06 +02:00
}
2022-04-23 01:43:25 +02:00
RowLayout {
2021-07-09 09:36:33 +02:00
visible: ! ! room
2022-04-23 01:43:25 +02:00
spacing: Nheko . paddingMedium
2021-06-18 16:22:06 +02:00
Layout.alignment: Qt . AlignHCenter
2022-04-23 01:43:25 +02:00
MatrixText {
2022-04-20 05:43:00 +02:00
text: qsTr ( "%n member(s)" , "" , room ? room.roomMemberCount : 0 )
2022-04-23 01:43:25 +02:00
}
ImageButton {
image: ":/icons/icons/ui/people.svg"
hoverEnabled: true
ToolTip.visible: hovered
2022-04-24 16:37:35 +02:00
ToolTip.text: qsTr ( "View members of %1" ) . arg ( room ? room.roomName : "" )
2022-04-23 01:43:25 +02:00
onClicked: TimelineManager . openRoomMembers ( room )
}
2021-06-18 16:22:06 +02:00
}
ScrollView {
Layout.alignment: Qt . AlignHCenter
2021-07-09 09:36:33 +02:00
Layout.fillWidth: true
Layout.leftMargin: Nheko . paddingLarge
Layout.rightMargin: Nheko . paddingLarge
2021-06-18 16:22:06 +02:00
TextArea {
2023-03-18 15:06:19 +01:00
text: roomPreview . isFetched ? TimelineManager . escapeEmoji ( preview . roomTopic ) : qsTr ( "This room is possibly inaccessible. If this room is private, you should remove it from this community." )
2021-06-18 16:22:06 +02:00
wrapMode: TextEdit . WordWrap
textFormat: TextEdit . RichText
readOnly: true
background: null
selectByMouse: true
color: Nheko . colors . text
horizontalAlignment: TextEdit . AlignHCenter
onLinkActivated: Nheko . openLink ( link )
CursorShape {
anchors.fill: parent
cursorShape: parent . hoveredLink ? Qt.PointingHandCursor : Qt . ArrowCursor
}
}
}
2021-07-09 09:36:33 +02:00
FlatButton {
visible: roomPreview && ! roomPreview . isInvite
Layout.alignment: Qt . AlignHCenter
text: qsTr ( "join the conversation" )
onClicked: Rooms . joinPreview ( roomPreview . roomid )
}
FlatButton {
visible: roomPreview && roomPreview . isInvite
Layout.alignment: Qt . AlignHCenter
text: qsTr ( "accept invite" )
onClicked: Rooms . acceptInvite ( roomPreview . roomid )
}
FlatButton {
visible: roomPreview && roomPreview . isInvite
Layout.alignment: Qt . AlignHCenter
text: qsTr ( "decline invite" )
onClicked: Rooms . declineInvite ( roomPreview . roomid )
}
2023-03-02 16:30:59 +01:00
FlatButton {
visible: ! ! room
Layout.alignment: Qt . AlignHCenter
text: qsTr ( "leave" )
onClicked: TimelineManager . openLeaveRoomDialog ( room . roomId )
}
2022-12-27 01:40:03 +01:00
ScrollView {
id: reasonField
property bool showReason: false
Layout.alignment: Qt . AlignHCenter
Layout.fillWidth: true
Layout.leftMargin: Nheko . paddingLarge
Layout.rightMargin: Nheko . paddingLarge
visible: preview . reason !== "" && showReason
TextArea {
text: TimelineManager . escapeEmoji ( preview . reason )
wrapMode: TextEdit . WordWrap
textFormat: TextEdit . RichText
readOnly: true
background: null
selectByMouse: true
color: Nheko . colors . text
horizontalAlignment: TextEdit . AlignHCenter
}
}
Button {
id: showReasonButton
Layout.alignment: Qt . AlignHCenter
//Layout.fillWidth: true
Layout.leftMargin: Nheko . paddingLarge
Layout.rightMargin: Nheko . paddingLarge
visible: preview . reason !== ""
text: reasonField . showReason ? qsTr ( "Hide invite reason" ) : qsTr ( "Show invite reason" )
onClicked: {
reasonField . showReason = ! reasonField . showReason ;
}
}
2021-07-09 09:36:33 +02:00
Item {
visible: room != null
Layout.preferredHeight: Math . ceil ( fontMetrics . lineSpacing * 2 )
}
2021-06-18 16:22:06 +02:00
Item {
Layout.fillHeight: true
}
}
2021-06-18 16:40:40 +02:00
ImageButton {
id: backToRoomsButton
anchors.top: parent . top
anchors.left: parent . left
anchors.margins: Nheko . paddingMedium
width: Nheko . avatarSize
height: Nheko . avatarSize
2021-11-03 22:35:54 +01:00
visible: ( room == null || room . isSpace ) && showBackButton
2021-06-18 16:40:40 +02:00
enabled: visible
2021-11-14 02:23:10 +01:00
image: ":/icons/icons/ui/angle-arrow-left.svg"
2021-06-18 16:40:40 +02:00
ToolTip.visible: hovered
ToolTip.text: qsTr ( "Back to room list" )
onClicked: Rooms . resetCurrentRoom ( )
}
2023-03-24 14:36:42 +01:00
TimelineEffects {
id: timelineEffects
2023-03-07 19:11:00 +01:00
2022-12-10 16:17:15 +01:00
anchors.fill: parent
2023-03-07 19:11:00 +01:00
}
2021-05-14 15:23:32 +02:00
NhekoDropArea {
2021-02-03 03:12:08 +01:00
anchors.fill: parent
2021-07-17 22:56:56 +02:00
roomid: room ? room.roomId : ""
2021-05-28 22:14:59 +02:00
}
2023-01-14 02:23:07 +01:00
Timer {
id: effectsTimer
onTriggered: shouldEffectsRun = false ;
2023-03-24 14:36:42 +01:00
interval: timelineEffects . maxLifespan
2023-01-14 02:23:07 +01:00
repeat: false
running: false
}
2021-07-30 13:24:48 +02:00
Connections {
function onOpenReadReceiptsDialog ( rr ) {
var dialog = readReceiptsDialog . createObject ( timelineRoot , {
"readReceipts" : rr ,
"room" : room
} ) ;
dialog . show ( ) ;
2022-02-21 05:01:01 +01:00
timelineRoot . destroyOnClose ( dialog ) ;
2021-07-30 13:24:48 +02:00
}
2021-07-31 04:13:58 +02:00
function onShowRawMessageDialog ( rawMessage ) {
2023-02-21 14:32:35 +01:00
var component = Qt . createComponent ( "qrc:/qml/dialogs/RawMessageDialog.qml" )
if ( component . status == Component . Ready ) {
var dialog = component . createObject ( timelineRoot , {
"rawMessage" : rawMessage
} ) ;
dialog . show ( ) ;
timelineRoot . destroyOnClose ( dialog ) ;
} else {
console . error ( "Failed to create component: " + component . errorString ( ) ) ;
}
2021-07-31 04:13:58 +02:00
}
2022-12-10 16:17:15 +01:00
function onConfetti ( )
{
if ( ! Settings . fancyEffects )
return
2023-01-14 02:23:07 +01:00
shouldEffectsRun = true ;
2023-03-24 14:36:42 +01:00
timelineEffects . pulseConfetti ( )
2022-12-10 16:17:15 +01:00
room . markSpecialEffectsDone ( )
}
2023-01-14 02:23:07 +01:00
function onConfettiDone ( )
{
if ( ! Settings . fancyEffects )
return
2023-03-07 19:11:00 +01:00
effectsTimer . restart ( ) ;
}
function onRainfall ( )
{
if ( ! Settings . fancyEffects )
return
shouldEffectsRun = true ;
2023-03-24 14:36:42 +01:00
timelineEffects . pulseRainfall ( )
2023-03-07 19:11:00 +01:00
room . markSpecialEffectsDone ( )
}
function onRainfallDone ( )
{
if ( ! Settings . fancyEffects )
return
effectsTimer . restart ( ) ;
2023-01-14 02:23:07 +01:00
}
2021-07-30 13:24:48 +02:00
target: room
}
2019-08-30 19:29:25 +02:00
}