matrixion/src/UserSettingsPage.cpp
2023-12-31 16:18:46 -03:00

2334 lines
78 KiB
C++

// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QApplication>
#include <QCoreApplication>
#include <QFileDialog>
#include <QFontDatabase>
#include <QInputDialog>
#include <QMessageBox>
#include <QStandardPaths>
#include <QString>
#include <QTextStream>
#include <mtx/secret_storage.hpp>
#include "Cache.h"
#include "JdenticonProvider.h"
#include "MainWindow.h"
#include "MatrixClient.h"
#include "UserSettingsPage.h"
#include "Utils.h"
#include "encryption/Olm.h"
#include "ui/Theme.h"
#include "voip/CallDevices.h"
#include "config/nheko.h"
QSharedPointer<UserSettings> UserSettings::instance_;
UserSettings::UserSettings()
{
connect(
QCoreApplication::instance(), &QCoreApplication::aboutToQuit, []() { instance_.clear(); });
}
QSharedPointer<UserSettings>
UserSettings::instance()
{
return instance_;
}
void
UserSettings::initialize(std::optional<QString> profile)
{
instance_.reset(new UserSettings());
instance_->load(profile);
}
void
UserSettings::load(std::optional<QString> profile)
{
tray_ = settings.value("user/window/tray", false).toBool();
startInTray_ = settings.value("user/window/start_in_tray", false).toBool();
roomListWidth_ = settings.value("user/sidebar/room_list_width", -1).toInt();
communityListWidth_ = settings.value("user/sidebar/community_list_width", -1).toInt();
hasDesktopNotifications_ = settings.value("user/desktop_notifications", true).toBool();
hasAlertOnNotification_ = settings.value("user/alert_on_notification", false).toBool();
groupView_ = settings.value("user/group_view", true).toBool();
scrollbarsInRoomlist_ = settings.value("user/scrollbars_in_roomlist", false).toBool();
buttonsInTimeline_ = settings.value("user/timeline/buttons", true).toBool();
timelineMaxWidth_ = settings.value("user/timeline/max_width", 0).toInt();
messageHoverHighlight_ =
settings.value("user/timeline/message_hover_highlight", false).toBool();
enlargeEmojiOnlyMessages_ =
settings.value("user/timeline/enlarge_emoji_only_msg", false).toBool();
markdown_ = settings.value("user/markdown_enabled", true).toBool();
invertEnterKey_ = settings.value("user/invert_enter_key", false).toBool();
bubbles_ = settings.value("user/bubbles_enabled", false).toBool();
smallAvatars_ = settings.value("user/small_avatars_enabled", false).toBool();
animateImagesOnHover_ = settings.value("user/animate_images_on_hover", false).toBool();
typingNotifications_ = settings.value("user/typing_notifications", true).toBool();
sortByImportance_ = settings.value("user/sort_by_unread", true).toBool();
sortByAlphabet_ = settings.value("user/sort_by_alphabet", false).toBool();
readReceipts_ = settings.value("user/read_receipts", true).toBool();
theme_ = settings.value("user/theme", defaultTheme_).toString();
font_ = settings.value("user/font_family", "").toString();
avatarCircles_ = settings.value("user/avatar_circles", true).toBool();
useIdenticon_ = settings.value("user/use_identicon", true).toBool();
openImageExternal_ = settings.value("user/open_image_external", false).toBool();
openVideoExternal_ = settings.value("user/open_video_external", false).toBool();
decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool();
decryptNotifications_ = settings.value("user/decrypt_notifications", true).toBool();
spaceNotifications_ = settings.value("user/space_notifications", true).toBool();
fancyEffects_ = settings.value("user/fancy_effects", true).toBool();
reducedMotion_ = settings.value("user/reduced_motion", false).toBool();
privacyScreen_ = settings.value("user/privacy_screen", false).toBool();
privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt();
exposeDBusApi_ = settings.value("user/expose_dbus_api", false).toBool();
updateSpaceVias_ = settings.value("user/space_background_maintenance", true).toBool();
expireEvents_ = settings.value("user/expired_events_background_maintenance", false).toBool();
mobileMode_ = settings.value("user/mobile_mode", false).toBool();
disableSwipe_ = settings.value("user/disable_swipe", false).toBool();
emojiFont_ = settings.value("user/emoji_font_family", "emoji").toString();
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
auto tempPresence = settings.value("user/presence", "").toString().toStdString();
auto presenceValue = QMetaEnum::fromType<Presence>().keyToValue(tempPresence.c_str());
if (presenceValue < 0)
presenceValue = 0;
presence_ = static_cast<Presence>(presenceValue);
ringtone_ = settings.value("user/ringtone", "Default").toString();
microphone_ = settings.value("user/microphone", QString()).toString();
camera_ = settings.value("user/camera", QString()).toString();
cameraResolution_ = settings.value("user/camera_resolution", QString()).toString();
cameraFrameRate_ = settings.value("user/camera_frame_rate", QString()).toString();
screenShareFrameRate_ = settings.value("user/screen_share_frame_rate", 5).toInt();
screenSharePiP_ = settings.value("user/screen_share_pip", true).toBool();
screenShareRemoteVideo_ = settings.value("user/screen_share_remote_video", false).toBool();
screenShareHideCursor_ = settings.value("user/screen_share_hide_cursor", false).toBool();
useStunServer_ = settings.value("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("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();
currentTagId_ = settings.value(prefix + "user/current_tag_id", "").toString();
hiddenTags_ = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList();
mutedTags_ = settings.value(prefix + "user/muted_tags", QStringList{"global"}).toStringList();
hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList();
hiddenWidgets_ = settings.value(prefix + "user/hidden_widgets", QStringList{}).toStringList();
recentReactions_ =
settings.value(prefix + "user/recent_reactions", QStringList{}).toStringList();
collapsedSpaces_.clear();
auto tempSpaces = settings.value(prefix + "user/collapsed_spaces", QList<QVariant>{}).toList();
for (const auto &e : std::as_const(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", true).toBool();
disableCertificateValidation_ =
settings.value("disable_certificate_validation", false).toBool();
applyTheme();
if (profile)
setProfile(profile_);
}
bool
UserSettings::useIdenticon() const
{
return useIdenticon_ && JdenticonProvider::isAvailable();
}
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::setDisableSwipe(bool state)
{
if (state == disableSwipe_)
return;
disableSwipe_ = state;
emit disableSwipeChanged(state);
save();
}
void
UserSettings::setGroupView(bool state)
{
if (groupView_ == state)
return;
groupView_ = state;
emit groupViewStateChanged(state);
save();
}
void
UserSettings::setScrollbarsInRoomlist(bool state)
{
if (scrollbarsInRoomlist_ == state)
return;
scrollbarsInRoomlist_ = state;
emit scrollbarsInRoomlistChanged(state);
save();
}
void
UserSettings::setHiddenTags(const QStringList &hiddenTags)
{
hiddenTags_ = hiddenTags;
save();
}
void
UserSettings::setMutedTags(const QStringList &mutedTags)
{
mutedTags_ = mutedTags;
save();
}
void
UserSettings::setHiddenPins(const QStringList &hiddenTags)
{
hiddenPins_ = hiddenTags;
save();
emit hiddenPinsChanged();
}
void
UserSettings::setHiddenWidgets(const QStringList &hiddenTags)
{
hiddenWidgets_ = hiddenTags;
save();
emit hiddenWidgetsChanged();
}
void
UserSettings::setRecentReactions(QStringList recent)
{
recentReactions_ = recent;
save();
emit recentReactionsChanged();
}
void
UserSettings::setCollapsedSpaces(QList<QStringList> spaces)
{
collapsedSpaces_ = spaces;
save();
}
void
UserSettings::setExposeDBusApi(bool state)
{
if (exposeDBusApi_ == state)
return;
exposeDBusApi_ = state;
emit exposeDBusApiChanged(state);
save();
}
void
UserSettings::setUpdateSpaceVias(bool state)
{
if (updateSpaceVias_ == state)
return;
updateSpaceVias_ = state;
emit updateSpaceViasChanged(state);
save();
}
void
UserSettings::setExpireEvents(bool state)
{
if (expireEvents_ == state)
return;
expireEvents_ = state;
emit expireEventsChanged(state);
save();
}
void
UserSettings::setMarkdown(bool state)
{
if (state == markdown_)
return;
markdown_ = state;
emit markdownChanged(state);
save();
}
void
UserSettings::setInvertEnterKey(bool state)
{
if (state == invertEnterKey_)
return;
invertEnterKey_ = state;
emit invertEnterKeyChanged(state);
save();
}
void
UserSettings::setBubbles(bool state)
{
if (state == bubbles_)
return;
bubbles_ = state;
emit bubblesChanged(state);
save();
}
void
UserSettings::setSmallAvatars(bool state)
{
if (state == smallAvatars_)
return;
smallAvatars_ = state;
emit smallAvatarsChanged(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 roomSortingChangedImportance(state);
save();
}
void
UserSettings::setSortByAlphabet(bool state)
{
if (state == sortByAlphabet_)
return;
sortByAlphabet_ = state;
emit roomSortingChangedAlphabetical(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::setDecryptNotifications(bool state)
{
if (state == decryptNotifications_)
return;
decryptNotifications_ = state;
emit decryptNotificationsChanged(state);
save();
}
void
UserSettings::setSpaceNotifications(bool state)
{
if (state == spaceNotifications_)
return;
spaceNotifications_ = state;
emit spaceNotificationsChanged(state);
save();
}
void
UserSettings::setFancyEffects(bool state)
{
if (state == fancyEffects_)
return;
fancyEffects_ = state;
emit fancyEffectsChanged(state);
save();
}
void
UserSettings::setReducedMotion(bool state)
{
if (state == reducedMotion_)
return;
reducedMotion_ = state;
emit reducedMotionChanged(state);
save();
// Also toggle other motion related settings
if (reducedMotion_) {
setFancyEffects(false);
setAnimateImagesOnHover(true);
}
}
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;
const static auto defaultFamily = QFont().defaultFamily();
QFont f((font_.isEmpty() || font_ == QStringLiteral("default")) ? defaultFamily : font_);
f.setPointSizeF(fontSize());
QApplication::setFont(f);
emit fontSizeChanged(size);
save();
}
void
UserSettings::setFontFamily(QString family)
{
if (family == font_)
return;
font_ = family;
const static auto defaultFamily = QFont().defaultFamily();
QFont f((family.isEmpty() || family == QStringLiteral("default")) ? defaultFamily : family);
f.setPointSizeF(fontSize());
QApplication::setFont(f);
emit fontChanged(family);
save();
}
void
UserSettings::setEmojiFontFamily(QString family)
{
if (family == emojiFont_)
return;
if (family == tr("Default")) {
emojiFont_ = QStringLiteral("emoji");
} 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();
if (useBackup)
olm::download_full_keybackup();
}
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)
{
// always set this to allow setting this when loading and it is overwritten on the cli
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::setCurrentTagId(const QString currentTagId)
{
if (currentTagId == currentTagId_)
return;
currentTagId_ = currentTagId;
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::setOpenImageExternal(bool state)
{
if (state == openImageExternal_)
return;
openImageExternal_ = state;
emit openImageExternalChanged(openImageExternal_);
save();
}
void
UserSettings::setOpenVideoExternal(bool state)
{
if (state == openVideoExternal_)
return;
openVideoExternal_ = state;
emit openVideoExternalChanged(openVideoExternal_);
save();
}
void
UserSettings::applyTheme()
{
QGuiApplication::setPalette(Theme::paletteFromTheme(this->theme()));
QApplication::setPalette(Theme::paletteFromTheme(this->theme()));
}
void
UserSettings::save()
{
settings.beginGroup("user");
settings.beginGroup("window");
settings.setValue("tray", tray_);
settings.setValue("start_in_tray", startInTray_);
settings.endGroup(); // window
settings.beginGroup("sidebar");
settings.setValue("community_list_width", communityListWidth_);
settings.setValue("room_list_width", roomListWidth_);
settings.endGroup(); // window
settings.beginGroup("timeline");
settings.setValue("buttons", buttonsInTimeline_);
settings.setValue("message_hover_highlight", messageHoverHighlight_);
settings.setValue("enlarge_emoji_only_msg", enlargeEmojiOnlyMessages_);
settings.setValue("max_width", timelineMaxWidth_);
settings.endGroup(); // timeline
settings.setValue("avatar_circles", avatarCircles_);
settings.setValue("decrypt_sidebar", decryptSidebar_);
settings.setValue("decrypt_notifications", decryptNotifications_);
settings.setValue("space_notifications", spaceNotifications_);
settings.setValue("fancy_effects", fancyEffects_);
settings.setValue("reduced_motion", reducedMotion_);
settings.setValue("privacy_screen", privacyScreen_);
settings.setValue("privacy_screen_timeout", privacyScreenTimeout_);
settings.setValue("mobile_mode", mobileMode_);
settings.setValue("disable_swipe", disableSwipe_);
settings.setValue("font_size", baseFontSize_);
settings.setValue("typing_notifications", typingNotifications_);
settings.setValue("sort_by_unread", sortByImportance_);
settings.setValue("sort_by_alphabet", sortByAlphabet_);
settings.setValue("minor_events", sortByImportance_);
settings.setValue("read_receipts", readReceipts_);
settings.setValue("group_view", groupView_);
settings.setValue("scrollbars_in_roomlist", scrollbarsInRoomlist_);
settings.setValue("markdown_enabled", markdown_);
settings.setValue("invert_enter_key", invertEnterKey_);
settings.setValue("bubbles_enabled", bubbles_);
settings.setValue("small_avatars_enabled", smallAvatars_);
settings.setValue("animate_images_on_hover", animateImagesOnHover_);
settings.setValue("desktop_notifications", hasDesktopNotifications_);
settings.setValue("alert_on_notification", hasAlertOnNotification_);
settings.setValue("theme", theme());
settings.setValue("font_family", font_);
settings.setValue("emoji_font_family", emojiFont_);
settings.setValue(
"presence",
QString::fromUtf8(QMetaEnum::fromType<Presence>().valueToKey(static_cast<int>(presence_))));
settings.setValue("ringtone", ringtone_);
settings.setValue("microphone", microphone_);
settings.setValue("camera", camera_);
settings.setValue("camera_resolution", cameraResolution_);
settings.setValue("camera_frame_rate", cameraFrameRate_);
settings.setValue("screen_share_frame_rate", screenShareFrameRate_);
settings.setValue("screen_share_pip", screenSharePiP_);
settings.setValue("screen_share_remote_video", screenShareRemoteVideo_);
settings.setValue("screen_share_hide_cursor", screenShareHideCursor_);
settings.setValue("use_stun_server", useStunServer_);
settings.setValue("currentProfile", profile_);
settings.setValue("use_identicon", useIdenticon_);
settings.setValue("open_image_external", openImageExternal_);
settings.setValue("open_video_external", openVideoExternal_);
settings.setValue("expose_dbus_api", exposeDBusApi_);
settings.setValue("space_background_maintenance", updateSpaceVias_);
settings.setValue("expired_events_background_maintenance", expireEvents_);
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/current_tag_id", currentTagId_);
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/muted_tags", mutedTags_);
settings.setValue(prefix + "user/hidden_pins", hiddenPins_);
settings.setValue(prefix + "user/hidden_widgets", hiddenWidgets_);
settings.setValue(prefix + "user/recent_reactions", recentReactions_);
QVariantList v;
v.reserve(collapsedSpaces_.size());
for (const auto &e : std::as_const(collapsedSpaces_))
v.push_back(e);
settings.setValue(prefix + "user/collapsed_spaces", v);
settings.setValue("disable_certificate_validation", disableCertificateValidation_);
settings.sync();
}
QHash<int, QByteArray>
UserSettingsModel::roleNames() const
{
static QHash<int, QByteArray> roles{
{Name, "name"},
{Description, "description"},
{Value, "value"},
{Type, "type"},
{ValueLowerBound, "valueLowerBound"},
{ValueUpperBound, "valueUpperBound"},
{ValueStep, "valueStep"},
{Values, "values"},
{Good, "good"},
{Enabled, "enabled"},
};
return roles;
}
QVariant
UserSettingsModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= COUNT)
return {};
auto i = UserSettings::instance();
if (!i)
return {};
if (role == Name) {
switch (index.row()) {
case Theme:
return tr("Theme");
case ScaleFactor:
return tr("Scale factor");
case MessageHoverHighlight:
return tr("Highlight message on hover");
case EnlargeEmojiOnlyMessages:
return tr("Large Emoji in timeline");
case Tray:
return tr("Minimize to tray");
case StartInTray:
return tr("Start in tray");
case GroupView:
return tr("Communities sidebar");
case ScrollbarsInRoomlist:
return tr("Scrollbars in room list");
case Markdown:
return tr("Send messages as Markdown");
case InvertEnterKey:
return tr("Use shift+enter to send and enter to start a new line");
case Bubbles:
return tr("Enable message bubbles");
case SmallAvatars:
return tr("Enable small Avatars");
case AnimateImagesOnHover:
return tr("Play animated images only on hover");
case TypingNotifications:
return tr("Typing notifications");
case SortByImportance:
return tr("Sort rooms by unreads");
case SortByAlphabet:
return tr("Sort rooms alphabetically");
case ButtonsInTimeline:
return tr("Show buttons in timeline");
case TimelineMaxWidth:
return tr("Limit width of timeline");
case ReadReceipts:
return tr("Read receipts");
case HiddenTimelineEvents:
return tr("Hidden events");
case IgnoredUsers:
return tr("Ignored users");
case DesktopNotifications:
return tr("Desktop notifications");
case AlertOnNotification:
return tr("Alert on notification");
case AvatarCircles:
return tr("Circular Avatars");
case UseIdenticon:
return tr("Use identicons");
case OpenImageExternal:
return tr("Open images with external program");
case OpenVideoExternal:
return tr("Open videos with external program");
case DecryptSidebar:
return tr("Decrypt messages in sidebar");
case DecryptNotifications:
return tr("Decrypt notifications");
case SpaceNotifications:
return tr("Show message counts for communities and tags");
case FancyEffects:
return tr("Display fancy effects such as confetti");
case ReducedMotion:
return tr("Reduce or disable animations");
case PrivacyScreen:
return tr("Privacy Screen");
case PrivacyScreenTimeout:
return tr("Privacy screen timeout (in seconds [0 - 3600])");
case MobileMode:
return tr("Touchscreen mode");
case DisableSwipe:
return tr("Disable swipe motions");
case FontSize:
return tr("Font size");
case Font:
return tr("Font Family");
case EmojiFont:
return tr("Emoji Font Family");
case Ringtone:
return tr("Ringtone");
case Microphone:
return tr("Microphone");
case Camera:
return tr("Camera");
case CameraResolution:
return tr("Camera resolution");
case CameraFrameRate:
return tr("Camera frame rate");
case UseStunServer:
return tr("Allow fallback call assist server");
case OnlyShareKeysWithVerifiedUsers:
return tr("Send encrypted messages to verified users only");
case ShareKeysWithTrustedUsers:
return tr("Share keys with verified users and devices");
case UseOnlineKeyBackup:
return tr("Online Key Backup");
case Profile:
return tr("Profile");
case UserId:
return tr("User ID");
case AccessToken:
return tr("Accesstoken");
case DeviceId:
return tr("Device ID");
case DeviceFingerprint:
return tr("Device Fingerprint");
case Homeserver:
return tr("Homeserver");
case Version:
return tr("Version");
case Platform:
return tr("Platform");
case GeneralSection:
return tr("GENERAL");
case AccessibilitySection:
return tr("ACCESSIBILITY");
case TimelineSection:
return tr("TIMELINE");
case SidebarSection:
return tr("SIDEBAR");
case TraySection:
return tr("TRAY");
case MessageVisibilitySection:
return tr("GLOBAL MESSAGE VISIBILITY");
case NotificationsSection:
return tr("NOTIFICATIONS");
case VoipSection:
return tr("CALLS");
case EncryptionSection:
return tr("ENCRYPTION");
case LoginInfoSection:
return tr("INFO");
case SessionKeys:
return tr("Session Keys");
case CrossSigningSecrets:
return tr("Cross Signing Secrets");
case OnlineBackupKey:
return tr("Online backup key");
case SelfSigningKey:
return tr("Self signing key");
case UserSigningKey:
return tr("User signing key");
case MasterKey:
return tr("Master signing key");
case ExposeDBusApi:
return tr("Expose room information via D-Bus");
case UpdateSpaceVias:
return tr("Periodically update community routing information");
case ExpireEvents:
return tr("Periodically delete expired events");
}
} else if (role == Value) {
switch (index.row()) {
case Theme:
return QStringList{
QStringLiteral("light"),
QStringLiteral("dark"),
QStringLiteral("system"),
}
.indexOf(i->theme());
case ScaleFactor:
return utils::scaleFactor();
case MessageHoverHighlight:
return i->messageHoverHighlight();
case EnlargeEmojiOnlyMessages:
return i->enlargeEmojiOnlyMessages();
case Tray:
return i->tray();
case StartInTray:
return i->startInTray();
case GroupView:
return i->groupView();
case ScrollbarsInRoomlist:
return i->scrollbarsInRoomlist();
case Markdown:
return i->markdown();
case InvertEnterKey:
return i->invertEnterKey();
case Bubbles:
return i->bubbles();
case SmallAvatars:
return i->smallAvatars();
case AnimateImagesOnHover:
return i->animateImagesOnHover();
case TypingNotifications:
return i->typingNotifications();
case SortByImportance:
return i->sortByImportance();
case SortByAlphabet:
return i->sortByAlphabet();
case ButtonsInTimeline:
return i->buttonsInTimeline();
case TimelineMaxWidth:
return i->timelineMaxWidth();
case ReadReceipts:
return i->readReceipts();
case DesktopNotifications:
return i->hasDesktopNotifications();
case AlertOnNotification:
return i->hasAlertOnNotification();
case AvatarCircles:
return i->avatarCircles();
case UseIdenticon:
return i->useIdenticon();
case OpenImageExternal:
return i->openImageExternal();
case OpenVideoExternal:
return i->openVideoExternal();
case DecryptSidebar:
return i->decryptSidebar();
case DecryptNotifications:
return i->decryptNotifications();
case SpaceNotifications:
return i->spaceNotifications();
case FancyEffects:
return i->fancyEffects();
case ReducedMotion:
return i->reducedMotion();
case PrivacyScreen:
return i->privacyScreen();
case PrivacyScreenTimeout:
return i->privacyScreenTimeout();
case MobileMode:
return i->mobileMode();
case DisableSwipe:
return i->disableSwipe();
case FontSize:
return i->fontSize();
case Font: {
if (i->font().isEmpty())
return 0;
else
return data(index, Values).toStringList().indexOf(i->font());
}
case EmojiFont: {
if (i->emojiFont().isEmpty())
return 0;
else
return data(index, Values).toStringList().indexOf(i->emojiFont());
}
case Ringtone: {
auto v = i->ringtone();
if (v == QStringView(u"Mute"))
return 0;
else if (v == QStringView(u"Default"))
return 1;
else if (v == QStringView(u"Other"))
return 2;
else
return 3;
}
case Microphone:
return data(index, Values).toStringList().indexOf(i->microphone());
case Camera:
return data(index, Values).toStringList().indexOf(i->camera());
case CameraResolution:
return data(index, Values).toStringList().indexOf(i->cameraResolution());
case CameraFrameRate:
return data(index, Values).toStringList().indexOf(i->cameraFrameRate());
case UseStunServer:
return i->useStunServer();
case OnlyShareKeysWithVerifiedUsers:
return i->onlyShareKeysWithVerifiedUsers();
case ShareKeysWithTrustedUsers:
return i->shareKeysWithTrustedUsers();
case UseOnlineKeyBackup:
return i->useOnlineKeyBackup();
case Profile:
return i->profile().isEmpty() ? tr("Default") : i->profile();
case UserId:
return i->userId();
case AccessToken:
return i->accessToken();
case DeviceId:
return i->deviceId();
case DeviceFingerprint:
return utils::humanReadableFingerprint(olm::client()->identity_keys().ed25519);
case Homeserver:
return i->homeserver();
case Version:
return QString::fromStdString(nheko::version);
case Platform:
return QString::fromStdString(nheko::build_os);
case OnlineBackupKey:
return cache::secret(mtx::secret_storage::secrets::megolm_backup_v1).has_value();
case SelfSigningKey:
return cache::secret(mtx::secret_storage::secrets::cross_signing_self_signing)
.has_value();
case UserSigningKey:
return cache::secret(mtx::secret_storage::secrets::cross_signing_user_signing)
.has_value();
case MasterKey:
return cache::secret(mtx::secret_storage::secrets::cross_signing_master).has_value();
case ExposeDBusApi:
return i->exposeDBusApi();
case UpdateSpaceVias:
return i->updateSpaceVias();
case ExpireEvents:
return i->expireEvents();
}
} else if (role == Description) {
switch (index.row()) {
case Theme:
case Font:
case EmojiFont:
return {};
case Microphone:
return tr("Set the notification sound to play when a call invite arrives");
case Camera:
case CameraResolution:
case CameraFrameRate:
case Ringtone:
return {};
case TimelineMaxWidth:
return tr("Set the max width of messages in the timeline (in pixels). This can help "
"readability on wide screen when Nheko is maximized");
case PrivacyScreenTimeout:
return 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)");
case FontSize:
return {};
case MessageHoverHighlight:
return tr("Change the background color of messages when you hover over them.");
case EnlargeEmojiOnlyMessages:
return tr("Make font size larger if messages with only a few emojis are displayed.");
case Tray:
return tr(
"Keep the application running in the background after closing the client window.");
case StartInTray:
return tr("Start the application in the background without showing the client window.");
case GroupView:
return tr("Show a column containing communities and tags next to the room list.");
case ScrollbarsInRoomlist:
return tr("Shows scrollbars in the room list and communities list.");
case Markdown:
return tr(
"Allow using markdown in messages.\nWhen disabled, all messages are sent as a plain "
"text.");
case InvertEnterKey:
return tr(
"Invert the behavior of the enter key in the text input, making it send the message "
"when shift+enter is pressed and starting a new line when enter is pressed.");
case Bubbles:
return tr(
"Messages get a bubble background. This also triggers some layout changes (WIP).");
case SmallAvatars:
return tr("Avatars are resized to fit above the message.");
case AnimateImagesOnHover:
return tr("Plays media like GIFs or WEBPs only when explicitly hovering over them.");
case TypingNotifications:
return tr(
"Show who is typing in a room.\nThis will also enable or disable sending typing "
"notifications to others.");
case SortByImportance:
return tr(
"Display rooms with new messages first.\nIf this is off, the list of rooms will only "
"be sorted by the preferred sorting order.\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 the preferred sorting order, "
"since you don't "
"seem to consider them as important as the other rooms.");
case SortByAlphabet:
return tr(
"Sort rooms alphabetically.\nIf this is off, the list of rooms will be sorted by the "
"timestamp of the last message in a room.\nIf this is on, rooms that come first "
"alphabetically "
"will be sorted earlier than ones that come later.");
case ButtonsInTimeline:
return tr(
"Show buttons to quickly reply, react or access additional options next to each "
"message.");
case ReadReceipts:
return tr(
"Show if your message was read.\nStatus is displayed next to timestamps.\nWarning: "
"If your homeserver does not support this, your rooms will never be marked as read!");
case HiddenTimelineEvents:
return tr("Configure whether to show or hide certain events like room joins.");
case DesktopNotifications:
return tr("Notify about received messages when the client is not currently focused.");
case AlertOnNotification:
return tr(
"Show an alert when a message is received.\nThis usually causes the application "
"icon in the task bar to animate in some fashion.");
case AvatarCircles:
return tr(
"Change the appearance of user avatars in chats.\nOFF - square, ON - circle.");
case UseIdenticon:
return tr("Display an identicon instead of a letter when no avatar is set.");
case OpenImageExternal:
return tr("Opens images with an external program when tapping the image.\nNote that "
"when this option is ON, opened files are left unencrypted on disk and must "
"be manually deleted.");
case OpenVideoExternal:
return tr("Opens videos with an external program when tapping the video.\nNote that "
"when this option is ON, opened files are left unencrypted on disk and must "
"be manually deleted.");
case DecryptSidebar:
return tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in "
"encrypted chats.");
case DecryptNotifications:
return tr("Decrypt messages shown in notifications for encrypted chats.");
case SpaceNotifications:
return tr("Choose where to show the total number of notifications contained within a "
"community or tag.");
case FancyEffects:
return tr("Some messages can be sent with fancy effects. For example, messages sent "
"with '/confetti' will show confetti on screen.");
case ReducedMotion:
return tr("Nheko uses animations in several places to make stuff pretty. This allows "
"you to turn those off if they make you feel unwell.");
case PrivacyScreen:
return tr("When the window loses focus, the timeline will\nbe blurred.");
case MobileMode:
return tr(
"Will prevent text selection in the timeline to make touch scrolling easier.");
case DisableSwipe:
return tr("Will prevent swipe motions like swiping left/right between Rooms and "
"Timeline, or swiping a message to reply.");
case ScaleFactor:
return tr("Change the scale factor of the whole user interface.");
case UseStunServer:
return tr(
"Will use turn.matrix.org as assist when your home server does not offer one.");
case OnlyShareKeysWithVerifiedUsers:
return tr("Requires a user to be verified to send encrypted messages to them. This "
"improves safety but makes E2EE more tedious.");
case ShareKeysWithTrustedUsers:
return 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.");
case UseOnlineKeyBackup:
return tr(
"Download message encryption keys from and upload to the encrypted online key "
"backup.");
case Profile:
case UserId:
case AccessToken:
case DeviceId:
case DeviceFingerprint:
case Homeserver:
case Version:
case Platform:
case GeneralSection:
case AccessibilitySection:
case TimelineSection:
case SidebarSection:
case TraySection:
case MessageVisibilitySection:
case NotificationsSection:
case VoipSection:
case EncryptionSection:
case LoginInfoSection:
case SessionKeys:
case CrossSigningSecrets:
return {};
case OnlineBackupKey:
return 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.");
case SelfSigningKey:
return 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.");
case UserSigningKey:
return tr(
"The key to verify other users. If it is cached, verifying a user will verify "
"all their devices.");
case MasterKey:
return 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.");
case ExposeDBusApi:
return tr("Allow third-party plugins and applications to load information about rooms "
"you are in via D-Bus. "
"This can have useful applications, but it also could be used for nefarious "
"purposes. Enable at your own risk.\n\n"
"This setting will take effect upon restart.");
case UpdateSpaceVias:
return tr(
"To allow new users to join a community, the community needs to expose some "
"information about what servers participate in a room to community members. Since "
"the room participants can change over time, this needs to be updated from time to "
"time. This setting enables a background job to do that automatically.");
case ExpireEvents:
return tr("Regularly redact expired events as specified in the event expiration "
"configuration. Since this is currently not executed server side, you need "
"to have one client running this regularly.");
case IgnoredUsers:
return tr("Manage your ignored users.");
}
} else if (role == Type) {
switch (index.row()) {
case Theme:
case Font:
case EmojiFont:
case Microphone:
case Camera:
case CameraResolution:
case CameraFrameRate:
case Ringtone:
return Options;
case TimelineMaxWidth:
case PrivacyScreenTimeout:
return Integer;
case FontSize:
case ScaleFactor:
return Double;
case MessageHoverHighlight:
case EnlargeEmojiOnlyMessages:
case Tray:
case StartInTray:
case GroupView:
case ScrollbarsInRoomlist:
case Markdown:
case InvertEnterKey:
case Bubbles:
case SmallAvatars:
case AnimateImagesOnHover:
case TypingNotifications:
case SortByImportance:
case SortByAlphabet:
case ButtonsInTimeline:
case ReadReceipts:
case DesktopNotifications:
case AlertOnNotification:
case AvatarCircles:
case UseIdenticon:
case OpenImageExternal:
case OpenVideoExternal:
case DecryptSidebar:
case DecryptNotifications:
case PrivacyScreen:
case MobileMode:
case DisableSwipe:
case UseStunServer:
case OnlyShareKeysWithVerifiedUsers:
case ShareKeysWithTrustedUsers:
case UseOnlineKeyBackup:
case ExposeDBusApi:
case UpdateSpaceVias:
case ExpireEvents:
case SpaceNotifications:
case FancyEffects:
case ReducedMotion:
return Toggle;
case Profile:
case UserId:
case AccessToken:
case DeviceId:
case DeviceFingerprint:
case Homeserver:
case Version:
case Platform:
return ReadOnlyText;
case GeneralSection:
case AccessibilitySection:
case TimelineSection:
case SidebarSection:
case TraySection:
case MessageVisibilitySection:
case NotificationsSection:
case VoipSection:
case EncryptionSection:
case LoginInfoSection:
return SectionTitle;
case SessionKeys:
return SessionKeyImportExport;
case CrossSigningSecrets:
return XSignKeysRequestDownload;
case OnlineBackupKey:
case SelfSigningKey:
case UserSigningKey:
case MasterKey:
return KeyStatus;
case HiddenTimelineEvents:
return ConfigureHiddenEvents;
case IgnoredUsers:
return ManageIgnoredUsers;
}
} else if (role == ValueLowerBound) {
switch (index.row()) {
case TimelineMaxWidth:
return 0;
case PrivacyScreenTimeout:
return 0;
case FontSize:
return 8.0;
case ScaleFactor:
return 1.0;
}
} else if (role == ValueUpperBound) {
switch (index.row()) {
case TimelineMaxWidth:
return 20000;
case PrivacyScreenTimeout:
return 3600;
case FontSize:
return 24.0;
case ScaleFactor:
return 3.0;
}
} else if (role == ValueStep) {
switch (index.row()) {
case TimelineMaxWidth:
return 20;
case PrivacyScreenTimeout:
return 10;
case FontSize:
return 0.5;
case ScaleFactor:
return .25;
}
} else if (role == Values) {
auto vecToList = [](const std::vector<std::string> &vec) {
QStringList l;
for (const auto &d : vec)
l.push_back(QString::fromStdString(d));
return l;
};
switch (index.row()) {
case Theme:
return QStringList{
QStringLiteral("Light"),
QStringLiteral("Dark"),
QStringLiteral("System"),
};
case Microphone:
return vecToList(CallDevices::instance().names(false, i->microphone().toStdString()));
case Camera:
return vecToList(CallDevices::instance().names(true, i->camera().toStdString()));
case CameraResolution:
return vecToList(CallDevices::instance().resolutions(i->camera().toStdString()));
case CameraFrameRate:
return vecToList(CallDevices::instance().frameRates(
i->camera().toStdString(), i->cameraResolution().toStdString()));
case Font: {
auto fonts = QFontDatabase::families();
fonts.prepend(tr("System font"));
return fonts;
}
case EmojiFont: {
auto fonts = QFontDatabase::families(QFontDatabase::WritingSystem::Symbol);
fonts.prepend(tr("System emoji font"));
return fonts;
}
case Ringtone: {
QStringList l{
QStringLiteral("Mute"),
QStringLiteral("Default"),
QStringLiteral("Other"),
};
if (!l.contains(i->ringtone()))
l.push_back(i->ringtone());
return l;
}
}
} else if (role == Good) {
switch (index.row()) {
case OnlineBackupKey:
return cache::secret(mtx::secret_storage::secrets::megolm_backup_v1).has_value();
case SelfSigningKey:
return cache::secret(mtx::secret_storage::secrets::cross_signing_self_signing)
.has_value();
case UserSigningKey:
return cache::secret(mtx::secret_storage::secrets::cross_signing_user_signing)
.has_value();
case MasterKey:
return true;
}
} else if (role == Enabled) {
switch (index.row()) {
case StartInTray:
return i->tray();
case PrivacyScreenTimeout:
return i->privacyScreen();
case UseIdenticon:
return JdenticonProvider::isAvailable();
default:
return true;
}
}
return {};
}
bool
UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
auto i = UserSettings::instance();
if (role == Value) {
switch (index.row()) {
case Theme: {
if (value == 0) {
i->setTheme("light");
return true;
} else if (value == 1) {
i->setTheme("dark");
return true;
} else if (value == 2) {
i->setTheme("system");
return true;
} else
return false;
}
case MessageHoverHighlight: {
if (value.userType() == QMetaType::Bool) {
i->setMessageHoverHighlight(value.toBool());
return true;
} else
return false;
}
case ScaleFactor: {
if (value.canConvert(QMetaType::fromType<double>())) {
utils::setScaleFactor(static_cast<float>(value.toDouble()));
return true;
} else
return false;
}
case EnlargeEmojiOnlyMessages: {
if (value.userType() == QMetaType::Bool) {
i->setEnlargeEmojiOnlyMessages(value.toBool());
return true;
} else
return false;
}
case Tray: {
if (value.userType() == QMetaType::Bool) {
i->setTray(value.toBool());
return true;
} else
return false;
}
case StartInTray: {
if (value.userType() == QMetaType::Bool) {
i->setStartInTray(value.toBool());
return true;
} else
return false;
}
case GroupView: {
if (value.userType() == QMetaType::Bool) {
i->setGroupView(value.toBool());
return true;
} else
return false;
}
case ScrollbarsInRoomlist: {
if (value.userType() == QMetaType::Bool) {
i->setScrollbarsInRoomlist(value.toBool());
return true;
} else
return false;
}
case Markdown: {
if (value.userType() == QMetaType::Bool) {
i->setMarkdown(value.toBool());
return true;
} else
return false;
}
case InvertEnterKey: {
if (value.userType() == QMetaType::Bool) {
i->setInvertEnterKey(value.toBool());
return true;
} else
return false;
}
case Bubbles: {
if (value.userType() == QMetaType::Bool) {
i->setBubbles(value.toBool());
return true;
} else
return false;
}
case SmallAvatars: {
if (value.userType() == QMetaType::Bool) {
i->setSmallAvatars(value.toBool());
return true;
} else
return false;
}
case AnimateImagesOnHover: {
if (value.userType() == QMetaType::Bool) {
i->setAnimateImagesOnHover(value.toBool());
return true;
} else
return false;
}
case TypingNotifications: {
if (value.userType() == QMetaType::Bool) {
i->setTypingNotifications(value.toBool());
return true;
} else
return false;
}
case SortByImportance: {
if (value.userType() == QMetaType::Bool) {
i->setSortByImportance(value.toBool());
return true;
} else
return false;
}
case SortByAlphabet: {
if (value.userType() == QMetaType::Bool) {
i->setSortByAlphabet(value.toBool());
return true;
} else
return false;
}
case ButtonsInTimeline: {
if (value.userType() == QMetaType::Bool) {
i->setButtonsInTimeline(value.toBool());
return true;
} else
return false;
}
case TimelineMaxWidth: {
if (value.canConvert(QMetaType::fromType<int>())) {
i->setTimelineMaxWidth(value.toInt());
return true;
} else
return false;
}
case ReadReceipts: {
if (value.userType() == QMetaType::Bool) {
i->setReadReceipts(value.toBool());
return true;
} else
return false;
}
case DesktopNotifications: {
if (value.userType() == QMetaType::Bool) {
i->setDesktopNotifications(value.toBool());
return true;
} else
return false;
}
case AlertOnNotification: {
if (value.userType() == QMetaType::Bool) {
i->setAlertOnNotification(value.toBool());
return true;
} else
return false;
}
case AvatarCircles: {
if (value.userType() == QMetaType::Bool) {
i->setAvatarCircles(value.toBool());
return true;
} else
return false;
}
case UseIdenticon: {
if (value.userType() == QMetaType::Bool) {
i->setUseIdenticon(value.toBool());
return true;
} else
return false;
}
case OpenImageExternal: {
if (value.userType() == QMetaType::Bool) {
i->setOpenImageExternal(value.toBool());
return true;
} else
return false;
}
case OpenVideoExternal: {
if (value.userType() == QMetaType::Bool) {
i->setOpenVideoExternal(value.toBool());
return true;
} else
return false;
}
case DecryptSidebar: {
if (value.userType() == QMetaType::Bool) {
i->setDecryptSidebar(value.toBool());
return true;
} else
return false;
}
case DecryptNotifications: {
if (value.userType() == QMetaType::Bool) {
i->setDecryptNotifications(value.toBool());
return true;
} else
return false;
}
case SpaceNotifications: {
if (value.userType() == QMetaType::Bool) {
i->setSpaceNotifications(value.toBool());
return true;
} else
return false;
}
case FancyEffects: {
if (value.userType() == QMetaType::Bool) {
i->setFancyEffects(value.toBool());
return true;
} else
return false;
}
case ReducedMotion: {
if (value.userType() == QMetaType::Bool) {
i->setReducedMotion(value.toBool());
return true;
} else
return false;
}
case PrivacyScreen: {
if (value.userType() == QMetaType::Bool) {
i->setPrivacyScreen(value.toBool());
return true;
} else
return false;
}
case PrivacyScreenTimeout: {
if (value.canConvert(QMetaType::fromType<int>())) {
i->setPrivacyScreenTimeout(value.toInt());
return true;
} else
return false;
}
case MobileMode: {
if (value.userType() == QMetaType::Bool) {
i->setMobileMode(value.toBool());
return true;
} else
return false;
}
case DisableSwipe: {
if (value.userType() == QMetaType::Bool) {
i->setDisableSwipe(value.toBool());
return true;
} else
return false;
}
case FontSize: {
if (value.canConvert(QMetaType::fromType<double>())) {
i->setFontSize(value.toDouble());
return true;
} else
return false;
}
case Font: {
if (value.userType() == QMetaType::Int) {
// Special handling to grab our injected system font option
auto v = value.toInt();
i->setFontFamily(v == 0 ? QString{} : QFontDatabase::families().at(v - 1));
return true;
} else
return false;
}
case EmojiFont: {
if (value.userType() == QMetaType::Int) {
// More special handling for the default font option
auto v = value.toInt();
i->setEmojiFontFamily(
v == 0 ? QStringLiteral("emoji")
: QFontDatabase::families(QFontDatabase::WritingSystem::Symbol).at(v - 1));
return true;
} else
return false;
}
case Ringtone: {
if (value.userType() == QMetaType::Int) {
int ringtone = value.toInt();
// setRingtone is called twice, because updating the list breaks the set value,
// because it does not exist yet!
if (ringtone == 2) {
QString homeFolder =
QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
auto filepath = QFileDialog::getOpenFileName(
nullptr, tr("Select a file"), homeFolder, tr("All Files (*)"));
if (!filepath.isEmpty()) {
i->setRingtone(filepath);
i->setRingtone(filepath);
}
} else if (ringtone == 0) {
i->setRingtone(QStringLiteral("Mute"));
i->setRingtone(QStringLiteral("Mute"));
} else if (ringtone == 1) {
i->setRingtone(QStringLiteral("Default"));
i->setRingtone(QStringLiteral("Default"));
}
return true;
}
return false;
}
case Microphone: {
if (value.userType() == QMetaType::Int) {
i->setMicrophone(data(index, Values).toStringList().at(value.toInt()));
return true;
} else
return false;
}
case Camera: {
if (value.userType() == QMetaType::Int) {
i->setCamera(data(index, Values).toStringList().at(value.toInt()));
return true;
} else
return false;
}
case CameraResolution: {
if (value.userType() == QMetaType::Int) {
i->setCameraResolution(data(index, Values).toStringList().at(value.toInt()));
return true;
} else
return false;
}
case CameraFrameRate: {
if (value.userType() == QMetaType::Int) {
i->setCameraFrameRate(data(index, Values).toStringList().at(value.toInt()));
return true;
} else
return false;
}
case UseStunServer: {
if (value.userType() == QMetaType::Bool) {
i->setUseStunServer(value.toBool());
return true;
} else
return false;
}
case OnlyShareKeysWithVerifiedUsers: {
if (value.userType() == QMetaType::Bool) {
i->setOnlyShareKeysWithVerifiedUsers(value.toBool());
return true;
} else
return false;
}
case ShareKeysWithTrustedUsers: {
if (value.userType() == QMetaType::Bool) {
i->setShareKeysWithTrustedUsers(value.toBool());
return true;
} else
return false;
}
case UseOnlineKeyBackup: {
if (value.userType() == QMetaType::Bool) {
i->setUseOnlineKeyBackup(value.toBool());
return true;
} else
return false;
}
case ExposeDBusApi: {
if (value.userType() == QMetaType::Bool) {
i->setExposeDBusApi(value.toBool());
return true;
} else
return false;
}
case UpdateSpaceVias: {
if (value.userType() == QMetaType::Bool) {
i->setUpdateSpaceVias(value.toBool());
return true;
} else
return false;
}
case ExpireEvents: {
if (value.userType() == QMetaType::Bool) {
i->setExpireEvents(value.toBool());
return true;
} else
return false;
}
}
}
return false;
}
void
UserSettingsModel::importSessionKeys()
{
const QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
const QString fileName = QFileDialog::getOpenFileName(
nullptr, tr("Open Sessions File"), homeFolder, QLatin1String(""));
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::warning(nullptr, 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(nullptr,
tr("File Password"),
tr("Enter the passphrase to decrypt the file:"),
QLineEdit::Password,
QLatin1String(""),
&ok);
if (!ok)
return;
if (password.isEmpty()) {
QMessageBox::warning(nullptr, 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(nullptr, tr("Error"), e.what());
}
}
void
UserSettingsModel::exportSessionKeys()
{
// Open password dialog.
bool ok;
auto password = QInputDialog::getText(nullptr,
tr("File Password"),
tr("Enter passphrase to encrypt your session keys:"),
QLineEdit::Password,
QLatin1String(""),
&ok);
if (!ok)
return;
if (password.isEmpty()) {
QMessageBox::warning(nullptr, 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(
nullptr, tr("File to save the exported session keys"), homeFolder);
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::warning(nullptr, 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(nullptr, tr("Error"), e.what());
}
}
void
UserSettingsModel::requestCrossSigningSecrets()
{
olm::request_cross_signing_keys();
}
void
UserSettingsModel::downloadCrossSigningSecrets()
{
olm::download_cross_signing_keys();
}
UserSettingsModel::UserSettingsModel(QObject *p)
: QAbstractListModel(p)
{
auto s = UserSettings::instance();
connect(s.get(), &UserSettings::themeChanged, this, [this]() {
emit dataChanged(index(Theme), index(Theme), {Value});
});
connect(s.get(), &UserSettings::mobileModeChanged, this, [this]() {
emit dataChanged(index(MobileMode), index(MobileMode), {Value});
});
connect(s.get(), &UserSettings::disableSwipeChanged, this, [this]() {
emit dataChanged(index(DisableSwipe), index(DisableSwipe), {Value});
});
connect(s.get(), &UserSettings::fontChanged, this, [this]() {
emit dataChanged(index(Font), index(Font), {Value});
});
connect(s.get(), &UserSettings::fontSizeChanged, this, [this]() {
emit dataChanged(index(FontSize), index(FontSize), {Value});
});
connect(s.get(), &UserSettings::emojiFontChanged, this, [this]() {
emit dataChanged(index(EmojiFont), index(EmojiFont), {Value});
});
connect(s.get(), &UserSettings::avatarCirclesChanged, this, [this]() {
emit dataChanged(index(AvatarCircles), index(AvatarCircles), {Value});
});
connect(s.get(), &UserSettings::useIdenticonChanged, this, [this]() {
emit dataChanged(index(UseIdenticon), index(UseIdenticon), {Value});
});
connect(s.get(), &UserSettings::openImageExternalChanged, this, [this]() {
emit dataChanged(index(OpenImageExternal), index(OpenImageExternal), {Value});
});
connect(s.get(), &UserSettings::openVideoExternalChanged, this, [this]() {
emit dataChanged(index(OpenVideoExternal), index(OpenVideoExternal), {Value});
});
connect(s.get(), &UserSettings::privacyScreenChanged, this, [this]() {
emit dataChanged(index(PrivacyScreen), index(PrivacyScreen), {Value});
emit dataChanged(index(PrivacyScreenTimeout), index(PrivacyScreenTimeout), {Enabled});
});
connect(s.get(), &UserSettings::privacyScreenTimeoutChanged, this, [this]() {
emit dataChanged(index(PrivacyScreenTimeout), index(PrivacyScreenTimeout), {Value});
});
connect(s.get(), &UserSettings::timelineMaxWidthChanged, this, [this]() {
emit dataChanged(index(TimelineMaxWidth), index(TimelineMaxWidth), {Value});
});
connect(s.get(), &UserSettings::messageHoverHighlightChanged, this, [this]() {
emit dataChanged(index(MessageHoverHighlight), index(MessageHoverHighlight), {Value});
});
connect(s.get(), &UserSettings::enlargeEmojiOnlyMessagesChanged, this, [this]() {
emit dataChanged(index(EnlargeEmojiOnlyMessages), index(EnlargeEmojiOnlyMessages), {Value});
});
connect(s.get(), &UserSettings::animateImagesOnHoverChanged, this, [this]() {
emit dataChanged(index(AnimateImagesOnHover), index(AnimateImagesOnHover), {Value});
});
connect(s.get(), &UserSettings::typingNotificationsChanged, this, [this]() {
emit dataChanged(index(TypingNotifications), index(TypingNotifications), {Value});
});
connect(s.get(), &UserSettings::readReceiptsChanged, this, [this]() {
emit dataChanged(index(ReadReceipts), index(ReadReceipts), {Value});
});
connect(s.get(), &UserSettings::buttonInTimelineChanged, this, [this]() {
emit dataChanged(index(ButtonsInTimeline), index(ButtonsInTimeline), {Value});
});
connect(s.get(), &UserSettings::markdownChanged, this, [this]() {
emit dataChanged(index(Markdown), index(Markdown), {Value});
});
connect(s.get(), &UserSettings::invertEnterKeyChanged, this, [this]() {
emit dataChanged(index(InvertEnterKey), index(InvertEnterKey), {Value});
});
connect(s.get(), &UserSettings::bubblesChanged, this, [this]() {
emit dataChanged(index(Bubbles), index(Bubbles), {Value});
});
connect(s.get(), &UserSettings::smallAvatarsChanged, this, [this]() {
emit dataChanged(index(SmallAvatars), index(SmallAvatars), {Value});
});
connect(s.get(), &UserSettings::groupViewStateChanged, this, [this]() {
emit dataChanged(index(GroupView), index(GroupView), {Value});
});
connect(s.get(), &UserSettings::scrollbarsInRoomlistChanged, this, [this]() {
emit dataChanged(index(ScrollbarsInRoomlist), index(ScrollbarsInRoomlist), {Value});
});
connect(s.get(), &UserSettings::roomSortingChangedImportance, this, [this]() {
emit dataChanged(index(SortByImportance), index(SortByImportance), {Value});
});
connect(s.get(), &UserSettings::roomSortingChangedAlphabetical, this, [this]() {
emit dataChanged(index(SortByAlphabet), index(SortByAlphabet), {Value});
});
connect(s.get(), &UserSettings::decryptSidebarChanged, this, [this]() {
emit dataChanged(index(DecryptSidebar), index(DecryptSidebar), {Value});
});
connect(s.get(), &UserSettings::decryptNotificationsChanged, this, [this]() {
emit dataChanged(index(DecryptNotifications), index(DecryptNotifications), {Value});
});
connect(s.get(), &UserSettings::spaceNotificationsChanged, this, [this]() {
emit dataChanged(index(SpaceNotifications), index(SpaceNotifications), {Value});
});
connect(s.get(), &UserSettings::fancyEffectsChanged, this, [this]() {
emit dataChanged(index(FancyEffects), index(FancyEffects), {Value});
});
connect(s.get(), &UserSettings::reducedMotionChanged, this, [this]() {
emit dataChanged(index(ReducedMotion), index(ReducedMotion), {Value});
});
connect(s.get(), &UserSettings::trayChanged, this, [this]() {
emit dataChanged(index(Tray), index(Tray), {Value});
emit dataChanged(index(StartInTray), index(StartInTray), {Enabled});
});
connect(s.get(), &UserSettings::startInTrayChanged, this, [this]() {
emit dataChanged(index(StartInTray), index(StartInTray), {Value});
});
connect(s.get(), &UserSettings::desktopNotificationsChanged, this, [this]() {
emit dataChanged(index(DesktopNotifications), index(DesktopNotifications), {Value});
});
connect(s.get(), &UserSettings::alertOnNotificationChanged, this, [this]() {
emit dataChanged(index(AlertOnNotification), index(AlertOnNotification), {Value});
});
connect(s.get(), &UserSettings::useStunServerChanged, this, [this]() {
emit dataChanged(index(UseStunServer), index(UseStunServer), {Value});
});
connect(s.get(), &UserSettings::microphoneChanged, this, [this]() {
emit dataChanged(index(Microphone), index(Microphone), {Value, Values});
});
connect(s.get(), &UserSettings::cameraChanged, this, [this]() {
emit dataChanged(index(Camera), index(Camera), {Value, Values});
});
connect(s.get(), &UserSettings::cameraResolutionChanged, this, [this]() {
emit dataChanged(index(CameraResolution), index(CameraResolution), {Value, Values});
});
connect(s.get(), &UserSettings::cameraFrameRateChanged, this, [this]() {
emit dataChanged(index(CameraFrameRate), index(CameraFrameRate), {Value, Values});
});
connect(s.get(), &UserSettings::ringtoneChanged, this, [this]() {
emit dataChanged(index(Ringtone), index(Ringtone), {Values, Value});
});
connect(s.get(), &UserSettings::onlyShareKeysWithVerifiedUsersChanged, this, [this]() {
emit dataChanged(
index(OnlyShareKeysWithVerifiedUsers), index(OnlyShareKeysWithVerifiedUsers), {Value});
});
connect(s.get(), &UserSettings::shareKeysWithTrustedUsersChanged, this, [this]() {
emit dataChanged(
index(ShareKeysWithTrustedUsers), index(ShareKeysWithTrustedUsers), {Value});
});
connect(s.get(), &UserSettings::useOnlineKeyBackupChanged, this, [this]() {
emit dataChanged(index(UseOnlineKeyBackup), index(UseOnlineKeyBackup), {Value});
});
connect(MainWindow::instance(), &MainWindow::secretsChanged, this, [this]() {
emit dataChanged(index(OnlineBackupKey), index(MasterKey), {Value, Good});
});
connect(s.get(), &UserSettings::exposeDBusApiChanged, this, [this] {
emit dataChanged(index(ExposeDBusApi), index(ExposeDBusApi), {Value});
});
connect(s.get(), &UserSettings::updateSpaceViasChanged, this, [this] {
emit dataChanged(index(UpdateSpaceVias), index(UpdateSpaceVias), {Value});
});
connect(s.get(), &UserSettings::expireEventsChanged, this, [this] {
emit dataChanged(index(ExpireEvents), index(ExpireEvents), {Value});
});
}