Skip to content
Snippets Groups Projects
Commit 6041f5a8 authored by Nicolas Werner's avatar Nicolas Werner
Browse files

Clean up chat page a bit

parent 2d7fe9a4
No related branches found
No related tags found
No related merge requests found
Showing
with 447 additions and 312 deletions
......@@ -40,6 +40,7 @@ moc_files = qt5.preprocess(moc_headers :
[
'src/client.h',
'src/debug_out.h',
'src/delegate_chooser.h',
'src/dbusactivation.h',
'src/dbusadapter.h',
'src/login.h',
......@@ -57,6 +58,7 @@ sources = [
'src/harbour-spoon.cpp',
'src/client.cpp',
'src/debug_out.cpp',
'src/delegate_chooser.cpp',
'src/dbusactivation.cpp',
'src/dbusadapter.cpp',
'src/login.cpp',
......
......@@ -2,21 +2,21 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
CoverBackground {
// CoverActionList {
// id: coverAction
// CoverAction {
// iconSource: "image://theme/icon-cover-next"
// }
// CoverAction {
// iconSource: "image://theme/icon-cover-pause"
// }
// }
Label {
id: label
anchors.centerIn: parent
text: qsTr("Matrix")
}
// CoverActionList {
// id: coverAction
// CoverAction {
// iconSource: "image://theme/icon-cover-next"
// }
// CoverAction {
// iconSource: "image://theme/icon-cover-pause"
// }
// }
}
......@@ -2,9 +2,14 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
import "pages"
ApplicationWindow
{
initialPage: Component { LoginPage { } }
ApplicationWindow {
cover: Qt.resolvedUrl("cover/CoverPage.qml")
allowedOrientations: defaultAllowedOrientations
initialPage: Component {
LoginPage {
}
}
}
......@@ -9,22 +9,31 @@ Page {
SilicaListView {
id: listView
model: 20
anchors.fill: parent
VerticalScrollDecorator {
}
header: PageHeader {
title: qsTr("Nested Page")
}
delegate: BackgroundItem {
id: delegate
onClicked: console.log("Clicked " + index)
Label {
x: Theme.horizontalPageMargin
text: qsTr("Item") + " " + index
anchors.verticalCenter: parent.verticalCenter
color: delegate.highlighted ? Theme.highlightColor : Theme.primaryColor
}
onClicked: console.log("Clicked " + index)
}
VerticalScrollDecorator {}
}
}
import QtQuick 2.0
import QtQuick.Layouts 1.0
import Sailfish.Silica 1.0
import Sailfish.Silica.private 1.0
import dev.neko.spoon 1.0
Page {
id: chatPage
property alias room: chatView.model
// The effective value will be restricted by ApplicationWindow.allowedOrientations
allowedOrientations: Orientation.All
property alias room: chatView.model
Column {
anchors.fill: parent
PageHeader {
id: header
title: room.roomName()
anchors {
left: parent.left
right: parent.right
}
id: header
title: room.roomName()
Connections {
target: room
onRoomNameChanged: header.title = name
}
}
SilicaListView {
id: chatView
property bool atEnd: false
property int maxIndex: -1
height: parent.height - bot.height - header.height
contentHeight: height
cacheBuffer: 1000
clip: true
ScrollDecorator { flickable: chatView }
boundsBehavior: Flickable.DragOverBounds
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Theme.horizontalPageMargin
anchors.rightMargin: Theme.horizontalPageMargin
property bool atEnd: false
ListView.onAdd: {
console.log("atYEnd::" + atYEnd)
atEnd = atYEnd
console.log("atYEnd::" + atYEnd);
atEnd = atYEnd;
}
onCountChanged: {
console.log("atEnd::" + atEnd)
console.log("atEnd::" + atEnd);
if (atEnd) {
scrollToBottom()
scrollToBottom();
currentIndex = count - 1;
}
}
onMovementEnded: {
atEnd = false
console.log("movement ended")
atEnd = false;
console.log("movement ended");
if (chatView.atYBeginning) {
console.log("load older messages")
room.fetchOlderMessages()
console.log("load older messages");
room.fetchOlderMessages();
}
if (chatView.atYEnd) {
console.log("at end")
atEnd = true
}
else {
console.log("not at end")
console.log("at end");
atEnd = true;
} else {
console.log("not at end");
atEnd = false;
}
var lastIndex = maxIndex
for(var child in chatView.contentItem.children) {
var lastIndex = maxIndex;
for (var child in chatView.contentItem.children) {
if (chatView.contentItem.children[child].isFullyVisible && chatView.contentItem.children[child].index > maxIndex)
lastIndex = chatView.contentItem.children[child].index;
}
if (lastIndex > maxIndex) {
maxIndex = lastIndex
}
}
property int maxIndex: -1
}
if (lastIndex > maxIndex)
maxIndex = lastIndex;
}
onMaxIndexChanged: {
console.log("max index: " + maxIndex)
room.markRead(maxIndex)
console.log("max index: " + maxIndex);
room.markRead(maxIndex);
}
Component.onCompleted: {
maxIndex = room.lastRead();
if (maxIndex = count -1)
if (maxIndex = count - 1)
atEnd = true;
positionViewAtIndex(maxIndex, ListView.Contain);
}
section.property: "UserId"
section.criteria: ViewSection.FullString
ScrollDecorator {
flickable: chatView
}
delegate: ListItem {
id: messageItem
contentHeight: loader.item.height + ts.height
property int yoff: Math.round(messageItem.y - chatView.contentY - 1)
property bool isFullyVisible: yoff + messageItem.height < chatView.height
property int index: model.index
contentHeight: content.height + 2 * Theme.paddingMedium
width: chatView.width
BubbleBackground {
id: bubble
property int fullMessageWidth: parent.width - 2 * Theme.paddingLarge
property int yoff: Math.round(messageItem.y - chatView.contentY - 1)
property bool isFullyVisible: yoff + messageItem.height < chatView.height
property int index: model.index
color: Theme.rgba(Theme.primaryColor, Theme.opacityFaint)
opacity: model.IsSender ? Theme.opacityFaint : Theme.opacityHigh
radius: Theme.paddingLarge
height: content.implicitHeight + 2 * Theme.paddingSmall
width: content.implicitWidth + 2 * Theme.paddingSmall
roundedCorners: BubbleBackground.NoCorners | (model.IsSender ? BubbleBackground.BottomLeft : BubbleBackground.BottomRight)
anchors {
topMargin: Theme.paddingSmall
bottomMargin: Theme.paddingSmall
leftMargin: Theme.paddingMedium
rightMargin: Theme.paddingMedium
right: model.IsSender ? parent.right : undefined
left: !model.IsSender ? parent.left : undefined
top: parent.top
bottom: parent.bottom
}
Loader {
id: loader
height: item.height
source: switch(model.Type) {
case EventType.Aliases: return "delegates/Aliases.qml"
case EventType.Avatar: return "delegates/Avatar.qml"
case EventType.CanonicalAlias: return "delegates/CanonicalAlias.qml"
case EventType.Create: return "delegates/Create.qml"
case EventType.GuestAccess: return "delegates/GuestAccess.qml"
case EventType.HistoryVisibility: return "delegates/HistoryVisibility.qml"
case EventType.JoinRules: return "delegates/JoinRules.qml"
case EventType.Member: return "delegates/Member.qml"
case EventType.Name: return "delegates/Name.qml"
case EventType.PowerLevels: return "delegates/PowerLevels.qml"
case EventType.Topic: return "delegates/Topic.qml"
case EventType.NoticeMessage: return "delegates/NoticeMessage.qml"
case EventType.TextMessage: return "delegates/TextMessage.qml"
case EventType.ImageMessage: return "delegates/ImageMessage.qml"
case EventType.VideoMessage: return "delegates/VideoMessage.qml"
default: return "delegates/placeholder.qml"
Behavior on width {
NumberAnimation {
duration: 100
easing.type: Easing.InOutQuad
}
property variant modelData: model
}
}
ColumnLayout {
id: content
anchors {
margins: Theme.paddingSmall
right: model.IsSender ? bubble.right : undefined
left: !model.IsSender ? bubble.left : undefined
top: bubble.top
bottom: bubble.bottom
}
Message {
id: loader
Layout.alignment: model.IsSender ? Qt.AlignRight : Qt.AlignLeft
modelData: model
opacity: 1
}
Text {
id: ts
anchors.top: loader.bottom
anchors.right: parent.right
text: model.Timestamp.toLocaleTimeString()
Layout.alignment: model.IsSender ? Qt.AlignRight : Qt.AlignLeft
text: model.Timestamp.toLocaleString()
color: Theme.highlightColor
font.pixelSize: Theme.fontSizeTiny
horizontalAlignment: Text.AlignRight
}
}
}
section.property: "UserId"
section.criteria: ViewSection.FullString
section.delegate: Text {
text: room.userIdToUserName(section)
color: room.userColor(section, "#00000000")
......@@ -160,8 +191,9 @@ Page {
id: messageText
anchors.bottom: parent.bottom
placeholderText: qsTr("Enter your message")
label: qsTr("Matrix")
labelVisible: true
wrapMode: Text.Wrap
width: parent.width - sendButton.width
}
......@@ -170,15 +202,17 @@ Page {
id: sendButton
anchors.top: parent.top
icon.source: "image://theme/icon-m-enter"
icon.source: "image://theme/icon-m-send"
onClicked: {
if (messageText.text != "") {
room.sendTextMessage(messageText.text)
}
messageText.text = ""
if (messageText.text != "")
room.sendTextMessage(messageText.text);
messageText.text = "";
}
}
}
}
}
......@@ -6,11 +6,10 @@ Page {
// The effective value will be restricted by ApplicationWindow.allowedOrientations
allowedOrientations: Orientation.All
onStatusChanged: {
if (status === PageStatus.Active) {
if (login.isLoggedIn()) {
pageStack.clear()
pageStack.clear();
pageStack.replace(Qt.resolvedUrl("MainView.qml"));
}
}
......@@ -19,6 +18,8 @@ Page {
// To enable PullDownMenu, place our content in a SilicaFlickable
SilicaFlickable {
anchors.fill: parent
// Tell SilicaFlickable the height of its content.
contentHeight: column.height
// PullDownMenu and PushUpMenu must be declared in SilicaFlickable, SilicaListView or SilicaGridView
PullDownMenu {
......@@ -26,51 +27,50 @@ Page {
text: qsTr("About")
onClicked: pageStack.push(Qt.resolvedUrl("AboutPage.qml"))
}
MenuItem {
text: qsTr("Register")
onClicked: pageStack.push(Qt.resolvedUrl("AboutPage.qml"))
}
}
}
Connections {
target: login
onLoginSuccess: {
pageStack.clear()
pageStack.clear();
pageStack.replace(Qt.resolvedUrl("MainView.qml"));
}
}
// Tell SilicaFlickable the height of its content.
contentHeight: column.height
// Place our content in a Column. The PageHeader is always placed at the top
// of the page, followed by our content.
Column {
id: column
width: page.width
spacing: Theme.paddingLarge
function validateLogin() {
if (!user_parse_error.visible && password.length > 0
&& mxid.length > 0)
pageStack.pushAttached(loggingIn)
if (!user_parse_error.visible && password.length > 0 && mxid.length > 0)
pageStack.pushAttached(loggingIn);
else if (page.forwardNavigation)
pageStack.popAttached(undefined, PageStackAction.Animated)
pageStack.popAttached(undefined, PageStackAction.Animated);
}
width: page.width
spacing: Theme.paddingLarge
PageHeader {
title: qsTr("Login")
}
TextArea {
id: user_parse_error
visible: false
color: "red"
font.pixelSize: 0
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
readOnly: true
anchors {
left: parent.left
right: parent.right
......@@ -79,35 +79,34 @@ Page {
Connections {
target: login
onUserParseError: {
user_parse_error.text = error
user_parse_error.visible = true
user_parse_error.text = error;
user_parse_error.visible = true;
}
}
Connections {
target: login
onHomeserverInvalid: {
user_parse_error.text = error
user_parse_error.visible = true
column.validateLogin()
user_parse_error.text = error;
user_parse_error.visible = true;
column.validateLogin();
}
}
Connections {
target: login
onLoginError: {
user_parse_error.text = error
user_parse_error.visible = true
pageStack.navigateBack()
column.validateLogin()
user_parse_error.text = error;
user_parse_error.visible = true;
pageStack.navigateBack();
column.validateLogin();
}
}
}
TextField {
id: mxid
anchors {
left: parent.left
right: parent.right
}
//: TextField hint for entering matrix id
label: qsTr("@matrix-id:your.homeserver")
......@@ -115,27 +114,28 @@ Page {
EnterKey.enabled: text || inputMethodComposing
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: {
if (homeserver.text.length == 0) {
homeserver.visible = false
}
login.mxidEntered(text)
password.focus = true
column.validateLogin()
if (homeserver.text.length == 0)
homeserver.visible = false;
login.mxidEntered(text);
password.focus = true;
column.validateLogin();
}
inputMethodHints: Qt.ImhNoPredictiveText
anchors {
left: parent.left
right: parent.right
}
validator: RegExpValidator {
regExp: /@[a-z0-9\._=\-/]+:.+/
}
inputMethodHints: Qt.ImhNoPredictiveText
}
PasswordField {
id: password
anchors {
left: parent.left
right: parent.right
}
//: Simply the password prompt
label: qsTr("Password")
......@@ -144,48 +144,68 @@ Page {
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: {
if (homeserver.visible)
homeserver.focus = true
homeserver.focus = true;
column.validateLogin()
column.validateLogin();
}
}
TextField {
id: homeserver
anchors {
left: parent.left
right: parent.right
}
//: TextField hint for entering homeserver
}
TextField {
//inputMethodHints: Qt.ImhNoPredictiveText
id: homeserver
label: qsTr("your.homeserver:optional port")
placeholderText: label
EnterKey.enabled: text || inputMethodComposing
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: {
user_parse_error.visible = false
login.homeserverEntered(text)
column.validateLogin()
}
validator: RegExpValidator {
regExp: /.+/
user_parse_error.visible = false;
login.homeserverEntered(text);
column.validateLogin();
}
visible: false
anchors {
left: parent.left
right: parent.right
}
//: TextField hint for entering homeserver
Connections {
target: login
onHomeserverInvalid: {
homeserver.text = server
homeserver.visible = true
homeserver.text = server;
homeserver.visible = true;
}
}
//inputMethodHints: Qt.ImhNoPredictiveText
validator: RegExpValidator {
regExp: /.+/
}
}
}
}
Component {
id: loggingIn
Page {
onStatusChanged: {
if (status === PageStatus.Activating) {
console.log("Login call from qml");
login.login(mxid.text, password.text, homeserver.text);
}
}
BusyIndicator {
size: BusyIndicatorSize.Large
......@@ -193,12 +213,8 @@ Page {
running: true
}
onStatusChanged: {
if (status === PageStatus.Activating) {
console.log("Login call from qml")
login.login(mxid.text, password.text, homeserver.text)
}
}
}
}
}
......@@ -9,12 +9,15 @@ Page {
Component {
id: roomContextMenu
ContextMenu {
MenuItem {
text: "Remove"
onClicked: highlight()
}
}
}
Connections {
......@@ -24,9 +27,13 @@ Page {
SilicaListView {
id: roomView
anchors.fill: parent
model: rooms
ScrollDecorator { flickable: roomView }
ScrollDecorator {
flickable: roomView
}
// PullDownMenu and PushUpMenu must be declared in SilicaFlickable, SilicaListView or SilicaGridView
PullDownMenu {
......@@ -34,99 +41,112 @@ Page {
text: qsTr("About")
onClicked: pageStack.push(Qt.resolvedUrl("AboutPage.qml"))
}
MenuItem {
text: qsTr("Exit")
onClicked: Qt.quit()
}
MenuItem {
text: qsTr("Logout")
onClicked: login.logout()
}
}
header: Column {
width: parent.width
PageHeader {
title: qsTr("Rooms")
}
SearchField {
width: parent.width
placeholderText: "Search"
onTextChanged: {
rooms.setFilter(text);
}
}
}
model: rooms
}
delegate: ListItem {
id: roomItem
menu: roomContextMenu
onClicked: pageStack.push(Qt.resolvedUrl("ChatPage.qml"), {room: model.roomModel} );
menu: roomContextMenu
onClicked: pageStack.push(Qt.resolvedUrl("ChatPage.qml"), {
"room": model.roomModel
})
contentHeight: mainRow.height + Theme.paddingSmall
contentWidth: parent.width
Row {
id: mainRow
anchors.left: parent.left
anchors.right: parent.right
spacing: Theme.paddingMedium
width: roomItem.width
id: mainRow
Label {
BackgroundItem {
Rectangle {
color: Theme.highlightBackgroundColor
}
}
id: unreadMessages
color: unreadNotifications > 0 ? Theme.highlightColor : Theme.secondaryColor
width: 2 * Theme.fontSizeLarge
id: unreadMessages
text: unreadNotifications
font.pixelSize: unreadNotifications < 100 ? Theme.fontSizeHuge : Theme.fontSizeLarge
horizontalAlignment: Text.AlignRight
BackgroundItem {
Rectangle {
color: Theme.highlightBackgroundColor
}
}
}
Column {
width: mainRow.width - unreadMessages.width
- roomImage.width - 3* Theme.paddingMedium
Label {
anchors {
left: parent.left
right: parent.right
}
width: mainRow.width - unreadMessages.width - roomImage.width - 3 * Theme.paddingMedium
Label {
id: roomName
text: model.roomName
truncationMode: TruncationMode.Fade
color: Theme.primaryColor
font.pixelSize: Theme.fontSizeLarge
}
Label {
anchors {
left: parent.left
right: parent.right
}
}
Label {
id: lastMessage
text: model.lastMessage
color: unreadNotifications > 0 ? Theme.primaryColor : Theme.secondaryColor
wrapMode: Text.Wrap
truncationMode: TruncationMode.Fade
maximumLineCount: 3
font.pixelSize: Theme.fontSizeExtraSmall
anchors {
left: parent.left
right: parent.right
}
}
}
Image {
id: roomImage
width: unreadMessages.width
height: unreadMessages.height
sourceSize.width: unreadMessages.width
......@@ -135,7 +155,11 @@ Page {
source: model.roomPicture
asynchronous: true
}
}
}
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
import "delegates"
import dev.neko.spoon 1.0
Item {
property var modelData
height: chooser.childrenRect.height
width: Math.min(chooser.childrenRect.width, bubble.fullMessageWidth)
DelegateChooser {
id: chooser
roleValue: model.Type
anchors.fill: parent
DelegateChoice {
roleValue: EventType.Aliases
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.Avatar
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.CanonicalAlias
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.Create
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.GuestAccess
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.HistoryVisibility
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.JoinRules
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.Member
NoticeMessage {
text: model.Body
}
}
DelegateChoice {
roleValue: EventType.Name
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.PowerLevels
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.Topic
Placeholder {
}
}
DelegateChoice {
roleValue: EventType.TextMessage
TextMessage {
}
}
DelegateChoice {
roleValue: EventType.NoticeMessage
NoticeMessage {
}
}
DelegateChoice {
roleValue: EventType.ImageMessage
ImageMessage {
}
}
DelegateChoice {
roleValue: EventType.VideoMessage
VideoMessage {
}
}
DelegateChoice {
Placeholder {
}
}
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: qsTr("Changed aliases")
color: Theme.secondaryColor
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: qsTr("Changed Avatar")
color: Theme.secondaryColor
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: qsTr("Changed canonical alias to: ") + modelData.Body
color: Theme.secondaryColor
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: qsTr("Created this room")
color: Theme.secondaryColor
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: qsTr("Changed guest access to: ")
color: Theme.secondaryColor
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: qsTr("Changed history visibility to: ")
color: Theme.secondaryColor
}
}
import QtQuick 2.0
import QtQuick.Layouts 1.0
import Sailfish.Silica 1.0
Rectangle {
id: container
height: col.height
ColumnLayout {
id: col
Column {
id: col
Item {
id: imageContainer
width: Math.min(bubble.fullMessageWidth, image.sourceSize.width)
height: width * model.ProportionalHeight
Image {
id: image
fillMode: Image.PreserveAspectFit
width: chatView.width
height: chatView.width * modelData.ProportionalHeight
anchors.fill: parent
source: modelData.Url.replace("mxc://", "image://MXCImage/")
asynchronous: true
BusyIndicator {
id: busy
size: BusyIndicatorSize.Medium
anchors.centerIn: image
running: image.status != Image.Ready
}
}
Text {
id: body
text: modelData.FormattedBody
color: Theme.secondaryColor
wrapMode: Text.Wrap
width: chatView.width
font.pixelSize: Theme.fontSizeTiny
}
}
Text {
id: body
text: modelData.FormattedBody
visible: modelData.FormattedBody
color: Theme.secondaryColor
wrapMode: Text.Wrap
width: imageContainer.width
font.pixelSize: Theme.fontSizeTiny
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: qsTr("Changed join rules")
color: Theme.secondaryColor
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: modelData.Body
color: Theme.secondaryColor
wrapMode: Text.Wrap
width: chatView.width
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: textItem.contentHeight
Text {
id: textItem
text: qsTr("Changed room name to: ") + modelData.Body
color: Theme.secondaryColor
wrapMode: Text.Wrap
width: chatView.width
}
}
import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
height: messageText.contentHeight
Text {
id: messageText
text: modelData.FormattedBody
color: Theme.secondaryColor
wrapMode: Text.Wrap
width: chatView.width
}
TextMessage {
color: Theme.secondaryColor
}
import QtQuick 2.0
import Sailfish.Silica 1.0
NoticeMessage {
text: "generic placeholder " + modelData.Type.toString()
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment