diff --git a/src/jamichatview/jamichatview.qrc b/src/jamichatview/jamichatview.qrc index f2262078..fcaadf87 100644 --- a/src/jamichatview/jamichatview.qrc +++ b/src/jamichatview/jamichatview.qrc @@ -1,16 +1,17 @@ qml/chatbox.qml qml/chatview.qml qml/textbubble.qml qml/categoryheader.qml qml/textmessagegroup.qml qml/callgroup.qml qml/groupheader.qml qml/groupfooter.qml qml/chatpage.qml + qml/navigation.qml qml/snapshots.qml qml/slideshow.qml qml/thumbnail.qml diff --git a/src/jamichatview/plugin.cpp b/src/jamichatview/plugin.cpp index 58419306..6fbd3100 100644 --- a/src/jamichatview/plugin.cpp +++ b/src/jamichatview/plugin.cpp @@ -1,54 +1,55 @@ /************************************************************************************ * Copyright (C) 2018 by BlueSystems GmbH * * Author : Emmanuel Lepage Vallee * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***********************************************************************************/ #include "plugin.h" // Qt #include #include // QRC #include // ChatView #include "bubble.h" void JamiChatView::registerTypes(const char *uri) { Q_ASSERT(uri == QByteArray("org.kde.ringkde.jamichatview")); qmlRegisterType(uri, 1, 0, "Bubble"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/chatbox.qml") , uri, 1, 0, "ChatBox"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/chatview.qml") , uri, 1, 0, "ChatView"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/textbubble.qml") , uri, 1, 0, "TextBubble"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/categoryheader.qml") , uri, 1, 0, "CategoryHeader"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/textmessagegroup.qml"), uri, 1, 0, "TextMessageGroup"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/callgroup.qml") , uri, 1, 0, "CallGroup"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/groupheader.qml") , uri, 1, 0, "GroupHeader"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/groupfooter.qml") , uri, 1, 0, "GroupFooter"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/snapshots.qml") , uri, 1, 0, "Snapshots"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/slideshow.qml") , uri, 1, 0, "Slideshow"); qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/thumbnail.qml") , uri, 1, 0, "Thumbnail"); - qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/chatpage.qml") , uri, 1, 0, "ChatPage"); + qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/chatpage.qml") , uri, 1, 0, "ChatPage"); + qmlRegisterType(QStringLiteral("qrc:/jamichatview/qml/navigation.qml") , uri, 1, 0, "Navigation"); } void JamiChatView::initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(engine) Q_UNUSED(uri) } diff --git a/src/jamichatview/qml/chatpage.qml b/src/jamichatview/qml/chatpage.qml index df1e43e0..337ae459 100644 --- a/src/jamichatview/qml/chatpage.qml +++ b/src/jamichatview/qml/chatpage.qml @@ -1,239 +1,270 @@ /*************************************************************************** * Copyright (C) 2017 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.2 as Kirigami import QtGraphicalEffects 1.0 import org.kde.ringkde.jamitimeline 1.0 as JamiTimeline import org.kde.ringkde.jamitimelinebase 1.0 as JamiTimelineBase import org.kde.ringkde.jamichatview 1.0 as JamiChatView import org.kde.ringkde.jamicontactview 1.0 as JamiContactView import net.lvindustries.ringqtquick 1.0 as RingQtQuick +import org.kde.playground.kquickitemviews 1.0 as KQuickItemViews Rectangle { id: timelinePage signal disableContactRequests() property bool showScrollbar: true property bool _sendRequestOverride: true + property var currentContactMethod: null + property var currentIndividual: null + property var timelineModel: null + + property bool canSendTexts: currentIndividual ? currentIndividual.canSendTexts : false + property bool sendRequest: _sendRequestOverride && ( sendRequestLoader.active && sendRequestLoader.item && sendRequestLoader.item.sendRequests ) onDisableContactRequests: { if (timelinePage.setContactMethod()) currentContactMethod.confirmationEnabled = false timelinePage._sendRequestOverride = send } Kirigami.Theme.colorSet: Kirigami.Theme.View function focusEdit() { chatBox.focusEdit() } function showNewContent() { chatView.moveTo(Qt.BottomEdge) } function setContactMethod() { if (currentIndividual && !currentContactMethod) { currentContactMethod = currentIndividual.preferredContactMethod( RingQtQuick.Media.TEXT ) if (!currentContactMethod) console.log("Cannot find a valid ContactMethod for", currentIndividual) } return currentContactMethod } onCurrentIndividualChanged: { currentContactMethod = null setContactMethod() } color: Kirigami.Theme.backgroundColor - property var currentContactMethod: null - property var currentIndividual: null - property var timelineModel: null + // Scroll to the search, unread messages, bookmark, etc + RingQtQuick.TimelineIterator { + id: iterator + currentIndividual: timelinePage.currentIndividual + firstVisibleIndex: chatView.topLeft + lastVisibleIndex: chatView.bottomLeft + onContentAdded: { + lastVisibleIndex = chatView.indexAt(Qt.BottomEdge) + timelinePage.showNewContent() + } - property bool canSendTexts: currentIndividual ? currentIndividual.canSendTexts : false + onProposeIndex: { + if (poposedIndex == newestIndex) + timelinePage.showNewContent() + else + chatView.contentY = chatView.itemRect(newestIndex).y + } + } onTimelineModelChanged: { if (!fixmeTimer.running) chatView.model = timelineModel } // Add a blurry background ShaderEffectSource { id: effectSource visible: chatView.displayExtraTime sourceItem: chatView anchors.right: timelinePage.right anchors.top: timelinePage.top width: scrollbar.fullWidth + 15 height: chatView.height sourceRect: Qt.rect( blurryOverlay.x, blurryOverlay.y, blurryOverlay.width, blurryOverlay.height ) } ColumnLayout { anchors.fill: parent clip: true spacing: 0 Loader { id: sendRequestLoader height: active && item ? item.implicitHeight : 0 Layout.fillWidth: true active: chatBox.requireContactRequest Layout.minimumHeight: active && item ? item.implicitHeight : 0 Layout.maximumHeight: active && item ? item.implicitHeight : 0 sourceComponent: JamiContactView.SendRequest { width: sendRequestLoader.width } } RowLayout { id: chatScrollView Layout.fillHeight: true Layout.fillWidth: true Layout.bottomMargin: 0 property bool lock: false Item { Layout.fillHeight: true Layout.fillWidth: true + // Buttons to navigate to relevant content + JamiChatView.Navigation { + timelineIterator: iterator + anchors.rightMargin: blurryOverlay.width + anchors.right: parent.right + anchors.bottom: parent.bottom + Behavior on anchors.rightMargin { + NumberAnimation {duration: 100; easing.type: Easing.InQuad} + } + } + JamiChatView.ChatView { id: chatView width: Math.min(600, timelinePage.width - 50) height: parent.height anchors.horizontalCenter: parent.horizontalCenter model: null//FIXME timelinePage.timelineModel forceTime: scrollbar.overlayVisible // Due to a race condition, wait a bit, it should be fixed elsewhere, //FIXME but it would take much longer. Timer { id: fixmeTimer repeat: false running: true interval: 33 onTriggered: { chatView.model = timelinePage.timelineModel } } } // It needs to be here due to z-index conflicts between // chatScrollView and timelinePage Item { id: blurryOverlay z: 2 opacity: chatView.displayExtraTime && scrollbar.hasContent ? 1 : 0 anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: - 15 height: chatScrollView.height clip: true width: chatView.displayExtraTime ? scrollbar.fullWidth + 15 : 0 visible: opacity > 0 Behavior on opacity { NumberAnimation {duration: 300; easing.type: Easing.InQuad} } Repeater { anchors.fill: parent model: 5 FastBlur { anchors.fill: parent source: effectSource radius: 30 } } Rectangle { anchors.fill: parent color: Kirigami.Theme.backgroundColor opacity: 0.75 } } } JamiTimelineBase.Scrollbar { id: scrollbar z: 1000 bottomUp: true Layout.fillHeight: true Layout.preferredWidth: 10 display: chatView.moving || timelinePage.showScrollbar model: timelinePage.timelineModel view: chatView forceOverlay: chatView.displayExtraTime } } Kirigami.Separator { Layout.fillWidth: true } JamiChatView.ChatBox { id: chatBox Layout.fillWidth: true height: 90 visible: canSendTexts RingQtQuick.MessageBuilder {id: builder} requireContactRequest: currentContactMethod && currentContactMethod.confirmationStatus == RingQtQuick.ContactMethod.UNCONFIRMED && currentContactMethod.confirmationStatus != RingQtQuick.ContactMethod.DISABLED textColor: Kirigami.Theme.textColor backgroundColor: Kirigami.Theme.backgroundColor emojiColor: Kirigami.Theme.highlightColor } } Connections { target: chatBox onSendMessage: { timelinePage.setContactMethod() if (currentContactMethod) { if (currentContactMethod.account && currentContactMethod.confirmationStatus == RingQtQuick.ContactMethod.UNCONFIRMED) currentContactMethod.sendContactRequest() builder.addPayload("text/plain", message) builder.sendWidth(currentContactMethod) } } } } diff --git a/src/jamichatview/qml/navigation.qml b/src/jamichatview/qml/navigation.qml new file mode 100644 index 00000000..8a6bf8d6 --- /dev/null +++ b/src/jamichatview/qml/navigation.qml @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2019 by Bluesystems * + * Author : Emmanuel Lepage Vallee * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + **************************************************************************/ +import QtQuick 2.9 +import org.kde.kirigami 2.2 as Kirigami + +Column { + property QtObject timelineIterator: null + property int buttonCount: 1 + spacing: Kirigami.Units.largeSpacing + property real buttonSize: Kirigami.Units.iconSizes.large + width: buttonCount > 0 ? buttonSize : 0 + height: buttonCount * buttonSize + buttonCount*spacing + visible: opacity > 0 + opacity: buttonCount > 0 ? 1 : 0 + z: 200 + + Behavior on opacity { + NumberAnimation {duration: 200; easing.type: Easing.InQuad} + } + + Rectangle { + color: Kirigami.Theme.highlightColor + radius: 99 + height: buttonSize + width: buttonSize + opacity: chatView.contentHeight - (chatView.contentY + chatView.height) < 10 ? 0 : 1 + visible: opacity > 0 + border.width: 2 + border.color: Kirigami.Theme.highlightedTextColor + Kirigami.Icon { + source: "go-down" + color: Kirigami.Theme.highlightedTextColor + width: Kirigami.Units.iconSizes.smallMedium + height: Kirigami.Units.iconSizes.smallMedium + anchors.centerIn: parent + } + + MouseArea { + anchors.fill: parent + onClicked: { + timelineIterator.proposeNewest() + } + } + } +}