AccountSettingsManager: add client-side preferences

Added the ability for users to set client-side preferences. The current
implementation only has one preference available, that being the ability
to override the welcome page image.

GitLab: #2116
Change-Id: Ibdb6ec48e21961ecb39a0c8fa7889598bab4b188
This commit is contained in:
Ilyas Erdogan
2025-10-15 16:35:56 -04:00
parent d9f2dbde83
commit 7e5e29e43e
12 changed files with 296 additions and 2 deletions

View File

@@ -335,6 +335,7 @@ set(COMMON_SOURCES
${APP_SRC_DIR}/screensaver.cpp
${APP_SRC_DIR}/systemtray.cpp
${APP_SRC_DIR}/appsettingsmanager.cpp
${APP_SRC_DIR}/accountsettingsmanager.cpp
${APP_SRC_DIR}/lrcinstance.cpp
${APP_SRC_DIR}/selectablelistproxymodel.cpp
${APP_SRC_DIR}/conversationlistmodelbase.cpp
@@ -409,6 +410,7 @@ set(COMMON_HEADERS
${APP_SRC_DIR}/screensaver.h
${APP_SRC_DIR}/systemtray.h
${APP_SRC_DIR}/appsettingsmanager.h
${APP_SRC_DIR}/accountsettingsmanager.h
${APP_SRC_DIR}/lrcinstance.h
${APP_SRC_DIR}/selectablelistproxymodel.h
${APP_SRC_DIR}/conversationlistmodelbase.h
@@ -439,7 +441,9 @@ set(COMMON_HEADERS
${APP_SRC_DIR}/crashreporter.h
${APP_SRC_DIR}/linkdevicemodel.h
${APP_SRC_DIR}/qrcodescannermodel.h
${APP_SRC_DIR}/spellchecker.h)
${APP_SRC_DIR}/spellchecker.h
${APP_SRC_DIR}/accountsettingspropertymap.h
)
set(CONTRIB_DARWIN_FOLDER "")
if(APPLE)

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2025 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "accountsettingsmanager.h"
#include "lrcinstance.h"
#include <QApplication>
// X macro to future proof if other keys are needed
#define PROPERTY_KEYS \
/* key defaultValue */ \
X(backgroundUri, "")
AccountSettingsManager::AccountSettingsManager(QObject* parent)
: QObject {parent}
, accountSettings_ {new QSettings("jami.net", "Account", this)}
, accountSettingsPropertyMap_ {this}
{}
void
AccountSettingsManager::initalizeAccountSettings()
{
auto lrcInstance = qApp->property("LRCInstance").value<LRCInstance*>();
// The LRC instance has to exists for us to get the currentAccountID
if (!lrcInstance) {
qWarning() << "LRCInstance not available!";
return;
}
currentAccountID_ = lrcInstance->get_currentAccountId();
// Connect the property map's valueChanged signal
// to the account settings manager's setValue function
connect(&accountSettingsPropertyMap_,
&AccountSettingsPropertyMap::valueChanged,
this,
&AccountSettingsManager::setValue);
// Connect the LRC's currentAccountIdChanged signal
// to the account settings manager's updateCurrentAccount function
connect(lrcInstance, &LRCInstance::currentAccountIdChanged, this, [this, lrcInstance]() {
updateCurrentAccount(lrcInstance->get_currentAccountId());
});
// Check for an existing config for this account
if (accountSettings_->allKeys().size() == 0) {
// No existing file
qWarning() << "Creating config file for account:" << currentAccountID_;
accountSettings_->beginGroup(currentAccountID_);
#define X(key, defaultValue) accountSettings_->setValue(#key, defaultValue);
PROPERTY_KEYS
#undef X
accountSettings_->endGroup();
// Force writing to the configuration file for immediate access
accountSettings_->sync();
// Populate the property map
#define X(key, defaultValue) \
accountSettingsPropertyMap_.setAccountSettingProperty( \
#key, accountSettings_->value(currentAccountID_ + "/" + #key).toString());
PROPERTY_KEYS
#undef X
} else {
// Populate the map with the current value found in the QSettings config
// Get the current background URL of the account
#define X(key, defaultValue) \
accountSettingsPropertyMap_.setAccountSettingProperty( \
#key, accountSettings_->value(currentAccountID_ + "/" + #key).toString());
PROPERTY_KEYS
#undef X
qWarning() << "Loaded existing settings for account:" << currentAccountID_;
}
}
void
AccountSettingsManager::updateCurrentAccount(const QString& newCurrentAccountID)
{
// Accounts didn't change, no need to update anything
if (currentAccountID_ == newCurrentAccountID) {
return;
}
qWarning() << "Account changed from" << currentAccountID_ << "to" << newCurrentAccountID;
currentAccountID_ = newCurrentAccountID;
// Load existing settings for this account
#define X(key, defaultValue) \
accountSettingsPropertyMap_.setAccountSettingProperty( \
#key, accountSettings_->value(currentAccountID_ + "/" + #key).toString());
PROPERTY_KEYS
#undef X
}
void
AccountSettingsManager::setValue(const QString& key, const QVariant& value)
{
accountSettings_->beginGroup(currentAccountID_);
accountSettings_->setValue(key, value);
accountSettings_->endGroup();
}
QVariant
AccountSettingsManager::getValue(const QString& key)
{
return accountSettingsPropertyMap_.value(key);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2025 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QObject>
#include <QSettings>
#include "accountsettingspropertymap.h"
class AccountSettingsManager : public QObject
{
Q_OBJECT
Q_PROPERTY(AccountSettingsPropertyMap* accountSettingsPropertyMap READ accountSettingsPropertyMap CONSTANT)
public:
explicit AccountSettingsManager(QObject* parent = nullptr);
AccountSettingsPropertyMap* accountSettingsPropertyMap()
{
return &accountSettingsPropertyMap_;
}
Q_INVOKABLE void initalizeAccountSettings();
Q_INVOKABLE void updateCurrentAccount(const QString& newCurrentAccountID);
Q_INVOKABLE void setValue(const QString& key, const QVariant& value);
Q_INVOKABLE QVariant getValue(const QString& key);
private:
QSettings* accountSettings_;
AccountSettingsPropertyMap accountSettingsPropertyMap_;
QString currentAccountID_;
};

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2025 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QQmlPropertyMap>
class AccountSettingsPropertyMap : public QQmlPropertyMap
{
Q_OBJECT
public:
explicit AccountSettingsPropertyMap(QObject* parent = nullptr)
: QQmlPropertyMap(this, parent)
{}
void setAccountSettingProperty(const QString& key, const QVariant& value)
{
updateValue(key, value);
}
protected:
QVariant updateValue(const QString& key, const QVariant& value) override
{
// No need to update the value if theyre the same!
if (this->value(key) != value) {
insert(key, value);
Q_EMIT valueChanged(key, value);
}
return value;
}
Q_SIGNALS:
void valueChanged(const QString& key, const QVariant& value);
};

View File

@@ -20,6 +20,7 @@
#include "global.h"
#include "qmlregister.h"
#include "appsettingsmanager.h"
#include "accountsettingsmanager.h"
#include "connectivitymonitor.h"
#include "systemtray.h"
#include "previewengine.h"
@@ -192,6 +193,7 @@ MainApplication::init()
// enabled.
settingsManager_ = new AppSettingsManager(this);
crashReporter_ = new CrashReporter(settingsManager_, this);
accountSettingsManager_ = new AccountSettingsManager(this);
// This 2-phase initialisation prevents ephemeral instances from
// performing unnecessary tasks, like initializing the WebEngine.
@@ -271,6 +273,8 @@ MainApplication::init()
initQmlLayer();
accountSettingsManager_->initalizeAccountSettings();
settingsManager_->setValue(Settings::Key::StartMinimized,
runOptions_[Option::StartMinimized].toBool());
@@ -424,6 +428,7 @@ MainApplication::initQmlLayer()
lrcInstance_.get(),
systemTray_,
settingsManager_,
accountSettingsManager_,
connectivityMonitor_,
previewEngine_,
&screenInfo_,

View File

@@ -31,6 +31,7 @@
class ConnectivityMonitor;
class SystemTray;
class AppSettingsManager;
class AccountSettingsManager;
class CrashReporter;
class PreviewEngine;
@@ -118,6 +119,7 @@ private:
ConnectivityMonitor* connectivityMonitor_;
SystemTray* systemTray_;
AppSettingsManager* settingsManager_;
AccountSettingsManager* accountSettingsManager_;
PreviewEngine* previewEngine_;
CrashReporter* crashReporter_;

View File

@@ -66,6 +66,9 @@ ListSelectionView {
property bool hasLogo: true
property bool hasTips: true
property bool hasOverridenBgImage: false
property string overridenImageUrl: ""
//logoSize has to be between 0 and 1
property real logoSize: 1
@@ -106,6 +109,9 @@ ListSelectionView {
description = hasCustomUi && uiCustomization.description !== undefined ? uiCustomization.description : JamiStrings.hereIsIdentifier;
hasLogo = hasCustomUi ? uiCustomization.logoUrl !== "" : true;
hasTips = hasCustomUi ? uiCustomization.areTipsEnabled : true;
const bgOverride = AccountSettingsManager.accountSettingsPropertyMap.backgroundUri;
overridenImageUrl = bgOverride === undefined ? "" : bgOverride;
hasOverridenBgImage = bgOverride !== undefined && bgOverride !== "";
hasCustomBgImage = (hasCustomUi && uiCustomization.backgroundType === "image");
customBgUrl = hasCustomBgImage ? (CurrentAccount.managerUri + uiCustomization.backgroundColorOrUrl) : "";
hasCustomBgColor = (hasCustomUi && uiCustomization.backgroundType === "color");
@@ -148,7 +154,9 @@ ListSelectionView {
CachedImage {
id: cachedImgLogo
downloadUrl: hasCustomBgImage ? customBgUrl : JamiTheme.welcomeBg
downloadUrl: (AccountSettingsManager.accountSettingsPropertyMap.backgroundUri === undefined || AccountSettingsManager.accountSettingsPropertyMap.backgroundUri === "")
? (hasCustomBgImage ? customBgUrl : JamiTheme.welcomeBg)
: AccountSettingsManager.accountSettingsPropertyMap.backgroundUri
visible: !hasCustomBgColor
anchors.fill: parent
opacity: visible ? 1 : 0

View File

@@ -894,6 +894,9 @@ Item {
// Appearence
property string theme: qsTr("Theme")
property string zoomLevel: qsTr("Text zoom level")
property string backgroundImage: qsTr("Background image")
property string selectBackgroundImage: qsTr("Select background image")
property string defaultImage: qsTr("Default")
// Donation campaign
property string donationTipBoxText: qsTr("Free and private sharing. <a href=\"https://jami.net/donate/\">Donate</a> to expand it.")

View File

@@ -58,6 +58,7 @@
#include "avatarimageprovider.h"
#include "avatarregistry.h"
#include "appsettingsmanager.h"
#include "accountsettingsmanager.h"
#include "mainapplication.h"
#include "namedirectory.h"
#include "pluginversionmanager.h"
@@ -120,6 +121,7 @@ registerTypes(QQmlEngine* engine,
LRCInstance* lrcInstance,
SystemTray* systemTray,
AppSettingsManager* settingsManager,
AccountSettingsManager* accountSettingsManager,
ConnectivityMonitor* connectivityMonitor,
PreviewEngine* previewEngine,
ScreenInfo* screenInfo,
@@ -206,6 +208,7 @@ registerTypes(QQmlEngine* engine,
qApp->setProperty("LRCInstance", QVariant::fromValue(lrcInstance));
qApp->setProperty("SystemTray", QVariant::fromValue(systemTray));
qApp->setProperty("AppSettingsManager", QVariant::fromValue(settingsManager));
qApp->setProperty("AccountSettingsManager", QVariant::fromValue(accountSettingsManager));
qApp->setProperty("ConnectivityMonitor", QVariant::fromValue(connectivityMonitor));
qApp->setProperty("PreviewEngine", QVariant::fromValue(previewEngine));
@@ -260,6 +263,7 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, screenInfo, "CurrentScreenInfo")
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, lrcInstance, "LRCInstance")
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, settingsManager, "AppSettingsManager")
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, accountSettingsManager, "AccountSettingsManager")
// Lrc namespaces, models, and singletons
QML_REGISTERNAMESPACE(NS_MODELS, lrc::api::staticMetaObject, "Lrc");

View File

@@ -32,6 +32,7 @@
class SystemTray;
class LRCInstance;
class AppSettingsManager;
class AccountSettingsManager;
class PreviewEngine;
class ScreenInfo;
class MainApplication;
@@ -61,6 +62,7 @@ void registerTypes(QQmlEngine* engine,
LRCInstance* lrcInstance,
SystemTray* systemTray,
AppSettingsManager* appSettingsManager,
AccountSettingsManager* accountSettingsManager,
ConnectivityMonitor* connectivityMonitor,
PreviewEngine* previewEngine,
ScreenInfo* screenInfo,

View File

@@ -17,6 +17,7 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.platform
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Enums 1.1
@@ -140,6 +141,52 @@ SettingsPageBase {
}
}
}
RowLayout {
SettingMaterialButton {
id: btnBackgroundImageDirSelect
Layout.fillWidth: true
enabled: true
textField: AccountSettingsManager.accountSettingsPropertyMap.backgroundUri === "" ? JamiStrings.defaultImage : UtilsAdapter.getAbsPath(AccountSettingsManager.accountSettingsPropertyMap.backgroundUri)
titleField: JamiStrings.backgroundImage
itemWidth: root.itemWidth
onSettingMaterialButtonClicked: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/JamiFileDialog.qml", {
title: JamiStrings.selectBackgroundImage,
fileMode: JamiFileDialog.OpenFile,
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation),
nameFilters: [JamiStrings.imageFiles, JamiStrings.allFiles]
});
dlg.fileAccepted.connect(function (file) {
if (file.toString().length !== 0) {
AccountSettingsManager.accountSettingsPropertyMap.backgroundUri = file;
}
});
}
}
JamiPushButton {
id: shareButton
width: visible ? preferredSize : 0
height: visible ? preferredSize : 0
Layout.alignment: Qt.AlignVCenter
visible: AccountSettingsManager.accountSettingsPropertyMap.backgroundUri !== ""
toolTipText: JamiStrings.reset
source: JamiResources.round_close_24dp_svg
normalColor: JamiTheme.transparentColor
imageColor: hovered ? JamiTheme.buttonTintedBlueHovered : JamiTheme.textColor
onClicked: AccountSettingsManager.accountSettingsPropertyMap.backgroundUri = ""
}
}
}
ColumnLayout {
@@ -265,6 +312,7 @@ SettingsPageBase {
zoomSpinBox.value = Math.round(UtilsAdapter.getDefault(Settings.BaseZoom) * 100.0);
UtilsAdapter.setToDefault(Settings.Key.AppTheme);
UtilsAdapter.setToDefault(Settings.Key.BaseZoom);
AccountSettingsManager.accountSettingsPropertyMap.backgroundUri = "";
themeSettings.isComplete();
}
}

View File

@@ -16,6 +16,7 @@
*/
#include "appsettingsmanager.h"
#include "accountsettingsmanager.h"
#include "connectivitymonitor.h"
#include "mainapplication.h"
#include "previewengine.h"
@@ -92,6 +93,7 @@ public Q_SLOTS:
connectivityMonitor_.reset(new ConnectivityMonitor(this));
settingsManager_.reset(new AppSettingsManager(this));
accountSettingsManager_.reset(new AccountSettingsManager(this));
systemTray_.reset(new SystemTray(settingsManager_.get(), this));
previewEngine_.reset(new PreviewEngine(connectivityMonitor_.get(), this));
@@ -152,6 +154,7 @@ public Q_SLOTS:
lrcInstance_.get(),
systemTray_.get(),
settingsManager_.get(),
accountSettingsManager_.get(),
connectivityMonitor_.get(),
previewEngine_.get(),
&screenInfo_,
@@ -169,6 +172,7 @@ private:
QScopedPointer<ConnectivityMonitor> connectivityMonitor_;
QScopedPointer<AppSettingsManager> settingsManager_;
QScopedPointer<AccountSettingsManager> accountSettingsManager_;
QScopedPointer<SystemTray> systemTray_;
QScopedPointer<PreviewEngine> previewEngine_;
ScreenInfo screenInfo_;