mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-08-19 14:15:54 +02:00
migration: use image provider to show avatar image
1. Use avatarimageprovider 2. Remove redundant base64 code Change-Id: I2a2517890e95b4a9f9a363fbea2251d6d5dd1c8f
This commit is contained in:
parent
b4b56aec4b
commit
173cf2be50
50 changed files with 587 additions and 562 deletions
|
@ -42,7 +42,6 @@ set(COMMON_SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/smartlistmodel.cpp
|
src/smartlistmodel.cpp
|
||||||
src/utils.cpp
|
src/utils.cpp
|
||||||
src/pixbufmanipulator.cpp
|
|
||||||
src/rendermanager.cpp
|
src/rendermanager.cpp
|
||||||
src/connectivitymonitor.cpp
|
src/connectivitymonitor.cpp
|
||||||
src/mainapplication.cpp
|
src/mainapplication.cpp
|
||||||
|
@ -85,7 +84,6 @@ set(COMMON_HEADERS
|
||||||
src/globalsystemtray.h
|
src/globalsystemtray.h
|
||||||
src/appsettingsmanager.h
|
src/appsettingsmanager.h
|
||||||
src/webchathelpers.h
|
src/webchathelpers.h
|
||||||
src/pixbufmanipulator.h
|
|
||||||
src/rendermanager.h
|
src/rendermanager.h
|
||||||
src/connectivitymonitor.h
|
src/connectivitymonitor.h
|
||||||
src/jamiavatartheme.h
|
src/jamiavatartheme.h
|
||||||
|
|
|
@ -111,6 +111,7 @@ unix {
|
||||||
|
|
||||||
# Input
|
# Input
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
src/avatarimageprovider.h \
|
||||||
src/networkmanager.h \
|
src/networkmanager.h \
|
||||||
src/smartlistmodel.h \
|
src/smartlistmodel.h \
|
||||||
src/updatemanager.h \
|
src/updatemanager.h \
|
||||||
|
@ -123,7 +124,6 @@ HEADERS += \
|
||||||
src/globalsystemtray.h \
|
src/globalsystemtray.h \
|
||||||
src/appsettingsmanager.h \
|
src/appsettingsmanager.h \
|
||||||
src/webchathelpers.h \
|
src/webchathelpers.h \
|
||||||
src/pixbufmanipulator.h \
|
|
||||||
src/rendermanager.h \
|
src/rendermanager.h \
|
||||||
src/connectivitymonitor.h \
|
src/connectivitymonitor.h \
|
||||||
src/jamiavatartheme.h \
|
src/jamiavatartheme.h \
|
||||||
|
@ -168,7 +168,6 @@ SOURCES += \
|
||||||
src/main.cpp \
|
src/main.cpp \
|
||||||
src/smartlistmodel.cpp \
|
src/smartlistmodel.cpp \
|
||||||
src/utils.cpp \
|
src/utils.cpp \
|
||||||
src/pixbufmanipulator.cpp \
|
|
||||||
src/rendermanager.cpp \
|
src/rendermanager.cpp \
|
||||||
src/connectivitymonitor.cpp \
|
src/connectivitymonitor.cpp \
|
||||||
src/mainapplication.cpp \
|
src/mainapplication.cpp \
|
||||||
|
|
2
qml.qrc
2
qml.qrc
|
@ -97,7 +97,6 @@
|
||||||
<file>src/mainview/components/ProjectCreditsScrollView.qml</file>
|
<file>src/mainview/components/ProjectCreditsScrollView.qml</file>
|
||||||
<file>src/mainview/components/AccountComboBoxPopup.qml</file>
|
<file>src/mainview/components/AccountComboBoxPopup.qml</file>
|
||||||
<file>src/mainview/components/ConversationSmartListViewItemDelegate.qml</file>
|
<file>src/mainview/components/ConversationSmartListViewItemDelegate.qml</file>
|
||||||
<file>src/mainview/components/ConversationSmartListUserImage.qml</file>
|
|
||||||
<file>src/mainview/components/SidePanelTabBar.qml</file>
|
<file>src/mainview/components/SidePanelTabBar.qml</file>
|
||||||
<file>src/mainview/components/WelcomePageQrDialog.qml</file>
|
<file>src/mainview/components/WelcomePageQrDialog.qml</file>
|
||||||
<file>src/commoncomponents/GeneralMenuItem.qml</file>
|
<file>src/commoncomponents/GeneralMenuItem.qml</file>
|
||||||
|
@ -137,5 +136,6 @@
|
||||||
<file>src/commoncomponents/SimpleMessageDialog.qml</file>
|
<file>src/commoncomponents/SimpleMessageDialog.qml</file>
|
||||||
<file>src/commoncomponents/ResponsiveImage.qml</file>
|
<file>src/commoncomponents/ResponsiveImage.qml</file>
|
||||||
<file>src/commoncomponents/PresenceIndicator.qml</file>
|
<file>src/commoncomponents/PresenceIndicator.qml</file>
|
||||||
|
<file>src/commoncomponents/AvatarImage.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -373,13 +373,15 @@ AccountAdapter::connectAccount(const QString& accountId)
|
||||||
&lrc::api::NewAccountModel::profileUpdated,
|
&lrc::api::NewAccountModel::profileUpdated,
|
||||||
[this](const QString& accountId) {
|
[this](const QString& accountId) {
|
||||||
if (LRCInstance::getCurrAccId() == accountId)
|
if (LRCInstance::getCurrAccId() == accountId)
|
||||||
emit accountStatusChanged();
|
emit accountStatusChanged(accountId);
|
||||||
});
|
});
|
||||||
|
|
||||||
accountStatusChangedConnection_
|
accountStatusChangedConnection_
|
||||||
= QObject::connect(accInfo.accountModel,
|
= QObject::connect(accInfo.accountModel,
|
||||||
&lrc::api::NewAccountModel::accountStatusChanged,
|
&lrc::api::NewAccountModel::accountStatusChanged,
|
||||||
[this] { emit accountStatusChanged(); });
|
[this](const QString& accountId) {
|
||||||
|
emit accountStatusChanged(accountId);
|
||||||
|
});
|
||||||
|
|
||||||
contactAddedConnection_
|
contactAddedConnection_
|
||||||
= QObject::connect(accInfo.contactModel.get(),
|
= QObject::connect(accInfo.contactModel.get(),
|
||||||
|
|
|
@ -110,7 +110,7 @@ signals:
|
||||||
/*
|
/*
|
||||||
* Trigger other components to reconnect account related signals.
|
* Trigger other components to reconnect account related signals.
|
||||||
*/
|
*/
|
||||||
void accountStatusChanged();
|
void accountStatusChanged(QString accountId = {});
|
||||||
void updateConversationForAddedContact();
|
void updateConversationForAddedContact();
|
||||||
/*
|
/*
|
||||||
* send report failure to QML to make it show the right UI state .
|
* send report failure to QML to make it show the right UI state .
|
||||||
|
|
|
@ -21,10 +21,7 @@
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
#include "globalinstances.h"
|
|
||||||
|
|
||||||
#include "lrcinstance.h"
|
#include "lrcinstance.h"
|
||||||
#include "pixbufmanipulator.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
AccountListModel::AccountListModel(QObject* parent)
|
AccountListModel::AccountListModel(QObject* parent)
|
||||||
|
@ -68,6 +65,8 @@ AccountListModel::data(const QModelIndex& index, int role) const
|
||||||
|
|
||||||
auto& accountInfo = LRCInstance::accountModel().getAccountInfo(accountList.at(index.row()));
|
auto& accountInfo = LRCInstance::accountModel().getAccountInfo(accountList.at(index.row()));
|
||||||
|
|
||||||
|
// Since we are using image provider right now, image url representation should be unique to
|
||||||
|
// be able to use the image cache, account avatar will only be updated once PictureUid changed
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Role::Alias:
|
case Role::Alias:
|
||||||
return QVariant(Utils::bestNameForAccount(accountInfo));
|
return QVariant(Utils::bestNameForAccount(accountInfo));
|
||||||
|
@ -77,11 +76,10 @@ AccountListModel::data(const QModelIndex& index, int role) const
|
||||||
return QVariant(static_cast<int>(accountInfo.profileInfo.type));
|
return QVariant(static_cast<int>(accountInfo.profileInfo.type));
|
||||||
case Role::Status:
|
case Role::Status:
|
||||||
return QVariant(static_cast<int>(accountInfo.status));
|
return QVariant(static_cast<int>(accountInfo.status));
|
||||||
case Role::Picture:
|
|
||||||
return QString::fromLatin1(
|
|
||||||
Utils::QImageToByteArray(Utils::accountPhoto(accountInfo)).toBase64().data());
|
|
||||||
case Role::ID:
|
case Role::ID:
|
||||||
return QVariant(accountInfo.id);
|
return QVariant(accountInfo.id);
|
||||||
|
case Role::PictureUid:
|
||||||
|
return avatarUidMap_[accountInfo.id];
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -92,10 +90,10 @@ AccountListModel::roleNames() const
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles[Alias] = "Alias";
|
roles[Alias] = "Alias";
|
||||||
roles[Username] = "Username";
|
roles[Username] = "Username";
|
||||||
roles[Picture] = "Picture";
|
|
||||||
roles[Type] = "Type";
|
roles[Type] = "Type";
|
||||||
roles[Status] = "Status";
|
roles[Status] = "Status";
|
||||||
roles[ID] = "ID";
|
roles[ID] = "ID";
|
||||||
|
roles[PictureUid] = "PictureUid";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,5 +132,28 @@ void
|
||||||
AccountListModel::reset()
|
AccountListModel::reset()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
fillAvatarUidMap(LRCInstance::accountModel().getAccountList());
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AccountListModel::updateAvatarUid(const QString& accountId)
|
||||||
|
{
|
||||||
|
avatarUidMap_[accountId] = Utils::generateUid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AccountListModel::fillAvatarUidMap(const QStringList& accountList)
|
||||||
|
{
|
||||||
|
if (accountList.size() == 0) {
|
||||||
|
avatarUidMap_.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatarUidMap_.isEmpty() || accountList.size() != avatarUidMap_.size()) {
|
||||||
|
for (int i = 0; i < accountList.size(); ++i) {
|
||||||
|
if (!avatarUidMap_.contains(accountList.at(i)))
|
||||||
|
avatarUidMap_.insert(accountList.at(i), Utils::generateUid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ class AccountListModel : public QAbstractListModel
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Role { Alias = Qt::UserRole + 1, Username, Picture, Type, Status, ID };
|
enum Role { Alias = Qt::UserRole + 1, Username, Type, Status, ID, PictureUid };
|
||||||
Q_ENUM(Role)
|
Q_ENUM(Role)
|
||||||
|
|
||||||
explicit AccountListModel(QObject* parent = 0);
|
explicit AccountListModel(QObject* parent = 0);
|
||||||
|
@ -55,4 +55,17 @@ public:
|
||||||
* This function is to reset the model when there's new account added.
|
* This function is to reset the model when there's new account added.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void reset();
|
Q_INVOKABLE void reset();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is to update avatar uuid when there's an avatar changed.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void updateAvatarUid(const QString& accountId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*
|
||||||
|
* Give a uuid for each account avatar and it will serve PictureUid role
|
||||||
|
*/
|
||||||
|
void fillAvatarUidMap(const QStringList& accountList);
|
||||||
|
|
||||||
|
QMap<QString, QString> avatarUidMap_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -92,9 +92,6 @@ AccountsToMigrateListModel::data(const QModelIndex& index, int role) const
|
||||||
return QVariant(avatarInfo.confProperties.username);
|
return QVariant(avatarInfo.confProperties.username);
|
||||||
case Role::Alias:
|
case Role::Alias:
|
||||||
return QVariant(LRCInstance::accountModel().getAccountInfo(accountId).profileInfo.alias);
|
return QVariant(LRCInstance::accountModel().getAccountInfo(accountId).profileInfo.alias);
|
||||||
case Role::Picture:
|
|
||||||
return QString::fromLatin1(
|
|
||||||
Utils::QImageToByteArray(Utils::accountPhoto(avatarInfo)).toBase64().data());
|
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -108,7 +105,6 @@ AccountsToMigrateListModel::roleNames() const
|
||||||
roles[ManagerUri] = "ManagerUri";
|
roles[ManagerUri] = "ManagerUri";
|
||||||
roles[Username] = "Username";
|
roles[Username] = "Username";
|
||||||
roles[Alias] = "Alias";
|
roles[Alias] = "Alias";
|
||||||
roles[Picture] = "Picture";
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,14 +31,7 @@ class AccountsToMigrateListModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum Role {
|
enum Role { Account_ID = Qt::UserRole + 1, ManagerUsername, ManagerUri, Username, Alias };
|
||||||
Account_ID = Qt::UserRole + 1,
|
|
||||||
ManagerUsername,
|
|
||||||
ManagerUri,
|
|
||||||
Username,
|
|
||||||
Alias,
|
|
||||||
Picture
|
|
||||||
};
|
|
||||||
Q_ENUM(Role)
|
Q_ENUM(Role)
|
||||||
|
|
||||||
explicit AccountsToMigrateListModel(QObject* parent = 0);
|
explicit AccountsToMigrateListModel(QObject* parent = 0);
|
||||||
|
|
71
src/avatarimageprovider.h
Normal file
71
src/avatarimageprovider.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 by Savoir-faire Linux
|
||||||
|
* Author: Mingrui Zhang <mingrui.zhang@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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QQuickImageProvider>
|
||||||
|
|
||||||
|
class AvatarImageProvider : public QObject, public QQuickImageProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AvatarImageProvider()
|
||||||
|
: QQuickImageProvider(QQuickImageProvider::Image,
|
||||||
|
QQmlImageProviderBase::ForceAsynchronousImageLoading)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request function
|
||||||
|
* id could be
|
||||||
|
* 1. account_ + account id
|
||||||
|
* 2. file_ + file path
|
||||||
|
* 3. contact_+ contact uri
|
||||||
|
* 4. conversation_+ conversation uid
|
||||||
|
*/
|
||||||
|
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(size)
|
||||||
|
|
||||||
|
auto idInfo = id.split("_");
|
||||||
|
// Id type -> account_
|
||||||
|
auto idType = idInfo[1];
|
||||||
|
// Id content -> every after account_
|
||||||
|
auto idContent = id.mid(id.indexOf(idType) + idType.length() + 1);
|
||||||
|
|
||||||
|
if (idContent.isEmpty())
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
if (idType == "account") {
|
||||||
|
return Utils::accountPhoto(LRCInstance::accountModel().getAccountInfo(idContent),
|
||||||
|
requestedSize);
|
||||||
|
} else if (idType == "conversation") {
|
||||||
|
auto* convModel = LRCInstance::getCurrentAccountInfo().conversationModel.get();
|
||||||
|
const auto& conv = convModel->getConversationForUID(idContent);
|
||||||
|
return Utils::contactPhoto(conv.participants[0], requestedSize);
|
||||||
|
} else if (idType == "contact") {
|
||||||
|
return Utils::contactPhoto(idContent, requestedSize);
|
||||||
|
} else {
|
||||||
|
auto image = Utils::cropImage(QImage(idContent));
|
||||||
|
return image.scaled(requestedSize,
|
||||||
|
Qt::KeepAspectRatioByExpanding,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -61,12 +61,6 @@ BannedListModel::data(const QModelIndex& index, int role) const
|
||||||
return QVariant(contactInfo.registeredName);
|
return QVariant(contactInfo.registeredName);
|
||||||
case Role::ContactID:
|
case Role::ContactID:
|
||||||
return QVariant(contactInfo.profileInfo.uri);
|
return QVariant(contactInfo.profileInfo.uri);
|
||||||
case Role::ContactPicture:
|
|
||||||
QImage avatarImage = Utils::fallbackAvatar(contactInfo.profileInfo.uri,
|
|
||||||
contactInfo.registeredName,
|
|
||||||
QSize(48, 48));
|
|
||||||
|
|
||||||
return QString::fromLatin1(Utils::QImageToByteArray(avatarImage).toBase64().data());
|
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -77,7 +71,6 @@ BannedListModel::roleNames() const
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles[ContactName] = "ContactName";
|
roles[ContactName] = "ContactName";
|
||||||
roles[ContactID] = "ContactID";
|
roles[ContactID] = "ContactID";
|
||||||
roles[ContactPicture] = "ContactPicture";
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ class BannedListModel : public QAbstractListModel
|
||||||
BannedListModel(const BannedListModel& cpy);
|
BannedListModel(const BannedListModel& cpy);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Role { ContactName = Qt::UserRole + 1, ContactID, ContactPicture };
|
enum Role { ContactName = Qt::UserRole + 1, ContactID };
|
||||||
Q_ENUM(Role)
|
Q_ENUM(Role)
|
||||||
|
|
||||||
explicit BannedListModel(QObject* parent = nullptr);
|
explicit BannedListModel(QObject* parent = nullptr);
|
||||||
|
|
|
@ -42,7 +42,6 @@ Window {
|
||||||
|
|
||||||
property bool nonOperationClosing: true
|
property bool nonOperationClosing: true
|
||||||
property bool successState : true
|
property bool successState : true
|
||||||
property string imgBase64: ""
|
|
||||||
|
|
||||||
signal accountMigrationFinished
|
signal accountMigrationFinished
|
||||||
|
|
||||||
|
@ -88,8 +87,7 @@ Window {
|
||||||
accountID = accountsToMigrateListModel.data(accountsToMigrateListModel.index(
|
accountID = accountsToMigrateListModel.data(accountsToMigrateListModel.index(
|
||||||
0, 0), AccountsToMigrateListModel.Account_ID)
|
0, 0), AccountsToMigrateListModel.Account_ID)
|
||||||
|
|
||||||
imgBase64 = accountsToMigrateListModel.data(accountsToMigrateListModel.index(
|
avatarImg.updateImage(accountID)
|
||||||
0, 0), AccountsToMigrateListModel.Picture)
|
|
||||||
|
|
||||||
connectionMigrationEnded.enabled = false
|
connectionMigrationEnded.enabled = false
|
||||||
migrationPushButton.enabled = false
|
migrationPushButton.enabled = false
|
||||||
|
@ -284,17 +282,13 @@ Window {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
Image {
|
AvatarImage {
|
||||||
id: avatarImg
|
id: avatarImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: {
|
|
||||||
if (imgBase64.length === 0) {
|
showPresenceIndicator: false
|
||||||
return ""
|
|
||||||
} else {
|
|
||||||
return "data:image/png;base64," + imgBase64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
|
|
183
src/commoncomponents/AvatarImage.qml
Normal file
183
src/commoncomponents/AvatarImage.qml
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 by Savoir-faire Linux
|
||||||
|
* Author: Mingrui Zhang <mingrui.zhang@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 2.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Window 2.14
|
||||||
|
import net.jami.Models 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// FromUrl here is for grabToImage image url
|
||||||
|
enum Mode {
|
||||||
|
FromAccount = 0,
|
||||||
|
FromFile,
|
||||||
|
FromContactUri,
|
||||||
|
FromConvUid,
|
||||||
|
FromUrl,
|
||||||
|
Default
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias fillMode: rootImage.fillMode
|
||||||
|
property alias sourceSize: rootImage.sourceSize
|
||||||
|
property int mode: AvatarImage.Mode.FromAccount
|
||||||
|
property string imageProviderIdPrefix: {
|
||||||
|
switch(mode) {
|
||||||
|
case AvatarImage.Mode.FromAccount:
|
||||||
|
return "account_"
|
||||||
|
case AvatarImage.Mode.FromFile:
|
||||||
|
return "file_"
|
||||||
|
case AvatarImage.Mode.FromContactUri:
|
||||||
|
return "contact_"
|
||||||
|
case AvatarImage.Mode.FromConvUid:
|
||||||
|
return "conversation_"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full request url example: forceUpdateUrl_xxxxxxx_account_xxxxxxxx
|
||||||
|
property string imageProviderUrl: "image://avatarImage/" + forceUpdateUrl + "_" +
|
||||||
|
imageProviderIdPrefix
|
||||||
|
property string imageId: ""
|
||||||
|
property string defaultImgUrl: "qrc:/images/default_avatar_overlay.svg"
|
||||||
|
property string forceUpdateUrl: Date.now()
|
||||||
|
property alias presenceStatus: presenceIndicator.status
|
||||||
|
property bool showPresenceIndicator: true
|
||||||
|
property int unreadMessagesCount: 0
|
||||||
|
|
||||||
|
signal imageIsReady
|
||||||
|
|
||||||
|
function updateImage(updatedId, oneTimeForceUpdateUrl) {
|
||||||
|
imageId = updatedId
|
||||||
|
if (oneTimeForceUpdateUrl === undefined)
|
||||||
|
forceUpdateUrl = Date.now()
|
||||||
|
else
|
||||||
|
forceUpdateUrl = oneTimeForceUpdateUrl
|
||||||
|
|
||||||
|
if (mode === AvatarImage.Mode.FromUrl)
|
||||||
|
rootImage.source = imageId
|
||||||
|
else if (imageId)
|
||||||
|
rootImage.source = imageProviderUrl + imageId
|
||||||
|
}
|
||||||
|
|
||||||
|
onModeChanged: {
|
||||||
|
if (mode === AvatarImage.Mode.Default)
|
||||||
|
rootImage.source = defaultImgUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: rootImage
|
||||||
|
|
||||||
|
anchors.fill: root
|
||||||
|
|
||||||
|
smooth: false
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
sourceSize.width: Math.max(24, width)
|
||||||
|
sourceSize.height: Math.max(24, height)
|
||||||
|
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Image.Ready) {
|
||||||
|
rootImageOverlay.state = ""
|
||||||
|
rootImageOverlay.state = "rootImageLoading"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (imageId)
|
||||||
|
return source = imageProviderUrl + imageId
|
||||||
|
return source = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: rootImageOverlay
|
||||||
|
|
||||||
|
anchors.fill: rootImage
|
||||||
|
|
||||||
|
smooth: false
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
sourceSize.width: Math.max(24, width)
|
||||||
|
sourceSize.height: Math.max(24, height)
|
||||||
|
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
onOpacityChanged: {
|
||||||
|
if (opacity === 0)
|
||||||
|
source = rootImage.source
|
||||||
|
}
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Image.Ready && opacity === 0) {
|
||||||
|
opacity = 1
|
||||||
|
root.imageIsReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "rootImageLoading"
|
||||||
|
PropertyChanges { target: rootImageOverlay; opacity: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
transitions: Transition {
|
||||||
|
NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad; duration: 400}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PresenceIndicator {
|
||||||
|
id: presenceIndicator
|
||||||
|
|
||||||
|
anchors.right: root.right
|
||||||
|
anchors.bottom: root.bottom
|
||||||
|
|
||||||
|
size: root.width * 0.3
|
||||||
|
|
||||||
|
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.textFontSize - 2
|
||||||
|
}
|
||||||
|
|
||||||
|
radius: 30
|
||||||
|
color: JamiTheme.notificationRed
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,9 +10,10 @@ import net.jami.Adapters 1.0
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
property bool takePhotoState: false
|
property bool takePhotoState: false
|
||||||
property bool hasAvatar: false
|
property bool hasAvatar: false
|
||||||
property bool isDefaultIcon: false
|
// saveToConfig is to specify whether the image should be saved to account config
|
||||||
property string imgBase64: ""
|
property bool saveToConfig: false
|
||||||
property string fileName: ""
|
property string fileName: ""
|
||||||
|
property var boothImg: ""
|
||||||
|
|
||||||
property int boothWidth: 224
|
property int boothWidth: 224
|
||||||
|
|
||||||
|
@ -20,9 +21,6 @@ ColumnLayout {
|
||||||
buttonsRowLayout.height +
|
buttonsRowLayout.height +
|
||||||
JamiTheme.preferredMarginSize / 2
|
JamiTheme.preferredMarginSize / 2
|
||||||
|
|
||||||
signal imageAcquired
|
|
||||||
signal imageCleared
|
|
||||||
|
|
||||||
function startBooth(force = false){
|
function startBooth(force = false){
|
||||||
hasAvatar = false
|
hasAvatar = false
|
||||||
AccountAdapter.startPreviewing(force)
|
AccountAdapter.startPreviewing(force)
|
||||||
|
@ -39,12 +37,15 @@ ColumnLayout {
|
||||||
takePhotoState = false
|
takePhotoState = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAvatarPixmap(avatarPixmapBase64, defaultValue = false){
|
function setAvatarImage(mode = AvatarImage.Mode.FromAccount,
|
||||||
imgBase64 = avatarPixmapBase64
|
imageId = AccountAdapter.currentAccountId){
|
||||||
stopBooth()
|
if (mode === AvatarImage.Mode.Default)
|
||||||
if(defaultValue){
|
boothImg = ""
|
||||||
isDefaultIcon = defaultValue
|
|
||||||
}
|
avatarImg.mode = mode
|
||||||
|
|
||||||
|
if (imageId)
|
||||||
|
avatarImg.updateImage(imageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
|
@ -68,14 +69,13 @@ ColumnLayout {
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
fileName = file
|
fileName = file
|
||||||
if (fileName.length === 0) {
|
if (fileName.length === 0) {
|
||||||
imageCleared()
|
SettingsAdapter.clearCurrentAvatar()
|
||||||
|
setAvatarImage()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
imgBase64 = UtilsAdapter.getCroppedImageBase64FromFile(
|
|
||||||
UtilsAdapter.getAbsPath(fileName),
|
setAvatarImage(AvatarImage.Mode.FromFile,
|
||||||
boothWidth)
|
UtilsAdapter.getAbsPath(fileName))
|
||||||
imageAcquired()
|
|
||||||
stopBooth()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,29 +96,40 @@ ColumnLayout {
|
||||||
color: "grey"
|
color: "grey"
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
|
|
||||||
Image {
|
AvatarImage {
|
||||||
id: avatarImg
|
id: avatarImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: {
|
|
||||||
if(imgBase64.length === 0){
|
imageId: AccountAdapter.currentAccountId
|
||||||
return "qrc:/images/default_avatar_overlay.svg"
|
|
||||||
} else {
|
showPresenceIndicator: false
|
||||||
return "data:image/png;base64," + imgBase64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
maskSource: Rectangle {
|
maskSource: Rectangle {
|
||||||
width: avatarImg.width
|
width: avatarImg.width
|
||||||
height: avatarImg.height
|
height: avatarImg.height
|
||||||
radius: {
|
radius: {
|
||||||
var size = ((avatarImg.width <= avatarImg.height)? avatarImg.width:avatarImg.height)
|
var size = ((avatarImg.width <= avatarImg.height) ?
|
||||||
|
avatarImg.width:avatarImg.height)
|
||||||
return size / 2
|
return size / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onImageIsReady: {
|
||||||
|
// Once image is loaded (updated), save to boothImg
|
||||||
|
avatarImg.grabToImage(function(result) {
|
||||||
|
if (mode !== AvatarImage.Mode.Default)
|
||||||
|
boothImg = result.image
|
||||||
|
|
||||||
|
if (saveToConfig)
|
||||||
|
SettingsAdapter.setCurrAccAvatar(result.image)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,9 +137,7 @@ ColumnLayout {
|
||||||
PhotoboothPreviewRender {
|
PhotoboothPreviewRender {
|
||||||
id:previewWidget
|
id:previewWidget
|
||||||
|
|
||||||
onHideBooth:{
|
onHideBooth: stopBooth()
|
||||||
stopBooth()
|
|
||||||
}
|
|
||||||
|
|
||||||
visible: takePhotoState
|
visible: takePhotoState
|
||||||
focus: visible
|
focus: visible
|
||||||
|
@ -143,7 +152,8 @@ ColumnLayout {
|
||||||
width: previewWidget.width
|
width: previewWidget.width
|
||||||
height: previewWidget.height
|
height: previewWidget.height
|
||||||
radius: {
|
radius: {
|
||||||
var size = ((previewWidget.width <= previewWidget.height)? previewWidget.width:previewWidget.height)
|
var size = ((previewWidget.width <= previewWidget.height) ?
|
||||||
|
previewWidget.width:previewWidget.height)
|
||||||
return size / 2
|
return size / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +201,6 @@ ColumnLayout {
|
||||||
|
|
||||||
radius: height / 6
|
radius: height / 6
|
||||||
source: {
|
source: {
|
||||||
|
|
||||||
if(takePhotoState) {
|
if(takePhotoState) {
|
||||||
toolTipText = qsTr("Take photo")
|
toolTipText = qsTr("Take photo")
|
||||||
return cameraAltIconUrl
|
return cameraAltIconUrl
|
||||||
|
@ -205,9 +214,9 @@ ColumnLayout {
|
||||||
return addPhotoIconUrl
|
return addPhotoIconUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if(!takePhotoState){
|
if(!takePhotoState){
|
||||||
imageCleared()
|
|
||||||
startBooth()
|
startBooth()
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
@ -215,11 +224,13 @@ ColumnLayout {
|
||||||
flashOverlay.visible = true
|
flashOverlay.visible = true
|
||||||
flashAnimation.restart()
|
flashAnimation.restart()
|
||||||
|
|
||||||
// run concurrent function call to take photo
|
previewWidget.grabToImage(function(result) {
|
||||||
imgBase64 = previewWidget.takeCroppedPhotoToBase64(boothWidth)
|
|
||||||
|
setAvatarImage(AvatarImage.Mode.FromUrl, result.url)
|
||||||
|
|
||||||
hasAvatar = true
|
hasAvatar = true
|
||||||
imageAcquired()
|
|
||||||
stopBooth()
|
stopBooth()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,6 +209,14 @@ ConversationsAdapter::connectConversationModel(bool updateFilter)
|
||||||
emit modelSorted(QVariant::fromValue(conversation.uid));
|
emit modelSorted(QVariant::fromValue(conversation.uid));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
contactProfileUpdatedConnection_
|
||||||
|
= QObject::connect(LRCInstance::getCurrentAccountInfo().contactModel.get(),
|
||||||
|
&lrc::api::ContactModel::profileUpdated,
|
||||||
|
[this](const QString& contactUri) {
|
||||||
|
conversationSmartListModel_->updateContactAvatarUid(contactUri);
|
||||||
|
emit updateListViewRequested();
|
||||||
|
});
|
||||||
|
|
||||||
modelUpdatedConnection_ = QObject::connect(currentConversationModel,
|
modelUpdatedConnection_ = QObject::connect(currentConversationModel,
|
||||||
&lrc::api::ConversationModel::conversationUpdated,
|
&lrc::api::ConversationModel::conversationUpdated,
|
||||||
[this](const QString& convUid) {
|
[this](const QString& convUid) {
|
||||||
|
@ -295,6 +303,7 @@ ConversationsAdapter::disconnectConversationModel()
|
||||||
QObject::disconnect(interactionRemovedConnection_);
|
QObject::disconnect(interactionRemovedConnection_);
|
||||||
QObject::disconnect(searchStatusChangedConnection_);
|
QObject::disconnect(searchStatusChangedConnection_);
|
||||||
QObject::disconnect(searchResultUpdatedConnection_);
|
QObject::disconnect(searchResultUpdatedConnection_);
|
||||||
|
QObject::disconnect(contactProfileUpdatedConnection_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -82,6 +82,7 @@ private:
|
||||||
QMetaObject::Connection newConversationConnection_;
|
QMetaObject::Connection newConversationConnection_;
|
||||||
QMetaObject::Connection conversationRemovedConnection_;
|
QMetaObject::Connection conversationRemovedConnection_;
|
||||||
QMetaObject::Connection conversationClearedConnection;
|
QMetaObject::Connection conversationClearedConnection;
|
||||||
|
QMetaObject::Connection contactProfileUpdatedConnection_;
|
||||||
QMetaObject::Connection selectedCallChanged_;
|
QMetaObject::Connection selectedCallChanged_;
|
||||||
QMetaObject::Connection smartlistSelectionConnection_;
|
QMetaObject::Connection smartlistSelectionConnection_;
|
||||||
QMetaObject::Connection interactionRemovedConnection_;
|
QMetaObject::Connection interactionRemovedConnection_;
|
||||||
|
|
|
@ -336,15 +336,6 @@ public:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QPixmap getCurrAccPixmap()
|
|
||||||
{
|
|
||||||
return instance()
|
|
||||||
.accountListModel_
|
|
||||||
.data(instance().accountListModel_.index(getCurrentAccountIndex()),
|
|
||||||
AccountListModel::Role::Picture)
|
|
||||||
.value<QPixmap>();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setAvatarForAccount(const QPixmap& avatarPixmap, const QString& accountID)
|
static void setAvatarForAccount(const QPixmap& avatarPixmap, const QString& accountID)
|
||||||
{
|
{
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
|
|
|
@ -26,10 +26,8 @@
|
||||||
#include "globalsystemtray.h"
|
#include "globalsystemtray.h"
|
||||||
#include "qmlregister.h"
|
#include "qmlregister.h"
|
||||||
#include "qrimageprovider.h"
|
#include "qrimageprovider.h"
|
||||||
#include "pixbufmanipulator.h"
|
|
||||||
#include "tintedbuttonimageprovider.h"
|
#include "tintedbuttonimageprovider.h"
|
||||||
|
#include "avatarimageprovider.h"
|
||||||
#include "globalinstances.h"
|
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
@ -148,7 +146,6 @@ MainApplication::init()
|
||||||
gnutls_global_init();
|
gnutls_global_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GlobalInstances::setPixmapManipulator(std::make_unique<PixbufManipulator>());
|
|
||||||
initLrc(results[opts::UPDATEURL].toString(), connectivityMonitor_);
|
initLrc(results[opts::UPDATEURL].toString(), connectivityMonitor_);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
@ -322,6 +319,7 @@ MainApplication::initQmlEngine()
|
||||||
|
|
||||||
engine_->addImageProvider(QLatin1String("qrImage"), new QrImageProvider());
|
engine_->addImageProvider(QLatin1String("qrImage"), new QrImageProvider());
|
||||||
engine_->addImageProvider(QLatin1String("tintedPixmap"), new TintedButtonImageProvider());
|
engine_->addImageProvider(QLatin1String("tintedPixmap"), new TintedButtonImageProvider());
|
||||||
|
engine_->addImageProvider(QLatin1String("avatarImage"), new AvatarImageProvider());
|
||||||
|
|
||||||
engine_->load(QUrl(QStringLiteral("qrc:/src/MainApplicationWindow.qml")));
|
engine_->load(QUrl(QStringLiteral("qrc:/src/MainApplicationWindow.qml")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,8 +308,8 @@ Window {
|
||||||
mainViewWindowSidePanel.forceReselectConversationSmartListCurrentIndex()
|
mainViewWindowSidePanel.forceReselectConversationSmartListCurrentIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAccountStatusChanged() {
|
function onAccountStatusChanged(accountId) {
|
||||||
accountComboBox.resetAccountListModel()
|
accountComboBox.resetAccountListModel(accountId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ ComboBox {
|
||||||
signal settingBtnClicked
|
signal settingBtnClicked
|
||||||
|
|
||||||
// Reset accountListModel.
|
// Reset accountListModel.
|
||||||
function resetAccountListModel() {
|
function resetAccountListModel(accountId) {
|
||||||
|
accountListModel.updateAvatarUid(accountId)
|
||||||
accountListModel.reset()
|
accountListModel.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,9 +40,11 @@ ComboBox {
|
||||||
target: accountListModel
|
target: accountListModel
|
||||||
|
|
||||||
function onModelReset() {
|
function onModelReset() {
|
||||||
userImageRoot.source = "data:image/png;base64," + accountListModel.data(
|
userImageRoot.updateImage(
|
||||||
accountListModel.index(0, 0), AccountListModel.Picture)
|
AccountAdapter.currentAccountId,
|
||||||
currentAccountPresenceIndicator.status =
|
accountListModel.data(
|
||||||
|
accountListModel.index(0, 0), AccountListModel.PictureUid))
|
||||||
|
userImageRoot.presenceStatus =
|
||||||
accountListModel.data(accountListModel.index(0, 0), AccountListModel.Status)
|
accountListModel.data(accountListModel.index(0, 0), AccountListModel.Status)
|
||||||
textMetricsUserAliasRoot.text = accountListModel.data(accountListModel.index(0,0),
|
textMetricsUserAliasRoot.text = accountListModel.data(accountListModel.index(0,0),
|
||||||
AccountListModel.Alias)
|
AccountListModel.Alias)
|
||||||
|
@ -50,35 +53,21 @@ ComboBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
AvatarImage {
|
||||||
id: userImageRoot
|
id: userImageRoot
|
||||||
|
|
||||||
anchors.left: root.left
|
anchors.left: root.left
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
anchors.verticalCenter: root.verticalCenter
|
anchors.verticalCenter: root.verticalCenter
|
||||||
|
|
||||||
width: 30
|
width: 40
|
||||||
height: 30
|
height: 40
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
imageId: AccountAdapter.currentAccountId
|
||||||
|
|
||||||
// Base 64 format
|
presenceStatus: accountListModel.data(accountListModel.index(0, 0),
|
||||||
source: "data:image/png;base64," + accountListModel.data(
|
|
||||||
accountListModel.index(0, 0), AccountListModel.Picture)
|
|
||||||
mipmap: true
|
|
||||||
|
|
||||||
PresenceIndicator {
|
|
||||||
id: currentAccountPresenceIndicator
|
|
||||||
|
|
||||||
anchors.right: userImageRoot.right
|
|
||||||
anchors.rightMargin: -2
|
|
||||||
anchors.bottom: userImageRoot.bottom
|
|
||||||
anchors.bottomMargin: -2
|
|
||||||
|
|
||||||
status: accountListModel.data(accountListModel.index(0, 0),
|
|
||||||
AccountListModel.Status)
|
AccountListModel.Status)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: textUserAliasRoot
|
id: textUserAliasRoot
|
||||||
|
@ -251,8 +240,6 @@ ComboBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
indicator: null
|
indicator: null
|
||||||
|
|
||||||
// Overwrite the combo box pop up to add footer (for add accounts).
|
// Overwrite the combo box pop up to add footer (for add accounts).
|
||||||
|
|
|
@ -45,42 +45,29 @@ Popup {
|
||||||
contentItem: ListView {
|
contentItem: ListView {
|
||||||
id: comboBoxPopupListView
|
id: comboBoxPopupListView
|
||||||
|
|
||||||
|
|
||||||
// In list view, index is an interger.
|
// In list view, index is an interger.
|
||||||
clip: true
|
clip: true
|
||||||
model: accountListModel
|
model: accountListModel
|
||||||
implicitHeight: contentHeight
|
implicitHeight: contentHeight
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
Image {
|
AvatarImage {
|
||||||
id: userImage
|
id: userImage
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
width: 30
|
width: 40
|
||||||
height: 30
|
height: 40
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
presenceStatus: Status
|
||||||
mipmap: true
|
|
||||||
|
|
||||||
// Role::Picture
|
Component.onCompleted: {
|
||||||
source: {
|
return updateImage(
|
||||||
var data = accountListModel.data(accountListModel.index(index, 0),
|
accountListModel.data(
|
||||||
AccountListModel.Picture)
|
accountListModel.index(index, 0), AccountListModel.ID),
|
||||||
if (data === undefined) {
|
accountListModel.data(
|
||||||
return ""
|
accountListModel.index(index, 0), AccountListModel.PictureUid))
|
||||||
}
|
|
||||||
return "data:image/png;base64," + data
|
|
||||||
}
|
|
||||||
|
|
||||||
PresenceIndicator {
|
|
||||||
anchors.right: userImage.right
|
|
||||||
anchors.rightMargin: -2
|
|
||||||
anchors.bottom: userImage.bottom
|
|
||||||
anchors.bottomMargin: -2
|
|
||||||
|
|
||||||
status: Status
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ import "../../commoncomponents"
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: audioCallPageRect
|
id: audioCallPageRect
|
||||||
|
|
||||||
property string contactImgSource: ""
|
|
||||||
property string bestName: "Best Name"
|
property string bestName: "Best Name"
|
||||||
property string bestId: "Best Id"
|
property string bestId: "Best Id"
|
||||||
|
|
||||||
|
@ -37,8 +36,7 @@ Rectangle {
|
||||||
signal showFullScreenReqested
|
signal showFullScreenReqested
|
||||||
|
|
||||||
function updateUI(accountId, convUid) {
|
function updateUI(accountId, convUid) {
|
||||||
contactImgSource = "data:image/png;base64," + UtilsAdapter.getContactImageString(
|
contactImage.updateImage(convUid)
|
||||||
accountId, convUid)
|
|
||||||
bestName = UtilsAdapter.getBestName(accountId, convUid)
|
bestName = UtilsAdapter.getBestName(accountId, convUid)
|
||||||
|
|
||||||
var id = UtilsAdapter.getBestId(accountId, convUid)
|
var id = UtilsAdapter.getBestId(accountId, convUid)
|
||||||
|
@ -162,7 +160,7 @@ Rectangle {
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: audioCallPageRectColumnLayout
|
id: audioCallPageRectColumnLayout
|
||||||
|
|
||||||
Image {
|
AvatarImage {
|
||||||
id: contactImage
|
id: contactImage
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
@ -170,9 +168,8 @@ Rectangle {
|
||||||
Layout.preferredWidth: 100
|
Layout.preferredWidth: 100
|
||||||
Layout.preferredHeight: 100
|
Layout.preferredHeight: 100
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
mode: AvatarImage.Mode.FromConvUid
|
||||||
source: contactImgSource
|
showPresenceIndicator: false
|
||||||
asynchronous: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
|
|
@ -125,8 +125,6 @@ Popup {
|
||||||
}
|
}
|
||||||
|
|
||||||
onAboutToShow: {
|
onAboutToShow: {
|
||||||
|
|
||||||
|
|
||||||
// Reset the model on each show.
|
// Reset the model on each show.
|
||||||
contactPickerListView.model = ContactAdapter.getContactSelectableModel(
|
contactPickerListView.model = ContactAdapter.getContactSelectableModel(
|
||||||
type)
|
type)
|
||||||
|
|
|
@ -26,7 +26,7 @@ import "../../commoncomponents"
|
||||||
ItemDelegate {
|
ItemDelegate {
|
||||||
id: contactPickerItemDelegate
|
id: contactPickerItemDelegate
|
||||||
|
|
||||||
Image {
|
AvatarImage {
|
||||||
id: contactPickerContactImage
|
id: contactPickerContactImage
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -36,9 +36,8 @@ ItemDelegate {
|
||||||
width: 40
|
width: 40
|
||||||
height: 40
|
height: 40
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
mode: AvatarImage.Mode.FromContactUri
|
||||||
source: "data:image/png;base64," + Picture
|
imageId: URI
|
||||||
mipmap: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 by Savoir-faire Linux
|
|
||||||
* Author: Mingrui Zhang <mingrui.zhang@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 2.14
|
|
||||||
import QtQuick.Controls 2.14
|
|
||||||
import QtQuick.Layouts 1.14
|
|
||||||
import net.jami.Models 1.0
|
|
||||||
import "../../commoncomponents"
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: userImage
|
|
||||||
|
|
||||||
width: 40
|
|
||||||
height: 40
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: "data:image/png;base64," + Picture
|
|
||||||
mipmap: true
|
|
||||||
|
|
||||||
PresenceIndicator {
|
|
||||||
anchors.right: userImage.right
|
|
||||||
anchors.bottom: userImage.bottom
|
|
||||||
|
|
||||||
visible: Presence === undefined ? false : Presence
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: unreadMessageCountRect
|
|
||||||
|
|
||||||
anchors.right: userImage.right
|
|
||||||
anchors.rightMargin: -2
|
|
||||||
anchors.top: userImage.top
|
|
||||||
anchors.topMargin: -2
|
|
||||||
|
|
||||||
width: 14
|
|
||||||
height: 14
|
|
||||||
|
|
||||||
visible: UnreadMessagesCount > 0
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: unreadMessageCounttext
|
|
||||||
|
|
||||||
anchors.centerIn: unreadMessageCountRect
|
|
||||||
|
|
||||||
text: UnreadMessagesCount > 9 ? "···" : UnreadMessagesCount
|
|
||||||
color: "white"
|
|
||||||
font.pointSize: JamiTheme.textFontSize
|
|
||||||
}
|
|
||||||
|
|
||||||
radius: 30
|
|
||||||
color: JamiTheme.notificationRed
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -89,6 +89,8 @@ ListView {
|
||||||
|
|
||||||
delegate: ConversationSmartListViewItemDelegate {
|
delegate: ConversationSmartListViewItemDelegate {
|
||||||
id: smartListItemDelegate
|
id: smartListItemDelegate
|
||||||
|
|
||||||
|
onUpdateContactAvatarUidRequested: root.model.updateContactAvatarUid(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollIndicator.vertical: ScrollIndicator {}
|
ScrollIndicator.vertical: ScrollIndicator {}
|
||||||
|
|
|
@ -30,6 +30,8 @@ ItemDelegate {
|
||||||
|
|
||||||
property int lastInteractionPreferredWidth: 80
|
property int lastInteractionPreferredWidth: 80
|
||||||
|
|
||||||
|
signal updateContactAvatarUidRequested(string uid)
|
||||||
|
|
||||||
function convUid() {
|
function convUid() {
|
||||||
return UID
|
return UID
|
||||||
}
|
}
|
||||||
|
@ -76,14 +78,29 @@ ItemDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConversationSmartListUserImage {
|
AvatarImage {
|
||||||
id: conversationSmartListUserImage
|
id: conversationSmartListUserImage
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
}
|
|
||||||
|
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
|
||||||
|
mode: AvatarImage.Mode.FromContactUri
|
||||||
|
|
||||||
|
showPresenceIndicator: Presence === undefined ? false : Presence
|
||||||
|
|
||||||
|
unreadMessagesCount: UnreadMessagesCount
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
var contactUid = URI
|
||||||
|
if (ContactType === Profile.Type.TEMPORARY)
|
||||||
|
updateContactAvatarUidRequested(contactUid)
|
||||||
|
updateImage(contactUid, PictureUid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: rowUsernameAndLastInteractionDate
|
id: rowUsernameAndLastInteractionDate
|
||||||
|
@ -202,7 +219,7 @@ ItemDelegate {
|
||||||
userProfile.aliasText = DisplayName
|
userProfile.aliasText = DisplayName
|
||||||
userProfile.registeredNameText = DisplayID
|
userProfile.registeredNameText = DisplayID
|
||||||
userProfile.idText = URI
|
userProfile.idText = URI
|
||||||
userProfile.contactPicBase64 = Picture
|
userProfile.contactImageUid = UID
|
||||||
smartListContextMenu.openMenu()
|
smartListContextMenu.openMenu()
|
||||||
} else if (mouse.button === Qt.LeftButton) {
|
} else if (mouse.button === Qt.LeftButton) {
|
||||||
conversationSmartListView.currentIndex = -1
|
conversationSmartListView.currentIndex = -1
|
||||||
|
|
|
@ -39,6 +39,7 @@ Rectangle {
|
||||||
participantName.text = name
|
participantName.text = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: try to use AvatarImage as well
|
||||||
function setAvatar(avatar) {
|
function setAvatar(avatar) {
|
||||||
if (avatar === "") {
|
if (avatar === "") {
|
||||||
opacity = 0
|
opacity = 0
|
||||||
|
|
|
@ -30,13 +30,11 @@ Rectangle {
|
||||||
id: userInfoCallRect
|
id: userInfoCallRect
|
||||||
|
|
||||||
property int buttonPreferredSize: 48
|
property int buttonPreferredSize: 48
|
||||||
property string contactImgSource: ""
|
|
||||||
property string bestName: "Best Name"
|
property string bestName: "Best Name"
|
||||||
property string bestId: "Best Id"
|
property string bestId: "Best Id"
|
||||||
|
|
||||||
function updateUI(accountId, convUid) {
|
function updateUI(accountId, convUid) {
|
||||||
contactImgSource = "data:image/png;base64," + UtilsAdapter.getContactImageString(
|
contactImg.updateImage(convUid)
|
||||||
accountId, convUid)
|
|
||||||
bestName = UtilsAdapter.getBestName(accountId, convUid)
|
bestName = UtilsAdapter.getBestName(accountId, convUid)
|
||||||
var id = UtilsAdapter.getBestId(accountId, convUid)
|
var id = UtilsAdapter.getBestId(accountId, convUid)
|
||||||
bestId = (bestName !== id) ? id : ""
|
bestId = (bestName !== id) ? id : ""
|
||||||
|
@ -74,7 +72,7 @@ Rectangle {
|
||||||
onClicked: mainViewWindow.showWelcomeView()
|
onClicked: mainViewWindow.showWelcomeView()
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
AvatarImage {
|
||||||
id: contactImg
|
id: contactImg
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
@ -83,9 +81,8 @@ Rectangle {
|
||||||
Layout.preferredWidth: 100
|
Layout.preferredWidth: 100
|
||||||
Layout.preferredHeight: 100
|
Layout.preferredHeight: 100
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
mode: AvatarImage.Mode.FromConvUid
|
||||||
source: contactImgSource
|
showPresenceIndicator: false
|
||||||
asynchronous: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
|
@ -28,7 +28,7 @@ BaseDialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string responsibleConvUid: ""
|
property string responsibleConvUid: ""
|
||||||
property string contactPicBase64: ""
|
property string contactImageUid: ""
|
||||||
property string aliasText: ""
|
property string aliasText: ""
|
||||||
property string registeredNameText: ""
|
property string registeredNameText: ""
|
||||||
property string idText: ""
|
property string idText: ""
|
||||||
|
@ -53,17 +53,17 @@ BaseDialog {
|
||||||
rowSpacing: 16
|
rowSpacing: 16
|
||||||
columnSpacing: 24
|
columnSpacing: 24
|
||||||
|
|
||||||
Image {
|
AvatarImage {
|
||||||
id: contactImage
|
id: contactImage
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.preferredWidth: 130
|
Layout.preferredWidth: preferredImgSize
|
||||||
|
|
||||||
sourceSize.width: preferredImgSize
|
sourceSize.width: preferredImgSize
|
||||||
sourceSize.height: preferredImgSize
|
sourceSize.height: preferredImgSize
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
mode: AvatarImage.Mode.FromConvUid
|
||||||
mipmap: true
|
showPresenceIndicator: false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visible when user alias is not empty or equals to id.
|
// Visible when user alias is not empty or equals to id.
|
||||||
|
@ -196,8 +196,5 @@ BaseDialog {
|
||||||
contactQrImage.source = "image://qrImage/contact_" + responsibleConvUid
|
contactQrImage.source = "image://qrImage/contact_" + responsibleConvUid
|
||||||
}
|
}
|
||||||
|
|
||||||
onContactPicBase64Changed: {
|
onContactImageUidChanged: contactImage.updateImage(contactImageUid)
|
||||||
if (contactPicBase64 !== "")
|
|
||||||
contactImage.source = "data:image/png;base64," + contactPicBase64
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -457,7 +457,7 @@ MessagesAdapter::setConversationProfileData(const lrc::api::conversation::Info&
|
||||||
if (!contact.profileInfo.avatar.isEmpty()) {
|
if (!contact.profileInfo.avatar.isEmpty()) {
|
||||||
setSenderImage(contactUri, contact.profileInfo.avatar);
|
setSenderImage(contactUri, contact.profileInfo.avatar);
|
||||||
} else {
|
} else {
|
||||||
auto avatar = Utils::conversationPhoto(convInfo.uid, *accInfo, true);
|
auto avatar = Utils::contactPhoto(convInfo.participants[0]);
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
QBuffer bu(&ba);
|
QBuffer bu(&ba);
|
||||||
avatar.save(&bu, "PNG");
|
avatar.save(&bu, "PNG");
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015-2020 by Savoir-faire Linux
|
|
||||||
* Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
|
|
||||||
* Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com>
|
|
||||||
* Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com>
|
|
||||||
* Author: Andreas Traczyk <andreas.traczyk@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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "pixbufmanipulator.h"
|
|
||||||
|
|
||||||
#include <QBuffer>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QIODevice>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QMetaType>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QSize>
|
|
||||||
|
|
||||||
#include "globalinstances.h"
|
|
||||||
|
|
||||||
#include <api/account.h>
|
|
||||||
#include <api/contact.h>
|
|
||||||
#include <api/contactmodel.h>
|
|
||||||
#include <api/conversation.h>
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
#undef interface
|
|
||||||
|
|
||||||
QVariant
|
|
||||||
PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
|
|
||||||
{
|
|
||||||
QImage avatar;
|
|
||||||
const bool ret = avatar.loadFromData(QByteArray::fromBase64(data), type.toLatin1());
|
|
||||||
if (!ret) {
|
|
||||||
qDebug() << "vCard image loading failed";
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
return QPixmap::fromImage(Utils::getCirclePhoto(avatar, avatar.size().width()));
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant
|
|
||||||
PixbufManipulator::numberCategoryIcon(const QVariant& p,
|
|
||||||
const QSize& size,
|
|
||||||
bool displayPresence,
|
|
||||||
bool isPresent)
|
|
||||||
{
|
|
||||||
Q_UNUSED(p)
|
|
||||||
Q_UNUSED(size)
|
|
||||||
Q_UNUSED(displayPresence)
|
|
||||||
Q_UNUSED(isPresent)
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray
|
|
||||||
PixbufManipulator::toByteArray(const QVariant& pxm)
|
|
||||||
{
|
|
||||||
auto image = pxm.value<QImage>();
|
|
||||||
QByteArray ba = Utils::QImageToByteArray(image);
|
|
||||||
return ba;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant
|
|
||||||
PixbufManipulator::userActionIcon(const UserActionElement& state) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(state)
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant
|
|
||||||
PixbufManipulator::decorationRole(const QModelIndex& index)
|
|
||||||
{
|
|
||||||
Q_UNUSED(index)
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant
|
|
||||||
PixbufManipulator::decorationRole(const lrc::api::conversation::Info& conversationInfo,
|
|
||||||
const lrc::api::account::Info& accountInfo)
|
|
||||||
{
|
|
||||||
QImage photo;
|
|
||||||
auto contacts = conversationInfo.participants;
|
|
||||||
if (contacts.empty()) {
|
|
||||||
return QVariant::fromValue(photo);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
/*
|
|
||||||
* Get first contact photo.
|
|
||||||
*/
|
|
||||||
auto contactUri = contacts.front();
|
|
||||||
auto contactInfo = accountInfo.contactModel->getContact(contactUri);
|
|
||||||
auto contactPhoto = contactInfo.profileInfo.avatar;
|
|
||||||
auto bestName = Utils::bestNameForContact(contactInfo);
|
|
||||||
auto bestId = Utils::bestIdForContact(contactInfo);
|
|
||||||
if (accountInfo.profileInfo.type == lrc::api::profile::Type::SIP
|
|
||||||
&& contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY) {
|
|
||||||
photo = Utils::fallbackAvatar(QString(), QString());
|
|
||||||
} else if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY
|
|
||||||
&& contactInfo.profileInfo.uri.isEmpty()) {
|
|
||||||
photo = Utils::fallbackAvatar(QString(), QString());
|
|
||||||
} else if (!contactPhoto.isEmpty()) {
|
|
||||||
QByteArray byteArray = contactPhoto.toLocal8Bit();
|
|
||||||
photo = personPhoto(byteArray, nullptr).value<QImage>();
|
|
||||||
if (photo.isNull()) {
|
|
||||||
auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName;
|
|
||||||
photo = Utils::fallbackAvatar("ring:" + contactInfo.profileInfo.uri, avatarName);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName;
|
|
||||||
photo = Utils::fallbackAvatar("ring:" + contactInfo.profileInfo.uri, avatarName);
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
}
|
|
||||||
return QVariant::fromValue(Utils::scaleAndFrame(photo));
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015-2020 by Savoir-faire Linux
|
|
||||||
* Author: Edric Ladent Milaret <edric.ladent-milaret@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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QImage>
|
|
||||||
|
|
||||||
#include <interfaces/pixmapmanipulatori.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QImage);
|
|
||||||
|
|
||||||
class Person;
|
|
||||||
|
|
||||||
QByteArray QImageToByteArray(QImage image);
|
|
||||||
|
|
||||||
class PixbufManipulator : public Interfaces::PixmapManipulatorI
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QVariant personPhoto(const QByteArray& data, const QString& type = "PNG") override;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: the following methods return an empty QVariant/QByteArray.
|
|
||||||
*/
|
|
||||||
QVariant numberCategoryIcon(const QVariant& p,
|
|
||||||
const QSize& size,
|
|
||||||
bool displayPresence = false,
|
|
||||||
bool isPresent = false) override;
|
|
||||||
QByteArray toByteArray(const QVariant& pxm) override;
|
|
||||||
QVariant userActionIcon(const UserActionElement& state) const override;
|
|
||||||
QVariant decorationRole(const QModelIndex& index) override;
|
|
||||||
QVariant decorationRole(const lrc::api::conversation::Info& conversation,
|
|
||||||
const lrc::api::account::Info& accountInfo) override;
|
|
||||||
};
|
|
|
@ -112,24 +112,6 @@ PhotoboothPreviewRender::PhotoboothPreviewRender(QQuickItem* parent)
|
||||||
|
|
||||||
PhotoboothPreviewRender::~PhotoboothPreviewRender() {}
|
PhotoboothPreviewRender::~PhotoboothPreviewRender() {}
|
||||||
|
|
||||||
QImage
|
|
||||||
PhotoboothPreviewRender::takePhoto()
|
|
||||||
{
|
|
||||||
if (auto previewImage = LRCInstance::renderer()->getPreviewFrame()) {
|
|
||||||
return previewImage->copy();
|
|
||||||
}
|
|
||||||
return QImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString
|
|
||||||
PhotoboothPreviewRender::takeCroppedPhotoToBase64(int size)
|
|
||||||
{
|
|
||||||
auto image = Utils::cropImage(takePhoto());
|
|
||||||
auto avatar = image.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
|
||||||
|
|
||||||
return QString::fromLatin1(Utils::QImageToByteArray(avatar).toBase64().data());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
PhotoboothPreviewRender::paint(QPainter* painter)
|
PhotoboothPreviewRender::paint(QPainter* painter)
|
||||||
{
|
{
|
||||||
|
|
|
@ -63,9 +63,6 @@ public:
|
||||||
explicit PhotoboothPreviewRender(QQuickItem* parent = 0);
|
explicit PhotoboothPreviewRender(QQuickItem* parent = 0);
|
||||||
virtual ~PhotoboothPreviewRender();
|
virtual ~PhotoboothPreviewRender();
|
||||||
|
|
||||||
QImage takePhoto();
|
|
||||||
Q_INVOKABLE QString takeCroppedPhotoToBase64(int size);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void hideBooth();
|
void hideBooth();
|
||||||
|
|
||||||
|
|
|
@ -263,15 +263,6 @@ SettingsAdapter::getAccountBestName()
|
||||||
return Utils::bestNameForAccount(LRCInstance::getCurrentAccountInfo());
|
return Utils::bestNameForAccount(LRCInstance::getCurrentAccountInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
|
||||||
SettingsAdapter::getAvatarImage_Base64(int avatarSize)
|
|
||||||
{
|
|
||||||
auto& accountInfo = LRCInstance::getCurrentAccountInfo();
|
|
||||||
auto avatar = Utils::accountPhoto(accountInfo, {avatarSize, avatarSize});
|
|
||||||
|
|
||||||
return QString::fromLatin1(Utils::QImageToByteArray(avatar).toBase64().data());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SettingsAdapter::getIsDefaultAvatar()
|
SettingsAdapter::getIsDefaultAvatar()
|
||||||
{
|
{
|
||||||
|
@ -280,18 +271,10 @@ SettingsAdapter::getIsDefaultAvatar()
|
||||||
return accountInfo.profileInfo.avatar.isEmpty();
|
return accountInfo.profileInfo.avatar.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
SettingsAdapter::setCurrAccAvatar(QString avatarImgBase64)
|
SettingsAdapter::setCurrAccAvatar(QVariant avatarImg)
|
||||||
{
|
{
|
||||||
QImage avatarImg;
|
LRCInstance::setCurrAccAvatar(QPixmap::fromImage(avatarImg.value<QImage>()));
|
||||||
const bool ret = avatarImg.loadFromData(QByteArray::fromBase64(avatarImgBase64.toLatin1()));
|
|
||||||
if (!ret) {
|
|
||||||
qDebug() << "Current avatar loading from base64 fail";
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
LRCInstance::setCurrAccAvatar(QPixmap::fromImage(avatarImg));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -94,9 +94,8 @@ public:
|
||||||
Q_INVOKABLE QString getAccountBestName();
|
Q_INVOKABLE QString getAccountBestName();
|
||||||
|
|
||||||
// getters and setters of avatar image
|
// getters and setters of avatar image
|
||||||
Q_INVOKABLE QString getAvatarImage_Base64(int avatarSize);
|
|
||||||
Q_INVOKABLE bool getIsDefaultAvatar();
|
Q_INVOKABLE bool getIsDefaultAvatar();
|
||||||
Q_INVOKABLE bool setCurrAccAvatar(QString avatarImgBase64);
|
Q_INVOKABLE void setCurrAccAvatar(QVariant avatarImg);
|
||||||
Q_INVOKABLE void clearCurrentAvatar();
|
Q_INVOKABLE void clearCurrentAvatar();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -52,7 +52,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAvatar() {
|
function setAvatar() {
|
||||||
currentAccountAvatar.setAvatarPixmap(SettingsAdapter.getAvatarImage_Base64(currentAccountAvatar.boothWidth), SettingsAdapter.getIsDefaultAvatar())
|
currentAccountAvatar.setAvatarImage()
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopBooth() {
|
function stopBooth() {
|
||||||
|
@ -79,14 +79,8 @@ ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
|
saveToConfig: true
|
||||||
boothWidth: 180
|
boothWidth: 180
|
||||||
|
|
||||||
onImageAcquired: SettingsAdapter.setCurrAccAvatar(imgBase64)
|
|
||||||
|
|
||||||
onImageCleared: {
|
|
||||||
SettingsAdapter.clearCurrentAvatar()
|
|
||||||
setAvatar()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialLineEdit {
|
MaterialLineEdit {
|
||||||
|
|
|
@ -137,7 +137,6 @@ ColumnLayout {
|
||||||
|
|
||||||
contactName : ContactName
|
contactName : ContactName
|
||||||
contactID: ContactID
|
contactID: ContactID
|
||||||
contactPicture_base64: ContactPicture
|
|
||||||
|
|
||||||
onClicked: bannedListWidget.currentIndex = index
|
onClicked: bannedListWidget.currentIndex = index
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,13 @@ ItemDelegate {
|
||||||
|
|
||||||
property string contactName : ""
|
property string contactName : ""
|
||||||
property string contactID: ""
|
property string contactID: ""
|
||||||
property string contactPicture_base64:""
|
|
||||||
|
|
||||||
signal btnReAddContactClicked
|
signal btnReAddContactClicked
|
||||||
|
|
||||||
highlighted: ListView.isCurrentItem
|
highlighted: ListView.isCurrentItem
|
||||||
|
|
||||||
|
onContactIDChanged: avatarImg.updateImage(contactID)
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
@ -52,11 +53,14 @@ ItemDelegate {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
Image {
|
AvatarImage {
|
||||||
id: avatarImg
|
id: avatarImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: contactPicture_base64 === "" ? "" : "data:image/png;base64," + contactPicture_base64
|
|
||||||
|
mode: AvatarImage.Mode.FromContactUri
|
||||||
|
showPresenceIndicator: false
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
|
|
|
@ -21,12 +21,8 @@
|
||||||
#include "smartlistmodel.h"
|
#include "smartlistmodel.h"
|
||||||
|
|
||||||
#include "lrcinstance.h"
|
#include "lrcinstance.h"
|
||||||
#include "pixbufmanipulator.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include "api/contactmodel.h"
|
|
||||||
#include "globalinstances.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
SmartListModel::SmartListModel(QObject* parent,
|
SmartListModel::SmartListModel(QObject* parent,
|
||||||
|
@ -148,7 +144,6 @@ SmartListModel::roleNames() const
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles[DisplayName] = "DisplayName";
|
roles[DisplayName] = "DisplayName";
|
||||||
roles[DisplayID] = "DisplayID";
|
roles[DisplayID] = "DisplayID";
|
||||||
roles[Picture] = "Picture";
|
|
||||||
roles[Presence] = "Presence";
|
roles[Presence] = "Presence";
|
||||||
roles[URI] = "URI";
|
roles[URI] = "URI";
|
||||||
roles[UnreadMessagesCount] = "UnreadMessagesCount";
|
roles[UnreadMessagesCount] = "UnreadMessagesCount";
|
||||||
|
@ -163,6 +158,7 @@ SmartListModel::roleNames() const
|
||||||
roles[SectionName] = "SectionName";
|
roles[SectionName] = "SectionName";
|
||||||
roles[AccountId] = "AccountId";
|
roles[AccountId] = "AccountId";
|
||||||
roles[Draft] = "Draft";
|
roles[Draft] = "Draft";
|
||||||
|
roles[PictureUid] = "PictureUid";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +179,8 @@ void
|
||||||
SmartListModel::fillConversationsList()
|
SmartListModel::fillConversationsList()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
fillContactAvatarUidMap(LRCInstance::getCurrentAccountInfo().contactModel->getAllContacts());
|
||||||
|
|
||||||
auto* convModel = LRCInstance::getCurrentConversationModel();
|
auto* convModel = LRCInstance::getCurrentConversationModel();
|
||||||
conversations_.clear();
|
conversations_.clear();
|
||||||
|
|
||||||
|
@ -208,6 +206,39 @@ SmartListModel::updateConversation(const QString& convUid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SmartListModel::updateContactAvatarUid(const QString& contactUri)
|
||||||
|
{
|
||||||
|
contactAvatarUidMap_[contactUri] = Utils::generateUid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SmartListModel::fillContactAvatarUidMap(const ContactModel::ContactInfoMap& contacts)
|
||||||
|
{
|
||||||
|
if (contacts.size() == 0) {
|
||||||
|
contactAvatarUidMap_.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contactAvatarUidMap_.isEmpty() || contacts.size() != contactAvatarUidMap_.size()) {
|
||||||
|
bool useContacts = contacts.size() > contactAvatarUidMap_.size();
|
||||||
|
auto contactsKeyList = contacts.keys();
|
||||||
|
auto contactAvatarUidMapKeyList = contactAvatarUidMap_.keys();
|
||||||
|
|
||||||
|
for (int i = 0;
|
||||||
|
i < (useContacts ? contactsKeyList.size() : contactAvatarUidMapKeyList.size());
|
||||||
|
++i) {
|
||||||
|
// Insert or update
|
||||||
|
if (i < contactsKeyList.size() && !contactAvatarUidMap_.contains(contactsKeyList.at(i)))
|
||||||
|
contactAvatarUidMap_.insert(contactsKeyList.at(i), Utils::generateUid());
|
||||||
|
// Remove
|
||||||
|
if (i < contactAvatarUidMapKeyList.size()
|
||||||
|
&& !contacts.contains(contactAvatarUidMapKeyList.at(i)))
|
||||||
|
contactAvatarUidMap_.remove(contactAvatarUidMapKeyList.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SmartListModel::toggleSection(const QString& section)
|
SmartListModel::toggleSection(const QString& section)
|
||||||
{
|
{
|
||||||
|
@ -241,12 +272,10 @@ SmartListModel::getConversationItemData(const conversation::Info& item,
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
auto& contactModel = accountInfo.contactModel;
|
auto& contactModel = accountInfo.contactModel;
|
||||||
|
|
||||||
|
// Since we are using image provider right now, image url representation should be unique to
|
||||||
|
// be able to use the image cache, account avatar will only be updated once PictureUid changed
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Role::Picture: {
|
|
||||||
auto contactImage
|
|
||||||
= GlobalInstances::pixmapManipulator().decorationRole(item, accountInfo).value<QImage>();
|
|
||||||
return QString::fromLatin1(Utils::QImageToByteArray(contactImage).toBase64().data());
|
|
||||||
}
|
|
||||||
case Role::DisplayName: {
|
case Role::DisplayName: {
|
||||||
if (!item.participants.isEmpty()) {
|
if (!item.participants.isEmpty()) {
|
||||||
auto& contact = contactModel->getContact(item.participants[0]);
|
auto& contact = contactModel->getContact(item.participants[0]);
|
||||||
|
@ -268,10 +297,15 @@ SmartListModel::getConversationItemData(const conversation::Info& item,
|
||||||
}
|
}
|
||||||
return QVariant(false);
|
return QVariant(false);
|
||||||
}
|
}
|
||||||
|
case Role::PictureUid: {
|
||||||
|
if (!item.participants.isEmpty()) {
|
||||||
|
return QVariant(contactAvatarUidMap_[item.participants[0]]);
|
||||||
|
}
|
||||||
|
return QVariant("");
|
||||||
|
}
|
||||||
case Role::URI: {
|
case Role::URI: {
|
||||||
if (!item.participants.isEmpty()) {
|
if (!item.participants.isEmpty()) {
|
||||||
auto& contact = contactModel->getContact(item.participants[0]);
|
return QVariant(item.participants[0]);
|
||||||
return QVariant(contact.profileInfo.uri);
|
|
||||||
}
|
}
|
||||||
return QVariant("");
|
return QVariant("");
|
||||||
}
|
}
|
||||||
|
@ -331,13 +365,13 @@ SmartListModel::getConversationItemData(const conversation::Info& item,
|
||||||
if (!convInfo.uid.isEmpty()) {
|
if (!convInfo.uid.isEmpty()) {
|
||||||
auto* callModel = LRCInstance::getCurrentCallModel();
|
auto* callModel = LRCInstance::getCurrentCallModel();
|
||||||
const auto call = callModel->getCall(convInfo.callId);
|
const auto call = callModel->getCall(convInfo.callId);
|
||||||
return QVariant(callModel->hasCall(convInfo.callId)
|
return QVariant(
|
||||||
|
callModel->hasCall(convInfo.callId)
|
||||||
&& ((!call.isOutgoing
|
&& ((!call.isOutgoing
|
||||||
&& (call.status == lrc::api::call::Status::IN_PROGRESS
|
&& (call.status == lrc::api::call::Status::IN_PROGRESS
|
||||||
|| call.status == lrc::api::call::Status::PAUSED
|
|| call.status == lrc::api::call::Status::PAUSED
|
||||||
|| call.status == lrc::api::call::Status::INCOMING_RINGING))
|
|| call.status == lrc::api::call::Status::INCOMING_RINGING))
|
||||||
|| (call.isOutgoing
|
|| (call.isOutgoing && call.status != lrc::api::call::Status::ENDED)));
|
||||||
&& call.status != lrc::api::call::Status::ENDED)));
|
|
||||||
}
|
}
|
||||||
return QVariant(false);
|
return QVariant(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "api/contact.h"
|
#include "api/contact.h"
|
||||||
#include "api/conversation.h"
|
#include "api/conversation.h"
|
||||||
#include "api/conversationmodel.h"
|
#include "api/conversationmodel.h"
|
||||||
|
#include "api/contactmodel.h"
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
@ -42,7 +43,6 @@ public:
|
||||||
enum Role {
|
enum Role {
|
||||||
DisplayName = Qt::UserRole + 1,
|
DisplayName = Qt::UserRole + 1,
|
||||||
DisplayID,
|
DisplayID,
|
||||||
Picture,
|
|
||||||
Presence,
|
Presence,
|
||||||
URI,
|
URI,
|
||||||
UnreadMessagesCount,
|
UnreadMessagesCount,
|
||||||
|
@ -58,6 +58,7 @@ public:
|
||||||
CallState,
|
CallState,
|
||||||
SectionName,
|
SectionName,
|
||||||
AccountId,
|
AccountId,
|
||||||
|
PictureUid,
|
||||||
Draft
|
Draft
|
||||||
};
|
};
|
||||||
Q_ENUM(Role)
|
Q_ENUM(Role)
|
||||||
|
@ -85,15 +86,28 @@ public:
|
||||||
Q_INVOKABLE void fillConversationsList();
|
Q_INVOKABLE void fillConversationsList();
|
||||||
Q_INVOKABLE void updateConversation(const QString& conv);
|
Q_INVOKABLE void updateConversation(const QString& conv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is to update contact avatar uuid for current account when there's an contact
|
||||||
|
* avatar changed.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void updateContactAvatarUid(const QString& contactUri);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariant getConversationItemData(const ConversationInfo& item,
|
QVariant getConversationItemData(const ConversationInfo& item,
|
||||||
const AccountInfo& accountInfo,
|
const AccountInfo& accountInfo,
|
||||||
int role) const;
|
int role) const;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Give a uuid for each contact avatar for current account and it will serve PictureUid role
|
||||||
|
*/
|
||||||
|
void fillContactAvatarUidMap(const ContactModel::ContactInfoMap& contacts);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List sectioning.
|
* List sectioning.
|
||||||
*/
|
*/
|
||||||
Type listModelType_;
|
Type listModelType_;
|
||||||
QMap<QString, bool> sectionState_;
|
QMap<QString, bool> sectionState_;
|
||||||
QMap<ConferenceableItem, ConferenceableValue> conferenceables_;
|
QMap<ConferenceableItem, ConferenceableValue> conferenceables_;
|
||||||
|
QMap<QString, QString> contactAvatarUidMap_;
|
||||||
ConversationModel::ConversationQueue conversations_;
|
ConversationModel::ConversationQueue conversations_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,9 +25,7 @@
|
||||||
#include "globalsystemtray.h"
|
#include "globalsystemtray.h"
|
||||||
#include "jamiavatartheme.h"
|
#include "jamiavatartheme.h"
|
||||||
#include "lrcinstance.h"
|
#include "lrcinstance.h"
|
||||||
#include "pixbufmanipulator.h"
|
|
||||||
|
|
||||||
#include <globalinstances.h>
|
|
||||||
#include <qrencode.h>
|
#include <qrencode.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
@ -43,6 +41,7 @@
|
||||||
#include <QSvgRenderer>
|
#include <QSvgRenderer>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
#include <QtConcurrent/QtConcurrent>
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <lmcons.h>
|
#include <lmcons.h>
|
||||||
|
@ -245,14 +244,52 @@ Utils::GetISODate()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QImage
|
||||||
Utils::getContactImageString(const QString& accountId, const QString& uid)
|
Utils::contactPhoto(const QString& contactUri, const QSize& size)
|
||||||
{
|
{
|
||||||
return QString::fromLatin1(
|
QImage photo;
|
||||||
Utils::QImageToByteArray(
|
|
||||||
Utils::conversationPhoto(uid, LRCInstance::getAccountInfo(accountId)))
|
try {
|
||||||
.toBase64()
|
/*
|
||||||
.data());
|
* Get first contact photo.
|
||||||
|
*/
|
||||||
|
auto& accountInfo = LRCInstance::accountModel().getAccountInfo(LRCInstance::getCurrAccId());
|
||||||
|
auto contactInfo = accountInfo.contactModel->getContact(contactUri);
|
||||||
|
auto contactPhoto = contactInfo.profileInfo.avatar;
|
||||||
|
auto bestName = Utils::bestNameForContact(contactInfo);
|
||||||
|
auto bestId = Utils::bestIdForContact(contactInfo);
|
||||||
|
if (accountInfo.profileInfo.type == lrc::api::profile::Type::SIP
|
||||||
|
&& contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY) {
|
||||||
|
photo = Utils::fallbackAvatar(QString(), QString());
|
||||||
|
} else if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY
|
||||||
|
&& contactInfo.profileInfo.uri.isEmpty()) {
|
||||||
|
photo = Utils::fallbackAvatar(QString(), QString());
|
||||||
|
} else if (!contactPhoto.isEmpty()) {
|
||||||
|
QByteArray byteArray = contactPhoto.toLocal8Bit();
|
||||||
|
photo = contactPhotoFromBase64(byteArray, nullptr);
|
||||||
|
if (photo.isNull()) {
|
||||||
|
auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName;
|
||||||
|
photo = Utils::fallbackAvatar("ring:" + contactInfo.profileInfo.uri, avatarName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName;
|
||||||
|
photo = Utils::fallbackAvatar("ring:" + contactInfo.profileInfo.uri, avatarName);
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
return Utils::scaleAndFrame(photo, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage
|
||||||
|
Utils::contactPhotoFromBase64(const QByteArray& data, const QString& type)
|
||||||
|
{
|
||||||
|
QImage avatar;
|
||||||
|
const bool ret = avatar.loadFromData(QByteArray::fromBase64(data), type.toLatin1());
|
||||||
|
if (!ret) {
|
||||||
|
qDebug() << "Utils: vCard image loading failed";
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
return Utils::getCirclePhoto(avatar, avatar.size().width());
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage
|
QImage
|
||||||
|
@ -549,21 +586,6 @@ Utils::getReplyMessageBox(QWidget* widget, const QString& title, const QString&
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage
|
|
||||||
Utils::conversationPhoto(const QString& convUid,
|
|
||||||
const lrc::api::account::Info& accountInfo,
|
|
||||||
bool filtered)
|
|
||||||
{
|
|
||||||
auto* convModel = LRCInstance::getCurrentConversationModel();
|
|
||||||
const auto convInfo = convModel->getConversationForUID(convUid);
|
|
||||||
if (!convInfo.uid.isEmpty()) {
|
|
||||||
return GlobalInstances::pixmapManipulator()
|
|
||||||
.decorationRole(convInfo, accountInfo)
|
|
||||||
.value<QImage>();
|
|
||||||
}
|
|
||||||
return QImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor
|
QColor
|
||||||
Utils::getAvatarColor(const QString& canonicalUri)
|
Utils::getAvatarColor(const QString& canonicalUri)
|
||||||
{
|
{
|
||||||
|
@ -587,10 +609,12 @@ Utils::getAvatarColor(const QString& canonicalUri)
|
||||||
QImage
|
QImage
|
||||||
Utils::fallbackAvatar(const QString& canonicalUriStr, const QString& letterStr, const QSize& size)
|
Utils::fallbackAvatar(const QString& canonicalUriStr, const QString& letterStr, const QSize& size)
|
||||||
{
|
{
|
||||||
|
auto sizeToUse = size.height() >= defaultAvatarSize.height() ? size : defaultAvatarSize;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We start with a transparent avatar.
|
* We start with a transparent avatar.
|
||||||
*/
|
*/
|
||||||
QImage avatar(size, QImage::Format_ARGB32);
|
QImage avatar(sizeToUse, QImage::Format_ARGB32);
|
||||||
avatar.fill(Qt::transparent);
|
avatar.fill(Qt::transparent);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -651,7 +675,7 @@ Utils::fallbackAvatar(const QString& canonicalUriStr, const QString& letterStr,
|
||||||
painter.drawPixmap(overlayRect, QPixmap(":/images/default_avatar_overlay.svg"));
|
painter.drawPixmap(overlayRect, QPixmap(":/images/default_avatar_overlay.svg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return avatar;
|
return avatar.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage
|
QImage
|
||||||
|
@ -802,7 +826,7 @@ Utils::accountPhoto(const lrc::api::account::Info& accountInfo, const QSize& siz
|
||||||
QImage photo;
|
QImage photo;
|
||||||
if (!accountInfo.profileInfo.avatar.isEmpty()) {
|
if (!accountInfo.profileInfo.avatar.isEmpty()) {
|
||||||
QByteArray ba = accountInfo.profileInfo.avatar.toLocal8Bit();
|
QByteArray ba = accountInfo.profileInfo.avatar.toLocal8Bit();
|
||||||
photo = GlobalInstances::pixmapManipulator().personPhoto(ba, nullptr).value<QImage>();
|
photo = contactPhotoFromBase64(ba, nullptr);
|
||||||
} else {
|
} else {
|
||||||
auto bestId = bestIdForAccount(accountInfo);
|
auto bestId = bestIdForAccount(accountInfo);
|
||||||
auto bestName = bestNameForAccount(accountInfo);
|
auto bestName = bestNameForAccount(accountInfo);
|
||||||
|
@ -843,3 +867,9 @@ Utils::isImage(const QString& fileExt)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
Utils::generateUid()
|
||||||
|
{
|
||||||
|
return QUuid::createUuid().toString();
|
||||||
|
}
|
||||||
|
|
|
@ -100,11 +100,9 @@ bool getReplyMessageBox(QWidget* widget, const QString& title, const QString& te
|
||||||
* Image manipulation
|
* Image manipulation
|
||||||
*/
|
*/
|
||||||
static const QSize defaultAvatarSize {128, 128};
|
static const QSize defaultAvatarSize {128, 128};
|
||||||
QString getContactImageString(const QString& accountId, const QString& uid);
|
QImage contactPhotoFromBase64(const QByteArray& data, const QString& type);
|
||||||
|
QImage contactPhoto(const QString& contactUri, const QSize& size = defaultAvatarSize);
|
||||||
QImage getCirclePhoto(const QImage original, int sizePhoto);
|
QImage getCirclePhoto(const QImage original, int sizePhoto);
|
||||||
QImage conversationPhoto(const QString& convUid,
|
|
||||||
const lrc::api::account::Info& accountInfo,
|
|
||||||
bool filtered = false);
|
|
||||||
QColor getAvatarColor(const QString& canonicalUri);
|
QColor getAvatarColor(const QString& canonicalUri);
|
||||||
QImage fallbackAvatar(const QString& canonicalUriStr,
|
QImage fallbackAvatar(const QString& canonicalUriStr,
|
||||||
const QString& letterStr = QString(),
|
const QString& letterStr = QString(),
|
||||||
|
@ -123,6 +121,7 @@ QImage cropImage(const QImage& img);
|
||||||
QPixmap pixmapFromSvg(const QString& svg_resource, const QSize& size);
|
QPixmap pixmapFromSvg(const QString& svg_resource, const QSize& size);
|
||||||
QImage setupQRCode(QString ringID, int margin);
|
QImage setupQRCode(QString ringID, int margin);
|
||||||
bool isImage(const QString& fileExt);
|
bool isImage(const QString& fileExt);
|
||||||
|
QString generateUid();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Misc
|
* Misc
|
||||||
|
|
|
@ -105,12 +105,6 @@ UtilsAdapter::checkStartupLink()
|
||||||
return Utils::CheckStartupLink(L"Jami");
|
return Utils::CheckStartupLink(L"Jami");
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString
|
|
||||||
UtilsAdapter::getContactImageString(const QString& accountId, const QString& uid)
|
|
||||||
{
|
|
||||||
return Utils::getContactImageString(accountId, uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString
|
const QString
|
||||||
UtilsAdapter::getBestName(const QString& accountId, const QString& uid)
|
UtilsAdapter::getBestName(const QString& accountId, const QString& uid)
|
||||||
{
|
{
|
||||||
|
@ -356,17 +350,6 @@ UtilsAdapter::getAbsPath(QString path)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
|
||||||
UtilsAdapter::getCroppedImageBase64FromFile(QString fileName, int size)
|
|
||||||
{
|
|
||||||
auto image = Utils::cropImage(QImage(fileName));
|
|
||||||
auto croppedImage = image.scaled(size,
|
|
||||||
size,
|
|
||||||
Qt::KeepAspectRatioByExpanding,
|
|
||||||
Qt::SmoothTransformation);
|
|
||||||
return QString::fromLatin1(Utils::QImageToByteArray(croppedImage).toBase64().data());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
UtilsAdapter::checkShowPluginsButton()
|
UtilsAdapter::checkShowPluginsButton()
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,7 +44,6 @@ public:
|
||||||
Q_INVOKABLE bool createStartupLink();
|
Q_INVOKABLE bool createStartupLink();
|
||||||
Q_INVOKABLE QString GetRingtonePath();
|
Q_INVOKABLE QString GetRingtonePath();
|
||||||
Q_INVOKABLE bool checkStartupLink();
|
Q_INVOKABLE bool checkStartupLink();
|
||||||
Q_INVOKABLE const QString getContactImageString(const QString& accountId, const QString& uid);
|
|
||||||
Q_INVOKABLE void removeConversation(const QString& accountId,
|
Q_INVOKABLE void removeConversation(const QString& accountId,
|
||||||
const QString& uid,
|
const QString& uid,
|
||||||
bool banContact = false);
|
bool banContact = false);
|
||||||
|
@ -77,7 +76,6 @@ public:
|
||||||
Q_INVOKABLE QString toFileInfoName(QString inputFileName);
|
Q_INVOKABLE QString toFileInfoName(QString inputFileName);
|
||||||
Q_INVOKABLE QString toFileAbsolutepath(QString inputFileName);
|
Q_INVOKABLE QString toFileAbsolutepath(QString inputFileName);
|
||||||
Q_INVOKABLE QString getAbsPath(QString path);
|
Q_INVOKABLE QString getAbsPath(QString path);
|
||||||
Q_INVOKABLE QString getCroppedImageBase64FromFile(QString fileName, int size);
|
|
||||||
Q_INVOKABLE bool checkShowPluginsButton();
|
Q_INVOKABLE bool checkShowPluginsButton();
|
||||||
Q_INVOKABLE QString fileName(const QString& path);
|
Q_INVOKABLE QString fileName(const QString& path);
|
||||||
Q_INVOKABLE QString getExt(const QString& path);
|
Q_INVOKABLE QString getExt(const QString& path);
|
||||||
|
|
|
@ -385,14 +385,13 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSaveProfile: {
|
onSaveProfile: {
|
||||||
SettingsAdapter.setCurrAccAvatar(profilePage.boothImgBase64)
|
if (profilePage.profileImg)
|
||||||
|
SettingsAdapter.setCurrAccAvatar(profilePage.profileImg)
|
||||||
AccountAdapter.setCurrAccDisplayName(profilePage.displayName)
|
AccountAdapter.setCurrAccDisplayName(profilePage.displayName)
|
||||||
leave()
|
leave()
|
||||||
}
|
}
|
||||||
|
|
||||||
onLeavePage: {
|
onLeavePage: leave()
|
||||||
leave()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,6 @@ Rectangle {
|
||||||
property alias text_sipPasswordEditAlias: sipPasswordEdit.text
|
property alias text_sipPasswordEditAlias: sipPasswordEdit.text
|
||||||
property int preferredHeight: createSIPAccountPageColumnLayout.implicitHeight
|
property int preferredHeight: createSIPAccountPageColumnLayout.implicitHeight
|
||||||
|
|
||||||
property var boothImgBase64: null
|
|
||||||
|
|
||||||
signal createAccount
|
signal createAccount
|
||||||
signal leavePage
|
signal leavePage
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,13 @@ import "../../commoncomponents"
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property alias profileImg: setAvatarWidget.boothImg
|
||||||
property int preferredHeight: profilePageColumnLayout.implicitHeight
|
property int preferredHeight: profilePageColumnLayout.implicitHeight
|
||||||
|
|
||||||
function initializeOnShowUp() {
|
function initializeOnShowUp() {
|
||||||
|
setAvatarWidget.hasAvatar = false
|
||||||
|
setAvatarWidget.setAvatarImage(AvatarImage.Mode.Default, "")
|
||||||
clearAllTextFields()
|
clearAllTextFields()
|
||||||
boothImgBase64 = ""
|
|
||||||
saveProfileBtn.spinnerTriggered = true
|
saveProfileBtn.spinnerTriggered = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +50,6 @@ Rectangle {
|
||||||
signal saveProfile
|
signal saveProfile
|
||||||
|
|
||||||
property var showBottom: false
|
property var showBottom: false
|
||||||
property alias boothImgBase64: setAvatarWidget.imgBase64
|
|
||||||
property alias displayName: aliasEdit.text
|
property alias displayName: aliasEdit.text
|
||||||
property bool isRdv: false
|
property bool isRdv: false
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue