mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-04-21 21:52:03 +02:00
swarm: add context menu for members
In the members list, a right click allow the user to access some actions such as: + Perform a video or audio call with a member + Open a 1:1 conversation with this member + Block this contact + If allowed, kick a member from the conversation In the future, other actions can be added, such as promote a user to administrator. GitLab: #340 Change-Id: I3824ad4efa8faf89479e99c93b98d3dd9781582d
This commit is contained in:
parent
5bfe0851cd
commit
4788e963a6
11 changed files with 98 additions and 20 deletions
|
@ -2,10 +2,12 @@
|
|||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10S17.5,2,12,2z M4.1,8.4c1.4-3,4.5-5.1,7.9-5.1c2.1,0,4.1,0.7,5.6,2.1l-2,2
|
||||
c-0.3-1.8-1.7-3.1-3.6-3.1C10,4.4,8.4,6,8.4,8c0,1.7,1.1,3,2.6,3.5c-2.7,0.5-4.9,2.9-5.5,6l-0.1,0.1C3.2,15,2.7,11.4,4.1,8.4z
|
||||
M12.7,10.3c-0.2,0.1-0.5,0.1-0.7,0.1c-1.4,0-2.4-1-2.4-2.4s1-2.4,2.4-2.4c1.4,0,2.4,1,2.4,2.4c0,0.3,0,0.5-0.1,0.7L12.7,10.3z
|
||||
M9.8,13.2l-2.3,2.3C8.1,14.5,8.9,13.7,9.8,13.2z M6.6,18.8c0-0.2,0-0.4,0-0.6l5.6-5.6c2.8,0.2,5,2.8,5.1,6.1
|
||||
C15.9,20,14,20.7,12,20.7C10,20.7,8.2,20,6.6,18.8z M18.5,17.7c-0.5-3.1-2.5-5.5-5.1-6.1l0.4-0.4c0.5-0.3,1-0.7,1.3-1.3l3.5-3.5
|
||||
c2.2,2.6,2.7,6.2,1.3,9.2C19.5,16.4,19.1,17.1,18.5,17.7z"/>
|
||||
<g>
|
||||
<path d="M16.9,11.5c-2.8,0-5.1,2.3-5.1,5.1s2.3,5.1,5.1,5.1s5.1-2.3,5.1-5.1S19.7,11.5,16.9,11.5z M16.9,12.7
|
||||
c0.8,0,1.6,0.3,2.2,0.7l-5.6,5c-0.3-0.6-0.5-1.2-0.5-1.9C13.1,14.5,14.8,12.7,16.9,12.7z M16.9,20.5c-1,0-2-0.4-2.7-1.1l5.7-5.1
|
||||
c0.5,0.6,0.8,1.4,0.8,2.3C20.8,18.7,19.1,20.5,16.9,20.5z"/>
|
||||
<path d="M14.5,13.1l0.7-0.4c-1-0.7-2.1-1.2-3.3-1.4c1.9-0.6,3.2-2.3,3.2-4.4c0.1-2.6-2-4.6-4.5-4.6C8.1,2.3,6,4.4,6,7
|
||||
c0,2.1,1.4,3.8,3.2,4.4C5.1,12.1,2,16,2,20.7c0,0.4,0.3,0.7,0.7,0.7s0.7-0.3,0.7-0.7c0-4.5,3.3-8.2,7.4-8.2c1.3,0,2.5,0.4,3.6,1.1
|
||||
C14.4,13.3,14.5,13.1,14.5,13.1z M10.6,10.3c-1.9,0-3.4-1.5-3.4-3.3c0-1.9,1.5-3.4,3.4-3.4S14,5.1,14,7S12.5,10.3,10.6,10.3z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1,020 B After Width: | Height: | Size: 1 KiB |
13
resources/icons/gotoconversation.svg
Normal file
13
resources/icons/gotoconversation.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<path d="M22,12.5c0-1.9-0.9-3.8-2.6-5.1l0,0c-0.3-0.2-0.6-0.5-1-0.7c-0.4-0.7-1-1.3-1.7-1.8c-1.6-1.3-3.8-2-6.1-2s-4.4,0.7-6.1,2
|
||||
C2.9,6.2,2,8,2,10c0,1.9,0.9,3.7,2.5,5l-0.1,3c0,0.3,0.3,0.6,0.6,0.6c0.1,0,0.2,0,0.3,0l2.1-0.9c1.6,1.2,3.7,1.9,6,1.9l0,0
|
||||
c0.5,0,1.1,0,1.7-0.1l3.8,1.6c0.1,0,0.2,0,0.2,0c0.1,0,0.2,0,0.2,0c0.2-0.1,0.3-0.2,0.3-0.3c0-0.1,0.1-0.2,0-0.3l-0.1-3
|
||||
C21.1,16.1,22,14.4,22,12.5z M18.4,19.6l-3.1-1.3c-0.1,0-0.2,0-0.2,0H15c-0.5,0.1-1.1,0.1-1.6,0.1c-1.7,0-3.3-0.4-4.6-1.3L9,17
|
||||
c0.5,0.1,1.1,0.1,1.7,0.1c2.3,0,4.4-0.7,6.1-2c1.7-1.3,2.6-3.2,2.6-5.1c0-0.3,0-0.7-0.1-1c1,1,1.5,2.2,1.5,3.5c0,1.6-0.8,3-2.2,4.1
|
||||
l-0.1,0.1c-0.2,0.1-0.2,0.3-0.2,0.5L18.4,19.6z M5.5,14.2L5.5,14.2C4,13,3.2,11.6,3.2,10S4,7,5.4,5.9s3.3-1.7,5.3-1.7
|
||||
s3.9,0.6,5.3,1.7s2.2,2.6,2.2,4.1c0,1.6-0.8,3-2.2,4.1c-1.4,1.1-3.3,1.7-5.3,1.7c-0.5,0-1.1,0-1.6-0.1c-0.1,0-0.2,0-0.3,0L5.7,17
|
||||
l0.1-2.3C5.7,14.5,5.7,14.3,5.5,14.2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
14
resources/icons/kick_member.svg
Normal file
14
resources/icons/kick_member.svg
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M14.5,13.1l0.7-0.4c-1-0.7-2.1-1.2-3.3-1.4c1.9-0.6,3.2-2.3,3.2-4.4c0.1-2.6-2-4.6-4.5-4.6C8.1,2.3,6,4.4,6,7
|
||||
c0,2.1,1.4,3.8,3.2,4.4C5.1,12.1,2,16,2,20.7c0,0.4,0.3,0.7,0.7,0.7s0.7-0.3,0.7-0.7c0-4.5,3.3-8.2,7.4-8.2c1.3,0,2.5,0.4,3.6,1.1
|
||||
C14.4,13.3,14.5,13.1,14.5,13.1z M10.6,10.3c-1.9,0-3.4-1.5-3.4-3.3c0-1.9,1.5-3.4,3.4-3.4S14,5.1,14,7S12.5,10.3,10.6,10.3z"/>
|
||||
<path d="M16.9,11.5c-2.8,0-5.1,2.3-5.1,5.1c0,2.8,2.3,5.1,5.1,5.1c2.8,0,5.1-2.3,5.1-5.1C22,13.8,19.7,11.5,16.9,11.5z M20.3,18.4
|
||||
L18,16.3l2.2-1.9c0.4,0.6,0.7,1.4,0.7,2.2C20.8,17.3,20.6,17.9,20.3,18.4z M19.3,13.5l-2.2,2l-2.3-2.1c0.6-0.4,1.4-0.7,2.2-0.7
|
||||
C17.8,12.7,18.6,13,19.3,13.5z M13.9,14.3l2.3,2l-2.5,2.3c-0.3-0.6-0.6-1.3-0.6-2C13.1,15.7,13.4,14.9,13.9,14.3z M14.4,19.5
|
||||
l2.7-2.4l2.5,2.3c-0.7,0.7-1.6,1.1-2.7,1.1C16,20.5,15.1,20.1,14.4,19.5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -239,6 +239,13 @@ ContactAdapter::contactSelected(int index)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContactAdapter::removeContact(const QString& peerUri, bool banContact)
|
||||
{
|
||||
auto& accInfo = lrcInstance_->getCurrentAccountInfo();
|
||||
accInfo.contactModel->removeContact(peerUri, banContact);
|
||||
}
|
||||
|
||||
void
|
||||
ContactAdapter::connectSignals()
|
||||
{
|
||||
|
|
|
@ -91,6 +91,7 @@ public:
|
|||
Q_INVOKABLE QVariant getContactSelectableModel(int type);
|
||||
Q_INVOKABLE void setSearchFilter(const QString& filter);
|
||||
Q_INVOKABLE void contactSelected(int index);
|
||||
Q_INVOKABLE void removeContact(const QString& peerUri, bool banContact);
|
||||
|
||||
void connectSignals();
|
||||
|
||||
|
@ -104,7 +105,8 @@ private:
|
|||
|
||||
QStringList defaultModerators_;
|
||||
|
||||
bool hasDifferentMembers(const VectorString& currentMembers, const VectorString& convMembers) const;
|
||||
bool hasDifferentMembers(const VectorString& currentMembers,
|
||||
const VectorString& convMembers) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void defaultModeratorsUpdated();
|
||||
|
|
|
@ -381,6 +381,7 @@ ConversationsAdapter::setFilter(const QString& filterString)
|
|||
{
|
||||
convModel_->setFilter(filterString);
|
||||
searchSrcModel_->setFilter(filterString);
|
||||
Q_EMIT textFilterChanged(filterString);
|
||||
}
|
||||
|
||||
QVariantMap
|
||||
|
@ -499,6 +500,24 @@ ConversationsAdapter::updateConversationDescription(const QString& convId,
|
|||
convModel->updateConversationInfo(convId, details);
|
||||
}
|
||||
|
||||
QString
|
||||
ConversationsAdapter::dialogId(const QString& peerUri)
|
||||
{
|
||||
auto& convInfo = lrcInstance_->getConversationFromPeerUri(peerUri);
|
||||
if (!convInfo.uid.isEmpty() && convInfo.isCoreDialog())
|
||||
return convInfo.uid;
|
||||
return {};
|
||||
}
|
||||
|
||||
void
|
||||
ConversationsAdapter::openDialogConversationWith(const QString& peerUri)
|
||||
{
|
||||
auto& convInfo = lrcInstance_->getConversationFromPeerUri(peerUri);
|
||||
if (convInfo.uid.isEmpty() || !convInfo.isCoreDialog())
|
||||
return;
|
||||
lrcInstance_->selectConversation(convInfo.uid);
|
||||
}
|
||||
|
||||
bool
|
||||
ConversationsAdapter::connectConversationModel()
|
||||
{
|
||||
|
|
|
@ -59,9 +59,12 @@ public:
|
|||
Q_INVOKABLE void updateConversationDescription(const QString& convId,
|
||||
const QString& newDescription);
|
||||
|
||||
Q_INVOKABLE QString dialogId(const QString& peerUri);
|
||||
Q_INVOKABLE void openDialogConversationWith(const QString& peerUri);
|
||||
Q_SIGNALS:
|
||||
void showConversation(const QString& accountId, const QString& convUid);
|
||||
void showSearchStatus(const QString& status);
|
||||
void textFilterChanged(const QString& text);
|
||||
|
||||
void navigateToWelcomePageRequested();
|
||||
void conversationReady(const QString& convId);
|
||||
|
|
|
@ -156,6 +156,12 @@ Rectangle {
|
|||
function onShowSearchStatus(status) {
|
||||
searchStatusText.text = status
|
||||
}
|
||||
|
||||
function onTextFilterChanged(text) {
|
||||
// In the swarm details, "Go to conversation" can
|
||||
// change the search bar. Be sure to be synced
|
||||
contactSearchBar.textContent = text
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
|
@ -168,6 +168,7 @@ Rectangle {
|
|||
|
||||
SwarmParticipantContextMenu {
|
||||
id: contextMenu
|
||||
role: UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, CurrentAccount.uri)
|
||||
|
||||
function openMenuAt(x, y, participantUri) {
|
||||
contextMenu.x = x
|
||||
|
@ -187,6 +188,7 @@ Rectangle {
|
|||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: modelData != CurrentAccount.uri
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: function (mouse) {
|
||||
contextMenu.openMenuAt(x + mouse.x, y + mouse.y, modelData)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2021 by Savoir-faire Linux
|
||||
* Copyright (C) 2022 by Savoir-faire Linux
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -29,44 +29,55 @@ ContextMenuAutoLoader {
|
|||
id: root
|
||||
property var conversationId: ""
|
||||
property var participantUri: ""
|
||||
|
||||
// TODO get authorization
|
||||
property var role
|
||||
|
||||
property list<GeneralMenuItem> menuItems: [
|
||||
GeneralMenuItem {
|
||||
id: startVideoCallItem
|
||||
itemName: JamiStrings.startVideoCall
|
||||
canTrigger: ConversationsAdapter.dialogId(participantUri) !== ""
|
||||
iconSource: JamiResources.videocam_24dp_svg
|
||||
onClicked: {
|
||||
ConversationsAdapter.openDialogConversationWith(participantUri)
|
||||
CallAdapter.placeCall()
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: startAudioCall
|
||||
itemName: JamiStrings.startAudioCall
|
||||
canTrigger: ConversationsAdapter.dialogId(participantUri) !== ""
|
||||
iconSource: JamiResources.place_audiocall_24dp_svg
|
||||
onClicked: {
|
||||
ConversationsAdapter.openDialogConversationWith(participantUri)
|
||||
CallAdapter.placeAudioOnlyCall()
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: goToConversation
|
||||
|
||||
iconSource: JamiResources.gotoconversation_svg
|
||||
itemName: JamiStrings.goToConversation
|
||||
onClicked: {
|
||||
if (ConversationsAdapter.dialogId(participantUri) !== "")
|
||||
ConversationsAdapter.openDialogConversationWith(participantUri)
|
||||
else
|
||||
ConversationsAdapter.setFilter(participantUri)
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: promoteAdministrator
|
||||
canTrigger: false // No API yet
|
||||
itemName: JamiStrings.promoteAdministrator
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: blockContact
|
||||
itemName: JamiStrings.blockContact
|
||||
iconSource: JamiResources.block_black_24dp_svg
|
||||
onClicked: {
|
||||
ContactAdapter.removeContact(participantUri, true)
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: kickMember
|
||||
itemName: JamiStrings.kickMember
|
||||
iconSource: JamiResources.kick_member_svg
|
||||
canTrigger: role === Member.Role.ADMIN
|
||||
|
||||
// TODO can trigger (enough permission for self and member accepted)
|
||||
onClicked: {
|
||||
MessagesAdapter.removeConversationMember(conversationId, participantUri)
|
||||
}
|
||||
|
|
|
@ -376,7 +376,6 @@ Utils::contactPhoto(LRCInstance* instance,
|
|||
photo = Utils::fallbackAvatar("jami:" + contactInfo.profileInfo.uri, avatarName);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
qDebug() << e.what() << "; Using default avatar";
|
||||
photo = fallbackAvatar("jami:" + contactUri, QString(), size);
|
||||
}
|
||||
return Utils::scaleAndFrame(photo, size);
|
||||
|
@ -401,14 +400,14 @@ Utils::conversationAvatar(LRCInstance* instance,
|
|||
return avatar;
|
||||
if (members.size() == 1) {
|
||||
// Only member in the swarm or 1:1, draw only peer's avatar
|
||||
auto peerAvatar = Utils::contactPhoto(instance, members[0], size);
|
||||
auto peerAvatar = Utils::contactPhoto(instance, members[0], size, "");
|
||||
painter.drawImage(avatar.rect(), peerAvatar);
|
||||
return avatar;
|
||||
}
|
||||
// Else, combine avatars
|
||||
auto idx = 0;
|
||||
auto peerAAvatar = Utils::contactPhoto(instance, members[0], size);
|
||||
auto peerBAvatar = Utils::contactPhoto(instance, members[1], size);
|
||||
auto peerAAvatar = Utils::contactPhoto(instance, members[0], size, "");
|
||||
auto peerBAvatar = Utils::contactPhoto(instance, members[1], size, "");
|
||||
peerAAvatar = Utils::halfCrop(peerAAvatar, true);
|
||||
peerBAvatar = Utils::halfCrop(peerBAvatar, false);
|
||||
painter.drawImage(avatar.rect(), peerAAvatar);
|
||||
|
|
Loading…
Add table
Reference in a new issue