mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-07-06 00:25:26 +02:00
swarm creation: add ability to change avatar
PhotoBoothView has a new variable to be used during Swarm's creation This update an image in the cache and is used in the profile of the conversation. Also, add top bar for NewSwarmPage Change-Id: I156c9cffb85e15b7c041bcf16b1501851470e8a5 GitLab: #670
This commit is contained in:
parent
4a581d0a1a
commit
a5cfffef6d
13 changed files with 278 additions and 30 deletions
|
@ -60,9 +60,12 @@ public:
|
|||
}
|
||||
|
||||
auto type = idInfo.at(0);
|
||||
if (type == "conversation")
|
||||
if (type == "conversation") {
|
||||
if (imageId == "temp")
|
||||
return Utils::tempConversationAvatar(requestedSize);
|
||||
|
||||
return Utils::conversationAvatar(lrcInstance_, imageId, requestedSize);
|
||||
else if (type == "account")
|
||||
} else if (type == "account")
|
||||
return Utils::accountPhoto(lrcInstance_, imageId, requestedSize);
|
||||
else if (type == "contact")
|
||||
return Utils::contactPhoto(lrcInstance_, imageId, requestedSize);
|
||||
|
|
|
@ -35,6 +35,10 @@ AvatarRegistry::AvatarRegistry(LRCInstance* instance, QObject* parent)
|
|||
&AvatarRegistry::addOrUpdateImage,
|
||||
Qt::UniqueConnection);
|
||||
|
||||
connect(lrcInstance_, &LRCInstance::base64SwarmAvatarChanged, this, [&] {
|
||||
addOrUpdateImage("temp");
|
||||
});
|
||||
|
||||
if (!lrcInstance_->get_currentAccountId().isEmpty())
|
||||
connectAccount();
|
||||
}
|
||||
|
@ -62,6 +66,12 @@ AvatarRegistry::connectAccount()
|
|||
this,
|
||||
&AvatarRegistry::onProfileUpdated,
|
||||
Qt::UniqueConnection);
|
||||
|
||||
connect(lrcInstance_->getCurrentConversationModel(),
|
||||
&ConversationModel::conversationUpdated,
|
||||
this,
|
||||
&AvatarRegistry::addOrUpdateImage,
|
||||
Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -30,6 +30,7 @@ Item {
|
|||
|
||||
property bool isPreviewing: false
|
||||
property alias imageId: avatar.imageId
|
||||
property bool newConversation: false
|
||||
property real avatarSize
|
||||
|
||||
signal focusOnPreviousItem
|
||||
|
@ -94,7 +95,10 @@ Item {
|
|||
}
|
||||
|
||||
var filePath = UtilsAdapter.getAbsPath(file)
|
||||
AccountAdapter.setCurrentAccountAvatarFile(filePath)
|
||||
if (!root.newConversation)
|
||||
AccountAdapter.setCurrentAccountAvatarFile(filePath)
|
||||
else
|
||||
UtilsAdapter.setSwarmCreationImageFromFile(filePath, root.imageId)
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
|
@ -125,6 +129,8 @@ Item {
|
|||
|
||||
visible: !preview.visible
|
||||
|
||||
mode: newConversation? Avatar.Mode.Conversation : Avatar.Mode.Account
|
||||
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
showPresenceIndicator: false
|
||||
}
|
||||
|
@ -220,8 +226,11 @@ Item {
|
|||
onClicked: {
|
||||
if (isPreviewing) {
|
||||
flashAnimation.start()
|
||||
AccountAdapter.setCurrentAccountAvatarBase64(
|
||||
preview.takePhoto(avatarSize))
|
||||
var photo = preview.takePhoto(avatarSize)
|
||||
if (!root.newConversation)
|
||||
AccountAdapter.setCurrentAccountAvatarBase64(photo)
|
||||
else
|
||||
UtilsAdapter.setSwarmCreationImageFromString(photo, imageId)
|
||||
stopBooth()
|
||||
return
|
||||
}
|
||||
|
@ -237,7 +246,15 @@ Item {
|
|||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
visible: isPreviewing || LRCInstance.currentAccountAvatarSet
|
||||
visible: {
|
||||
if (isPreviewing)
|
||||
return true
|
||||
if (!newConversation && LRCInstance.currentAccountAvatarSet)
|
||||
return true
|
||||
if (newConversation && UtilsAdapter.swarmCreationImage(imageId).length !== 0)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
radius: JamiTheme.primaryRadius
|
||||
source: JamiResources.round_close_24dp_svg
|
||||
|
@ -265,8 +282,12 @@ Item {
|
|||
|
||||
onClicked: {
|
||||
stopBooth()
|
||||
if (!isPreviewing)
|
||||
AccountAdapter.setCurrentAccountAvatarBase64()
|
||||
if (!isPreviewing) {
|
||||
if (!root.newConversation)
|
||||
AccountAdapter.setCurrentAccountAvatarBase64()
|
||||
else
|
||||
UtilsAdapter.setSwarmCreationImageFromString("", imageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -629,4 +629,5 @@ Item {
|
|||
property string kickMember: qsTr("Kick member")
|
||||
property string administrator: qsTr("Administrator")
|
||||
property string invited: qsTr("Invited")
|
||||
property string removeMember: qsTr("Remove member")
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ Q_SIGNALS:
|
|||
void quitEngineRequested();
|
||||
void conversationUpdated(const QString& convId, const QString& accountId);
|
||||
void draftSaved(const QString& convId);
|
||||
void base64SwarmAvatarChanged();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Lrc> lrc_;
|
||||
|
|
|
@ -376,6 +376,10 @@ Rectangle {
|
|||
pushNewSwarmPage()
|
||||
}
|
||||
}
|
||||
|
||||
onHighlightedMembersChanged: {
|
||||
newSwarmPage.members = mainViewSidePanel.highlightedMembers
|
||||
}
|
||||
}
|
||||
|
||||
CallStackView {
|
||||
|
@ -426,6 +430,10 @@ Rectangle {
|
|||
mainViewSidePanel.showSwarmListView(newSwarmPage.visible)
|
||||
}
|
||||
|
||||
onRemoveMember: function(convId, member) {
|
||||
mainViewSidePanel.removeMember(convId, member)
|
||||
}
|
||||
|
||||
onCreateSwarmClicked: function(title, description, avatar) {
|
||||
ConversationsAdapter.createSwarm(title, description, avatar, mainViewSidePanel.highlightedMembers)
|
||||
backToMainView()
|
||||
|
|
|
@ -33,12 +33,95 @@ Rectangle {
|
|||
color: JamiTheme.chatviewBgColor
|
||||
|
||||
signal createSwarmClicked(string title, string description, string avatar)
|
||||
signal removeMember(string convId, string member)
|
||||
|
||||
onVisibleChanged: {
|
||||
UtilsAdapter.setSwarmCreationImageFromString()
|
||||
}
|
||||
|
||||
property var members: []
|
||||
|
||||
RowLayout {
|
||||
id: labelsMember
|
||||
anchors.top: root.top
|
||||
anchors.topMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
Layout.preferredWidth: root.width
|
||||
spacing: 16
|
||||
|
||||
Label {
|
||||
text: qsTr("To:")
|
||||
font.bold: true
|
||||
color: JamiTheme.textColor
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.preferredWidth: root.width
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 48
|
||||
Layout.topMargin: 16
|
||||
clip: true
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
delegate: Rectangle {
|
||||
id: delegate
|
||||
radius: (delegate.height + 12) / 2
|
||||
width: childrenRect.width + 12
|
||||
height: childrenRect.height + 12
|
||||
|
||||
RowLayout {
|
||||
anchors.centerIn: parent
|
||||
|
||||
Label {
|
||||
text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, modelData.uri)
|
||||
color: JamiTheme.textColor
|
||||
}
|
||||
|
||||
PushButton {
|
||||
id: removeUserBtn
|
||||
|
||||
Layout.leftMargin: 8
|
||||
|
||||
preferredSize: 24
|
||||
|
||||
source: JamiResources.round_close_24dp_svg
|
||||
toolTipText: JamiStrings.removeMember
|
||||
|
||||
normalColor: "transparent"
|
||||
imageColor: "transparent"
|
||||
|
||||
onClicked: root.removeMember(modelData.convId, modelData.uri)
|
||||
}
|
||||
}
|
||||
|
||||
color: "grey"
|
||||
}
|
||||
model: root.members
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
|
||||
anchors.centerIn: root
|
||||
|
||||
PhotoboothView {
|
||||
id: currentAccountAvatar
|
||||
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
newConversation: true
|
||||
imageId: root.visible ? "temp" : ""
|
||||
avatarSize: 180
|
||||
}
|
||||
|
||||
EditableLineEdit {
|
||||
id: title
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
@ -83,7 +166,7 @@ Rectangle {
|
|||
text: JamiStrings.createTheSwarm
|
||||
|
||||
onClicked: {
|
||||
createSwarmClicked(title.text, description.text, "")
|
||||
createSwarmClicked(title.text, description.text, UtilsAdapter.swarmCreationImage())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,20 +62,66 @@ Rectangle {
|
|||
property var highlighted: []
|
||||
property var highlightedMembers: []
|
||||
|
||||
function refreshHighlighted() {
|
||||
var result = []
|
||||
for (var idx in highlighted) {
|
||||
var convId = highlighted[idx]
|
||||
function refreshHighlighted(convId, highlightedStatus) {
|
||||
var newH = root.highlighted
|
||||
var newHm = root.highlightedMembers
|
||||
|
||||
if (highlightedStatus) {
|
||||
var item = ConversationsAdapter.getConvInfoMap(convId)
|
||||
var added = false
|
||||
for (var idx in item.uris) {
|
||||
var uri = item.uris[idx]
|
||||
if (!result.indexOf(uri) != -1 && uri != CurrentAccount.uri) {
|
||||
result.push(uri)
|
||||
if (!Array.from(newHm).find(r => r.uri === uri) && uri != CurrentAccount.uri) {
|
||||
newHm.push({"uri": uri, "convId": convId})
|
||||
added = true
|
||||
}
|
||||
}
|
||||
if (!added)
|
||||
return false
|
||||
} else {
|
||||
newH = Array.from(newH).filter(r => r !== convId)
|
||||
newHm = Array.from(newHm).filter(r => r.convId !== convId)
|
||||
}
|
||||
highlightedMembers = result
|
||||
|
||||
// We can't have more than 8 participants yet.
|
||||
if (newHm.length > 8) {
|
||||
return false
|
||||
}
|
||||
|
||||
newH.push(convId)
|
||||
root.highlighted = newH
|
||||
root.highlightedMembers = newHm
|
||||
ConversationsAdapter.ignoreFiltering(root.highlighted)
|
||||
return true
|
||||
}
|
||||
|
||||
function clearHighlighted() {
|
||||
root.highlighted = []
|
||||
root.highlightedMembers = []
|
||||
}
|
||||
|
||||
function removeMember(convId, member) {
|
||||
var refreshHighlighted = true
|
||||
var newHm = []
|
||||
for (var hm in root.highlightedMembers) {
|
||||
var m = root.highlightedMembers[hm]
|
||||
if (m.convId == convId && m.uri == member) {
|
||||
continue;
|
||||
} else if (m.convId == convId) {
|
||||
refreshHighlighted = false
|
||||
}
|
||||
newHm.push(m)
|
||||
}
|
||||
root.highlightedMembers = newHm
|
||||
|
||||
if (refreshHighlighted) {
|
||||
// Remove highlighted status if necessary
|
||||
for (var d in swarmCurrentConversationList.contentItem.children) {
|
||||
var delegate = swarmCurrentConversationList.contentItem.children[d]
|
||||
if (delegate.convId == convId)
|
||||
delegate.highlighted = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showSwarmListView(v) {
|
||||
|
@ -280,23 +326,21 @@ Rectangle {
|
|||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
highlighted = false
|
||||
root.refreshHighlighted()
|
||||
root.clearHighlighted()
|
||||
}
|
||||
}
|
||||
|
||||
onHighlightedChanged: function onHighlightedChanged() {
|
||||
var currentHighlighted = root.highlighted
|
||||
if (!root.refreshHighlighted(convId, highlighted)) {
|
||||
highlighted = false
|
||||
return
|
||||
}
|
||||
if (highlighted) {
|
||||
root.highlighted.push(convId)
|
||||
} else {
|
||||
root.highlighted = Array.from(root.highlighted).filter(r => r !== convId)
|
||||
}
|
||||
root.refreshHighlighted()
|
||||
// We can't have more than 8 participants yet.
|
||||
if (root.highlightedMembers.length > 8) {
|
||||
highlighted = false
|
||||
root.refreshHighlighted()
|
||||
}
|
||||
}
|
||||
}
|
||||
currentIndex: model.currentFilteredRow
|
||||
|
|
|
@ -42,18 +42,16 @@ Rectangle {
|
|||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
ConversationAvatar {
|
||||
id: conversationAvatar
|
||||
PhotoboothView {
|
||||
id: currentAccountAvatar
|
||||
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.preferredWidth: JamiTheme.avatarSizeInCall
|
||||
Layout.preferredHeight: JamiTheme.avatarSizeInCall
|
||||
Layout.topMargin: JamiTheme.swarmDetailsPageTopMargin
|
||||
Layout.bottomMargin: JamiTheme.preferredMarginSize
|
||||
|
||||
newConversation: true
|
||||
imageId: LRCInstance.selectedConvUid
|
||||
|
||||
showPresenceIndicator: false
|
||||
avatarSize: JamiTheme.avatarSizeInCall
|
||||
}
|
||||
|
||||
EditableLineEdit {
|
||||
|
|
|
@ -395,6 +395,10 @@ Utils::conversationAvatar(LRCInstance* instance,
|
|||
auto& accInfo = instance->accountModel().getAccountInfo(
|
||||
accountId.isEmpty() ? instance->get_currentAccountId() : accountId);
|
||||
auto* convModel = accInfo.conversationModel.get();
|
||||
auto avatarb64 = convModel->avatar(convId);
|
||||
if (!avatarb64.isEmpty())
|
||||
return scaleAndFrame(imageFromBase64String(avatarb64, true), size);
|
||||
// Else, generate an avatar
|
||||
auto members = convModel->peersForConversation(convId);
|
||||
if (members.size() < 1)
|
||||
return avatar;
|
||||
|
@ -418,6 +422,16 @@ Utils::conversationAvatar(LRCInstance* instance,
|
|||
return avatar;
|
||||
}
|
||||
|
||||
QImage
|
||||
Utils::tempConversationAvatar(const QSize& size)
|
||||
{
|
||||
QString img = QByteArrayFromFile(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
|
||||
+ "tmpSwarmImage");
|
||||
if (img.isEmpty())
|
||||
return fallbackAvatar(QString(), QString(), size);
|
||||
return scaleAndFrame(imageFromBase64String(img, true), size);
|
||||
}
|
||||
|
||||
QImage
|
||||
Utils::imageFromBase64String(const QString& str, bool circleCrop)
|
||||
{
|
||||
|
|
|
@ -97,6 +97,7 @@ QImage conversationAvatar(LRCInstance* instance,
|
|||
QImage getCirclePhoto(const QImage original, int sizePhoto);
|
||||
QImage halfCrop(const QImage original, bool leftSide);
|
||||
QColor getAvatarColor(const QString& canonicalUri);
|
||||
QImage tempConversationAvatar(const QSize& size);
|
||||
QImage fallbackAvatar(const QString& canonicalUriStr,
|
||||
const QString& letterStr = {},
|
||||
const QSize& size = defaultAvatarSize);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "api/datatransfermodel.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBuffer>
|
||||
#include <QClipboard>
|
||||
#include <QFileInfo>
|
||||
#include <QRegExp>
|
||||
|
@ -135,6 +136,12 @@ UtilsAdapter::getBestName(const QString& accountId, const QString& uid)
|
|||
return QString();
|
||||
}
|
||||
|
||||
QString
|
||||
UtilsAdapter::getBestNameForUri(const QString& accountId, const QString& uri)
|
||||
{
|
||||
return lrcInstance_->getAccountInfo(accountId).contactModel->bestNameForContact(uri);
|
||||
}
|
||||
|
||||
const QString
|
||||
UtilsAdapter::getPeerUri(const QString& accountId, const QString& uid)
|
||||
{
|
||||
|
@ -472,6 +479,55 @@ UtilsAdapter::supportedLang()
|
|||
return result;
|
||||
}
|
||||
|
||||
QString
|
||||
UtilsAdapter::swarmCreationImage(const QString& imageId) const
|
||||
{
|
||||
if (imageId == "temp")
|
||||
return Utils::QByteArrayFromFile(
|
||||
QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "tmpSwarmImage");
|
||||
return lrcInstance_->getCurrentConversationModel()->avatar(imageId);
|
||||
}
|
||||
|
||||
void
|
||||
UtilsAdapter::setSwarmCreationImageFromString(const QString& image, const QString& imageId)
|
||||
{
|
||||
// Compress the image before saving
|
||||
auto img = Utils::imageFromBase64String(image, false);
|
||||
setSwarmCreationImageFromImage(img);
|
||||
}
|
||||
|
||||
void
|
||||
UtilsAdapter::setSwarmCreationImageFromFile(const QString& path, const QString& imageId)
|
||||
{
|
||||
// Compress the image before saving
|
||||
auto image = Utils::QByteArrayFromFile(path);
|
||||
auto img = Utils::imageFromBase64Data(image, false);
|
||||
setSwarmCreationImageFromImage(img);
|
||||
}
|
||||
|
||||
void
|
||||
UtilsAdapter::setSwarmCreationImageFromImage(const QImage& image, const QString& imageId)
|
||||
{
|
||||
// Compress the image before saving
|
||||
auto img = Utils::scaleAndFrame(image, QSize(256, 256));
|
||||
QByteArray ba;
|
||||
QBuffer bu(&ba);
|
||||
img.save(&bu, "PNG");
|
||||
// Save the image
|
||||
if (imageId == "temp") {
|
||||
QFile file(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
|
||||
+ "tmpSwarmImage");
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(ba.toBase64());
|
||||
file.close();
|
||||
Q_EMIT lrcInstance_->base64SwarmAvatarChanged();
|
||||
} else {
|
||||
lrcInstance_->getCurrentConversationModel()->updateConversationInfo(imageId,
|
||||
{{"avatar",
|
||||
ba.toBase64()}});
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
UtilsAdapter::getContactPresence(const QString& accountId, const QString& uri)
|
||||
{
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
Q_INVOKABLE bool checkStartupLink();
|
||||
Q_INVOKABLE void setConversationFilter(const QString& filter);
|
||||
Q_INVOKABLE const QString getBestName(const QString& accountId, const QString& uid);
|
||||
Q_INVOKABLE QString getBestNameForUri(const QString& accountId, const QString& uri);
|
||||
Q_INVOKABLE const QString getPeerUri(const QString& accountId, const QString& uid);
|
||||
Q_INVOKABLE QString getBestId(const QString& accountId);
|
||||
Q_INVOKABLE const QString getBestId(const QString& accountId, const QString& uid);
|
||||
|
@ -91,6 +92,13 @@ public:
|
|||
Q_INVOKABLE void monitor(const bool& continuous);
|
||||
Q_INVOKABLE void clearInteractionsCache(const QString& accountId, const QString& convUid);
|
||||
Q_INVOKABLE QVariantMap supportedLang();
|
||||
Q_INVOKABLE QString swarmCreationImage(const QString& imageId = "temp") const;
|
||||
Q_INVOKABLE void setSwarmCreationImageFromString(const QString& image = "",
|
||||
const QString& imageId = "temp");
|
||||
Q_INVOKABLE void setSwarmCreationImageFromFile(const QString& path,
|
||||
const QString& imageId = "temp");
|
||||
Q_INVOKABLE void setSwarmCreationImageFromImage(const QImage& image,
|
||||
const QString& imageId = "temp");
|
||||
|
||||
// For Swarm details page
|
||||
Q_INVOKABLE bool getContactPresence(const QString& accountId, const QString& uri);
|
||||
|
|
Loading…
Add table
Reference in a new issue