improve unencrypted conversations warning indicator #LINQT-2061

allow user to choose an address for sending message when multiple addresses in contact #LINQT-2054

verify friend has a core to avoid crash in liblinphone #LINQT-1933

wait for window to be active before calling markAsRead (fix #LINQT-2048)

fix button text color (fix #LINQT-1832)

change format for mkv #LINQT-2056

Registration : check phone number format #LINQT-2044

fix window closing even if a new call is started #LINQT-2055

display popup to delete meetings on right click in meeting list item (allow to delete canceled meetings which cannot be displayed in the right panel)
This commit is contained in:
Gaelle Braud 2025-10-14 12:42:53 +02:00
parent a9a78cb4bf
commit 97b1d11adb
33 changed files with 1865 additions and 1682 deletions

View file

@ -1045,7 +1045,7 @@ void App::setCallsWindowProperty(const char *id, QVariant property) {
}
void App::closeCallsWindow() {
if (mCallsWindow) {
if (mCallsWindow && mCallList && !mCallList->getHaveCall()) {
mCallsWindow->close();
mCallsWindow->deleteLater();
mCallsWindow = nullptr;

View file

@ -29,6 +29,8 @@
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include <QQuickWindow>
DEFINE_ABSTRACT_OBJECT(ChatCore)
/***********************************************************************/
@ -232,7 +234,16 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
});
mChatModelConnection->makeConnectToCore(&ChatCore::lMarkAsRead, [this]() {
mChatModelConnection->invokeToModel([this]() { mChatModel->markAsRead(); });
auto mainWindow = Utils::getMainWindow();
if (mainWindow->isActive()) mChatModelConnection->invokeToModel([this]() { mChatModel->markAsRead(); });
else {
connect(mainWindow, &QQuickWindow::activeChanged, this, [this, mainWindow] {
if (mainWindow->isActive()) {
disconnect(mainWindow, &QQuickWindow::activeChanged, this, nullptr);
mChatModelConnection->invokeToModel([this, mainWindow] { mChatModel->markAsRead(); });
}
});
}
});
mChatModelConnection->makeConnectToModel(&ChatModel::messagesRead, [this]() {
auto unread = mChatModel->getUnreadMessagesCount();

View file

@ -22,6 +22,8 @@
#include "core/App.hpp"
#include "model/tool/ToolModel.hpp"
#include <QQuickWindow>
DEFINE_ABSTRACT_OBJECT(ChatMessageCore)
/***********************************************************************/
@ -213,7 +215,17 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
});
});
mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lMarkAsRead, [this] {
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->markAsRead(); });
auto mainWindow = Utils::getMainWindow();
if (mainWindow->isActive())
mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->markAsRead(); });
else {
connect(mainWindow, &QQuickWindow::activeChanged, this, [this, mainWindow] {
if (mainWindow->isActive()) {
disconnect(mainWindow, &QQuickWindow::activeChanged, this, nullptr);
mChatMessageModelConnection->invokeToModel([this, mainWindow] { mChatMessageModel->markAsRead(); });
}
});
}
});
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::messageRead, [this]() {
mChatMessageModelConnection->invokeToCore([this] { setIsRead(true); });

View file

@ -54,8 +54,8 @@ ParticipantCore::ParticipantCore(const std::shared_ptr<linphone::Participant> &p
mDisplayName = Utils::coreStringToAppString(participantAddress->getDisplayName());
if (mDisplayName.isEmpty()) mDisplayName = ToolModel::getDisplayName(participantAddress);
auto isFriend = ToolModel::findFriendByAddress(participantAddress);
mSecurityLevel =
isFriend ? LinphoneEnums::fromLinphone(isFriend->getSecurityLevel()) : LinphoneEnums::SecurityLevel::None;
if (isFriend && isFriend->getCore()) mSecurityLevel = LinphoneEnums::fromLinphone(isFriend->getSecurityLevel());
else mSecurityLevel = LinphoneEnums::SecurityLevel::None;
for (auto &device : participant->getDevices()) {
auto name = Utils::coreStringToAppString(device->getName());
auto address = Utils::coreStringToAppString(device->getAddress()->asStringUriOnly());

View file

@ -38,15 +38,18 @@ RegisterPage::~RegisterPage() {
void RegisterPage::registerNewAccount(const QString &username,
const QString &password,
const QString &email,
const QString &countryCallingCode,
const QString &phoneNumber) {
App::postModelAsync([=]() {
// Create on Model thread.
// registrationFailed(error); });
AccountManager::RegisterType registerType;
QString address;
if (email.isEmpty()) {
registerType = AccountManager::RegisterType::PhoneNumber;
address = phoneNumber;
auto dialPlan = linphone::DialPlan::byCcc(Utils::appStringToCoreString(countryCallingCode));
auto formattedPhoneNumber = dialPlan->formatPhoneNumber(Utils::appStringToCoreString(phoneNumber), false);
address = Utils::coreStringToAppString(formattedPhoneNumber);
} else {
registerType = AccountManager::RegisterType::Email;
address = email;

View file

@ -39,8 +39,9 @@ public:
Q_INVOKABLE void registerNewAccount(const QString &username,
const QString &password,
const QString &email,
const QString &phoneNumber);
const QString &email = QString(),
const QString &countryCallingCode = QString(),
const QString &phoneNumber = QString());
Q_INVOKABLE void
linkNewAccountUsingCode(const QString &code, bool registerWithEmail, const QString &sipIdentityAddress);

View file

@ -1 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M208,80H96V56a32,32,0,0,1,32-32c15.37,0,29.2,11,32.16,25.59a8,8,0,0,0,15.68-3.18C171.32,24.15,151.2,8,128,8A48.05,48.05,0,0,0,80,56V80H48A16,16,0,0,0,32,96V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V96A16,16,0,0,0,208,80Zm0,128H48V96H208V208Z"></path></svg>
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1_2)">
<path d="M208 88H48C43.5817 88 40 91.5817 40 96V208C40 212.418 43.5817 216 48 216H208C212.418 216 216 212.418 216 208V96C216 91.5817 212.418 88 208 88Z" stroke="black" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M88 88V56C88 45.3913 83.7857 35.2172 76.2843 27.7157C68.7828 20.2143 58.6087 16 48 16C28.65 16 11.71 29.74 8 48" stroke="black" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_1_2">
<rect width="256" height="256" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 367 B

After

Width:  |  Height:  |  Size: 682 B

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -56,7 +56,7 @@ void CallModel::accept(bool withVideo) {
params->setRecordFile(
Paths::getCapturesDirPath()
.append(Utils::generateSavedFilename(QString::fromStdString(mMonitor->getToAddress()->getUsername()), ""))
.append(".smff")
.append(".mkv")
.toStdString());
// Answer with local call address.
auto localAddress = mMonitor->getCallLog()->getLocalAddress();

View file

@ -36,8 +36,8 @@ ChatModel::ChatModel(const std::shared_ptr<linphone::ChatRoom> &chatroom, QObjec
mustBeInLinphoneThread(getClassName());
auto coreModel = CoreModel::getInstance();
if (coreModel)
connect(coreModel.get(), &CoreModel::messageReadInChatRoom, this,
[this](std::shared_ptr<linphone::ChatRoom> chatroom) {
connect(coreModel.get(), &CoreModel::chatRoomRead, this,
[this](const std::shared_ptr<linphone::Core> &core, std::shared_ptr<linphone::ChatRoom> chatroom) {
if (chatroom == mMonitor) emit messagesRead();
});
}
@ -90,7 +90,7 @@ std::list<std::shared_ptr<linphone::ChatMessage>> ChatModel::getChatMessageHisto
return res;
}
int ChatModel::getHistorySizeEvents() {
int ChatModel::getHistorySizeEvents() const {
return mMonitor->getHistoryEventsSize();
}

View file

@ -57,7 +57,7 @@ public:
std::list<std::shared_ptr<linphone::EventLog>>
getHistoryRangeNear(int before, int after, const std::shared_ptr<linphone::EventLog> firstEvent, int filters);
std::list<std::shared_ptr<linphone::ChatMessage>> getChatMessageHistory() const;
int getHistorySizeEvents();
int getHistorySizeEvents() const;
QString getIdentifier() const;
void deleteHistory();
void deleteMessage(std::shared_ptr<linphone::ChatMessage> message);

View file

@ -330,7 +330,7 @@ bool ToolModel::createCall(const QString &sipAddress,
params->setRecordFile(
Paths::getCapturesDirPath()
.append(Utils::generateSavedFilename(QString::fromStdString(address->getUsername()), ""))
.append(".smff")
.append(".mkv")
.toStdString());
}

View file

@ -233,8 +233,7 @@ void Utils::closeCallsWindow() {
}
QQuickWindow *Utils::getMainWindow() {
auto win = App::getInstance()->getMainWindow();
return win;
return App::getInstance()->getMainWindow();
}
QQuickWindow *Utils::getLastActiveWindow() {

View file

@ -120,13 +120,11 @@ Control.Button {
text: mainItem.text
textFormat: mainItem.textFormat
maximumLineCount: 1
color: mainItem.checkable && mainItem.checked
? mainItem.checkedColor || mainItem.pressedColor
: mainItem.pressed
? mainItem.pressedTextColor
: mainItem.hovered
? mainItem.hoveredTextColor
: mainItem.textColor
color: mainItem.checkable && mainItem.checked || mainItem.pressed
? mainItem.pressedTextColor
: mainItem.hovered
? mainItem.hoveredTextColor
: mainItem.textColor
font {
pixelSize: mainItem.textSize
weight: mainItem.textWeight

View file

@ -4,210 +4,193 @@ import QtQuick.Layouts
import QtQuick.Effects
import Linphone
ColumnLayout {
Control.ComboBox {
id: mainItem
property string label: ""
readonly property string currentText: combobox.model.getAt(combobox.currentIndex) ? "+" + combobox.model.getAt(combobox.currentIndex).countryCallingCode : ""
property string defaultCallingCode: ""
property bool enableBackgroundColors: false
Text {
visible: mainItem.label.length > 0
verticalAlignment: Text.AlignVCenter
text: mainItem.label
color: combobox.activeFocus ? DefaultStyle.main1_500_main : DefaultStyle.main2_600
font {
pixelSize: Typography.p2.pixelSize
weight: Typography.p2.weight
bold: true
property string text: combobox.model.getAt(combobox.currentIndex) ? combobox.model.getAt(combobox.currentIndex).countryCallingCode : ""
currentIndex: phoneNumberModel.count > 0 ? Math.max(0, phoneNumberModel.findIndexByCountryCallingCode(defaultCallingCode)) : -1
Accessible.name: mainItem.Accessible.name
model: PhoneNumberProxy {
id: phoneNumberModel
}
background: Rectangle {
anchors.fill: parent
radius: Math.round(63 * DefaultStyle.dp)
color: mainItem.enableBackgroundColor ? DefaultStyle.grey_100 : "transparent"
border.color: mainItem.enableBackgroundColors
? (mainItem.errorMessage.length > 0
? DefaultStyle.danger_500_main
: textField.activeFocus
? DefaultStyle.main1_500_main
: DefaultStyle.grey_200)
: "transparent"
}
contentItem: RowLayout {
readonly property var currentItem: combobox.model.getAt(combobox.currentIndex)
spacing: 0// Math.round(5 * DefaultStyle.dp)
Text {
id: selectedItemFlag
visible: text.length > 0
font.pixelSize: Math.round(21 * DefaultStyle.dp)
text: parent.currentItem ? parent.currentItem.flag : ""
font.family: DefaultStyle.flagFont
}
// Rectangle{
// id: mask
// visible: false
// layer.enabled: true
// anchors.centerIn: selectedItemFlag
// radius: Math.round(600 * DefaultStyle.dp)
// width: selectedItemFlag.width/2
// height: selectedItemFlag.height/2
// }
// MultiEffect {
// visible: selectedItemFlag.text.length > 0
// anchors.centerIn: selectedItemFlag
// clip: true
// source: selectedItemFlag
// maskEnabled: true
// width: selectedItemFlag.width/2
// height: selectedItemFlag.height/2
// maskSource: mask
// }
Text {
id: countryCallingcode
text: parent.currentItem ? "+" + parent.currentItem.countryCallingCode : ""
color: DefaultStyle.main2_600
elide: Text.ElideRight
font: Typography.p1
}
}
Control.ComboBox {
id: combobox
currentIndex: phoneNumberModel.count > 0 ? Math.max(0, phoneNumberModel.findIndexByCountryCallingCode(defaultCallingCode)) : -1
Accessible.name: mainItem.Accessible.name
model: PhoneNumberProxy {
id: phoneNumberModel
}
background: Rectangle {
implicitWidth: mainItem.implicitWidth
implicitHeight: mainItem.implicitHeight
radius: Math.round(63 * DefaultStyle.dp)
color: mainItem.enableBackgroundColor ? DefaultStyle.grey_100 : "transparent"
border.color: mainItem.enableBackgroundColors
? (mainItem.errorMessage.length > 0
? DefaultStyle.danger_500_main
: textField.activeFocus
? DefaultStyle.main1_500_main
: DefaultStyle.grey_200)
: "transparent"
}
contentItem: Item {
readonly property var currentItem: combobox.model.getAt(combobox.currentIndex)
anchors.leftMargin: Math.round(15 * DefaultStyle.dp)
Text {
id: selectedItemFlag
visible: text.length > 0
font.pixelSize: Math.round(21 * DefaultStyle.dp)
text: parent.currentItem ? parent.currentItem.flag : ""
font.family: DefaultStyle.flagFont
anchors.rightMargin: Math.round(5 * DefaultStyle.dp)
anchors.verticalCenter: parent.verticalCenter
}
// Rectangle{
// id: mask
// visible: false
// layer.enabled: true
// anchors.centerIn: selectedItemFlag
// radius: Math.round(600 * DefaultStyle.dp)
// width: selectedItemFlag.width/2
// height: selectedItemFlag.height/2
// }
// MultiEffect {
// visible: selectedItemFlag.text.length > 0
// anchors.centerIn: selectedItemFlag
// clip: true
// source: selectedItemFlag
// maskEnabled: true
// width: selectedItemFlag.width/2
// height: selectedItemFlag.height/2
// maskSource: mask
// }
Text {
leftPadding: Math.round(5 * DefaultStyle.dp)
text: parent.currentItem ? "+" + parent.currentItem.countryCallingCode : ""
color: DefaultStyle.main2_600
indicator: EffectImage {
id: indicImage
z: 1
anchors.right: parent.right
anchors.rightMargin: Math.round(15 * DefaultStyle.dp)
anchors.verticalCenter: parent.verticalCenter
imageSource: AppIcons.downArrow
width: Math.round(15 * DefaultStyle.dp)
height: Math.round(15 * DefaultStyle.dp)
fillMode: Image.PreserveAspectFit
colorizationColor: mainItem.indicatorColor
}
popup: Control.Popup {
id: listPopup
y: combobox.height - 1
width: Math.round(311 * DefaultStyle.dp)
height: Math.round(250 * DefaultStyle.dp)
contentItem: ListView {
id: listView
clip: true
anchors.fill: parent
model: PhoneNumberProxy{}
currentIndex: combobox.highlightedIndex >= 0 ? combobox.highlightedIndex : 0
keyNavigationEnabled: true
keyNavigationWraps: true
maximumFlickVelocity: 1500
spacing: Math.round(10 * DefaultStyle.dp)
highlight: Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.left: selectedItemFlag.right
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
font: Typography.p1
}
}
indicator: EffectImage {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
imageSource: AppIcons.downArrow
colorizationColor: DefaultStyle.main2_600
}
popup: Control.Popup {
id: listPopup
y: combobox.height - 1
width: Math.round(311 * DefaultStyle.dp)
height: Math.round(250 * DefaultStyle.dp)
contentItem: ListView {
id: listView
clip: true
anchors.fill: parent
model: PhoneNumberProxy{}
currentIndex: combobox.highlightedIndex >= 0 ? combobox.highlightedIndex : 0
keyNavigationEnabled: true
keyNavigationWraps: true
maximumFlickVelocity: 1500
spacing: Math.round(10 * DefaultStyle.dp)
highlight: Rectangle {
anchors.left: parent.left
anchors.right: parent.right
width: listView.width
height: listView.height
color: DefaultStyle.main2_300
// radius: Math.round(15 * DefaultStyle.dp)
}
delegate: Item {
width: listView.width
height: contentLayout.implicitHeight
RowLayout {
id: contentLayout
anchors.fill: parent
anchors.leftMargin: Math.round(20 * DefaultStyle.dp)
spacing: 0
Text {
id: delegateImg
visible: text.length > 0
text: $modelData.flag
font {
pixelSize: Math.round(28 * DefaultStyle.dp)
family: DefaultStyle.flagFont
}
}
Text {
id: countryText
text: $modelData.country
elide: Text.ElideRight
color: DefaultStyle.main2_500_main
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
Rectangle {
id: separator
width: Math.max(Math.round(1 * DefaultStyle.dp), 1)
height: combobox.height / 2
color: DefaultStyle.main2_500_main
}
Text {
text: "+" + $modelData.countryCallingCode
elide: Text.ElideRight
color: DefaultStyle.main2_500_main
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
Item {
Layout.fillWidth: true
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
Rectangle {
anchors.fill: parent
opacity: 0.1
radius: Math.round(15 * DefaultStyle.dp)
color: DefaultStyle.main2_500_main
visible: parent.containsMouse
}
onClicked: {
combobox.currentIndex = index
listPopup.close()
}
}
}
Control.ScrollIndicator.vertical: Control.ScrollIndicator { }
width: listView.width
height: listView.height
color: DefaultStyle.main2_300
// radius: Math.round(15 * DefaultStyle.dp)
}
onOpened: {
listView.positionViewAtIndex(listView.currentIndex, ListView.Center)
}
background: Item {
anchors.fill: parent
Rectangle {
id: popupBg
delegate: Item {
width: listView.width
height: contentLayout.implicitHeight
RowLayout {
id: contentLayout
anchors.fill: parent
radius: Math.round(15 * DefaultStyle.dp)
color: DefaultStyle.grey_100
anchors.leftMargin: Math.round(20 * DefaultStyle.dp)
spacing: Math.round(5 * DefaultStyle.dp)
Text {
id: delegateImg
visible: text.length > 0
text: $modelData.flag
font {
pixelSize: Math.round(28 * DefaultStyle.dp)
family: DefaultStyle.flagFont
}
}
Text {
id: countryText
text: $modelData.country
elide: Text.ElideRight
color: DefaultStyle.main2_500_main
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
Rectangle {
id: separator
width: Math.max(Math.round(1 * DefaultStyle.dp), 1)
height: combobox.height / 2
color: DefaultStyle.main2_500_main
}
Text {
text: "+" + $modelData.countryCallingCode
elide: Text.ElideRight
color: DefaultStyle.main2_500_main
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
Item {
Layout.fillWidth: true
}
}
MultiEffect {
anchors.fill: popupBg
source: popupBg
shadowEnabled: true
shadowColor: DefaultStyle.grey_1000
shadowBlur: 0.1
shadowOpacity: 0.1
MouseArea {
anchors.fill: parent
hoverEnabled: true
Rectangle {
anchors.fill: parent
opacity: 0.1
radius: Math.round(15 * DefaultStyle.dp)
color: DefaultStyle.main2_500_main
visible: parent.containsMouse
}
onClicked: {
combobox.currentIndex = index
listPopup.close()
}
}
}
Control.ScrollIndicator.vertical: Control.ScrollIndicator { }
}
onOpened: {
listView.positionViewAtIndex(listView.currentIndex, ListView.Center)
}
background: Item {
anchors.fill: parent
Rectangle {
id: popupBg
anchors.fill: parent
radius: Math.round(15 * DefaultStyle.dp)
color: DefaultStyle.grey_100
}
MultiEffect {
anchors.fill: popupBg
source: popupBg
shadowEnabled: true
shadowColor: DefaultStyle.grey_1000
shadowBlur: 0.1
shadowOpacity: 0.1
}
}
}
}

View file

@ -149,8 +149,12 @@ ColumnLayout {
button.onClicked: {
console.debug("[CallHistoryLayout.qml] Open conversation")
if (mainItem.specificAddress === "") {
mainWindow.displayChatPage(mainItem.contact.core.defaultAddress)
} else mainWindow.displayChatPage(mainItem.specificAddress)
mainWindow.sendMessageToContact(mainItem.contact)
}
else {
console.log("specific address", mainItem.specificAddress)
mainWindow.displayChatPage(mainItem.specificAddress)
}
}
}
LabelButton {

View file

@ -319,7 +319,7 @@ ListView {
visible: modelData != undefined && modelData?.core.isBasic
Layout.preferredWidth: visible ? 14 * DefaultStyle.dp : 0
Layout.preferredHeight: 14 * DefaultStyle.dp
colorizationColor: DefaultStyle.warning_500_main
colorizationColor: DefaultStyle.warning_700
imageSource: AppIcons.lockSimpleOpen
}
EffectImage {

View file

@ -16,6 +16,7 @@ ListView {
property int lastIndexFoundWithFilter: -1
property real busyIndicatorSize: Math.round(60 * DefaultStyle.dp)
property bool loading: false
property bool isEncrypted: chat && chat.core.isEncrypted
verticalLayoutDirection: ListView.BottomToTop
signal showReactionsForMessageRequested(ChatMessageGui chatMessage)
@ -127,7 +128,7 @@ ListView {
}
footer: Item {
visible: mainItem.chat && !mainItem.loading && mainItem.chat.core.isEncrypted && !eventLogProxy.haveMore
visible: mainItem.chat && !mainItem.loading
height: visible ? headerMessage.height + headerMessage.topMargin + headerMessage.bottomMargin : Math.round(30 * DefaultStyle.dp)
width: headerMessage.width
anchors.horizontalCenter: parent.horizontalCenter
@ -149,24 +150,30 @@ ListView {
EffectImage {
Layout.preferredWidth: Math.round(23 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(23 * DefaultStyle.dp)
imageSource: AppIcons.lockSimple
colorizationColor: DefaultStyle.info_500_main
imageSource: mainItem.isEncrypted ? AppIcons.lockSimple : AppIcons.lockSimpleOpen
colorizationColor: mainItem.isEncrypted ? DefaultStyle.info_500_main : DefaultStyle.warning_700
}
ColumnLayout {
spacing: Math.round(2 * DefaultStyle.dp)
Text {
//: End to end encrypted chat
text: qsTr("chat_message_list_encrypted_header_title")
text: mainItem.isEncrypted
//: End to end encrypted chat
? qsTr("chat_message_list_encrypted_header_title")
//: This conversation is not encrypted !
: qsTr("unencrypted_conversation_warning")
Layout.fillWidth: true
color: DefaultStyle.info_500_main
color: mainItem.isEncrypted ? DefaultStyle.info_500_main : DefaultStyle.warning_700
font {
pixelSize: Typography.p2.pixelSize
weight: Typography.p2.weight
}
}
Text {
//: Les messages de cette conversation sont chiffrés de bout \n en bout. Seul votre correspondant peut les déchiffrer.
text: qsTr("chat_message_list_encrypted_header_message")
text: mainItem.isEncrypted
//: Messages in this conversation are e2e encrypted. \n Only your correspondent can decrypt them.
? qsTr("chat_message_list_encrypted_header_message")
//: Messages are not end to end encrypted, \n may sure you don't share any sensitive information !
: qsTr("chat_message_list_not_encrypted_header_message")
Layout.fillWidth: true
color: DefaultStyle.grey_400
font {
@ -178,7 +185,7 @@ ListView {
}
}
}
footerPositioning: ListView.PullBackFooter
footerPositioning: mainItem.contentHeight > mainItem.height ? ListView.InlineFooter : ListView.PullBackFooter
headerPositioning: ListView.OverlayHeader
header: Control.Control {
visible: composeLayout.composingName !== "" && composeLayout.composingName !== undefined

View file

@ -119,7 +119,7 @@ Item {
width: Math.round(24 * DefaultStyle.dp)
height: Math.round(24 * DefaultStyle.dp)
imageSource: AppIcons.playFill
colorizationColor: DefaultStyle.main2_000
colorizationColor: DefaultStyle.main2_0
}
Text {
z: parent.z + 1

View file

@ -8,6 +8,7 @@ import QtQml
import UtilsCpp
import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
ListView {
id: mainItem
@ -24,6 +25,8 @@ ListView {
spacing: Math.round(8 * DefaultStyle.dp)
highlightFollowsCurrentItem: false
signal meetingDeletionRequested(ConferenceInfoGui confInfo, bool canCancel)
function selectIndex(index){
mainItem.currentIndex = index
@ -163,7 +166,7 @@ ListView {
visible: !mainItem.loading
height: Math.round(63 * DefaultStyle.dp) + (!isFirst && dateDay.visible ? topOffset : 0)
width: mainItem.width
enabled: !isCanceled && haveModel
enabled: haveModel
property var itemGui: $modelData
// Do not use itemAtIndex because of caching items. Using getAt ensure to have a GUI
@ -307,12 +310,42 @@ ListView {
}
}
MouseArea {
id: mouseArea
hoverEnabled: mainItem.hoverEnabled
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
cursorShape: itemDelegate.isCanceled ? Qt.ArrowCursor : Qt.PointingHandCursor
visible: itemDelegate.haveModel
onClicked: {
mainItem.selectIndex(index)
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
console.log("clicked", mouse.button)
if (mouse.button === Qt.RightButton) {
console.log("open popup")
deletePopup.x = mouse.x
deletePopup.y = mouse.y
deletePopup.open()
}
else if (!itemDelegate.isCanceled) mainItem.selectIndex(index)
}
Popup {
id: deletePopup
parent: mouseArea
padding: Utils.getSizeWithScreenRatio(10)
closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnPressOutside | Popup.CloseOnEscape
contentItem: IconLabelButton {
style: ButtonStyle.hoveredBackgroundRed
property var isMeObj: UtilsCpp.isMe(itemDelegate.itemGui?.core?.organizerAddress)
property bool canCancel: isMeObj && isMeObj.value && itemDelegate.itemGui?.core?.state !== LinphoneEnums.ConferenceInfoState.Cancelled
icon.source: AppIcons.trashCan
//: "Supprimer la réunion"
text: qsTr("meeting_info_delete")
onClicked: {
if (itemDelegate.itemGui) {
mainItem.meetingDeletionRequested(itemDelegate.itemGui, canCancel)
deletePopup.close()
}
}
}
}
}
}

View file

@ -277,7 +277,7 @@ Control.Control {
Rectangle {
id: hoverContent
anchors.fill: parent
color: DefaultStyle.main2_000
color: DefaultStyle.main2_0
visible: false
radius: Math.round(20 * DefaultStyle.dp)

View file

@ -1,5 +1,5 @@
import QtQuick
import QtQuick.Controls.Basic
import QtQuick.Controls.Basic as Control
import QtQuick.Layouts
import Linphone
import CustomControls 1.0
@ -15,10 +15,12 @@ ColumnLayout {
property real textInputWidth: width
property string initialPhoneNumber
readonly property string phoneNumber: textField.text
readonly property string countryCode: combobox.currentText
readonly property string countryCode: combobox.text
property string defaultCallingCode
property bool keyboardFocus: FocusHelper.keyboardFocus
spacing: Math.round(5 * DefaultStyle.dp)
Text {
visible: label.length > 0
verticalAlignment: Text.AlignVCenter
@ -30,13 +32,13 @@ ColumnLayout {
}
}
Item {
Layout.preferredWidth: contentBackground.width
Layout.preferredHeight: contentBackground.height
Rectangle {
Control.Control {
Layout.preferredWidth: mainItem.width
Layout.preferredHeight: Math.round(49 * DefaultStyle.dp)
leftPadding: Math.round(16 * DefaultStyle.dp)
background: Rectangle {
id: contentBackground
width: mainItem.textInputWidth
height: Math.round(49 * DefaultStyle.dp)
anchors.fill: parent
radius: Math.round(63 * DefaultStyle.dp)
color: DefaultStyle.grey_100
border.color: mainItem.errorMessage.length > 0
@ -44,52 +46,51 @@ ColumnLayout {
: (textField.hasActiveFocus || combobox.hasActiveFocus)
? DefaultStyle.main1_500_main
: DefaultStyle.grey_200
RowLayout {
anchors.fill: parent
CountryIndicatorCombobox {
id: combobox
implicitWidth: Math.round(110 * DefaultStyle.dp)
defaultCallingCode: mainItem.defaultCallingCode
property bool keyboardFocus: FocusHelper.keyboardFocus
//: %1 prefix
Accessible.name: qsTr("prefix_phone_number_accessible_name").arg(mainItem.Accessible.name)
}
Rectangle {
Layout.preferredWidth: Math.max(Math.round(1 * DefaultStyle.dp), 1)
Layout.fillHeight: true
Layout.topMargin: Math.round(10 * DefaultStyle.dp)
Layout.bottomMargin: Math.round(10 * DefaultStyle.dp)
color: DefaultStyle.main2_600
}
TextField {
id: textField
Layout.fillWidth: true
placeholderText: mainItem.placeholderText
background: Item{}
initialText: initialPhoneNumber
validator: RegularExpressionValidator{ regularExpression: /[0-9]+/}
property bool keyboardFocus: FocusHelper.keyboardFocus
//: %1 number
Accessible.name: qsTr("number_phone_number_accessible_name").arg(mainItem.Accessible.name)
}
}
contentItem: RowLayout {
CountryIndicatorCombobox {
id: combobox
implicitWidth: Math.round(110 * DefaultStyle.dp)
Layout.fillHeight: true
defaultCallingCode: mainItem.defaultCallingCode
property bool keyboardFocus: FocusHelper.keyboardFocus
//: %1 prefix
Accessible.name: qsTr("prefix_phone_number_accessible_name").arg(mainItem.Accessible.name)
}
Rectangle {
Layout.preferredWidth: Math.max(Math.round(1 * DefaultStyle.dp), 1)
Layout.fillHeight: true
Layout.topMargin: Math.round(10 * DefaultStyle.dp)
Layout.bottomMargin: Math.round(10 * DefaultStyle.dp)
color: DefaultStyle.main2_600
}
TextField {
id: textField
Layout.fillWidth: true
placeholderText: mainItem.placeholderText
background: Item{}
initialText: initialPhoneNumber
validator: RegularExpressionValidator{ regularExpression: /[0-9]+/}
property bool keyboardFocus: FocusHelper.keyboardFocus
//: %1 number
Accessible.name: qsTr("number_phone_number_accessible_name").arg(mainItem.Accessible.name)
}
}
TemporaryText {
id: errorText
anchors.top: contentBackground.bottom
// visible: mainItem.enableErrorText
text: mainItem.errorMessage
color: DefaultStyle.danger_500_main
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
wrapMode: Text.Wrap
font {
pixelSize: Math.round(13 * DefaultStyle.dp)
family: DefaultStyle.defaultFont
bold: true
}
Layout.preferredWidth: mainItem.textInputWidth
// Layout.preferredWidth: implicitWidth
}
TemporaryText {
id: errorText
Layout.fillWidth: true
Layout.topMargin: Math.round(-3 * DefaultStyle.dp)
// visible: mainItem.enableErrorText
text: mainItem.errorMessage
color: DefaultStyle.danger_500_main
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
wrapMode: Text.Wrap
font {
pixelSize: Math.round(13 * DefaultStyle.dp)
family: DefaultStyle.defaultFont
bold: true
}
}
}

View file

@ -102,14 +102,23 @@ FocusScope {
capitalization: Font.Capitalize
}
}
EffectImage {
RowLayout {
visible: mainItem.chat != undefined && mainItem.chat.core.isBasic
Layout.preferredWidth: visible ? 14 * DefaultStyle.dp : 0
Layout.preferredHeight: 14 * DefaultStyle.dp
colorizationColor: DefaultStyle.warning_500_main
imageSource: AppIcons.lockSimpleOpen
spacing: Math.round(8 * DefaultStyle.dp)
EffectImage {
Layout.preferredWidth: visible ? 14 * DefaultStyle.dp : 0
Layout.preferredHeight: 14 * DefaultStyle.dp
colorizationColor: DefaultStyle.warning_700
imageSource: AppIcons.lockSimpleOpen
}
Text {
Layout.fillWidth: true
color: DefaultStyle.warning_700
//: This conversation is not encrypted !
text: qsTr("unencrypted_conversation_warning")
font: Typography.p2
}
}
Layout.fillWidth: true
EffectImage {
visible: mainItem.chat?.core.muted || false
Layout.preferredWidth: 20 * DefaultStyle.dp

View file

@ -321,9 +321,9 @@ LoginLayout {
console.log("[RegisterPage] User: Call register")
mainItem.browserValidationRequested()
if (bar.currentIndex === 0)
RegisterPageCpp.registerNewAccount(usernameInput.text, pwdInput.text, "", phoneNumberInput.completePhoneNumber)
RegisterPageCpp.registerNewAccount(usernameInput.text, pwdInput.text, "", phoneNumberInput.countryCode, phoneNumberInput.phoneNumber)
else
RegisterPageCpp.registerNewAccount(usernameInput.text, pwdInput.text, emailInput.text, "")
RegisterPageCpp.registerNewAccount(usernameInput.text, pwdInput.text, emailInput.text)
}
}
}

View file

@ -153,7 +153,7 @@ Control.Page {
padding: 0
contentItem: Loader {
id: contentLoader
width: contentcontrol.width - contentControl.rightPadding
width: contentControl.width - contentControl.rightPadding
}
}
}

View file

@ -411,7 +411,7 @@ FriendGui{
button.icon.height: Math.round(24 * DefaultStyle.dp)
button.onClicked: {
console.debug("[ContactLayout.qml] Open conversation")
mainWindow.displayChatPage(contactDetail.contact.core.defaultAddress)
mainWindow.sendMessageToContact(contactDetail.contact)
}
}
LabelButton {

View file

@ -81,6 +81,7 @@ AbstractMainPage {
Dialog {
id: cancelAndDeleteConfDialog
property ConferenceInfoGui confInfoToDelete
property bool cancel: false
signal cancelRequested()
// width: Math.round(278 * DefaultStyle.dp)
@ -88,6 +89,13 @@ AbstractMainPage {
text: cancel ? qsTr("meeting_schedule_cancel_dialog_message")
//: Souhaitez-vous supprimer cette réunion ?
: qsTr("meeting_schedule_delete_dialog_message")
onCancelRequested: {
confInfoToDelete.core.lCancelConferenceInfo()
}
onAccepted: {
confInfoToDelete.core.lDeleteConferenceInfo()
}
buttons: [
BigButton {
visible: cancelAndDeleteConfDialog.cancel
@ -261,6 +269,11 @@ AbstractMainPage {
onSelectedConferenceChanged: {
mainItem.selectedConference = selectedConference
}
onMeetingDeletionRequested: (confInfo, canCancel) => {
cancelAndDeleteConfDialog.confInfoToDelete = confInfo
cancelAndDeleteConfDialog.cancel = canCancel
cancelAndDeleteConfDialog.open()
}
Keys.onPressed: (event) => {
if(event.key == Qt.Key_Escape){
@ -691,21 +704,12 @@ AbstractMainPage {
onClicked: {
if (mainItem.selectedConference) {
cancelAndDeleteConfDialog.confInfoToDelete = mainItem.selectedConference
cancelAndDeleteConfDialog.cancel = canCancel
cancelAndDeleteConfDialog.open()
// mainItem.contactDeletionRequested(mainItem.selectedConference)
deletePopup.close()
}
}
Connections {
target: cancelAndDeleteConfDialog
function onCancelRequested() {
mainItem.selectedConference.core.lCancelConferenceInfo()
}
function onAccepted() {
mainItem.selectedConference.core.lDeleteConferenceInfo()
}
}
}
}
}

View file

@ -51,96 +51,92 @@ ApplicationWindow {
}
}
Popup {
id: startCallPopup
property FriendGui contact
property bool videoEnabled
// if currentCall, transfer call to contact
property CallGui currentCall
onContactChanged: {
console.log("contact changed", contact)
}
underlineColor: DefaultStyle.main1_500_main
anchors.centerIn: parent
width: Math.round(370 * DefaultStyle.dp)
modal: true
leftPadding: Math.round(15 * DefaultStyle.dp)
rightPadding: Math.round(15 * DefaultStyle.dp)
topPadding: Math.round(20 * DefaultStyle.dp)
bottomPadding: Math.round(25 * DefaultStyle.dp)
contentItem: ColumnLayout {
spacing: Math.round(16 * DefaultStyle.dp)
RowLayout {
spacing: Math.round(5 * DefaultStyle.dp)
width: startCallPopup.width
Text {
//: "Choisissez un numéro ou adresse SIP"
text: qsTr("contact_dialog_pick_phone_number_or_sip_address_title")
wrapMode: Text.Wrap
Component {
id: addressChooserPopupComp
Popup {
id: addressChooserPopup
property FriendGui contact
signal addressChosen(string address)
underlineColor: DefaultStyle.main1_500_main
anchors.centerIn: parent
width: Math.round(370 * DefaultStyle.dp)
modal: true
leftPadding: Math.round(15 * DefaultStyle.dp)
rightPadding: Math.round(15 * DefaultStyle.dp)
topPadding: Math.round(20 * DefaultStyle.dp)
bottomPadding: Math.round(25 * DefaultStyle.dp)
contentItem: ColumnLayout {
spacing: Math.round(16 * DefaultStyle.dp)
RowLayout {
spacing: Math.round(5 * DefaultStyle.dp)
width: addressChooserPopup.width
Text {
//: "Choisissez un numéro ou adresse SIP"
text: qsTr("contact_dialog_pick_phone_number_or_sip_address_title")
wrapMode: Text.Wrap
Layout.fillWidth: true
font {
pixelSize: Typography.h4.pixelSize
weight: Typography.h4.weight
}
}
RoundButton {
Layout.alignment: Qt.AlignVCenter
style: ButtonStyle.noBackground
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
icon.source:AppIcons.closeX
onClicked: addressChooserPopup.close()
}
}
ListView {
id: popuplist
model: VariantList {
model: addressChooserPopup.contact && addressChooserPopup.contact.core.allAddresses || []
}
Layout.fillWidth: true
font {
pixelSize: Typography.h4.pixelSize
weight: Typography.h4.weight
}
}
RoundButton {
Layout.alignment: Qt.AlignVCenter
style: ButtonStyle.noBackground
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
icon.source:AppIcons.closeX
onClicked: startCallPopup.close()
}
}
ListView {
id: popuplist
model: VariantList {
model: startCallPopup.contact && startCallPopup.contact.core.allAddresses || []
}
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
spacing: Math.round(10 * DefaultStyle.dp)
delegate: Item {
width: popuplist.width
height: Math.round(56 * DefaultStyle.dp)
ColumnLayout {
Layout.preferredHeight: contentHeight
spacing: Math.round(10 * DefaultStyle.dp)
delegate: Item {
width: popuplist.width
anchors.verticalCenter: parent.verticalCenter
spacing: Math.round(10 * DefaultStyle.dp)
height: Math.round(56 * DefaultStyle.dp)
ColumnLayout {
spacing: Math.round(7 * DefaultStyle.dp)
Text {
Layout.leftMargin: Math.round(5 * DefaultStyle.dp)
text: modelData.label + " :"
font {
pixelSize: Typography.p2.pixelSize
weight: Typography.p2.weight
width: popuplist.width
anchors.verticalCenter: parent.verticalCenter
spacing: Math.round(10 * DefaultStyle.dp)
ColumnLayout {
spacing: Math.round(7 * DefaultStyle.dp)
Text {
Layout.leftMargin: Math.round(5 * DefaultStyle.dp)
text: modelData.label + " :"
font {
pixelSize: Typography.p2.pixelSize
weight: Typography.p2.weight
}
}
Text {
Layout.leftMargin: Math.round(5 * DefaultStyle.dp)
text: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(modelData.address) : modelData.address
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
}
Text {
Layout.leftMargin: Math.round(5 * DefaultStyle.dp)
text: SettingsCpp.hideSipAddresses ? UtilsCpp.getUsername(modelData.address) : modelData.address
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
Rectangle {
visible: index != popuplist.model.count - 1
Layout.fillWidth: true
Layout.preferredHeight: Math.max(Math.round(1 * DefaultStyle.dp), 1)
color: DefaultStyle.main2_200
}
}
Rectangle {
visible: index != popuplist.model.count - 1
Layout.fillWidth: true
Layout.preferredHeight: Math.max(Math.round(1 * DefaultStyle.dp), 1)
color: DefaultStyle.main2_200
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (startCallPopup.currentCall) startCallPopup.currentCall.core.lTransferCall(modelData.address)
else UtilsCpp.createCall(modelData.address, {'localVideoEnabled': startCallPopup.videoEnabled})
startCallPopup.close()
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
addressChooserPopup.addressChosen(modelData.address)
}
}
}
}
@ -150,13 +146,17 @@ ApplicationWindow {
function startCallWithContact(contact, videoEnabled, parentItem) {
if (parentItem == undefined) parentItem = mainWindow.contentItem
startCallPopup.parent = parentItem
if (contact) {
console.log("START CALL WITH", contact.core.fullName, "addresses count", contact.core.allAddresses.length)
if (contact.core.allAddresses.length > 1) {
startCallPopup.contact = contact
startCallPopup.videoEnabled = videoEnabled
startCallPopup.open()
var addressPopup = addressChooserPopupComp.createObject()
addressPopup.parent = parentItem
addressPopup.contact = contact
addressPopup.addressChosen.connect(function(address) {
UtilsCpp.createCall(address, {'localVideoEnabled': videoEnabled})
addressPopup.close()
})
addressPopup.open()
} else {
var addressToCall = contact.core.defaultAddress.length === 0
@ -169,16 +169,41 @@ ApplicationWindow {
}
}
function sendMessageToContact(contact, parentItem) {
if (parentItem == undefined) parentItem = mainWindow.contentItem
if (contact) {
console.log("SEND MESSAGE TO", contact.core.fullName, "addresses count", contact.core.allAddresses.length)
if (contact.core.allAddresses.length > 1) {
var addressPopup = addressChooserPopupComp.createObject()
addressPopup.parent = parentItem
addressPopup.contact = contact
addressPopup.addressChosen.connect(function(address) {
displayChatPage(address)
addressPopup.close()
})
addressPopup.open()
} else {
displayChatPage(contact.core.defaultAddress)
if (addressToCall.length != 0) UtilsCpp.createCall(addressToCall, {'localVideoEnabled':videoEnabled})
}
}
}
function transferCallToContact(call, contact, parentItem) {
if (!call || !contact) return
if (parentItem == undefined) parentItem = mainWindow.contentItem
startCallPopup.parent = parentItem
if (contact) {
console.log("[AbstractWindow] Transfer call to", contact.core.fullName, "addresses count", contact.core.allAddresses.length, call)
if (contact.core.allAddresses.length > 1) {
startCallPopup.contact = contact
startCallPopup.currentCall = call
startCallPopup.open()
var addressPopup = addressChooserPopupComp.createObject()
addressPopup.parent = parentItem
addressPopup.contact = contact
addressPopup.addressChosen.connect(function(address) {
call.core.lTransferCall(address)
addressPopup.close()
})
addressPopup.open()
} else {
var addressToCall = contact.core.defaultAddress.length === 0

View file

@ -218,6 +218,8 @@ AbstractWindow {
onHaveCallChanged: {
if (!haveCall) {
mainWindow.callEnded()
} else {
bottomButtonsLayout.setButtonsEnabled(true)
}
}
}
@ -1277,7 +1279,7 @@ AbstractWindow {
}
function setButtonsEnabled(enabled) {
for (var i = 0; i < children.length; ++i) {
children[i].enabled = false
children[i].enabled = enabled
}
}

View file

@ -15,7 +15,7 @@ QtObject {
property color main1_600: currentTheme.main600
property color main1_700: currentTheme.main700
property color main2_000: "#FAFEFF"
property color main2_0: "#FAFEFF"
property color main2_100: "#EEF6F8"
property color main2_200: "#DFECF2"
property color main2_300: "#C0D1D9"
@ -38,6 +38,7 @@ QtObject {
property color grey_1000: "#000000"
property color warning_600: "#DBB820"
property color warning_700: "#AF9308"
property color danger_500_main: "#DD5F5F"
property color warning_500_main: "#FFDC2E"
property color danger_700: "#9E3548"

View file

@ -21,7 +21,7 @@
}
var mainLightBorder = Object.assign({
borderColor : {
keybaordFocused: Linphone.DefaultStyle.main2_000
keybaordFocused: Linphone.DefaultStyle.main2_0
}
}, main)
@ -116,7 +116,7 @@
}
var phoneRedLightBorder = Object.assign({
borderColor : {
keybaordFocused: Linphone.DefaultStyle.main2_000
keybaordFocused: Linphone.DefaultStyle.main2_0
}
}, phoneRed)
@ -151,7 +151,7 @@
pressed: Linphone.DefaultStyle.grey_0
},
borderColor: {
keybaordFocused: Linphone.DefaultStyle.main2_000
keybaordFocused: Linphone.DefaultStyle.main2_0
},
image: {
normal: Linphone.DefaultStyle.grey_0,
@ -205,7 +205,7 @@
}
var noBackgroundLightBorder = Object.assign({
borderColor : {
keybaordFocused: Linphone.DefaultStyle.main2_000
keybaordFocused: Linphone.DefaultStyle.main2_0
}
}, noBackground)