Compare commits

...

7 Commits

Author SHA1 Message Date
bd3552a049 Metainfo.xml: update versions for new stable
Change-Id: I0c7e3c8a94c4c07de69ad25ec679f32c74b4b2e8
2025-09-12 14:30:24 -04:00
03bcd905e5 misc: bump daemon
Change-Id: I8afd7d7f2fde3b933906b95c2f06f104b0003459
2025-09-12 13:25:46 -04:00
9bfd149d45 Spelling: confrimLeaveGroup -> confirmLeaveGroup
Change-Id: I4451b9314cc40de5175da9257fcc9645ee481012
2025-09-12 11:28:35 -04:00
6c5ab1e483 JamiStrings: reword group and 1-to-1 leaving
Change-Id: I767f9f49f4e1ac46faef70804ff1bd71d0cb8acc
2025-09-11 16:44:51 -04:00
faba758254 conversation: add ended banner
Change-Id: I4e58f8da33a2776b6f2cdc15b4f8f11ad8f88482
2025-09-11 15:22:28 -04:00
d8f548261d conversation: add option remove one-to-one conversation
Change-Id: I44d2c69b99a5359f0fce0d6c134374c11efbd286
2025-09-11 15:22:28 -04:00
fe504827fa build: fix --debug option
The install.sh script's -d option and (therefore) the build.py script's
--debug option were broken by commit 22be4be864.

Change-Id: I165eb625735d93b5fb3a2a845387d29cc4f6c18a
2025-09-11 09:02:11 -04:00
11 changed files with 186 additions and 47 deletions

2
daemon

Submodule daemon updated: c53658e211...ce82bdbbf3

View File

@ -87,12 +87,27 @@
</developer>
<releases>
<release version="20250912.0" date="2025-09-12">
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250912</url>
</release>
<release version="20250815.1" date="2025-08-17">
<url type="detials">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#stable-20250817</url>
</release>
<release version="20250718.0" date="2025-07-18">
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250718</url>
</release>
<release version="20250613.0" date="2025-06-13">
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250613</url>
</release>
<release version="20250610.0" date="2025-06-10">
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250610</url>
</release>
<release version="20250523.0" date="2025-05-23">
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250523</url>
</release>
<release version="20250430.1" date="2025-04-30">
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250430</url>
</release>
</releases>
<branding>

View File

@ -129,6 +129,11 @@ else
BUILD_DIR="build"
fi
BUILD_TYPE="Release"
if [ "${debug}" = "true" ]; then
BUILD_TYPE="Debug"
fi
if [[ "$OSTYPE" == "darwin"* ]]; then
#detect arch for macos
CMAKE_OSX_ARCHITECTURES="arm64"

View File

