1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-03-28 14:56:19 +01:00

Feature: search messages

Change-Id: Ia458e2e6ee183cad9d0418af0dbbbcd990f14281
GitLab: #918
This commit is contained in:
Nicolas Vengeon 2023-02-03 11:29:37 -05:00 committed by Sébastien Blin
parent c2d81149be
commit 06ab19f213
16 changed files with 710 additions and 161 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z"/></svg>

After

Width:  |  Height:  |  Size: 417 B

View file

@ -354,6 +354,12 @@ Item {
property string noNetworkConnectivity: qsTr("No network connectivity")
property string deletedMessage: qsTr("Deleted message")
//MessagesResearch
property string jumpTo: qsTr("Jump to")
property string messages: qsTr("Messages")
property string files: qsTr("Files")
property string search: qsTr("Search")
// Chatview footer
property string jumpToLatest: qsTr("Jump to latest")
property string typeIndicatorSingle: qsTr("{} is typing…")

View file

@ -376,7 +376,7 @@ Item {
property real swarmDetailsPageDocumentsMargins: 5
property real swarmDetailsPageDocumentsMediaRadius: 15
property real swarmDetailsPageDocumentsPaperClipSize: 24
property real swarmDetailsPageDocumentsMediaSize: 175
property real swarmDetailsPageDocumentsMediaSize: 150
//Call information
property real textFontPointSize: calcSize(10)
@ -401,6 +401,11 @@ Item {
// Modal Popup
property real modalPopupRadius: 20
//MessagesResearch
property color blueLinkColor: darkTheme ? "#3366BB" : "#0645AD"
property real jumpToFontSize: calcSize(13)
property real searchbarSize: 200
// MessageWebView
property real chatViewHairLineSize: 1
property real chatViewMaximumWidth: 900

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Savoir-faire Linux Inc.
* Copyright (C) 2020-2022 Savoir-faire Linux Inc.
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
* Author: Trevor Tabah <trevor.tabah@savoirfairelinux.com>
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
@ -102,30 +102,53 @@ Rectangle {
onBackClicked: root.dismiss()
onShowDetailsClicked: {
addMemberPanel.visible = false
if (swarmDetailsPanel.visible) {
signal panelsVisibilityChange()
onPanelsVisibilityChange: {
if (!swarmDetailsPanel.visible && !messagesResearchPanel.visible) {
chatContents.visible = true
} else {
if (chatViewHeader.width - JamiTheme.detailsPageMinWidth < JamiTheme.chatViewHeaderMinimumWidth)
chatContents.visible = false
}
}
onShowDetailsClicked: {
addMemberPanel.visible = false
messagesResearchPanel.visible = false
swarmDetailsPanel.visible = !swarmDetailsPanel.visible
panelsVisibilityChange()
}
onSearchBarOpened: {
addMemberPanel.visible = false
swarmDetailsPanel.visible = false
messagesResearchPanel.visible = true
panelsVisibilityChange()
}
onSearchBarClosed: {
chatContents.visible = true
messagesResearchPanel.visible = false
panelsVisibilityChange()
}
onWidthChanged: {
const isExpanding = previousWidth < width
if (!swarmDetailsPanel.visible && !addMemberPanel.visible)
if (!swarmDetailsPanel.visible && !addMemberPanel.visible && !messagesResearchPanel.visible)
return
if (chatViewHeader.width < JamiTheme.detailsPageMinWidth + JamiTheme.chatViewHeaderMinimumWidth
&& !isExpanding && chatContents.visible) {
&& !isExpanding && chatContents.visible) {
lastContentsSplitSize = chatContents.width
lastDetailsSplitSize = Math.min(JamiTheme.detailsPageMinWidth, (swarmDetailsPanel.visible ?
swarmDetailsPanel.width :
addMemberPanel.width))
lastDetailsSplitSize = Math.min(JamiTheme.detailsPageMinWidth, (swarmDetailsPanel.visible
? swarmDetailsPanel.width
: addMemberPanel.visible
? addMemberPanel.width
: messagesResearchPanel.width))
chatContents.visible = false
} else if (chatViewHeader.width >= JamiTheme.chatViewHeaderMinimumWidth + lastDetailsSplitSize
&& isExpanding && !layoutManager.isFullScreen && !chatContents.visible) {
&& isExpanding && !layoutManager.isFullScreen && !chatContents.visible) {
chatContents.visible = true
}
previousWidth = width
@ -244,10 +267,7 @@ Rectangle {
id: chatContents
SplitView.maximumWidth: viewCoordinator.splitView.width
SplitView.minimumWidth: JamiTheme.chatViewHeaderMinimumWidth
SplitView.preferredWidth: chatViewHeader.width -
(swarmDetailsPanel.visible ? swarmDetailsPanel.width :
( addMemberPanel.visible ? addMemberPanel.width : 0))
SplitView.fillWidth: true
StackLayout {
id: chatViewStack
@ -315,6 +335,15 @@ Rectangle {
}
}
MessagesResearchPanel {
id: messagesResearchPanel
visible: false
SplitView.maximumWidth: viewCoordinator.splitView.width
SplitView.minimumWidth: JamiTheme.detailsPageMinWidth
SplitView.preferredWidth: JamiTheme.detailsPageMinWidth
}
SwarmDetailsPanel {
id: swarmDetailsPanel
visible: false

View file

@ -19,6 +19,7 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
@ -34,6 +35,8 @@ Rectangle {
signal addToConversationClicked
signal pluginSelector
signal showDetailsClicked
signal searchBarOpened
signal searchBarClosed
Connections {
target: CurrentConversation
@ -145,98 +148,117 @@ Rectangle {
RowLayout {
id: buttonGroup
property int buttonGroupMargin: 8
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
Layout.fillWidth: true
Layout.rightMargin: 8
Layout.rightMargin: buttonGroupMargin
spacing: 16
Layout.fillWidth: true
PushButton {
id: startAAudioCallButton
Searchbar {
id: rowSearchBar
visible: interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
source: JamiResources.place_audiocall_24dp_svg
toolTipText: JamiStrings.placeAudioCall
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: CallAdapter.placeAudioOnlyCall()
spacing: buttonGroup.spacing
visible: CurrentConversation.isSwarm
}
PushButton {
id: startAVideoCallButton
RowLayout {
id: pushbuttons
visible: CurrentAccount.videoEnabled_Video && interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
source: JamiResources.videocam_24dp_svg
toolTipText: JamiStrings.placeVideoCall
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
Layout.rightMargin: 8
spacing: 16
Layout.fillWidth: true
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
PushButton {
id: startAAudioCallButton
onClicked: {
CallAdapter.placeCall()
visible: interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
source: JamiResources.place_audiocall_24dp_svg
toolTipText: JamiStrings.placeAudioCall
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: CallAdapter.placeAudioOnlyCall()
}
PushButton {
id: startAVideoCallButton
visible: CurrentAccount.videoEnabled_Video && interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
source: JamiResources.videocam_24dp_svg
toolTipText: JamiStrings.placeVideoCall
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: {
CallAdapter.placeCall()
}
}
PushButton {
id: addParticipantsButton
source: JamiResources.add_people_24dp_svg
toolTipText: JamiStrings.addParticipants
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
visible: CurrentConversation.uris.length < 8 && addMemberVisibility
onClicked: addToConversationClicked()
}
PushButton {
id: selectPluginButton
visible: PluginAdapter.isEnabled && PluginAdapter.chatHandlersListCount &&
interactionButtonsVisibility
source: JamiResources.plugins_24dp_svg
toolTipText: JamiStrings.showPlugins
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: pluginSelector()
}
PushButton {
id: sendContactRequestButton
visible: CurrentConversation.isTemporary || CurrentConversation.isBanned
source: JamiResources.add_people_24dp_svg
toolTipText: JamiStrings.addToConversations
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: CurrentConversation.isBanned ?
MessagesAdapter.unbanConversation(CurrentConversation.id)
: MessagesAdapter.sendConversationRequest()
}
PushButton {
id: detailsButton
visible: swarmDetailsVisibility
source: JamiResources.swarm_details_panel_svg
toolTipText: JamiStrings.details
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: showDetailsClicked()
}
}
PushButton {
id: addParticipantsButton
source: JamiResources.add_people_24dp_svg
toolTipText: JamiStrings.addParticipants
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
visible: CurrentConversation.uris.length < 8 && addMemberVisibility
onClicked: addToConversationClicked()
}
PushButton {
id: selectPluginButton
visible: PluginAdapter.isEnabled && PluginAdapter.chatHandlersListCount &&
interactionButtonsVisibility
source: JamiResources.plugins_24dp_svg
toolTipText: JamiStrings.showPlugins
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: pluginSelector()
}
PushButton {
id: sendContactRequestButton
visible: CurrentConversation.isTemporary || CurrentConversation.isBanned
source: JamiResources.add_people_24dp_svg
toolTipText: JamiStrings.addToConversations
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: CurrentConversation.isBanned ?
MessagesAdapter.unbanConversation(CurrentConversation.id)
: MessagesAdapter.sendConversationRequest()
}
PushButton {
id: detailsButton
visible: swarmDetailsVisibility
source: JamiResources.swarm_details_panel_svg
toolTipText: JamiStrings.details
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: showDetailsClicked()
}
}
Component.onCompleted: JamiQmlUtils.messagingHeaderRectRowLayout = messagingHeaderRectRowLayout
}

View file

@ -128,7 +128,9 @@ Rectangle {
Shortcut {
sequence: "Ctrl+F"
context: Qt.ApplicationShortcut
onActivated: contactSearchBar.forceActiveFocus()
onActivated: {
contactSearchBar.forceActiveFocus()
}
}
Keys.onPressed: function (keyEvent) {

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2022-2023 Savoir-faire Linux Inc.
* Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
*
* 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
@ -35,39 +36,46 @@ Flickable {
contentWidth: width
property int spacingFlow: JamiTheme.swarmDetailsPageDocumentsMargins
property real flickableWidth: width
property int numberElementsPerRow: {
var sizeW = flow.width
var breakSize = JamiTheme.swarmDetailsPageDocumentsMediaSize
return Math.floor(sizeW / breakSize)
}
property int spacingLength: spacingFlow * (numberElementsPerRow - 1)
property color themeColor: CurrentConversation.color
property string textFilter: ""
onVisibleChanged: {
if (visible) {
MessagesAdapter.getConvMedias()
} else {
MessagesAdapter.mediaMessageListModel = null
MessagesAdapter.startSearch(textFilter,true)
}
}
onTextFilterChanged: {
MessagesAdapter.startSearch(textFilter,true)
}
Flow {
id: flow
width: parent.width
spacing: spacingFlow
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenter: parent.horizontalCenter
Repeater {
model: MessagesAdapter.mediaMessageListModel
model: root.visible ? MessagesAdapter.mediaMessageListModel : 0
delegate: Loader {
id: loaderRoot
sourceComponent: {
if(Status === Interaction.Status.TRANSFER_FINISHED || Status === Interaction.Status.SUCCESS ){
if (Object.keys(MessagesAdapter.getMediaInfo(Body)).length !== 0 && WITH_WEBENGINE)
return localMediaMsgComp
if (MessagesAdapter.isDocument(Type)) {
if(Status === Interaction.Status.TRANSFER_FINISHED || Status === Interaction.Status.SUCCESS ){
if (Object.keys(MessagesAdapter.getMediaInfo(Body)).length !== 0 && WITH_WEBENGINE)
return localMediaMsgComp
return fileMsgComp
return fileMsgComp
}
}
}

View file

@ -34,7 +34,7 @@ Component {
id: dataTransferRect
clip: true
width: (documents.width - spacingLength ) / numberElementsPerRow
width: (contentWidth - spacingLength ) / numberElementsPerRow
height: width
color: "transparent"
@ -64,7 +64,7 @@ Component {
anchors.fill: parent
anchors.margins: JamiTheme.swarmDetailsPageDocumentsMargins
color: "transparent"
border.color: CurrentConversation.color
border.color: themeColor
border.width: 2
radius: JamiTheme.swarmDetailsPageDocumentsMediaRadius
layer.enabled: true

View file

@ -33,7 +33,7 @@ Component {
Rectangle {
id: localMediaRect
width: (documents.width - spacingLength) / numberElementsPerRow
width: (flickableWidth - spacingLength) / numberElementsPerRow
height: width
color: "transparent"
@ -62,7 +62,7 @@ Component {
anchors.fill: parent
anchors.margins: JamiTheme.swarmDetailsPageDocumentsMargins
color: CurrentConversation.color
color: themeColor
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Item {

View file

@ -0,0 +1,109 @@
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
* Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.platform
import Qt5Compat.GraphicalEffects
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
import "../../settingsview/components"
Rectangle {
id: root
color: JamiTheme.chatviewBgColor
ColumnLayout {
anchors.fill: parent
TabBar {
id: researchTabBar
currentIndex: 0
Layout.preferredHeight: contentHeight + 10
Layout.preferredWidth: root.width
background.visible: false
signal filterTabChange()
onCurrentIndexChanged: {
filterTabChange()
}
onVisibleChanged: {
researchTabBar.currentIndex = 0
}
FilterTabButton {
id: messagesResearchTabButton
backgroundColor: "transparent"
hoverColor: "transparent"
borderWidth: 4
bottomMargin: JamiTheme.settingsMarginSize
fontSize: JamiTheme.menuFontSize
underlineContentOnly: true
down: researchTabBar.currentIndex === 0
labelText: JamiStrings.messages
Layout.fillWidth: true
}
FilterTabButton {
id: fileResearchTabButton
backgroundColor: "transparent"
hoverColor: "transparent"
borderWidth: 4
bottomMargin: JamiTheme.settingsMarginSize
fontSize: JamiTheme.menuFontSize
underlineContentOnly: true
down: researchTabBar.currentIndex === 1
labelText: JamiStrings.files
Layout.fillWidth: true
}
}
Rectangle {
id: view
color: JamiTheme.chatviewBgColor
Layout.fillWidth: true
Layout.fillHeight: true
MessagesResearchView {
anchors.fill: parent
visible: researchTabBar.currentIndex === 0
clip: true
}
DocumentsScrollview {
anchors.fill: parent
visible: researchTabBar.currentIndex === 1
clip: true
themeColor: JamiTheme.chatviewTextColor
textFilter: MessagesAdapter.searchbarPrompt
}
}
}
}

View file

@ -0,0 +1,163 @@
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
* Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.platform
import Qt5Compat.GraphicalEffects
import SortFilterProxyModel
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
import "../../settingsview/components"
ListView {
id: root
spacing: 10
model: SortFilterProxyModel {
id: proxyModel
property var messageListModel: MessagesAdapter.mediaMessageListModel
readonly property int textType: Interaction.Type.TEXT
onMessageListModelChanged: sourceModel = root.visible ? messageListModel : null
filters: ExpressionFilter {
expression: Type === proxyModel.textType
}
}
property var prompt: MessagesAdapter.searchbarPrompt
onPromptChanged: {
MessagesAdapter.startSearch(prompt)
}
Connections {
target: researchTabBar
function onFilterTabChange() {
MessagesAdapter.startSearch(prompt)
}
}
delegate: Item {
width: root.width
height: msgLayout.height
HoverHandler {
id: msgHover
target: parent
}
ColumnLayout {
id: msgLayout
width: root.width
TimestampInfo {
id: timestampItem
showDay: true
showTime: true
formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
}
RowLayout {
id: contentRow
property bool isMe: Author === CurrentAccount.uri
Avatar {
id: avatar
width: 30
height: 30
imageId: contentRow.isMe ? CurrentAccount.id : Author
showPresenceIndicator: false
mode: contentRow.isMe ? Avatar.Mode.Account : Avatar.Mode.Contact
Layout.leftMargin: 10
}
ColumnLayout {
Text {
text: contentRow.isMe
? CurrentAccount.bestName
: UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author) + " :"
Layout.preferredWidth: myText.width
Layout.rightMargin: 10
Layout.leftMargin: 10
font.pixelSize: 0
color: JamiTheme.chatviewUsernameColor
font.bold: true
}
Text {
id: myText
text: Body
color: JamiTheme.textColor
Layout.preferredWidth: msgLayout.width - avatar.width - 30 - 10
elide: Text.ElideRight
Layout.rightMargin: 10
Layout.leftMargin: 10
font.pixelSize: IsEmojiOnly? JamiTheme.chatviewEmojiSize : JamiTheme.chatviewFontSize
Layout.alignment:Qt.AlignHCenter
}
}
}
}
Button {
id: buttonJumpTo
visible: msgHover.hovered || hovered
anchors.top: msgLayout.top
anchors.right: msgLayout.right
anchors.rightMargin: 20
anchors.topMargin: timestampItem.height - 20
width: buttonJumpText.width + 10
height: buttonJumpText.height + 10
background.visible: false
onClicked: {
CurrentConversation.scrollToMsg(Id)
}
Text {
id: buttonJumpText
text: JamiStrings.jumpTo
color: buttonJumpTo.hovered ? JamiTheme.blueLinkColor : JamiTheme.chatviewUsernameColor
font.underline: buttonJumpTo.hovered
anchors.centerIn: parent
font.pointSize: JamiTheme.jumpToFontSize
}
}
}
}

View file

@ -0,0 +1,182 @@
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
* Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
RowLayout {
id: root
property real messagesResearchPanel: JamiTheme.detailsPageMinWidth
//TO DO: find a design to set dynamically the size of the searchbar
property real searchBarWidth: JamiTheme.searchbarSize
property string currentConversationId: CurrentConversation.id
property bool isOpened: false
function openSearchBar() {
searchBarOpened()
rectTextArea.isSearch = true
anim.start()
textArea.forceActiveFocus()
isOpened = true
}
function closeSearchbar() {
searchBarClosed()
rectTextArea.isSearch = false
anim.start()
isOpened = false
}
Connections {
target: chatViewHeader
function onShowDetailsClicked() {
if (rectTextArea.isSearch)
closeSearchbar()
}
}
onCurrentConversationIdChanged: {
if (isOpened)
closeSearchbar()
}
PushButton {
id: startSearchMessages
source: JamiResources.search_svg
normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor
onClicked: {
if (rectTextArea.isSearch)
closeSearchbar()
else
openSearchBar()
}
}
SequentialAnimation {
id: anim
PropertyAnimation {
target: rectTextArea; properties: "visible"
to: true
duration: 0
}
ParallelAnimation {
NumberAnimation {
target: rectTextArea; properties: "opacity"
from: rectTextArea.isSearch ? 0 : 1
to: rectTextArea.isSearch ? 1 : 0
duration: 150
}
NumberAnimation {
target: rectTextArea; properties: "Layout.preferredWidth"
from: rectTextArea.isSearch ? 0 : root.searchBarWidth
to: rectTextArea.isSearch ? root.searchBarWidth : 0
duration: 150
}
}
PropertyAnimation {
target: rectTextArea; properties: "visible"
to: rectTextArea.isSearch
duration: 0
}
}
Rectangle {
id: rectTextArea
visible: false
Layout.preferredHeight: startSearchMessages.height
Layout.alignment: Qt.AlignVCenter
color: "transparent"
border.color: JamiTheme.chatviewTextColor
radius: 10
border.width: 2
property bool isSearch: false
property int textAreaWidth: 200
property alias searchBarWidth: root.searchBarWidth
onSearchBarWidthChanged: {
Layout.preferredWidth = root.searchBarWidth
}
TextField {
id: textArea
background.visible: false
anchors.right: clearTextButton.left
anchors.left: rectTextArea.left
color: JamiTheme.chatviewTextColor
placeholderText: JamiStrings.search
placeholderTextColor: JamiTheme.chatviewTextColor
onTextChanged: {
MessagesAdapter.searchbarPrompt = text
}
}
PushButton {
id: clearTextButton
anchors.verticalCenter: rectTextArea.verticalCenter
anchors.right: rectTextArea.right
anchors.margins: 5
preferredSize: 21
radius: rectTextArea.radius
visible: textArea.text.length
opacity: visible ? 1 : 0
normalColor: "transparent"
imageColor: JamiTheme.chatviewButtonColor
source: JamiResources.ic_clear_24dp_svg
toolTipText: JamiStrings.clearText
property string convId: CurrentConversation.id
onConvIdChanged: {
textArea.clear()
}
onClicked: {
textArea.clear()
textArea.forceActiveFocus()
}
Behavior on opacity {
NumberAnimation { duration: 500; easing.type: Easing.OutCubic }
}
}
}
}

View file

@ -67,6 +67,8 @@ MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager,
filteredMsgListModel_->setSourceModel(conversation.interactions.get());
set_currentConvComposingList(conversationTypersUrlToName(conversation.typers));
mediaInteractions_.reset(new MessageListModel(this));
set_mediaMessageListModel(QVariant::fromValue(mediaInteractions_.get()));
});
connect(previewEngine_, &PreviewEngine::infoReady, this, &MessagesAdapter::onPreviewInfoReady);
@ -82,6 +84,12 @@ MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager,
connectConversationModel();
}
bool
MessagesAdapter::isDocument(const interaction::Type& type)
{
return interaction::Type::DATA_TRANSFER == type;
}
void
MessagesAdapter::loadMoreMessages()
{
@ -575,25 +583,16 @@ MessagesAdapter::onComposingStatusChanged(const QString& convId,
void
MessagesAdapter::onMessagesFoundProcessed(const QString& accountId,
const VectorMapStringString& messageIds,
const QVector<interaction::Info>& messageInformations)
const QMap<QString, interaction::Info>& messageInformation)
{
if (lrcInstance_->get_currentAccountId() != accountId) {
return;
}
bool isSearchInProgress = messageIds.length();
bool isSearchInProgress = messageInformation.size();
if (isSearchInProgress) {
int index = -1;
Q_FOREACH (const MapStringString& msg, messageIds) {
index++;
try {
std::pair<QString, interaction::Info> message(msg["id"],
messageInformations.at(index));
mediaInteractions_->insert(message);
} catch (...) {
qWarning() << "error in onMessagesFoundProcessed, message insertion on index: "
<< index;
}
for (auto it = messageInformation.begin(); it != messageInformation.end(); it++) {
mediaInteractions_->insert(qMakePair(it.key(), it.value()));
}
} else {
set_mediaMessageListModel(QVariant::fromValue(mediaInteractions_.get()));
@ -729,17 +728,24 @@ MessagesAdapter::getFormattedDay(const quint64 timestamp)
}
void
MessagesAdapter::getConvMedias()
MessagesAdapter::startSearch(QString& text, bool isMedia)
{
mediaInteractions_.reset(new MessageListModel(this));
set_mediaMessageListModel(QVariant::fromValue(mediaInteractions_.get()));
if (text.isEmpty() && !isMedia)
return;
auto accountId = lrcInstance_->get_currentAccountId();
auto convId = lrcInstance_->get_selectedConvUid();
mediaInteractions_.reset(new MessageListModel(this));
try {
lrcInstance_->getCurrentConversationModel()->getConvMediasInfos(accountId, convId);
lrcInstance_->getCurrentConversationModel()->getConvMediasInfos(accountId,
convId,
text,
isMedia);
} catch (...) {
qDebug() << "Exception during getConvMedia:";
qDebug() << "Exception during startSearch()";
}
}

View file

@ -60,6 +60,7 @@ class MessagesAdapter final : public QmlAdapterBase
QML_PROPERTY(QString, editId)
QML_RO_PROPERTY(QList<QString>, currentConvComposingList)
QML_PROPERTY(QVariant, mediaMessageListModel)
QML_PROPERTY(QString, searchbarPrompt)
public:
explicit MessagesAdapter(AppSettingsManager* settingsManager,
@ -70,6 +71,7 @@ public:
Q_SIGNALS:
void newInteraction(const QString& id, int type);
void newMessageBarPlaceholderText(QString& placeholderText);
void newFilePasted(QString filePath);
void newTextPasted();
void previewInformationToQML(QString messageId, QStringList previewInformation);
@ -77,6 +79,7 @@ Q_SIGNALS:
void timestampUpdated();
protected:
Q_INVOKABLE bool isDocument(const interaction::Type& type);
Q_INVOKABLE void loadMoreMessages();
Q_INVOKABLE void loadConversationUntil(const QString& to);
Q_INVOKABLE void connectConversationModel();
@ -129,7 +132,7 @@ protected:
Q_INVOKABLE QVariantMap getTransferStats(const QString& messageId, int);
Q_INVOKABLE QVariant dataForInteraction(const QString& interactionId,
int role = Qt::DisplayRole) const;
Q_INVOKABLE void getConvMedias();
Q_INVOKABLE void startSearch(QString& text, bool isMedia = false);
Q_INVOKABLE int getMessageIndexFromId(QString& id);
// Run corrsponding js functions, c++ to qml.
@ -150,8 +153,7 @@ private Q_SLOTS:
const QString& contactUri,
bool isComposing);
void onMessagesFoundProcessed(const QString& accountId,
const VectorMapStringString& messageIds,
const QVector<interaction::Info>& messageInformations);
const QMap<QString, interaction::Info>& messageInformation);
private:
QList<QString> conversationTypersUrlToName(const QSet<QString>& typersSet);

View file

@ -316,9 +316,12 @@ public:
api::datatransfer::Info& info) const;
/**
* Starts a search of all medias and files in a conversation
* Starts a search of all medias in a conversation
*/
void getConvMediasInfos(const QString& accountId, const QString& conversationId);
void getConvMediasInfos(const QString& accountId,
const QString& conversationId,
const QString& text,
bool isMedia);
/**
* @param convUid, uid of the conversation
* @return the number of unread messages for the conversation
@ -612,12 +615,10 @@ Q_SIGNALS:
/**
* Emitted once a message search has been done and processed
* @param accountId
* @param messageIds ids of all the messages found by the search
* @param messageInformations message datas
* @param messageInformation message datas
*/
void messagesFoundProcessed(const QString& accountId,
const VectorMapStringString& messageIds,
const QVector<interaction::Info>& messageInformations) const;
const QMap<QString, interaction::Info>& messageInformation) const;
/**
* Emitted once a conversation needs somebody to host the call
* @param callId

View file

@ -243,7 +243,8 @@ public:
std::map<QString, std::mutex> interactionsLocks; ///< {convId, mutex}
MapStringString transfIdToDbIntId;
uint32_t currentMsgRequestId;
uint32_t mediaResearchRequestId;
uint32_t msgResearchRequestId;
public Q_SLOTS:
/**
@ -2540,29 +2541,34 @@ ConversationModelPimpl::slotMessagesFound(uint32_t requestId,
const QString& conversationId,
const VectorMapStringString& messageIds)
{
if (requestId != currentMsgRequestId) {
return;
}
QVector<interaction::Info> messageDetailedinformations;
Q_FOREACH (const MapStringString& msg, messageIds) {
auto intInfo = interaction::Info(msg, "");
if (intInfo.type == interaction::Type::DATA_TRANSFER) {
auto fileId = msg["fileId"];
QMap<QString, interaction::Info> messageDetailedInformation;
if (requestId == mediaResearchRequestId) {
Q_FOREACH (const MapStringString& msg, messageIds) {
auto intInfo = interaction::Info(msg, "");
if (intInfo.type == interaction::Type::DATA_TRANSFER) {
auto fileId = msg["fileId"];
QString path;
qlonglong bytesProgress, totalSize;
linked.owner.dataTransferModel->fileTransferInfo(accountId,
conversationId,
fileId,
path,
totalSize,
bytesProgress);
intInfo.body = path;
QString path;
qlonglong bytesProgress, totalSize;
linked.owner.dataTransferModel->fileTransferInfo(accountId,
conversationId,
fileId,
path,
totalSize,
bytesProgress);
intInfo.body = path;
}
messageDetailedInformation[msg["id"]] = intInfo;
}
} else if (requestId == msgResearchRequestId) {
Q_FOREACH (const MapStringString& msg, messageIds) {
auto intInfo = interaction::Info(msg, "");
if (intInfo.type == interaction::Type::TEXT) {
messageDetailedInformation[msg["id"]] = intInfo;
}
}
messageDetailedinformations.append(intInfo);
}
Q_EMIT linked.messagesFoundProcessed(accountId, messageIds, messageDetailedinformations);
Q_EMIT linked.messagesFoundProcessed(accountId, messageDetailedInformation);
}
void
@ -4000,10 +4006,17 @@ ConversationModel::sendFile(const QString& convUid, const QString& path, const Q
}
void
ConversationModel::getConvMediasInfos(const QString& accountId, const QString& conversationId)
ConversationModel::getConvMediasInfos(const QString& accountId,
const QString& conversationId,
const QString& text,
bool isMedia)
{
pimpl_->currentMsgRequestId = ConfigurationManager::instance().searchConversation(
accountId, conversationId, "", "", "", "application/data-transfer+json", 0, 0, 0, 0);
if (isMedia)
pimpl_->mediaResearchRequestId = ConfigurationManager::instance().searchConversation(
accountId, conversationId, "", "", text, "application/data-transfer+json", 0, 0, 0, 0);
else
pimpl_->msgResearchRequestId = ConfigurationManager::instance().searchConversation(
accountId, conversationId, "", "", text, "text/plain", 0, 0, 0, 0);
}
void