// SPDX-FileCopyrightText: 2017 Konstantinos Sideris // SPDX-FileCopyrightText: 2021 Nheko Contributors // SPDX-FileCopyrightText: 2022 Nheko Contributors // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Cache.h" #include "Config.h" #include "MatrixClient.h" #include "UserSettingsPage.h" #include "Utils.h" #include "encryption/Olm.h" #include "ui/FlatButton.h" #include "ui/ToggleButton.h" #include "voip/CallDevices.h" #include "config/nheko.h" QSharedPointer UserSettings::instance_; UserSettings::UserSettings() { connect( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, []() { instance_.clear(); }); } QSharedPointer UserSettings::instance() { return instance_; } void UserSettings::initialize(std::optional profile) { instance_.reset(new UserSettings()); instance_->load(profile); } void UserSettings::load(std::optional profile) { tray_ = settings.value(QStringLiteral("user/window/tray"), false).toBool(); startInTray_ = settings.value(QStringLiteral("user/window/start_in_tray"), false).toBool(); roomListWidth_ = settings.value(QStringLiteral("user/sidebar/room_list_width"), -1).toInt(); communityListWidth_ = settings.value(QStringLiteral("user/sidebar/community_list_width"), -1).toInt(); hasDesktopNotifications_ = settings.value(QStringLiteral("user/desktop_notifications"), true).toBool(); hasAlertOnNotification_ = settings.value(QStringLiteral("user/alert_on_notification"), false).toBool(); groupView_ = settings.value(QStringLiteral("user/group_view"), true).toBool(); buttonsInTimeline_ = settings.value(QStringLiteral("user/timeline/buttons"), true).toBool(); timelineMaxWidth_ = settings.value(QStringLiteral("user/timeline/max_width"), 0).toInt(); messageHoverHighlight_ = settings.value(QStringLiteral("user/timeline/message_hover_highlight"), false).toBool(); enlargeEmojiOnlyMessages_ = settings.value(QStringLiteral("user/timeline/enlarge_emoji_only_msg"), false).toBool(); markdown_ = settings.value(QStringLiteral("user/markdown_enabled"), true).toBool(); animateImagesOnHover_ = settings.value(QStringLiteral("user/animate_images_on_hover"), false).toBool(); typingNotifications_ = settings.value(QStringLiteral("user/typing_notifications"), true).toBool(); sortByImportance_ = settings.value(QStringLiteral("user/sort_by_unread"), true).toBool(); readReceipts_ = settings.value(QStringLiteral("user/read_receipts"), true).toBool(); theme_ = settings.value(QStringLiteral("user/theme"), defaultTheme_).toString(); font_ = settings.value(QStringLiteral("user/font_family"), "default").toString(); avatarCircles_ = settings.value(QStringLiteral("user/avatar_circles"), true).toBool(); useIdenticon_ = settings.value(QStringLiteral("user/use_identicon"), true).toBool(); decryptSidebar_ = settings.value(QStringLiteral("user/decrypt_sidebar"), true).toBool(); privacyScreen_ = settings.value(QStringLiteral("user/privacy_screen"), false).toBool(); privacyScreenTimeout_ = settings.value(QStringLiteral("user/privacy_screen_timeout"), 0).toInt(); mobileMode_ = settings.value(QStringLiteral("user/mobile_mode"), false).toBool(); emojiFont_ = settings.value(QStringLiteral("user/emoji_font_family"), "default").toString(); baseFontSize_ = settings.value(QStringLiteral("user/font_size"), QFont().pointSizeF()).toDouble(); auto tempPresence = settings.value(QStringLiteral("user/presence"), "").toString().toStdString(); auto presenceValue = QMetaEnum::fromType().keyToValue(tempPresence.c_str()); if (presenceValue < 0) presenceValue = 0; presence_ = static_cast(presenceValue); ringtone_ = settings.value(QStringLiteral("user/ringtone"), "Default").toString(); microphone_ = settings.value(QStringLiteral("user/microphone"), QString()).toString(); camera_ = settings.value(QStringLiteral("user/camera"), QString()).toString(); cameraResolution_ = settings.value(QStringLiteral("user/camera_resolution"), QString()).toString(); cameraFrameRate_ = settings.value(QStringLiteral("user/camera_frame_rate"), QString()).toString(); screenShareFrameRate_ = settings.value(QStringLiteral("user/screen_share_frame_rate"), 5).toInt(); screenSharePiP_ = settings.value(QStringLiteral("user/screen_share_pip"), true).toBool(); screenShareRemoteVideo_ = settings.value(QStringLiteral("user/screen_share_remote_video"), false).toBool(); screenShareHideCursor_ = settings.value(QStringLiteral("user/screen_share_hide_cursor"), false).toBool(); useStunServer_ = settings.value(QStringLiteral("user/use_stun_server"), false).toBool(); if (profile) // set to "" if it's the default to maintain compatibility profile_ = (*profile == QLatin1String("default")) ? QLatin1String("") : *profile; else profile_ = settings.value(QStringLiteral("user/currentProfile"), "").toString(); QString prefix = (profile_ != QLatin1String("") && profile_ != QLatin1String("default")) ? "profile/" + profile_ + "/" : QLatin1String(""); accessToken_ = settings.value(prefix + "auth/access_token", "").toString(); homeserver_ = settings.value(prefix + "auth/home_server", "").toString(); userId_ = settings.value(prefix + "auth/user_id", "").toString(); deviceId_ = settings.value(prefix + "auth/device_id", "").toString(); hiddenTags_ = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList(); hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList(); recentReactions_ = settings.value(prefix + "user/recent_reactions", QStringList{}).toStringList(); collapsedSpaces_.clear(); auto tempSpaces = settings.value(prefix + "user/collapsed_spaces", QList{}).toList(); for (const auto &e : qAsConst(tempSpaces)) collapsedSpaces_.push_back(e.toStringList()); shareKeysWithTrustedUsers_ = settings.value(prefix + "user/automatically_share_keys_with_trusted_users", false).toBool(); onlyShareKeysWithVerifiedUsers_ = settings.value(prefix + "user/only_share_keys_with_verified_users", false).toBool(); useOnlineKeyBackup_ = settings.value(prefix + "user/online_key_backup", false).toBool(); disableCertificateValidation_ = settings.value(QStringLiteral("disable_certificate_validation"), false).toBool(); applyTheme(); } void UserSettings::setMessageHoverHighlight(bool state) { if (state == messageHoverHighlight_) return; messageHoverHighlight_ = state; emit messageHoverHighlightChanged(state); save(); } void UserSettings::setEnlargeEmojiOnlyMessages(bool state) { if (state == enlargeEmojiOnlyMessages_) return; enlargeEmojiOnlyMessages_ = state; emit enlargeEmojiOnlyMessagesChanged(state); save(); } void UserSettings::setTray(bool state) { if (state == tray_) return; tray_ = state; emit trayChanged(state); save(); } void UserSettings::setStartInTray(bool state) { if (state == startInTray_) return; startInTray_ = state; emit startInTrayChanged(state); save(); } void UserSettings::setMobileMode(bool state) { if (state == mobileMode_) return; mobileMode_ = state; emit mobileModeChanged(state); save(); } void UserSettings::setGroupView(bool state) { if (groupView_ == state) return; groupView_ = state; emit groupViewStateChanged(state); save(); } void UserSettings::setHiddenTags(QStringList hiddenTags) { hiddenTags_ = hiddenTags; save(); } void UserSettings::setHiddenPins(QStringList hiddenTags) { hiddenPins_ = hiddenTags; save(); emit hiddenPinsChanged(); } void UserSettings::setRecentReactions(QStringList recent) { recentReactions_ = recent; save(); emit recentReactionsChanged(); } void UserSettings::setCollapsedSpaces(QList spaces) { collapsedSpaces_ = spaces; save(); } void UserSettings::setMarkdown(bool state) { if (state == markdown_) return; markdown_ = state; emit markdownChanged(state); save(); } void UserSettings::setAnimateImagesOnHover(bool state) { if (state == animateImagesOnHover_) return; animateImagesOnHover_ = state; emit animateImagesOnHoverChanged(state); save(); } void UserSettings::setReadReceipts(bool state) { if (state == readReceipts_) return; readReceipts_ = state; emit readReceiptsChanged(state); save(); } void UserSettings::setTypingNotifications(bool state) { if (state == typingNotifications_) return; typingNotifications_ = state; emit typingNotificationsChanged(state); save(); } void UserSettings::setSortByImportance(bool state) { if (state == sortByImportance_) return; sortByImportance_ = state; emit roomSortingChanged(state); save(); } void UserSettings::setButtonsInTimeline(bool state) { if (state == buttonsInTimeline_) return; buttonsInTimeline_ = state; emit buttonInTimelineChanged(state); save(); } void UserSettings::setTimelineMaxWidth(int state) { if (state == timelineMaxWidth_) return; timelineMaxWidth_ = state; emit timelineMaxWidthChanged(state); save(); } void UserSettings::setCommunityListWidth(int state) { if (state == communityListWidth_) return; communityListWidth_ = state; emit communityListWidthChanged(state); save(); } void UserSettings::setRoomListWidth(int state) { if (state == roomListWidth_) return; roomListWidth_ = state; emit roomListWidthChanged(state); save(); } void UserSettings::setDesktopNotifications(bool state) { if (state == hasDesktopNotifications_) return; hasDesktopNotifications_ = state; emit desktopNotificationsChanged(state); save(); } void UserSettings::setAlertOnNotification(bool state) { if (state == hasAlertOnNotification_) return; hasAlertOnNotification_ = state; emit alertOnNotificationChanged(state); save(); } void UserSettings::setAvatarCircles(bool state) { if (state == avatarCircles_) return; avatarCircles_ = state; emit avatarCirclesChanged(state); save(); } void UserSettings::setDecryptSidebar(bool state) { if (state == decryptSidebar_) return; decryptSidebar_ = state; emit decryptSidebarChanged(state); save(); } void UserSettings::setPrivacyScreen(bool state) { if (state == privacyScreen_) { return; } privacyScreen_ = state; emit privacyScreenChanged(state); save(); } void UserSettings::setPrivacyScreenTimeout(int state) { if (state == privacyScreenTimeout_) { return; } privacyScreenTimeout_ = state; emit privacyScreenTimeoutChanged(state); save(); } void UserSettings::setFontSize(double size) { if (size == baseFontSize_) return; baseFontSize_ = size; emit fontSizeChanged(size); save(); } void UserSettings::setFontFamily(QString family) { if (family == font_) return; font_ = family; emit fontChanged(family); save(); } void UserSettings::setEmojiFontFamily(QString family) { if (family == emojiFont_) return; if (family == tr("Default")) { emojiFont_ = QStringLiteral("default"); } else { emojiFont_ = family; } emit emojiFontChanged(family); save(); } void UserSettings::setPresence(Presence state) { if (state == presence_) return; presence_ = state; emit presenceChanged(state); save(); } void UserSettings::setTheme(QString theme) { if (theme == theme_) return; theme_ = theme; save(); applyTheme(); emit themeChanged(theme); } void UserSettings::setUseStunServer(bool useStunServer) { if (useStunServer == useStunServer_) return; useStunServer_ = useStunServer; emit useStunServerChanged(useStunServer); save(); } void UserSettings::setOnlyShareKeysWithVerifiedUsers(bool shareKeys) { if (shareKeys == onlyShareKeysWithVerifiedUsers_) return; onlyShareKeysWithVerifiedUsers_ = shareKeys; emit onlyShareKeysWithVerifiedUsersChanged(shareKeys); save(); } void UserSettings::setShareKeysWithTrustedUsers(bool shareKeys) { if (shareKeys == shareKeysWithTrustedUsers_) return; shareKeysWithTrustedUsers_ = shareKeys; emit shareKeysWithTrustedUsersChanged(shareKeys); save(); } void UserSettings::setUseOnlineKeyBackup(bool useBackup) { if (useBackup == useOnlineKeyBackup_) return; useOnlineKeyBackup_ = useBackup; emit useOnlineKeyBackupChanged(useBackup); save(); } void UserSettings::setRingtone(QString ringtone) { if (ringtone == ringtone_) return; ringtone_ = ringtone; emit ringtoneChanged(ringtone); save(); } void UserSettings::setMicrophone(QString microphone) { if (microphone == microphone_) return; microphone_ = microphone; emit microphoneChanged(microphone); save(); } void UserSettings::setCamera(QString camera) { if (camera == camera_) return; camera_ = camera; emit cameraChanged(camera); save(); } void UserSettings::setCameraResolution(QString resolution) { if (resolution == cameraResolution_) return; cameraResolution_ = resolution; emit cameraResolutionChanged(resolution); save(); } void UserSettings::setCameraFrameRate(QString frameRate) { if (frameRate == cameraFrameRate_) return; cameraFrameRate_ = frameRate; emit cameraFrameRateChanged(frameRate); save(); } void UserSettings::setScreenShareFrameRate(int frameRate) { if (frameRate == screenShareFrameRate_) return; screenShareFrameRate_ = frameRate; emit screenShareFrameRateChanged(frameRate); save(); } void UserSettings::setScreenSharePiP(bool state) { if (state == screenSharePiP_) return; screenSharePiP_ = state; emit screenSharePiPChanged(state); save(); } void UserSettings::setScreenShareRemoteVideo(bool state) { if (state == screenShareRemoteVideo_) return; screenShareRemoteVideo_ = state; emit screenShareRemoteVideoChanged(state); save(); } void UserSettings::setScreenShareHideCursor(bool state) { if (state == screenShareHideCursor_) return; screenShareHideCursor_ = state; emit screenShareHideCursorChanged(state); save(); } void UserSettings::setProfile(QString profile) { if (profile == profile_) return; profile_ = profile; emit profileChanged(profile_); save(); } void UserSettings::setUserId(QString userId) { if (userId == userId_) return; userId_ = userId; emit userIdChanged(userId_); save(); } void UserSettings::setAccessToken(QString accessToken) { if (accessToken == accessToken_) return; accessToken_ = accessToken; emit accessTokenChanged(accessToken_); save(); } void UserSettings::setDeviceId(QString deviceId) { if (deviceId == deviceId_) return; deviceId_ = deviceId; emit deviceIdChanged(deviceId_); save(); } void UserSettings::setHomeserver(QString homeserver) { if (homeserver == homeserver_) return; homeserver_ = homeserver; emit homeserverChanged(homeserver_); save(); } void UserSettings::setDisableCertificateValidation(bool disabled) { if (disabled == disableCertificateValidation_) return; disableCertificateValidation_ = disabled; http::client()->verify_certificates(!disabled); emit disableCertificateValidationChanged(disabled); } void UserSettings::setUseIdenticon(bool state) { if (state == useIdenticon_) return; useIdenticon_ = state; emit useIdenticonChanged(useIdenticon_); save(); } void UserSettings::applyTheme() { QFile stylefile; if (this->theme() == QLatin1String("light")) { stylefile.setFileName(QStringLiteral(":/styles/styles/nheko.qss")); } else if (this->theme() == QLatin1String("dark")) { stylefile.setFileName(QStringLiteral(":/styles/styles/nheko-dark.qss")); } else { stylefile.setFileName(QStringLiteral(":/styles/styles/system.qss")); } QApplication::setPalette(Theme::paletteFromTheme(this->theme())); stylefile.open(QFile::ReadOnly); QString stylesheet = QString(stylefile.readAll()); qobject_cast(QApplication::instance())->setStyleSheet(stylesheet); } void UserSettings::save() { settings.beginGroup(QStringLiteral("user")); settings.beginGroup(QStringLiteral("window")); settings.setValue(QStringLiteral("tray"), tray_); settings.setValue(QStringLiteral("start_in_tray"), startInTray_); settings.endGroup(); // window settings.beginGroup(QStringLiteral("sidebar")); settings.setValue(QStringLiteral("community_list_width"), communityListWidth_); settings.setValue(QStringLiteral("room_list_width"), roomListWidth_); settings.endGroup(); // window settings.beginGroup(QStringLiteral("timeline")); settings.setValue(QStringLiteral("buttons"), buttonsInTimeline_); settings.setValue(QStringLiteral("message_hover_highlight"), messageHoverHighlight_); settings.setValue(QStringLiteral("enlarge_emoji_only_msg"), enlargeEmojiOnlyMessages_); settings.setValue(QStringLiteral("max_width"), timelineMaxWidth_); settings.endGroup(); // timeline settings.setValue(QStringLiteral("avatar_circles"), avatarCircles_); settings.setValue(QStringLiteral("decrypt_sidebar"), decryptSidebar_); settings.setValue(QStringLiteral("privacy_screen"), privacyScreen_); settings.setValue(QStringLiteral("privacy_screen_timeout"), privacyScreenTimeout_); settings.setValue(QStringLiteral("mobile_mode"), mobileMode_); settings.setValue(QStringLiteral("font_size"), baseFontSize_); settings.setValue(QStringLiteral("typing_notifications"), typingNotifications_); settings.setValue(QStringLiteral("sort_by_unread"), sortByImportance_); settings.setValue(QStringLiteral("minor_events"), sortByImportance_); settings.setValue(QStringLiteral("read_receipts"), readReceipts_); settings.setValue(QStringLiteral("group_view"), groupView_); settings.setValue(QStringLiteral("markdown_enabled"), markdown_); settings.setValue(QStringLiteral("animate_images_on_hover"), animateImagesOnHover_); settings.setValue(QStringLiteral("desktop_notifications"), hasDesktopNotifications_); settings.setValue(QStringLiteral("alert_on_notification"), hasAlertOnNotification_); settings.setValue(QStringLiteral("theme"), theme()); settings.setValue(QStringLiteral("font_family"), font_); settings.setValue(QStringLiteral("emoji_font_family"), emojiFont_); settings.setValue( QStringLiteral("presence"), QString::fromUtf8(QMetaEnum::fromType().valueToKey(static_cast(presence_)))); settings.setValue(QStringLiteral("ringtone"), ringtone_); settings.setValue(QStringLiteral("microphone"), microphone_); settings.setValue(QStringLiteral("camera"), camera_); settings.setValue(QStringLiteral("camera_resolution"), cameraResolution_); settings.setValue(QStringLiteral("camera_frame_rate"), cameraFrameRate_); settings.setValue(QStringLiteral("screen_share_frame_rate"), screenShareFrameRate_); settings.setValue(QStringLiteral("screen_share_pip"), screenSharePiP_); settings.setValue(QStringLiteral("screen_share_remote_video"), screenShareRemoteVideo_); settings.setValue(QStringLiteral("screen_share_hide_cursor"), screenShareHideCursor_); settings.setValue(QStringLiteral("use_stun_server"), useStunServer_); settings.setValue(QStringLiteral("currentProfile"), profile_); settings.setValue(QStringLiteral("use_identicon"), useIdenticon_); settings.endGroup(); // user QString prefix = (profile_ != QLatin1String("") && profile_ != QLatin1String("default")) ? "profile/" + profile_ + "/" : QLatin1String(""); settings.setValue(prefix + "auth/access_token", accessToken_); settings.setValue(prefix + "auth/home_server", homeserver_); settings.setValue(prefix + "auth/user_id", userId_); settings.setValue(prefix + "auth/device_id", deviceId_); settings.setValue(prefix + "user/automatically_share_keys_with_trusted_users", shareKeysWithTrustedUsers_); settings.setValue(prefix + "user/only_share_keys_with_verified_users", onlyShareKeysWithVerifiedUsers_); settings.setValue(prefix + "user/online_key_backup", useOnlineKeyBackup_); settings.setValue(prefix + "user/hidden_tags", hiddenTags_); settings.setValue(prefix + "user/hidden_pins", hiddenPins_); settings.setValue(prefix + "user/recent_reactions", recentReactions_); QVariantList v; v.reserve(collapsedSpaces_.size()); for (const auto &e : qAsConst(collapsedSpaces_)) v.push_back(e); settings.setValue(prefix + "user/collapsed_spaces", v); settings.setValue(QStringLiteral("disable_certificate_validation"), disableCertificateValidation_); settings.sync(); } HorizontalLine::HorizontalLine(QWidget *parent) : QFrame{parent} { setFrameShape(QFrame::HLine); setFrameShadow(QFrame::Sunken); } UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidget *parent) : QWidget{parent} , settings_{settings} { topLayout_ = new QVBoxLayout{this}; QIcon icon; icon.addFile(QStringLiteral(":/icons/icons/ui/angle-arrow-left.svg")); auto backBtn_ = new FlatButton{this}; backBtn_->setMinimumSize(QSize(24, 24)); backBtn_->setIcon(icon); backBtn_->setIconSize(QSize(24, 24)); QFont font; font.setPointSizeF(font.pointSizeF() * 1.1); auto versionInfo = new QLabel(QStringLiteral("%1 | %2").arg(nheko::version, nheko::build_os)); if (QCoreApplication::applicationName() != QLatin1String("nheko")) versionInfo->setText(versionInfo->text() + " | " + tr("profile: %1").arg(QCoreApplication::applicationName())); versionInfo->setTextInteractionFlags(Qt::TextBrowserInteraction); topBarLayout_ = new QHBoxLayout; topBarLayout_->setSpacing(0); topBarLayout_->setContentsMargins(0, 0, 0, 0); topBarLayout_->addWidget(backBtn_, 1, Qt::AlignLeft | Qt::AlignVCenter); topBarLayout_->addStretch(1); formLayout_ = new QFormLayout; formLayout_->setLabelAlignment(Qt::AlignLeft); formLayout_->setFormAlignment(Qt::AlignRight); formLayout_->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); formLayout_->setRowWrapPolicy(QFormLayout::WrapLongRows); formLayout_->setHorizontalSpacing(0); auto general_ = new QLabel{tr("GENERAL"), this}; general_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); general_->setFont(font); trayToggle_ = new Toggle{this}; startInTrayToggle_ = new Toggle{this}; avatarCircles_ = new Toggle{this}; useIdenticon_ = new Toggle{this}; decryptSidebar_ = new Toggle(this); privacyScreen_ = new Toggle{this}; onlyShareKeysWithVerifiedUsers_ = new Toggle(this); shareKeysWithTrustedUsers_ = new Toggle(this); useOnlineKeyBackup_ = new Toggle(this); groupViewToggle_ = new Toggle{this}; timelineButtonsToggle_ = new Toggle{this}; typingNotifications_ = new Toggle{this}; messageHoverHighlight_ = new Toggle{this}; enlargeEmojiOnlyMessages_ = new Toggle{this}; sortByImportance_ = new Toggle{this}; readReceipts_ = new Toggle{this}; markdown_ = new Toggle{this}; animateImagesOnHover_ = new Toggle{this}; desktopNotifications_ = new Toggle{this}; alertOnNotification_ = new Toggle{this}; useStunServer_ = new Toggle{this}; mobileMode_ = new Toggle{this}; scaleFactorCombo_ = new QComboBox{this}; fontSizeCombo_ = new QComboBox{this}; fontSelectionCombo_ = new QFontComboBox{this}; emojiFontSelectionCombo_ = new QComboBox{this}; ringtoneCombo_ = new QComboBox{this}; microphoneCombo_ = new QComboBox{this}; cameraCombo_ = new QComboBox{this}; cameraResolutionCombo_ = new QComboBox{this}; cameraFrameRateCombo_ = new QComboBox{this}; timelineMaxWidthSpin_ = new QSpinBox{this}; privacyScreenTimeout_ = new QSpinBox{this}; trayToggle_->setChecked(settings_->tray()); startInTrayToggle_->setChecked(settings_->startInTray()); avatarCircles_->setChecked(settings_->avatarCircles()); useIdenticon_->setChecked(settings_->useIdenticon()); decryptSidebar_->setChecked(settings_->decryptSidebar()); privacyScreen_->setChecked(settings_->privacyScreen()); onlyShareKeysWithVerifiedUsers_->setChecked(settings_->onlyShareKeysWithVerifiedUsers()); shareKeysWithTrustedUsers_->setChecked(settings_->shareKeysWithTrustedUsers()); useOnlineKeyBackup_->setChecked(settings_->useOnlineKeyBackup()); groupViewToggle_->setChecked(settings_->groupView()); timelineButtonsToggle_->setChecked(settings_->buttonsInTimeline()); typingNotifications_->setChecked(settings_->typingNotifications()); messageHoverHighlight_->setChecked(settings_->messageHoverHighlight()); enlargeEmojiOnlyMessages_->setChecked(settings_->enlargeEmojiOnlyMessages()); sortByImportance_->setChecked(settings_->sortByImportance()); readReceipts_->setChecked(settings_->readReceipts()); markdown_->setChecked(settings_->markdown()); animateImagesOnHover_->setChecked(settings_->animateImagesOnHover()); desktopNotifications_->setChecked(settings_->hasDesktopNotifications()); alertOnNotification_->setChecked(settings_->hasAlertOnNotification()); useStunServer_->setChecked(settings_->useStunServer()); mobileMode_->setChecked(settings_->mobileMode()); if (!settings_->tray()) { startInTrayToggle_->setState(false); startInTrayToggle_->setDisabled(true); } if (!settings_->privacyScreen()) { privacyScreenTimeout_->setDisabled(true); } avatarCircles_->setFixedSize(64, 48); auto uiLabel_ = new QLabel{tr("INTERFACE"), this}; uiLabel_->setFixedHeight(uiLabel_->minimumHeight() + LayoutTopMargin); uiLabel_->setAlignment(Qt::AlignBottom); uiLabel_->setFont(font); for (double option = 1; option <= 3; option += 0.25) scaleFactorCombo_->addItem(QString::number(option)); for (double option = 6; option <= 24; option += 0.5) fontSizeCombo_->addItem(QStringLiteral("%1 ").arg(QString::number(option))); QFontDatabase fontDb; // TODO: Is there a way to limit to just emojis, rather than // all emoji fonts? auto emojiFamilies = fontDb.families(QFontDatabase::Symbol); emojiFontSelectionCombo_->addItem(tr("Default")); for (const auto &family : emojiFamilies) { emojiFontSelectionCombo_->addItem(family); } QString currentFont = settings_->font(); if (currentFont != QLatin1String("default") || currentFont != QLatin1String("")) { fontSelectionCombo_->setCurrentIndex(fontSelectionCombo_->findText(currentFont)); } emojiFontSelectionCombo_->setCurrentIndex( emojiFontSelectionCombo_->findText(settings_->emojiFont())); themeCombo_ = new QComboBox{this}; themeCombo_->addItem(QStringLiteral("Light")); themeCombo_->addItem(QStringLiteral("Dark")); themeCombo_->addItem(QStringLiteral("System")); QString themeStr = settings_->theme(); themeStr.replace(0, 1, themeStr[0].toUpper()); int themeIndex = themeCombo_->findText(themeStr); themeCombo_->setCurrentIndex(themeIndex); timelineMaxWidthSpin_->setMinimum(0); timelineMaxWidthSpin_->setMaximum(100'000'000); timelineMaxWidthSpin_->setSingleStep(10); privacyScreenTimeout_->setMinimum(0); privacyScreenTimeout_->setMaximum(3600); privacyScreenTimeout_->setSingleStep(10); auto callsLabel = new QLabel{tr("CALLS"), this}; callsLabel->setFixedHeight(callsLabel->minimumHeight() + LayoutTopMargin); callsLabel->setAlignment(Qt::AlignBottom); callsLabel->setFont(font); auto encryptionLabel_ = new QLabel{tr("ENCRYPTION"), this}; encryptionLabel_->setFixedHeight(encryptionLabel_->minimumHeight() + LayoutTopMargin); encryptionLabel_->setAlignment(Qt::AlignBottom); encryptionLabel_->setFont(font); QFont monospaceFont; monospaceFont.setFamily(QStringLiteral("Monospace")); monospaceFont.setStyleHint(QFont::Monospace); monospaceFont.setPointSizeF(monospaceFont.pointSizeF() * 0.9); deviceIdValue_ = new QLabel{this}; deviceIdValue_->setTextInteractionFlags(Qt::TextSelectableByMouse); deviceIdValue_->setFont(monospaceFont); deviceFingerprintValue_ = new QLabel{this}; deviceFingerprintValue_->setTextInteractionFlags(Qt::TextSelectableByMouse); deviceFingerprintValue_->setFont(monospaceFont); deviceFingerprintValue_->setText(utils::humanReadableFingerprint(QString(44, 'X'))); backupSecretCached = new QLabel{this}; masterSecretCached = new QLabel{this}; selfSigningSecretCached = new QLabel{this}; userSigningSecretCached = new QLabel{this}; backupSecretCached->setFont(monospaceFont); masterSecretCached->setFont(monospaceFont); selfSigningSecretCached->setFont(monospaceFont); userSigningSecretCached->setFont(monospaceFont); auto sessionKeysLabel = new QLabel{tr("Session Keys"), this}; sessionKeysLabel->setFont(font); sessionKeysLabel->setMargin(OptionMargin); auto sessionKeysImportBtn = new QPushButton{tr("IMPORT"), this}; auto sessionKeysExportBtn = new QPushButton{tr("EXPORT"), this}; auto sessionKeysLayout = new QHBoxLayout; sessionKeysLayout->addWidget(new QLabel{QLatin1String(""), this}, 1, Qt::AlignRight); sessionKeysLayout->addWidget(sessionKeysExportBtn, 0, Qt::AlignRight); sessionKeysLayout->addWidget(sessionKeysImportBtn, 0, Qt::AlignRight); auto crossSigningKeysLabel = new QLabel{tr("Cross Signing Keys"), this}; crossSigningKeysLabel->setFont(font); crossSigningKeysLabel->setMargin(OptionMargin); auto crossSigningRequestBtn = new QPushButton{tr("REQUEST"), this}; auto crossSigningDownloadBtn = new QPushButton{tr("DOWNLOAD"), this}; auto crossSigningKeysLayout = new QHBoxLayout; crossSigningKeysLayout->addWidget(new QLabel{QLatin1String(""), this}, 1, Qt::AlignRight); crossSigningKeysLayout->addWidget(crossSigningRequestBtn, 0, Qt::AlignRight); crossSigningKeysLayout->addWidget(crossSigningDownloadBtn, 0, Qt::AlignRight); auto boxWrap = [this, &font](QString labelText, QWidget *field, QString tooltipText = QLatin1String("")) { auto label = new QLabel{labelText, this}; label->setFont(font); label->setMargin(OptionMargin); if (!tooltipText.isEmpty()) { label->setToolTip(tooltipText); } auto layout = new QHBoxLayout; layout->addWidget(field, 0, Qt::AlignRight); formLayout_->addRow(label, layout); }; formLayout_->addRow(general_); formLayout_->addRow(new HorizontalLine{this}); boxWrap(tr("Minimize to tray"), trayToggle_, tr("Keep the application running in the background after closing the client window.")); boxWrap(tr("Start in tray"), startInTrayToggle_, tr("Start the application in the background without showing the client window.")); formLayout_->addRow(new HorizontalLine{this}); boxWrap(tr("Circular Avatars"), avatarCircles_, tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle.")); boxWrap(tr("Use identicons"), useIdenticon_, tr("Display an identicon instead of a letter when no avatar is set.")); boxWrap(tr("Group's sidebar"), groupViewToggle_, tr("Show a column containing groups and tags next to the room list.")); boxWrap(tr("Decrypt messages in sidebar"), decryptSidebar_, tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in " "encrypted chats.")); boxWrap(tr("Privacy Screen"), privacyScreen_, tr("When the window loses focus, the timeline will\nbe blurred.")); boxWrap(tr("Privacy screen timeout (in seconds [0 - 3600])"), privacyScreenTimeout_, tr("Set timeout (in seconds) for how long after window loses\nfocus before the screen" " will be blurred.\nSet to 0 to blur immediately after focus loss. Max value of 1 " "hour (3600 seconds)")); boxWrap(tr("Show buttons in timeline"), timelineButtonsToggle_, tr("Show buttons to quickly reply, react or access additional options next to each " "message.")); boxWrap(tr("Limit width of timeline"), timelineMaxWidthSpin_, tr("Set the max width of messages in the timeline (in pixels). This can help " "readability on wide screen, when Nheko is maximised")); boxWrap(tr("Typing notifications"), typingNotifications_, tr("Show who is typing in a room.\nThis will also enable or disable sending typing " "notifications to others.")); boxWrap( tr("Sort rooms by unreads"), sortByImportance_, tr("Display rooms with new messages first.\nIf this is off, the list of rooms will only " "be sorted by the timestamp of the last message in a room.\nIf this is on, rooms which " "have active notifications (the small circle with a number in it) will be sorted on " "top. Rooms, that you have muted, will still be sorted by timestamp, since you don't " "seem to consider them as important as the other rooms.")); formLayout_->addRow(new HorizontalLine{this}); boxWrap(tr("Read receipts"), readReceipts_, tr("Show if your message was read.\nStatus is displayed next to timestamps.")); boxWrap(tr("Send messages as Markdown"), markdown_, tr("Allow using markdown in messages.\nWhen disabled, all messages are sent as a plain " "text.")); boxWrap(tr("Play animated images only on hover"), animateImagesOnHover_, tr("Plays media like GIFs or WEBPs only when explicitly hovering over them.")); boxWrap(tr("Desktop notifications"), desktopNotifications_, tr("Notify about received message when the client is not currently focused.")); boxWrap(tr("Alert on notification"), alertOnNotification_, tr("Show an alert when a message is received.\nThis usually causes the application " "icon in the task bar to animate in some fashion.")); boxWrap(tr("Highlight message on hover"), messageHoverHighlight_, tr("Change the background color of messages when you hover over them.")); boxWrap(tr("Large Emoji in timeline"), enlargeEmojiOnlyMessages_, tr("Make font size larger if messages with only a few emojis are displayed.")); formLayout_->addRow(uiLabel_); formLayout_->addRow(new HorizontalLine{this}); boxWrap(tr("Touchscreen mode"), mobileMode_, tr("Will prevent text selection in the timeline to make touch scrolling easier.")); #if !defined(Q_OS_MAC) boxWrap(tr("Scale factor"), scaleFactorCombo_, tr("Change the scale factor of the whole user interface.")); #else scaleFactorCombo_->hide(); #endif boxWrap(tr("Font size"), fontSizeCombo_); boxWrap(tr("Font Family"), fontSelectionCombo_); #if !defined(Q_OS_MAC) boxWrap(tr("Emoji Font Family"), emojiFontSelectionCombo_); #else emojiFontSelectionCombo_->hide(); #endif boxWrap(tr("Theme"), themeCombo_); formLayout_->addRow(callsLabel); formLayout_->addRow(new HorizontalLine{this}); boxWrap(tr("Ringtone"), ringtoneCombo_, tr("Set the notification sound to play when a call invite arrives")); boxWrap(tr("Microphone"), microphoneCombo_); boxWrap(tr("Camera"), cameraCombo_); boxWrap(tr("Camera resolution"), cameraResolutionCombo_); boxWrap(tr("Camera frame rate"), cameraFrameRateCombo_); ringtoneCombo_->setSizeAdjustPolicy(QComboBox::AdjustToContents); ringtoneCombo_->addItem(QStringLiteral("Mute")); ringtoneCombo_->addItem(QStringLiteral("Default")); ringtoneCombo_->addItem(QStringLiteral("Other...")); const QString &ringtone = settings_->ringtone(); if (!ringtone.isEmpty() && ringtone != QLatin1String("Mute") && ringtone != QLatin1String("Default")) ringtoneCombo_->addItem(ringtone); microphoneCombo_->setSizeAdjustPolicy(QComboBox::AdjustToContents); cameraCombo_->setSizeAdjustPolicy(QComboBox::AdjustToContents); cameraResolutionCombo_->setSizeAdjustPolicy(QComboBox::AdjustToContents); cameraFrameRateCombo_->setSizeAdjustPolicy(QComboBox::AdjustToContents); boxWrap(tr("Allow fallback call assist server"), useStunServer_, tr("Will use turn.matrix.org as assist when your home server does not offer one.")); formLayout_->addRow(encryptionLabel_); formLayout_->addRow(new HorizontalLine{this}); boxWrap(tr("Device ID"), deviceIdValue_); boxWrap(tr("Device Fingerprint"), deviceFingerprintValue_); boxWrap(tr("Send encrypted messages to verified users only"), onlyShareKeysWithVerifiedUsers_, tr("Requires a user to be verified to send encrypted messages to them. This " "improves safety but makes E2EE more tedious.")); boxWrap(tr("Share keys with verified users and devices"), shareKeysWithTrustedUsers_, tr("Automatically replies to key requests from other users, if they are verified, " "even if that device shouldn't have access to those keys otherwise.")); boxWrap(tr("Online Key Backup"), useOnlineKeyBackup_, tr("Download message encryption keys from and upload to the encrypted online key " "backup.")); formLayout_->addRow(new HorizontalLine{this}); formLayout_->addRow(sessionKeysLabel, sessionKeysLayout); formLayout_->addRow(crossSigningKeysLabel, crossSigningKeysLayout); boxWrap(tr("Master signing key"), masterSecretCached, tr("Your most important key. You don't need to have it cached, since not caching " "it makes it less likely it can be stolen and it is only needed to rotate your " "other signing keys.")); boxWrap(tr("User signing key"), userSigningSecretCached, tr("The key to verify other users. If it is cached, verifying a user will verify " "all their devices.")); boxWrap(tr("Self signing key"), selfSigningSecretCached, tr("The key to verify your own devices. If it is cached, verifying one of your devices " "will mark it verified for all your other devices and for users, that have verified " "you.")); boxWrap(tr("Backup key"), backupSecretCached, tr("The key to decrypt online key backups. If it is cached, you can enable online " "key backup to store encryption keys securely encrypted on the server.")); // updateSecretStatus(); auto scrollArea_ = new QScrollArea{this}; scrollArea_->setFrameShape(QFrame::NoFrame); scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea_->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); scrollArea_->setWidgetResizable(true); scrollArea_->setAlignment(Qt::AlignTop | Qt::AlignVCenter); QScroller::grabGesture(scrollArea_, QScroller::TouchGesture); auto spacingAroundForm = new QHBoxLayout; spacingAroundForm->addStretch(1); spacingAroundForm->addLayout(formLayout_, 0); spacingAroundForm->addStretch(1); auto scrollAreaContents_ = new QWidget{this}; scrollAreaContents_->setObjectName(QStringLiteral("UserSettingScrollWidget")); scrollAreaContents_->setLayout(spacingAroundForm); scrollArea_->setWidget(scrollAreaContents_); topLayout_->addLayout(topBarLayout_); topLayout_->addWidget(scrollArea_, Qt::AlignTop); topLayout_->addStretch(1); topLayout_->addWidget(versionInfo); connect(themeCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString &text) { settings_->setTheme(text.toLower()); emit themeChanged(); }); connect(scaleFactorCombo_, static_cast(&QComboBox::currentTextChanged), this, [](const QString &factor) { utils::setScaleFactor(factor.toFloat()); }); connect(fontSizeCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString &size) { settings_->setFontSize(size.trimmed().toDouble()); }); connect(fontSelectionCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString &family) { settings_->setFontFamily(family.trimmed()); }); connect(emojiFontSelectionCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString &family) { settings_->setEmojiFontFamily(family.trimmed()); }); connect( ringtoneCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString &ringtone) { if (ringtone == QLatin1String("Other...")) { QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); auto filepath = QFileDialog::getOpenFileName( this, tr("Select a file"), homeFolder, tr("All Files (*)")); if (!filepath.isEmpty()) { const auto &oldSetting = settings_->ringtone(); if (oldSetting != QLatin1String("Mute") && oldSetting != QLatin1String("Default")) ringtoneCombo_->removeItem(ringtoneCombo_->findText(oldSetting)); settings_->setRingtone(filepath); ringtoneCombo_->addItem(filepath); ringtoneCombo_->setCurrentText(filepath); } else { ringtoneCombo_->setCurrentText(settings_->ringtone()); } } else if (ringtone == QLatin1String("Mute") || ringtone == QLatin1String("Default")) { const auto &oldSetting = settings_->ringtone(); if (oldSetting != QLatin1String("Mute") && oldSetting != QLatin1String("Default")) ringtoneCombo_->removeItem(ringtoneCombo_->findText(oldSetting)); settings_->setRingtone(ringtone); } }); connect(microphoneCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString µphone) { settings_->setMicrophone(microphone); }); connect(cameraCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString &camera) { settings_->setCamera(camera); std::vector resolutions = CallDevices::instance().resolutions(camera.toStdString()); cameraResolutionCombo_->clear(); for (const auto &resolution : resolutions) cameraResolutionCombo_->addItem(QString::fromStdString(resolution)); }); connect(cameraResolutionCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString &resolution) { settings_->setCameraResolution(resolution); std::vector frameRates = CallDevices::instance().frameRates( settings_->camera().toStdString(), resolution.toStdString()); cameraFrameRateCombo_->clear(); for (const auto &frameRate : frameRates) cameraFrameRateCombo_->addItem(QString::fromStdString(frameRate)); }); connect(cameraFrameRateCombo_, static_cast(&QComboBox::currentTextChanged), this, [this](const QString &frameRate) { settings_->setCameraFrameRate(frameRate); }); connect(trayToggle_, &Toggle::toggled, this, [this](bool enabled) { settings_->setTray(enabled); if (enabled) { startInTrayToggle_->setChecked(false); startInTrayToggle_->setEnabled(true); startInTrayToggle_->setState(false); settings_->setStartInTray(false); } else { startInTrayToggle_->setChecked(false); startInTrayToggle_->setState(false); startInTrayToggle_->setDisabled(true); settings_->setStartInTray(false); } emit trayOptionChanged(enabled); }); connect(startInTrayToggle_, &Toggle::toggled, this, [this](bool enabled) { settings_->setStartInTray(enabled); }); connect(mobileMode_, &Toggle::toggled, this, [this](bool enabled) { settings_->setMobileMode(enabled); }); connect(groupViewToggle_, &Toggle::toggled, this, [this](bool enabled) { settings_->setGroupView(enabled); }); connect(decryptSidebar_, &Toggle::toggled, this, [this](bool enabled) { settings_->setDecryptSidebar(enabled); }); connect(privacyScreen_, &Toggle::toggled, this, [this](bool enabled) { settings_->setPrivacyScreen(enabled); if (enabled) { privacyScreenTimeout_->setEnabled(true); } else { privacyScreenTimeout_->setDisabled(true); } }); connect(onlyShareKeysWithVerifiedUsers_, &Toggle::toggled, this, [this](bool enabled) { settings_->setOnlyShareKeysWithVerifiedUsers(enabled); }); connect(shareKeysWithTrustedUsers_, &Toggle::toggled, this, [this](bool enabled) { settings_->setShareKeysWithTrustedUsers(enabled); }); connect(useOnlineKeyBackup_, &Toggle::toggled, this, [this](bool enabled) { if (enabled) { if (QMessageBox::question( this, tr("Enable online key backup"), tr("The Nheko authors recommend not enabling online key backup until " "symmetric online key backup is available. Enable anyway?")) != QMessageBox::StandardButton::Yes) { useOnlineKeyBackup_->setState(false); return; } } settings_->setUseOnlineKeyBackup(enabled); }); connect(avatarCircles_, &Toggle::toggled, this, [this](bool enabled) { settings_->setAvatarCircles(enabled); }); if (JdenticonProvider::isAvailable()) connect(useIdenticon_, &Toggle::toggled, this, [this](bool enabled) { settings_->setUseIdenticon(enabled); }); else useIdenticon_->setDisabled(true); connect( markdown_, &Toggle::toggled, this, [this](bool enabled) { settings_->setMarkdown(enabled); }); connect(animateImagesOnHover_, &Toggle::toggled, this, [this](bool enabled) { settings_->setAnimateImagesOnHover(enabled); }); connect(typingNotifications_, &Toggle::toggled, this, [this](bool enabled) { settings_->setTypingNotifications(enabled); }); connect(sortByImportance_, &Toggle::toggled, this, [this](bool enabled) { settings_->setSortByImportance(enabled); }); connect(timelineButtonsToggle_, &Toggle::toggled, this, [this](bool enabled) { settings_->setButtonsInTimeline(enabled); }); connect(readReceipts_, &Toggle::toggled, this, [this](bool enabled) { settings_->setReadReceipts(enabled); }); connect(desktopNotifications_, &Toggle::toggled, this, [this](bool enabled) { settings_->setDesktopNotifications(enabled); }); connect(alertOnNotification_, &Toggle::toggled, this, [this](bool enabled) { settings_->setAlertOnNotification(enabled); }); connect(messageHoverHighlight_, &Toggle::toggled, this, [this](bool enabled) { settings_->setMessageHoverHighlight(enabled); }); connect(enlargeEmojiOnlyMessages_, &Toggle::toggled, this, [this](bool enabled) { settings_->setEnlargeEmojiOnlyMessages(enabled); }); connect(useStunServer_, &Toggle::toggled, this, [this](bool enabled) { settings_->setUseStunServer(enabled); }); connect(timelineMaxWidthSpin_, qOverload(&QSpinBox::valueChanged), this, [this](int newValue) { settings_->setTimelineMaxWidth(newValue); }); connect(privacyScreenTimeout_, qOverload(&QSpinBox::valueChanged), this, [this](int newValue) { settings_->setPrivacyScreenTimeout(newValue); }); connect( sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys); connect( sessionKeysExportBtn, &QPushButton::clicked, this, &UserSettingsPage::exportSessionKeys); connect(crossSigningRequestBtn, &QPushButton::clicked, this, []() { olm::request_cross_signing_keys(); }); connect(crossSigningDownloadBtn, &QPushButton::clicked, this, []() { olm::download_cross_signing_keys(); }); connect(backBtn_, &QPushButton::clicked, this, [this]() { settings_->save(); emit moveBack(); }); } void UserSettingsPage::showEvent(QShowEvent *) { // FIXME macOS doesn't show the full option unless a space is added. utils::restoreCombobox(fontSizeCombo_, QString::number(settings_->fontSize()) + " "); utils::restoreCombobox(scaleFactorCombo_, QString::number(utils::scaleFactor())); utils::restoreCombobox(themeCombo_, settings_->theme()); utils::restoreCombobox(ringtoneCombo_, settings_->ringtone()); trayToggle_->setState(settings_->tray()); startInTrayToggle_->setState(settings_->startInTray()); groupViewToggle_->setState(settings_->groupView()); decryptSidebar_->setState(settings_->decryptSidebar()); privacyScreen_->setState(settings_->privacyScreen()); onlyShareKeysWithVerifiedUsers_->setState(settings_->onlyShareKeysWithVerifiedUsers()); shareKeysWithTrustedUsers_->setState(settings_->shareKeysWithTrustedUsers()); useOnlineKeyBackup_->setState(settings_->useOnlineKeyBackup()); avatarCircles_->setState(settings_->avatarCircles()); typingNotifications_->setState(settings_->typingNotifications()); sortByImportance_->setState(settings_->sortByImportance()); timelineButtonsToggle_->setState(settings_->buttonsInTimeline()); mobileMode_->setState(settings_->mobileMode()); readReceipts_->setState(settings_->readReceipts()); markdown_->setState(settings_->markdown()); desktopNotifications_->setState(settings_->hasDesktopNotifications()); alertOnNotification_->setState(settings_->hasAlertOnNotification()); messageHoverHighlight_->setState(settings_->messageHoverHighlight()); enlargeEmojiOnlyMessages_->setState(settings_->enlargeEmojiOnlyMessages()); deviceIdValue_->setText(QString::fromStdString(http::client()->device_id())); timelineMaxWidthSpin_->setValue(settings_->timelineMaxWidth()); privacyScreenTimeout_->setValue(settings_->privacyScreenTimeout()); auto mics = CallDevices::instance().names(false, settings_->microphone().toStdString()); microphoneCombo_->clear(); for (const auto &m : mics) microphoneCombo_->addItem(QString::fromStdString(m)); auto cameraResolution = settings_->cameraResolution(); auto cameraFrameRate = settings_->cameraFrameRate(); auto cameras = CallDevices::instance().names(true, settings_->camera().toStdString()); cameraCombo_->clear(); for (const auto &c : cameras) cameraCombo_->addItem(QString::fromStdString(c)); utils::restoreCombobox(cameraResolutionCombo_, cameraResolution); utils::restoreCombobox(cameraFrameRateCombo_, cameraFrameRate); useStunServer_->setState(settings_->useStunServer()); deviceFingerprintValue_->setText( utils::humanReadableFingerprint(olm::client()->identity_keys().ed25519)); } void UserSettingsPage::paintEvent(QPaintEvent *) { QStyleOption opt; opt.initFrom(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void UserSettingsPage::importSessionKeys() { const QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); const QString fileName = QFileDialog::getOpenFileName(this, tr("Open Sessions File"), homeFolder, QLatin1String("")); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, tr("Error"), file.errorString()); return; } auto bin = file.peek(file.size()); auto payload = std::string(bin.data(), bin.size()); bool ok; auto password = QInputDialog::getText(this, tr("File Password"), tr("Enter the passphrase to decrypt the file:"), QLineEdit::Password, QLatin1String(""), &ok); if (!ok) return; if (password.isEmpty()) { QMessageBox::warning(this, tr("Error"), tr("The password cannot be empty")); return; } try { auto sessions = mtx::crypto::decrypt_exported_sessions(payload, password.toStdString()); cache::importSessionKeys(std::move(sessions)); } catch (const std::exception &e) { QMessageBox::warning(this, tr("Error"), e.what()); } } void UserSettingsPage::exportSessionKeys() { // Open password dialog. bool ok; auto password = QInputDialog::getText(this, tr("File Password"), tr("Enter passphrase to encrypt your session keys:"), QLineEdit::Password, QLatin1String(""), &ok); if (!ok) return; if (password.isEmpty()) { QMessageBox::warning(this, tr("Error"), tr("The password cannot be empty")); return; } // Open file dialog to save the file. const QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); const QString fileName = QFileDialog::getSaveFileName(this, tr("File to save the exported session keys"), homeFolder); QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::warning(this, tr("Error"), file.errorString()); return; } // Export sessions & save to file. try { auto encrypted_blob = mtx::crypto::encrypt_exported_sessions(cache::exportSessionKeys(), password.toStdString()); QString b64 = QString::fromStdString(mtx::crypto::bin2base64(encrypted_blob)); QString prefix(QStringLiteral("-----BEGIN MEGOLM SESSION DATA-----")); QString suffix(QStringLiteral("-----END MEGOLM SESSION DATA-----")); QString newline(QStringLiteral("\n")); QTextStream out(&file); out << prefix << newline << b64 << newline << suffix << newline; file.close(); } catch (const std::exception &e) { QMessageBox::warning(this, tr("Error"), e.what()); } } void UserSettingsPage::updateSecretStatus() { QString ok = QStringLiteral("QLabel { color : #00cc66; }"); QString notSoOk = QStringLiteral("QLabel { color : #ff9933; }"); auto updateLabel = [&ok, ¬SoOk](QLabel *label, const std::string &secretName) { if (cache::secret(secretName)) { label->setStyleSheet(ok); label->setText(tr("CACHED")); } else { if (secretName == mtx::secret_storage::secrets::cross_signing_master) label->setStyleSheet(ok); else label->setStyleSheet(notSoOk); label->setText(tr("NOT CACHED")); } }; updateLabel(masterSecretCached, mtx::secret_storage::secrets::cross_signing_master); updateLabel(userSigningSecretCached, mtx::secret_storage::secrets::cross_signing_user_signing); updateLabel(selfSigningSecretCached, mtx::secret_storage::secrets::cross_signing_self_signing); updateLabel(backupSecretCached, mtx::secret_storage::secrets::megolm_backup_v1); }