diff --git a/TODO.md b/TODO.md index c8dd3d2d..b17a493d 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,4 @@ -- aiofiles Thumbnails +- Set Qt.application.* stuff from C++ - Devices and client settings in edit account page - Multiaccount aliases - If avatar is set, name color from average color? diff --git a/src/python/config_files.py b/src/python/config_files.py index ab779666..91eb3fec 100644 --- a/src/python/config_files.py +++ b/src/python/config_files.py @@ -11,6 +11,8 @@ from dataclasses import dataclass, field from .backend import Backend +JsonData = Dict[str, Any] + WRITE_LOCK = asyncio.Lock() @@ -27,14 +29,18 @@ class ConfigFile: @dataclass class JSONConfigFile(ConfigFile): - async def read(self) -> Dict[str, Any]: + async def default_data(self) -> JsonData: + return {} + + + async def read(self) -> JsonData: try: return json.loads(self.path.read_text()) except (json.JSONDecodeError, FileNotFoundError): - return {} + return await self.default_data() - async def write(self, data: Dict[str, Any]) -> None: + async def write(self, data: JsonData) -> None: js = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True) async with WRITE_LOCK: @@ -76,3 +82,8 @@ class Accounts(JSONConfigFile): @dataclass class UISettings(JSONConfigFile): filename: str = "ui-settings.json" + + async def default_data(self) -> JsonData: + return { + "write_aliases": {} + } diff --git a/src/qml/Pages/EditAccount/Profile.qml b/src/qml/Pages/EditAccount/Profile.qml index eeb7ad1c..48daff6b 100644 --- a/src/qml/Pages/EditAccount/Profile.qml +++ b/src/qml/Pages/EditAccount/Profile.qml @@ -19,6 +19,11 @@ HGridLayout { ) } + if (aliasField.changed) { + window.settings.write_aliases[userId] = aliasField.field.text + window.settingsChanged() + } + if (avatar.changed) { saveButton.avatarChangeRunning = true let path = Qt.resolvedUrl(avatar.imageUrl).replace(/^file:/, "") @@ -32,6 +37,13 @@ HGridLayout { } } + function cancelChanges() { + nameField.field.text = userInfo.displayName + aliasField.field.text = aliasField.currentAlias + fileDialog.selectedFile = "" + fileDialog.file = "" + } + columns: 2 flow: window.isWide ? GridLayout.LeftToRight : GridLayout.TopToBottom rowSpacing: currentSpacing @@ -123,6 +135,21 @@ HGridLayout { Layout.fillWidth: true Layout.maximumWidth: 480 } + + HLabeledTextField { + property string currentAlias: + window.settings.write_aliases[userId] || "" + + property bool changed: field.text != currentAlias + + id: aliasField + label.text: qsTr("Write alias:") + field.text: currentAlias + field.onAccepted: applyChanges() + + Layout.fillWidth: true + Layout.maximumWidth: 480 + } } HSpacer {} @@ -138,7 +165,8 @@ HGridLayout { iconName: "apply" text: qsTr("Apply") loading: nameChangeRunning || avatarChangeRunning - enabled: nameField.changed || avatar.changed + enabled: + nameField.changed || aliasField.changed || avatar.changed Layout.fillWidth: true Layout.alignment: Qt.AlignBottom @@ -149,16 +177,12 @@ HGridLayout { HUIButton { iconName: "cancel" text: qsTr("Cancel") + enabled: saveButton.enabled && ! saveButton.loading Layout.fillWidth: true Layout.alignment: Qt.AlignBottom - enabled: saveButton.enabled && ! saveButton.loading - onClicked: { - nameField.field.text = userInfo.displayName - fileDialog.selectedFile = "" - fileDialog.file = "" - } + onClicked: cancelChanges() } } } diff --git a/src/qml/Python.qml b/src/qml/Python.qml index a82ea070..0d6d53c1 100644 --- a/src/qml/Python.qml +++ b/src/qml/Python.qml @@ -33,6 +33,11 @@ Python { call("APP.call_client_coro", [accountId, name, uuid, args]) } + function saveSettings(callback=null) { + if (! py.ready) { return } // config not loaded yet + callCoro("ui_settings.write", [window.settings], callback) + } + Component.onCompleted: { for (var func in EventHandlers) { if (EventHandlers.hasOwnProperty(func)) { @@ -46,16 +51,20 @@ Python { call("APP.is_debug_on", [Qt.application.arguments], on => { window.debug = on - callCoro("saved_accounts.any_saved", [], any => { - py.ready = true - willLoadAccounts(any) + callCoro("ui_settings.read", [], settings => { + window.settings = settings - if (any) { - py.loadingAccounts = true - py.callCoro("load_saved_accounts", [], () => { - py.loadingAccounts = false - }) - } + callCoro("saved_accounts.any_saved", [], any => { + py.ready = true + willLoadAccounts(any) + + if (any) { + py.loadingAccounts = true + py.callCoro("load_saved_accounts", [], () => { + py.loadingAccounts = false + }) + } + }) }) }) }) diff --git a/src/qml/Window.qml b/src/qml/Window.qml index c29ab563..23b20c6d 100644 --- a/src/qml/Window.qml +++ b/src/qml/Window.qml @@ -16,10 +16,6 @@ ApplicationWindow { title: "Harmony QML" color: "black" - property bool debug: false - property bool ready: false - property bool isWide: width > theme.isWideAbove - Component.onCompleted: { Qt.application.organization = "harmonyqml" Qt.application.name = "harmonyqml" @@ -28,6 +24,14 @@ ApplicationWindow { window.ready = true } + property bool debug: false + property bool ready: false + property bool isWide: width > theme.isWideAbove + + // Note: window.settingsChanged() must be called manually + property var settings: ({}) + onSettingsChanged: py.saveSettings() + Theme { id: theme } Python { id: py }