Commit e64a9e7e authored by Andreas Traczyk's avatar Andreas Traczyk

sidepanel: improve smartlist interface with underlying models

Minor cosmetic changes to the account combo box, search bar, filter
tabs, and smartlist.

Change-Id: Ie8173504859b325374e42f0dbb4e0ae75f3ed740
Gitlab: #373
Gitlab: #374
Gitlab: #388
parent 0d6d94d1
......@@ -67,7 +67,11 @@ set(COMMON_SOURCES
${SRC_DIR}/screensaver.cpp
${SRC_DIR}/systemtray.cpp
${SRC_DIR}/appsettingsmanager.cpp
${SRC_DIR}/lrcinstance.cpp)
${SRC_DIR}/lrcinstance.cpp
${SRC_DIR}/selectablelistproxymodel.cpp
${SRC_DIR}/conversationlistmodelbase.cpp
${SRC_DIR}/conversationlistmodel.cpp
${SRC_DIR}/searchresultslistmodel.cpp)
set(COMMON_HEADERS
${SRC_DIR}/avatarimageprovider.h
......@@ -118,7 +122,11 @@ set(COMMON_HEADERS
${SRC_DIR}/screensaver.h
${SRC_DIR}/systemtray.h
${SRC_DIR}/appsettingsmanager.h
${SRC_DIR}/lrcinstance.h)
${SRC_DIR}/lrcinstance.h
${SRC_DIR}/selectablelistproxymodel.h
${SRC_DIR}/conversationlistmodelbase.h
${SRC_DIR}/conversationlistmodel.h
${SRC_DIR}/searchresultslistmodel.h)
set(QML_LIBS
Qt5::Quick
......
......@@ -102,7 +102,6 @@
<file>src/mainview/components/MessageWebView.qml</file>
<file>src/mainview/components/MessageWebViewHeader.qml</file>
<file>src/mainview/components/AccountComboBox.qml</file>
<file>src/mainview/components/ConversationSmartListView.qml</file>
<file>src/mainview/components/CallStackView.qml</file>
<file>src/mainview/components/IncomingCallPage.qml</file>
<file>src/mainview/components/OutgoingCallPage.qml</file>
......@@ -114,7 +113,6 @@
<file>src/mainview/components/ParticipantOverlay.qml</file>
<file>src/mainview/components/ProjectCreditsScrollView.qml</file>
<file>src/mainview/components/AccountComboBoxPopup.qml</file>
<file>src/mainview/components/ConversationSmartListViewItemDelegate.qml</file>
<file>src/mainview/components/SidePanelTabBar.qml</file>
<file>src/mainview/components/WelcomePageQrDialog.qml</file>
<file>src/mainview/components/ConversationSmartListContextMenu.qml</file>
......@@ -138,5 +136,8 @@
<file>src/mainview/js/pluginhandlerpickercreation.js</file>
<file>src/mainview/components/FilterTabButton.qml</file>
<file>src/mainview/components/AccountItemDelegate.qml</file>
<file>src/mainview/components/ConversationListView.qml</file>
<file>src/mainview/components/SmartListItemDelegate.qml</file>
<file>src/mainview/components/BadgeNotifier.qml</file>
</qresource>
</RCC>
......@@ -43,8 +43,6 @@ AccountAdapter::safeInit()
this,
&AccountAdapter::onCurrentAccountChanged);
deselectConversation();
auto accountId = lrcInstance_->getCurrAccId();
setProperties(accountId);
connectAccount(accountId);
......@@ -65,7 +63,6 @@ AccountAdapter::getDeviceModel()
void
AccountAdapter::changeAccount(int row)
{
deselectConversation(); // Hack UI
auto accountList = lrcInstance_->accountModel().getAccountList();
if (accountList.size() > row) {
lrcInstance_->setSelectedAccountId(accountList.at(row));
......@@ -269,12 +266,6 @@ AccountAdapter::setCurrAccDisplayName(const QString& text)
lrcInstance_->setCurrAccDisplayName(text);
}
void
AccountAdapter::setSelectedConvId(const QString& convId)
{
lrcInstance_->set_selectedConvUid(convId);
}
lrc::api::profile::Type
AccountAdapter::getCurrentAccountType()
{
......@@ -347,23 +338,6 @@ AccountAdapter::passwordSetStatusMessageBox(bool success, QString title, QString
}
}
void
AccountAdapter::deselectConversation()
{
if (lrcInstance_->get_selectedConvUid().isEmpty()) {
return;
}
// TODO: remove this unhealthy section
auto currentConversationModel = lrcInstance_->getCurrentConversationModel();
if (currentConversationModel == nullptr) {
return;
}
lrcInstance_->set_selectedConvUid();
}
void
AccountAdapter::connectAccount(const QString& accountId)
{
......@@ -374,7 +348,7 @@ AccountAdapter::connectAccount(const QString& accountId)
QObject::disconnect(accountProfileUpdatedConnection_);
QObject::disconnect(contactAddedConnection_);
QObject::disconnect(addedToConferenceConnection_);
QObject::disconnect(contactUnbannedConnection_);
QObject::disconnect(bannedStatusChangedConnection_);
accountStatusChangedConnection_
= QObject::connect(accInfo.accountModel,
......@@ -390,27 +364,22 @@ AccountAdapter::connectAccount(const QString& accountId)
Q_EMIT accountStatusChanged(accountId);
});
contactAddedConnection_
= QObject::connect(accInfo.contactModel.get(),
&lrc::api::ContactModel::contactAdded,
[this, accountId](const QString& contactUri) {
const auto& convInfo = lrcInstance_->getConversationFromConvUid(
lrcInstance_->get_selectedConvUid());
if (convInfo.uid.isEmpty()) {
return;
}
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(
accountId);
if (contactUri
== accInfo.contactModel
->getContact(convInfo.participants.at(0))
.profileInfo.uri) {
/*
* Update conversation.
*/
Q_EMIT updateConversationForAddedContact();
}
});
contactAddedConnection_ = QObject::connect(
accInfo.contactModel.get(),
&lrc::api::ContactModel::contactAdded,
[this, accountId](const QString& contactUri) {
const auto& convInfo = lrcInstance_->getConversationFromConvUid(
lrcInstance_->get_selectedConvUid());
if (convInfo.uid.isEmpty()) {
return;
}
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
auto selectedContactUri
= accInfo.contactModel->getContact(convInfo.participants.at(0)).profileInfo.uri;
if (contactUri == selectedContactUri) {
Q_EMIT selectedContactAdded(convInfo.uid);
}
});
addedToConferenceConnection_
= QObject::connect(accInfo.callModel.get(),
......@@ -420,12 +389,15 @@ AccountAdapter::connectAccount(const QString& accountId)
lrcInstance_->renderer()->addDistantRenderer(confId);
});
contactUnbannedConnection_ = QObject::connect(accInfo.contactModel.get(),
&lrc::api::ContactModel::bannedStatusChanged,
[this](const QString&, bool banned) {
if (!banned)
Q_EMIT contactUnbanned();
});
bannedStatusChangedConnection_
= QObject::connect(accInfo.contactModel.get(),
&lrc::api::ContactModel::bannedStatusChanged,
[this](const QString& uri, bool banned) {
if (!banned)
Q_EMIT contactUnbanned();
else
Q_EMIT lrcInstance_->contactBanned(uri);
});
} catch (...) {
qWarning() << "Couldn't get account: " << accountId;
}
......
......@@ -95,16 +95,14 @@ public:
Q_INVOKABLE bool hasVideoCall();
Q_INVOKABLE bool isPreviewing();
Q_INVOKABLE void setCurrAccDisplayName(const QString& text);
Q_INVOKABLE void setSelectedConvId(const QString& convId = {});
Q_INVOKABLE lrc::api::profile::Type getCurrentAccountType();
Q_INVOKABLE void setCurrAccAvatar(bool fromFile, const QString& source);
Q_SIGNALS:
// Trigger other components to reconnect account related signals.
void accountStatusChanged(QString accountId = {});
void updateConversationForAddedContact();
void accountStatusChanged(QString accountId);
void selectedContactAdded(QString convId);
// Send report failure to QML to make it show the right UI state .
void reportFailure();
......@@ -119,8 +117,6 @@ private:
lrc::api::profile::Type currentAccountType_ {};
int accountListSize_ {};
void deselectConversation();
// Make account signal connections.
void connectAccount(const QString& accountId);
......@@ -134,7 +130,7 @@ private:
QMetaObject::Connection accountProfileUpdatedConnection_;
QMetaObject::Connection contactAddedConnection_;
QMetaObject::Connection addedToConferenceConnection_;
QMetaObject::Connection contactUnbannedConnection_;
QMetaObject::Connection bannedStatusChangedConnection_;
QMetaObject::Connection registeredNameSavedConnection_;
AppSettingsManager* settingsManager_;
......
......@@ -59,9 +59,9 @@ CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject*
[this](const QString& accountId, const QString& convUid) {
acceptACall(accountId, convUid);
Q_EMIT lrcInstance_->notificationClicked();
lrcInstance_->selectConversation(accountId, convUid);
lrcInstance_->selectConversation(convUid, accountId);
updateCall(convUid, accountId);
Q_EMIT callSetupMainViewRequired(accountId, convUid);
Q_EMIT lrcInstance_->conversationUpdated(convUid, accountId);
});
connect(systemTray_,
&SystemTray::declineCallActivated,
......@@ -200,6 +200,7 @@ CallAdapter::onShowIncomingCallView(const QString& accountId, const QString& con
auto selectedAccountId = lrcInstance_->getCurrAccId();
auto* callModel = lrcInstance_->getCurrentCallModel();
// new call
if (!callModel->hasCall(convInfo.callId)) {
if (QApplication::focusObject() == nullptr || accountId != selectedAccountId) {
showNotification(accountId, convInfo.uid);
......@@ -219,17 +220,21 @@ CallAdapter::onShowIncomingCallView(const QString& accountId, const QString& con
return;
}
}
Q_EMIT callSetupMainViewRequired(accountId, convInfo.uid);
Q_EMIT lrcInstance_->updateSmartList();
// select
lrcInstance_->selectConversation(convInfo.uid, accountId);
return;
}
// this slot has been triggered as a result of either selecting a conversation
// with an active call, placing a call, or an incoming call for the current
// or any other conversation
auto call = callModel->getCall(convInfo.callId);
auto isCallSelected = lrcInstance_->get_selectedConvUid() == convInfo.uid;
if (call.isOutgoing) {
if (isCallSelected) {
Q_EMIT callSetupMainViewRequired(accountId, convInfo.uid);
// don't reselect
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
}
} else {
auto accountProperties = lrcInstance_->accountModel().getAccountConfig(selectedAccountId);
......@@ -254,10 +259,12 @@ CallAdapter::onShowIncomingCallView(const QString& accountId, const QString& con
showNotification(accountId, convInfo.uid);
return;
} else {
Q_EMIT callSetupMainViewRequired(accountId, convInfo.uid);
// only update
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
}
} else {
Q_EMIT callSetupMainViewRequired(accountId, convInfo.uid);
// only update
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
}
} else { // Not current conversation
if (currentConvHasCall) {
......@@ -269,19 +276,19 @@ CallAdapter::onShowIncomingCallView(const QString& accountId, const QString& con
return;
}
}
Q_EMIT callSetupMainViewRequired(accountId, convInfo.uid);
// reselect
lrcInstance_->selectConversation(convInfo.uid, accountId);
}
}
}
Q_EMIT callStatusChanged(static_cast<int>(call.status), accountId, convInfo.uid);
Q_EMIT lrcInstance_->updateSmartList();
}
void
CallAdapter::onShowCallView(const QString& accountId, const QString& convUid)
{
updateCall(convUid, accountId);
Q_EMIT callSetupMainViewRequired(accountId, convUid);
Q_EMIT lrcInstance_->conversationUpdated(convUid, accountId);
}
void
......@@ -399,8 +406,6 @@ CallAdapter::showNotification(const QString& accountId, const QString& convUid)
from = accInfo.contactModel->bestNameForContact(convInfo.participants[0]);
}
Q_EMIT lrcInstance_->updateSmartList();
#ifdef Q_OS_LINUX
auto contactPhoto = Utils::contactPhoto(lrcInstance_,
convInfo.participants[0],
......@@ -414,12 +419,11 @@ CallAdapter::showNotification(const QString& accountId, const QString& convUid)
Utils::QImageToByteArray(contactPhoto));
#else
auto onClicked = [this, accountId, convUid = convInfo.uid]() {
Q_EMIT lrcInstance_->notificationClicked();
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
if (convInfo.uid.isEmpty()) {
if (convInfo.uid.isEmpty())
return;
}
Q_EMIT lrcInstance_->notificationClicked();
Q_EMIT callSetupMainViewRequired(accountId, convInfo.uid);
lrcInstance_->selectConversation(convInfo.uid, accountId);
};
systemTray_->showNotification(tr("is calling you"), from, onClicked);
#endif
......@@ -484,7 +488,7 @@ CallAdapter::connectCallModel(const QString& accountId)
case lrc::api::call::Status::TIMEOUT:
case lrc::api::call::Status::TERMINATING: {
lrcInstance_->renderer()->removeDistantRenderer(callId);
Q_EMIT callSetupMainViewRequired(accountId, convInfo.uid);
Q_EMIT lrcInstance_->conversationUpdated(convInfo.uid, accountId);
if (convInfo.uid.isEmpty()) {
break;
}
......@@ -517,7 +521,7 @@ CallAdapter::connectCallModel(const QString& accountId)
/*
* Reset the call view corresponding accountId, uid.
*/
lrcInstance_->set_selectedConvUid(otherConv.uid);
lrcInstance_->selectConversation(otherConv.uid);
updateCall(otherConv.uid, otherConv.accountId, forceCallOnly);
}
}
......
......@@ -80,11 +80,9 @@ public:
Q_SIGNALS:
void callStatusChanged(int index, const QString& accountId, const QString& convUid);
void updateConversationSmartList();
void updateParticipantsInfos(const QVariantList& infos,
const QString& accountId,
const QString& callId);
void callSetupMainViewRequired(const QString& accountId, const QString& convUid);
void previewVisibilityNeedToChange(bool visible);
// For Call Overlay
......
......@@ -38,6 +38,7 @@ Item {
property alias fillMode: rootImage.fillMode
property alias sourceSize: rootImage.sourceSize
property int transitionDuration: 150
property bool saveToConfig: false
property int mode: AvatarImage.Mode.FromAccount
property string imageProviderIdPrefix: {
......@@ -178,7 +179,7 @@ Item {
NumberAnimation {
properties: "opacity"
easing.type: Easing.InOutQuad
duration: 400
duration: transitionDuration
}
}
}
......@@ -188,38 +189,15 @@ Item {
id: presenceIndicator
anchors.right: root.right
anchors.rightMargin: -1
anchors.bottom: root.bottom
anchors.bottomMargin: -1
size: root.width * 0.3
size: root.width * 0.26
visible: showPresenceIndicator
}
Rectangle {
id: unreadMessageCountRect
anchors.right: root.right
anchors.top: root.top
width: root.width * 0.3
height: root.width * 0.3
visible: unreadMessagesCount > 0
Text {
id: unreadMessageCounttext
anchors.centerIn: unreadMessageCountRect
text: unreadMessagesCount > 9 ? "" : unreadMessagesCount
color: "white"
font.pointSize: JamiTheme.indicatorFontSize
}
radius: 30
color: JamiTheme.notificationRed
}
Connections {
target: ScreenInfo
......
......@@ -31,6 +31,12 @@ Menu {
property int commonBorderWidth: 1
font.pointSize: JamiTheme.menuFontSize
modal: true
Overlay.modal: Rectangle {
color: "transparent"
}
// TODO: investigate
function openMenu(){
visible = true
visible = false
......@@ -38,6 +44,8 @@ Menu {
}
background: Rectangle {
id: container
implicitWidth: menuItemsPreferredWidth
implicitHeight: menuItemsPreferredHeight
* (root.count - generalMenuSeparatorCount)
......@@ -45,5 +53,15 @@ Menu {
border.width: commonBorderWidth
border.color: JamiTheme.tabbarBorderColor
color: JamiTheme.backgroundColor
layer.enabled: true
layer.effect: DropShadow {
z: -1
horizontalOffset: 3.0
verticalOffset: 3.0
radius: 16.0
samples: 16
color: JamiTheme.shadowColor
}
}
}
......@@ -67,7 +67,7 @@ Popup {
height: root.height
horizontalOffset: 3.0
verticalOffset: 3.0
radius: container.radius * 2
radius: container.radius * 4
samples: 16
color: JamiTheme.shadowColor
source: container
......
......@@ -30,7 +30,7 @@ Rectangle {
// This is set to REGISTERED for contact presence
// as status is not currently tracked for contact items.
property int status: Account.Status.REGISTERED
property int size: 12
property int size: 15
width: size
height: size
......
......@@ -23,6 +23,12 @@ import QtQuick.Controls 2.12
Rectangle {
property alias name: label.text
property bool stretchParent: false
property string tag: this.toString()
signal moveX(real dx)
signal moveY(real dy)
property real ox: 0
property real oy: 0
property real step: 0.5
border.width: 1
color: {
......@@ -33,6 +39,19 @@ Rectangle {
}
anchors.fill: parent
focus: false
Keys.onPressed: {
if (event.key === Qt.Key_Left)
moveX(-step)
else if (event.key === Qt.Key_Right)
moveX(step)
else if (event.key === Qt.Key_Down)
moveY(step)
else if (event.key === Qt.Key_Up)
moveY(-step)
console.log(tag, ox, oy)
event.accepted = true;
}
Component.onCompleted: {
// fallback to some description of the object
if (label.text === "")
......@@ -45,10 +64,24 @@ Rectangle {
}
}
onMoveX: {
parent.anchors.leftMargin += dx
parent.x += dx
ox += dx;
}
onMoveY: {
parent.anchors.topMargin += dy
parent.y += dy
oy += dy
}
Label {
id: label
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onPressed: parent.forceActiveFocus()
}
}
......@@ -330,9 +330,6 @@ Item {
"Use the \"Link Another Device\" feature to obtain a PIN.")
property string connectFromAnotherDevice: qsTr("Link device")
// KeyBoardShortcutTable
property string conversations: qsTr("Conversations")
// LinkDevicesDialog
property string pinTimerInfos: qsTr("The PIN and the account password should be entered in your device within 10 minutes.")
property string close: qsTr("Close")
......@@ -405,6 +402,8 @@ Item {
// SmartList
property string clearText: qsTr("Clear Text")
property string conversations: qsTr("Conversations")
property string searchResults: qsTr("Search Results")
// SmartList context menu
property string declineContactRequest: qsTr("Decline contact request")
......
......@@ -55,10 +55,10 @@ Item {
property color notificationBlue: "#31b7ff"
property color unPresenceOrange: "orange"
property color placeHolderTextFontColor: "#767676"
property color draftRed: "#cf5300"
property color draftTextColor: "#cf5300"
property color selectedTabColor: primaryForegroundColor
property color filterBadgeColor: mediumGrey
property color filterBadgeTextColor: blackColor
property color filterBadgeColor: "#eed4d8"
property color filterBadgeTextColor: "#cc0022"
// General buttons
property color pressedButtonColor: darkTheme ? pressColor : "#a0a0a0"
......@@ -174,10 +174,14 @@ Item {
property real titleFontSize: 16
property real primaryRadius: 4
property real smartlistItemFontSize: 10.5
property real smartlistItemInfoFontSize: 9
property real filterItemFontSize: smartlistItemFontSize
property real filterBadgeFontSize: 8.25
property real accountListItemHeight: 64
property real accountListAvatarSize: 40
property real smartListItemHeight: 64
property real smartListAvatarSize: 52
property real smartListTransitionDuration: 120
property real maximumWidthSettingsView: 600
property real settingsHeaderpreferredHeight: 64
......
/*!
/*
* Copyright (C) 2020 by Savoir-faire Linux
* Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
......@@ -48,13 +48,13 @@ ContactAdapter::getContactSelectableModel(int type)
switch (listModeltype_) {
case SmartListModel::Type::CONVERSATION:
selectableProxyModel_->setPredicate([this](const QModelIndex& index, const QRegExp&) {
return !defaultModerators_.contains(index.data(SmartListModel::URI).toString());
return !defaultModerators_.contains(index.data(Role::URI).toString());
});
break;
case SmartListModel::Type::CONFERENCE:
selectableProxyModel_->setPredicate([](const QModelIndex& index, const QRegExp&) {
return index.data(SmartListModel::Presence).toBool();
return index.data(Role::Presence).toBool();
});
break;
case SmartListModel::Type::TRANSFER:
......@@ -68,13 +68,11 @@ ContactAdapter::getContactSelectableModel(int type)
.contactModel->bestIdForContact(conv.participants[0]);
QRegExp matchExcept = QRegExp(QString("\\b(?!" + calleeDisplayId + "\\b)\\w+"));
match = matchExcept.indexIn(index.data(SmartListModel::Role::DisplayID).toString())