1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-07-19 06:55:24 +02:00

link-device: apply new design and add QR code

GitLab: #1253
Change-Id: Ib47a081e4e5d714e98fb397732ff9232b62afc36
This commit is contained in:
cberthet 2023-09-20 12:10:12 -04:00 committed by Capucine Berthet
parent 63c3d0bf2e
commit ad35d108f2
8 changed files with 271 additions and 98 deletions

View file

@ -436,7 +436,7 @@ Item {
// LinkedDevices
property string tipLinkNewDevice: qsTr("Link a new device to this account")
property string linkNewDevice: qsTr("Exporting account…")
property string linkDevice: qsTr("Exporting account…")
property string removeDevice: qsTr("Remove Device")
property string sureToRemoveDevice: qsTr("Are you sure you wish to remove this device?")
property string yourPinIs: qsTr("Your PIN is:")
@ -575,13 +575,20 @@ Item {
// LinkDevicesDialog
property string pinTimerInfos: qsTr("The PIN and the account password should be entered in your device within 10 minutes.")
property string close: qsTr("Close")
property string enterAccountPassword: qsTr("Enter account's password")
property string enterAccountPassword: qsTr("Enter account password")
property string enterPasswordPinCode: qsTr("This account is password encrypted, enter the password to generate a PIN code.")
property string addDevice: qsTr("Add Device")
property string pinExpired: qsTr("PIN expired")
property string onAnotherDevice: qsTr("On another device")
property string onAnotherDeviceInstruction: qsTr("Install and launch Jami, select \"Import from another device\" and scan the QR code.")
property string linkNewDevice: qsTr("Link new device")
property string linkingInstructions: qsTr("In Jami, scan QR code or manually enter the PIN.")
property string pinValidity: qsTr("The PIN code is valid for: ")
// PasswordDialog
property string enterPassword: qsTr("Enter the password")
property string enterPassword: qsTr("Enter password")
property string enterCurrentPassword: qsTr("Enter current password")
property string confirmRemoval: qsTr("Enter this account's password to confirm the removal of this device")
property string confirmRemoval: qsTr("Enter account password to confirm the removal of this device")
property string enterNewPassword: qsTr("Enter new password")
property string confirmNewPassword: qsTr("Confirm new password")
property string change: qsTr("Change")

View file

@ -643,6 +643,9 @@ Item {
property int settingsMenuHeaderButtonHeight: 50
property int settingsListViewsSpacing: 10
// Link Device
property color pinBackgroundColor: "#D6E4EF"
// MaterialRadioButton
property int radioImageSize: 30
property color radioBackgroundColor: darkTheme ? "#303030" : "#F0EFEF"

View file

@ -33,7 +33,7 @@ public:
instance)
{}
enum class QrType { Account, Contact };
enum class QrType { Account, Contact, Raw };
/*
* Id should be string like account_0 (account index),
@ -64,6 +64,8 @@ public:
} catch (...) {
}
return {QrType::Contact, {}};
} else if (list.contains("raw") && list.size() > 1) {
return {QrType::Raw, list[1]};
}
return {QrType::Account, {}};
}
@ -73,26 +75,24 @@ public:
Q_UNUSED(size);
QString uri;
auto indexPair = getIndexFromID(id);
auto [type, identifier] = getIndexFromID(id);
if (indexPair.first == QrType::Contact) {
uri = indexPair.second;
} else {
if (indexPair.second.isEmpty())
if (type == QrType::Account) {
if (identifier.isEmpty())
return QImage();
auto accountId = indexPair.second;
try {
auto& accountInfo = lrcInstance_->getAccountInfo(accountId);
auto& accountInfo = lrcInstance_->getAccountInfo(identifier);
uri = accountInfo.profileInfo.uri;
} catch (const std::out_of_range&) {
qWarning() << "Couldn't get account info for id:" << accountId;
qWarning() << "Couldn't get account info for id:" << identifier;
return QImage();
}
}
} else
uri = identifier;
if (!requestedSize.isEmpty())
return Utils::setupQRCode(uri, 0).scaled(requestedSize, Qt::KeepAspectRatio);
return Utils::getQRCodeImage(uri, 0).scaled(requestedSize, Qt::KeepAspectRatio);
else
return Utils::setupQRCode(uri, 0);
return Utils::getQRCodeImage(uri, 0);
}
};

View file

@ -23,13 +23,16 @@ import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
import "../../mainview/components"
BaseModalDialog {
id: root
signal accepted
title: JamiStrings.addDevice
title: JamiStrings.linkNewDevice
property bool darkTheme: UtilsAdapter.useApplicationTheme()
popupContent: StackLayout {
id: stackedWidget
@ -47,13 +50,11 @@ BaseModalDialog {
function setExportPage(status, pin) {
if (status === NameDirectory.ExportOnRingStatus.SUCCESS) {
infoLabel.success = true;
yourPinLabel.visible = true;
exportedPIN.visible = true;
infoLabel.text = JamiStrings.pinTimerInfos;
pinRectangle.visible = true
exportedPIN.text = pin;
} else {
infoLabel.success = false;
infoLabelsRowLayout.visible = false;
pinRectangle.success = false;
infoLabel.visible = true;
switch (status) {
case NameDirectory.ExportOnRingStatus.WRONG_PASSWORD:
infoLabel.text = JamiStrings.incorrectPassword;
@ -86,15 +87,14 @@ BaseModalDialog {
function onExportOnRingEnded(status, pin) {
stackedWidget.setExportPage(status, pin);
countdownTimer.start();
}
}
onVisibleChanged: {
if (visible) {
infoLabel.text = JamiStrings.pinTimerInfos;
if (CurrentAccount.hasArchivePassword) {
stackedWidget.currentIndex = enterPasswordPage.pageIndex;
passwordEdit.forceActiveFocus();
} else {
setGeneratingPage();
}
@ -107,14 +107,23 @@ BaseModalDialog {
readonly property int pageIndex: 0
Component.onCompleted: passwordEdit.forceActiveFocus()
onHeightChanged: {
stackedWidget.height = passwordLayout.implicitHeight
}
ColumnLayout {
id: passwordLayout
spacing: JamiTheme.preferredMarginSize
anchors.centerIn: parent
Label {
Layout.alignment: Qt.AlignCenter
Layout.maximumWidth: root.width - 4 * JamiTheme.preferredMarginSize
wrapMode: Text.Wrap
text: JamiStrings.enterAccountPassword
text: JamiStrings.enterPasswordPinCode
color: JamiTheme.textColor
font.pointSize: JamiTheme.textFontSize
font.kerning: true
@ -122,70 +131,48 @@ BaseModalDialog {
verticalAlignment: Text.AlignVCenter
}
PasswordTextEdit {
id: passwordEdit
firstEntry: true
placeholderText: JamiStrings.password
RowLayout {
Layout.topMargin: 10
Layout.leftMargin: JamiTheme.cornerIconSize
Layout.rightMargin: JamiTheme.cornerIconSize
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
KeyNavigation.up: btnConfirm
KeyNavigation.down: KeyNavigation.up
onDynamicTextChanged: {
btnConfirm.enabled = dynamicText.length > 0;
}
onAccepted: btnConfirm.clicked()
}
RowLayout {
Layout.alignment: Qt.AlignCenter
spacing: JamiTheme.preferredMarginSize
Layout.bottomMargin: JamiTheme.preferredMarginSize
spacing: 16
MaterialButton {
PasswordTextEdit {
id: passwordEdit
firstEntry: true
placeholderText: JamiStrings.password
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
KeyNavigation.up: btnConfirm
KeyNavigation.down: KeyNavigation.up
onDynamicTextChanged: {
btnConfirm.enabled = dynamicText.length > 0;
btnConfirm.hoverEnabled = dynamicText.length > 0;
}
onAccepted: btnConfirm.clicked()
}
JamiPushButton {
id: btnConfirm
Layout.alignment: Qt.AlignHCenter
Layout.alignment: Qt.AlignCenter
height: 40
width: 40
preferredSize: 60
preferredWidth: JamiTheme.preferredFieldWidth / 2 - 8
buttontextHeightMargin: JamiTheme.buttontextHeightMargin
color: enabled ? JamiTheme.buttonTintedBlack : JamiTheme.buttonTintedGrey
hoveredColor: JamiTheme.buttonTintedBlackHovered
pressedColor: JamiTheme.buttonTintedBlackPressed
secondary: true
autoAccelerator: true
hoverEnabled: false
enabled: false
text: JamiStrings.exportAccount
imageColor: JamiTheme.tintedBlue
source: JamiResources.check_box_24dp_svg
onClicked: stackedWidget.setGeneratingPage()
}
MaterialButton {
id: btnCancel
Layout.alignment: Qt.AlignHCenter
preferredWidth: JamiTheme.preferredFieldWidth / 2 - 8
buttontextHeightMargin: JamiTheme.buttontextHeightMargin
color: JamiTheme.buttonTintedBlack
hoveredColor: JamiTheme.buttonTintedBlackHovered
pressedColor: JamiTheme.buttonTintedBlackPressed
secondary: true
autoAccelerator: true
enabled: true
text: JamiStrings.optionCancel
onClicked: close()
}
}
}
@ -197,6 +184,11 @@ BaseModalDialog {
readonly property int pageIndex: 1
onHeightChanged: {
stackedWidget.height = spinnerLayout.implicitHeight
}
ColumnLayout {
id: spinnerLayout
@ -206,7 +198,7 @@ BaseModalDialog {
Label {
Layout.alignment: Qt.AlignCenter
text: JamiStrings.linkNewDevice
text: JamiStrings.linkDevice
color: JamiTheme.textColor
font.pointSize: JamiTheme.headerFontSize
font.kerning: true
@ -246,40 +238,171 @@ BaseModalDialog {
ColumnLayout {
id: exportingLayout
spacing: JamiTheme.preferredMarginSize
Label {
id: yourPinLabel
id: instructionLabel
Layout.maximumWidth: JamiTheme.preferredDialogWidth
Layout.alignment: Qt.AlignCenter
text: JamiStrings.yourPinIs
color: JamiTheme.textColor
font.pointSize: JamiTheme.headerFontSize
padding: 8
wrapMode: Text.Wrap
text: JamiStrings.linkingInstructions
font.pointSize: JamiTheme.textFontSize
font.kerning: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
MaterialLineEdit {
id: exportedPIN
padding: 0
Rectangle {
Layout.alignment: Qt.AlignCenter
text: JamiStrings.pin
wrapMode: Text.NoWrap
border.width: 3
border.color: JamiTheme.textColor
radius: JamiTheme.primaryRadius
color: darkTheme ? JamiTheme.textColor : JamiTheme.secondaryBackgroundColor
width: 170
height: 170
color: JamiTheme.textColor
selectByMouse: true
readOnly: true
font.pointSize: JamiTheme.headerFontSize
font.kerning: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
Image {
id: qrImage
anchors.fill: parent
anchors.margins: 10
mipmap: false
smooth: false
source: "image://qrImage/raw_" + exportedPIN.text
sourceSize.width: 150
sourceSize.height: 150
}
}
Rectangle {
id: pinRectangle
radius: 15
color: darkTheme ? JamiTheme.tintedBlue : JamiTheme.pinBackgroundColor
width: exportedPIN.implicitWidth + 4 * JamiTheme.preferredMarginSize
height: exportedPIN.implicitHeight + 2 * JamiTheme.preferredMarginSize
Layout.alignment: Qt.AlignCenter
Layout.margins: JamiTheme.preferredMarginSize
MaterialLineEdit {
id: exportedPIN
padding: 0
anchors.centerIn: parent
text: JamiStrings.pin
wrapMode: Text.NoWrap
backgroundColor: darkTheme ? JamiTheme.tintedBlue : JamiTheme.pinBackgroundColor
color: darkTheme ? JamiTheme.textColor : JamiTheme.tintedBlue
selectByMouse: true
readOnly: true
font.pointSize: JamiTheme.headerFontSize
font.kerning: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
RowLayout {
Layout.alignment: Qt.AlignCenter
Layout.bottomMargin: JamiTheme.preferredMarginSize
spacing: 0
Label {
id: validityLabel
Layout.alignment: Qt.AlignRight
color: JamiTheme.textColor
text: JamiStrings.pinValidity
font.pointSize: JamiTheme.textFontSize
font.kerning: true
}
Label {
id: countdownLabel
color: JamiTheme.textColor
Layout.alignment: Qt.AlignLeft
font.pointSize: JamiTheme.textFontSize
font.kerning: true
text: "10:00"
}
Timer {
id: countdownTimer
interval: 1000
repeat: true
property int remainingTime: 600
onTriggered: {
remainingTime--
var minutes = Math.floor(remainingTime / 60)
var seconds = remainingTime % 60
countdownLabel.text = (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds
if (remainingTime <= 0) {
validityLabel.visible = false
countdownLabel.text = JamiStrings.pinExpired
countdownLabel.color = JamiTheme.redColor
countdownTimer.stop()
}
}
}
}
Label {
id: otherDeviceLabel
Layout.alignment: Qt.AlignCenter
color: JamiTheme.textColor
text: JamiStrings.onAnotherDevice
font.pointSize: JamiTheme.smallFontSize
font.kerning: true
font.bold: true
}
Label {
id: otherInstructionLabel
Layout.maximumWidth: JamiTheme.preferredDialogWidth
Layout.bottomMargin: JamiTheme.preferredMarginSize
Layout.alignment: Qt.AlignCenter
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: JamiTheme.textColor
text: JamiStrings.onAnotherDeviceInstruction
font.pointSize: JamiTheme.smallFontSize
font.kerning: true
}
// Displays error messages
Label {
id: infoLabel
visible: false
property bool success: false
property int borderWidth: success ? 1 : 0
property int borderRadius: success ? 15 : 0
@ -295,7 +418,6 @@ BaseModalDialog {
padding: success ? 8 : 0
wrapMode: Text.Wrap
text: JamiStrings.pinTimerInfos
font.pointSize: success ? JamiTheme.textFontSize : JamiTheme.textFontSize + 3
font.kerning: true
horizontalAlignment: Text.AlignHCenter

View file

@ -39,6 +39,47 @@ SettingsPageBase {
anchors.left: parent.left
anchors.leftMargin: JamiTheme.preferredSettingsMarginSize
Text {
id: linkDescription
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
text: JamiStrings.linkDescription
color: JamiTheme.textColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
font.kerning: true
lineHeight: JamiTheme.wizardViewTextLineHeight
}
MaterialButton {
id: linkDeviceBtn
TextMetrics {
id: linkDeviceBtnTextSize
font.weight: Font.Bold
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
text: linkDeviceBtn.text
}
preferredWidth: linkDeviceBtnTextSize.width + 2 * JamiTheme.buttontextWizzardPadding
Layout.bottomMargin: JamiTheme.preferredMarginSize
primary: true
Layout.alignment: Qt.AlignLeft
toolTipText: JamiStrings.tipLinkNewDevice
text: JamiStrings.linkNewDevice
onClicked: viewCoordinator.presentDialog(appWindow, "settingsview/components/LinkDeviceDialog.qml")
}
Text {
id: linkedDevicesTitle

View file

@ -218,7 +218,7 @@ SettingsPageBase {
Layout.alignment: Qt.AlignLeft
toolTipText: JamiStrings.tipLinkNewDevice
text: JamiStrings.linkAnotherDevice
text: JamiStrings.linkNewDevice
onClicked: viewCoordinator.presentDialog(appWindow, "settingsview/components/LinkDeviceDialog.qml")
}

View file

@ -805,9 +805,9 @@ Utils::pixmapFromSvg(const QString& svg_resource, const QSize& size)
}
QImage
Utils::setupQRCode(QString ringID, int margin)
Utils::getQRCodeImage(QString data, int margin)
{
auto qrcode = QRcode_encodeString(ringID.toStdString().c_str(),
auto qrcode = QRcode_encodeString(data.toStdString().c_str(),
0, // Let the version be decided by libqrencode
QR_ECLEVEL_L, // Lowest level of error correction
QR_MODE_8, // 8-bit data mode

View file

@ -119,7 +119,7 @@ QPixmap generateTintedPixmap(const QPixmap& pix, QColor color);
QImage scaleAndFrame(const QImage photo, const QSize& size = defaultAvatarSize);
QImage cropImage(const QImage& img);
QPixmap pixmapFromSvg(const QString& svg_resource, const QSize& size);
QImage setupQRCode(QString ringID, int margin);
QImage getQRCodeImage(QString data, int margin);
bool isImage(const QString& fileExt);
QString generateUid();