@ -45,6 +45,7 @@ Rectangle {
color: JamiTheme.chatviewBgColor
property var mapPositions: PositionManager.mapStatus
property bool isConversationEndedFlag: false
// The purpose of this alias is to make the message bar
// accessible to the EmojiPicker
@ -85,6 +86,32 @@ Rectangle {
}
}
function isConversationEnded() {
if (!CurrentConversation.isSwarm)
return false;
var myRole = UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, CurrentAccount.uri);
var info = ConversationsAdapter.getConvInfoMap(CurrentConversation.id);
var peers = info && info.uris ? info.uris : [];
peers = peers.filter(function(u) { return u !== CurrentAccount.uri; });
for (var i = 0; i < peers.length; i++) {
var role = UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, peers[i]);
if (!(role === Member.Role.LEFT || role === Member.Role.BANNED)) {
return false;
}
}
if (CurrentConversation.isCoreDialog) {
return true
}
return myRole !== Member.Role.ADMIN;
}
function updateConversationEndedFlag() {
var newVal = isConversationEnded();
if (isConversationEndedFlag !== newVal) {
isConversationEndedFlag = newVal;
}
}
// Used externally to switch to a extras panel.
function switchToPanel(panel, toggle = true) {
extrasPanel.switchToPanel(panel, toggle);
@ -102,16 +129,33 @@ Rectangle {
}
}
Connections {
target: LRCInstance
function onConversationUpdated(convId, accountId) {
if (convId === CurrentConversation.id) {
updateConversationEndedFlag();
}
}
}
Connections {
target: CurrentConversation.members
function onCountChanged() {
updateConversationEndedFlag();
}
}
Connections {
target: CurrentConversation
function onIdChanged() {
MessagesAdapter.loadMoreMessages();
updateConversationEndedFlag();
}
}
onVisibleChanged: {
if (visible) {
chatViewSplitView.resolvePanes(true);
Qt.callLater(updateConversationEndedFlag);
}
}
@ -196,6 +240,29 @@ Rectangle {
visible: false
}
Control {
id: conversationEndedBanner
Layout.fillWidth: true
visible: isConversationEndedFlag
padding: 10
background: Rectangle {
color: JamiTheme.infoRectangleColor
radius: 5
}
contentItem: RowLayout {
spacing: 8
Label {
text: JamiStrings.conversationEnded
color: JamiTheme.textColor
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
}
}
}
NotificationArea {
id: notificationArea
Layout.fillWidth: true
@ -333,6 +400,8 @@ Rectangle {
return false;
else if (CurrentConversation.isRequest)
return false;
else if (isConversationEndedFlag)
return false;
return CurrentConversation.isSwarm || CurrentConversation.isTemporary;
}

View File

@ -73,28 +73,37 @@ ContextMenuAutoLoader {
onClicked: MessagesAdapter.clearConversationHistory(responsibleAccountId, responsibleConvUid)
},
GeneralMenuItem {
id: removeContact
id: removeConversation
canTrigger: !hasCall && !root.isBanned
itemName: {
if (mode !== Conversation.Mode.NON_SWARM)
return JamiStrings.removeConversation;
else
return JamiStrings.removeContact;
}
itemName: mode === Conversation.Mode.ONE_TO_ONE ? JamiStrings.removeConversation : JamiStrings.leaveGroup
iconSource: JamiResources.ic_hangup_participant_24dp_svg
onClicked: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ConfirmDialog.qml", {
"title": JamiStrings.confirmAction,
"textLabel": JamiStrings.confirmLeaveConversation,
"confirmLabel": JamiStrings.optionLeave
});
"title": JamiStrings.confirmAction,
"textLabel": mode === Conversation.Mode.ONE_TO_ONE ? JamiStrings.confirmRemoveOneToOneConversation : JamiStrings.confirmLeaveGroup,
"confirmLabel": mode === Conversation.Mode.ONE_TO_ONE ? JamiStrings.optionRemove : JamiStrings.optionLeave
});
dlg.accepted.connect(function () {
if (mode !== Conversation.Mode.NON_SWARM)
MessagesAdapter.removeConversation(responsibleConvUid);
else
MessagesAdapter.removeContact(responsibleConvUid);
});
MessagesAdapter.removeConversation(responsibleConvUid, true);
});
}
},
GeneralMenuItem {
id: removeContact
canTrigger: !hasCall && !root.isBanned && mode === Conversation.Mode.ONE_TO_ONE
itemName: JamiStrings.removeContact
iconSource: JamiResources.kick_member_svg
onClicked: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ConfirmDialog.qml", {
"title": JamiStrings.confirmAction,
"textLabel": JamiStrings.confirmRemoveContact,
"confirmLabel": JamiStrings.optionRemove
});
dlg.accepted.connect(function () {
MessagesAdapter.removeContact(responsibleConvUid);
});
}
},
GeneralMenuItem {
@ -129,13 +138,13 @@ ContextMenuAutoLoader {
iconSource: JamiResources.block_black_24dp_svg
onClicked: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ConfirmDialog.qml", {
"title": JamiStrings.confirmAction,
"textLabel": JamiStrings.confirmBlockConversation,
"confirmLabel": JamiStrings.optionBlock
});
"title": JamiStrings.confirmAction,
"textLabel": JamiStrings.confirmBlockContact,
"confirmLabel": JamiStrings.optionBlock
});
dlg.accepted.connect(function () {
MessagesAdapter.blockConversation(responsibleConvUid);
});
MessagesAdapter.blockConversation(responsibleConvUid);
});
}
},
GeneralMenuItem {
@ -155,11 +164,11 @@ ContextMenuAutoLoader {
onClicked: {
if (isCoreDialog) {
viewCoordinator.presentDialog(appWindow, "mainview/components/UserProfile.qml", {
"aliasText": aliasText,
"registeredNameText": registeredNameText,
"idText": idText,
"convId": responsibleConvUid
});
"aliasText": aliasText,
"registeredNameText": registeredNameText,
"idText": idText,
"convId": responsibleConvUid
});
} else {
root.showSwarmDetails();
}

View File

@ -297,7 +297,7 @@ Rectangle {
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: JamiTheme.preferredMarginSize
text: JamiStrings.leaveConversation
text: CurrentConversation.modeString.indexOf("group") >= 0 ? JamiStrings.leaveGroup : JamiStrings.removeConversation
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
font.kerning: true
elide: Text.ElideRight
@ -312,13 +312,49 @@ Rectangle {
enabled: parent.visible
onTapped: function onTapped(eventPoint) {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ConfirmDialog.qml", {
"title": JamiStrings.confirmAction,
"textLabel": JamiStrings.confirmLeaveConversation,
"confirmLabel": JamiStrings.optionLeave
});
"title": JamiStrings.confirmAction,
"textLabel": CurrentConversation.modeString.indexOf("group") >= 0 ? JamiStrings.confirmLeaveGroup : JamiStrings.confirmRemoveOneToOneConversation,
"confirmLabel": CurrentConversation.modeString.indexOf("group") >= 0 ? JamiStrings.optionLeave : JamiStrings.optionRemove
});
dlg.accepted.connect(function () {
MessagesAdapter.removeConversation(LRCInstance.selectedConvUid);
});
MessagesAdapter.removeConversation(LRCInstance.selectedConvUid, true);
});
}
}
}
SwarmDetailsItem {
Layout.fillWidth: true
Layout.preferredHeight: JamiTheme.settingsFontSize + 2 * JamiTheme.preferredMarginSize + 4
visible: CurrentConversation.isCoreDialog
Text {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: JamiTheme.preferredMarginSize
text: JamiStrings.removeContact
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
font.kerning: true
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
color: JamiTheme.textColor
}
TapHandler {
target: parent
enabled: parent.visible
onTapped: function onTapped(eventPoint) {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ConfirmDialog.qml", {
"title": JamiStrings.confirmAction,
"textLabel": JamiStrings.confirmRemoveContact,
"confirmLabel": JamiStrings.optionRemove
});
dlg.accepted.connect(function () {
MessagesAdapter.removeConversation(LRCInstance.selectedConvUid);
});
}
}
}
@ -500,8 +536,6 @@ Rectangle {
verticalAlignment: Text.AlignVCenter
color: JamiTheme.textColor
}
Text {

View File

@ -480,10 +480,10 @@ MessagesAdapter::clearConversationHistory(const QString& accountId, const QStrin
}
void
MessagesAdapter::removeConversation(const QString& convUid)
MessagesAdapter::removeConversation(const QString& convUid, bool keepContact)
{
auto& accInfo = lrcInstance_->getCurrentAccountInfo();
accInfo.conversationModel->removeConversation(convUid);
accInfo.conversationModel->removeConversation(convUid, false, keepContact);
}
void

View File

@ -113,7 +113,7 @@ public:
Q_INVOKABLE void loadMoreMessages();
Q_INVOKABLE void connectConversationModel();
Q_INVOKABLE void sendConversationRequest();
Q_INVOKABLE void removeConversation(const QString& convUid);
Q_INVOKABLE void removeConversation(const QString& convUid, bool keepContact = false);
Q_INVOKABLE void addConversationMember(const QString& convUid, const QString& participantUri);
Q_INVOKABLE void removeConversationMember(const QString& convUid, const QString& participantUri);
Q_INVOKABLE void removeContact(const QString& convUid, bool banContact = false);

View File

@ -282,9 +282,13 @@ Item {
property string startVideoCall: qsTr("Start video call")
property string deleteConversation: qsTr("Delete conversation")
property string confirmAction: qsTr("Confirm action")
property string removeConversation: qsTr("Leave conversation")
property string confirmLeaveConversation: qsTr("Do you want to leave this conversation?")
property string confirmBlockConversation: qsTr("Do you want to block this conversation?")
property string removeConversation: qsTr("Remove conversation")
property string confirmRemoveConversation: qsTr("Do you want to leave this conversation?")
property string leaveGroup: qsTr("Leave group")
property string confirmLeaveGroup: qsTr("Are you sure you want to leave this group?")
property string confirmRemoveContact: qsTr("Do you want to remove this contact? The existing conversation will be deleted.")
property string confirmBlockContact: qsTr("Do you want to block this contact?")
property string confirmRemoveOneToOneConversation: qsTr("Are you sure you want to remove this conversation? The contact will not be removed.")
property string removeContact: qsTr("Remove contact")
property string blockContact: qsTr("Block contact")
property string convDetails: qsTr("Conversation details")
@ -366,6 +370,9 @@ Item {
property string deletedMedia: qsTr("%1 deleted a media")
property string returnToCall: qsTr("Return to call")
// Conversation ended banner
property string conversationEnded: qsTr("This conversation has ended.")
// MessagesResearch
property string jumpTo: qsTr("Jump to")
property string messages: qsTr("Messages")
@ -828,7 +835,6 @@ Item {
property string ignoreNotificationsTooltip: qsTr("Ignore all notifications from this conversation")
property string chooseAColor: qsTr("Color")
property string defaultCallHost: qsTr("Default host (calls)")
property string leaveConversation: qsTr("Leave conversation")
property string typeOfSwarm: qsTr("Conversation type")
property string none: qsTr("None")

View File

@ -191,8 +191,9 @@ public:
* Remove a conversation and the contact if it's a dialog
* @param uid of the conversation
* @param banned if we want to ban the contact.
* @param keepContact if we want to keep the contact. New conversation could be created
*/
void removeConversation(const QString& uid, bool banned = false);
void removeConversation(const QString& uid, bool banned = false, bool keepContact = false);
/**
* Get the action wanted by the user when they click on the conversation
* @param uid of the conversation

View File

@ -819,7 +819,7 @@ ConversationModel::selectConversation(const QString& uid) const
}
void
ConversationModel::removeConversation(const QString& uid, bool banned)
ConversationModel::removeConversation(const QString& uid, bool banned, bool keepContact)
{
// Get conversation
auto conversationIdx = pimpl_->indexOf(uid);
@ -837,7 +837,7 @@ ConversationModel::removeConversation(const QString& uid, bool banned)
"participant.";
return;
}
if (conversation.isSwarm() && !banned && !conversation.isCoreDialog()) {
if (conversation.isSwarm() && !banned && (!conversation.isCoreDialog() || keepContact)) {
if (conversation.isRequest) {
ConfigurationManager::instance().declineConversationRequest(owner.id, uid);
} else {