From 688238fdd3e3c92aa27e7ad75c53ac0515af72c1 Mon Sep 17 00:00:00 2001 From: Andrey Bocharnikov Date: Thu, 2 May 2024 19:14:55 +0400 Subject: [PATCH] Fix(Profile): Fix saving of profile identity changes (#14464) Problem: There were no difference between the fact that the avatar is not changed and the avatar is deleted. The same for bio and displayName On NIM, this state was simply rewritten Solution (The problem was discovered due to the fact that if the picture does not change, then Nim receives a base64 encoded source - instead of the path to the new Avatar. Which Status go could not parse as a file.) On the QML side, if we see that there have been changes, we add changes to changesMap Json, if there are no changes, then do not add. (This is the way to implement Option). On the NIM Side - convert to saveData On the Module side - distinguish Nil from an empty string fixes #14464 --- .../profile_section/profile/io_interface.nim | 2 +- .../profile/models/profile_save_data.nim | 25 +++++++++++-------- .../main/profile_section/profile/module.nim | 25 +++++++++++++------ .../main/profile_section/profile/view.nim | 8 +++--- .../Profile/stores/ProfileStore.qml | 23 ++++++----------- .../Profile/views/MyProfileView.qml | 24 ++++++++++++------ 6 files changed, 61 insertions(+), 46 deletions(-) diff --git a/src/app/modules/main/profile_section/profile/io_interface.nim b/src/app/modules/main/profile_section/profile/io_interface.nim index 9598f38117..f91ab347c9 100644 --- a/src/app/modules/main/profile_section/profile/io_interface.nim +++ b/src/app/modules/main/profile_section/profile/io_interface.nim @@ -45,7 +45,7 @@ method onProfileShowcasePreferencesSaveSucceeded*(self: AccessInterface) {.base. method onProfileShowcasePreferencesSaveFailed*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method saveProfileIdentity*(self: AccessInterface, identity: IdentitySaveData) {.base.} = +method saveProfileIdentityChanges*(self: AccessInterface, identity: IdentityChangesSaveData) {.base.} = raise newException(ValueError, "No implementation available") method saveProfileShowcasePreferences*(self: AccessInterface, showcase: ShowcaseSaveData) {.base.} = diff --git a/src/app/modules/main/profile_section/profile/models/profile_save_data.nim b/src/app/modules/main/profile_section/profile/models/profile_save_data.nim index c105dd1436..566045331b 100644 --- a/src/app/modules/main/profile_section/profile/models/profile_save_data.nim +++ b/src/app/modules/main/profile_section/profile/models/profile_save_data.nim @@ -1,4 +1,5 @@ import json, strutils, sequtils +import std/options include app_service/common/json_utils include app_service/common/utils @@ -30,10 +31,12 @@ type IdentityImage* = ref object of RootObj bX*: int bY*: int -type IdentitySaveData* = ref object of RootObj - displayName*: string - bio*: string - image*: IdentityImage +# Struct that contains the profile changes +# if field is none, it means that the user doesn't want to change it +type IdentityChangesSaveData* = ref object of RootObj + displayName*: Option[string] + bio*: Option[string] + image*: Option[IdentityImage] proc toShowcaseSaveEntry*(jsonObj: JsonNode): ShowcaseSaveEntry = result = ShowcaseSaveEntry() @@ -78,11 +81,11 @@ proc toIdentityImage*(jsonObj: JsonNode): IdentityImage = discard jsonObj.getProp("bX", result.bX) discard jsonObj.getProp("bY", result.bY) -proc toIdentitySaveData*(jsonObj: JsonNode): IdentitySaveData = - result = IdentitySaveData() - discard jsonObj.getProp("displayName", result.displayName) - discard jsonObj.getProp("bio", result.bio) +proc toIdentityChangesSaveData*(jsonObj: JsonNode): IdentityChangesSaveData = + result = IdentityChangesSaveData() + if jsonObj{"displayName"} != nil and jsonObj{"displayName"}.kind != JNull: + result.displayName = some(jsonObj{"displayName"}.getStr) + if jsonObj{"bio"} != nil and jsonObj{"bio"}.kind != JNull: + result.bio = some(jsonObj{"bio"}.getStr) if jsonObj{"image"} != nil and jsonObj{"image"}.kind != JNull: - result.image = jsonObj{"image"}.toIdentityImage() - else: - result.image = nil + result.image = some(jsonObj{"image"}.toIdentityImage()) diff --git a/src/app/modules/main/profile_section/profile/module.nim b/src/app/modules/main/profile_section/profile/module.nim index 8f6c12cd36..d327edb5d8 100644 --- a/src/app/modules/main/profile_section/profile/module.nim +++ b/src/app/modules/main/profile_section/profile/module.nim @@ -116,13 +116,24 @@ proc deleteIdentityImage*(self: Module): bool = let keyUid = singletonInstance.userProfile.getKeyUid() self.controller.deleteIdentityImage(keyUid) -method saveProfileIdentity*(self: Module, identity: IdentitySaveData) = - var ok = self.controller.setDisplayName(identity.displayName) - ok = ok and self.controller.setBio(identity.bio) - if identity.image != nil: - ok = ok and self.storeIdentityImage(identity.image) - else: - ok = ok and self.deleteIdentityImage() +method saveProfileIdentityChanges*(self: Module, identityChanges: IdentityChangesSaveData) = + var ok = true + + # Update only the fields that have changed + if identityChanges.displayName.isSome: + ok = self.controller.setDisplayName(identityChanges.displayName.get) + + if identityChanges.bio.isSome: + ok = ok and self.controller.setBio(identityChanges.bio.get) + + if identityChanges.image.isSome: + var image = identityChanges.image.get + # If the image source is empty, delete the image + if image.source.isEmptyOrWhitespace: + ok = ok and self.deleteIdentityImage() + else: + ok = ok and self.storeIdentityImage(image) + if ok: self.view.emitProfileIdentitySaveSucceededSignal() else: diff --git a/src/app/modules/main/profile_section/profile/view.nim b/src/app/modules/main/profile_section/profile/view.nim index 788ec2cd02..59e5883805 100644 --- a/src/app/modules/main/profile_section/profile/view.nim +++ b/src/app/modules/main/profile_section/profile/view.nim @@ -204,10 +204,10 @@ QtObject: QtProperty[QVariant] showcasePreferencesSocialLinksModel: read = getShowcasePreferencesSocialLinksModel - proc saveProfileIdentity(self: View, profileData: string) {.slot.} = - let profileDataObj = profileData.parseJson - let identityInfo = profileDataObj.toIdentitySaveData() - self.delegate.saveProfileIdentity(identityInfo) + proc saveProfileIdentityChanges(self: View, profileDataChanges: string) {.slot.} = + let profileDataChangesObj = profileDataChanges.parseJson + let identityChangesInfo = profileDataChangesObj.toIdentityChangesSaveData() + self.delegate.saveProfileIdentityChanges(identityChangesInfo) proc saveProfileShowcasePreferences(self: View, profileData: string) {.slot.} = let profileDataObj = profileData.parseJson diff --git a/ui/app/AppLayouts/Profile/stores/ProfileStore.qml b/ui/app/AppLayouts/Profile/stores/ProfileStore.qml index bf1beae4d6..92cec5710a 100644 --- a/ui/app/AppLayouts/Profile/stores/ProfileStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ProfileStore.qml @@ -77,21 +77,14 @@ QtObject { globalUtils.copyToClipboard(value) } - // Identity related: - function saveProfileIdentity(displayName, bio, source, aX, aY, bX, bY) { - var identityInfo = { - "displayName": displayName, - "bio": bio, - "image": source ? { - "source": source, - "aX": aX, - "aY": aY, - "bX": bX, - "bY": bY - } : null - } - let json = JSON.stringify(identityInfo) - root.profileModule.saveProfileIdentity(json) + function saveProfileIdentityChanges(displayName, bio, imageInfo) { + const changes = Object.assign({}, + displayName !== undefined && { displayName }, + bio !== undefined && { bio }, + imageInfo !== undefined && { image: imageInfo }) + + const json = JSON.stringify(changes) + root.profileModule.saveProfileIdentityChanges(json) } function getProfileShowcaseEntriesLimit() { diff --git a/ui/app/AppLayouts/Profile/views/MyProfileView.qml b/ui/app/AppLayouts/Profile/views/MyProfileView.qml index 98082f30cb..3fde35ea90 100644 --- a/ui/app/AppLayouts/Profile/views/MyProfileView.qml +++ b/ui/app/AppLayouts/Profile/views/MyProfileView.qml @@ -231,15 +231,23 @@ SettingsContentBase { root.profileStore.saveProfileShowcasePreferences(showcaseModels.buildJSONModelsCurrentState()) } - // Identity info + // Identity info. Update only those fields that have changed if (isIdentityTabDirty) { - root.profileStore.saveProfileIdentity(descriptionPanel.displayName.text, - descriptionPanel.bio.text.trim(), - profileHeader.icon, - profileHeader.cropRect.x, - profileHeader.cropRect.y, - (profileHeader.cropRect.x + profileHeader.cropRect.width), - (profileHeader.cropRect.y + profileHeader.cropRect.height)) + const imageChanged = profileHeader.icon !== profileStore.profileLargeImage + const displayNameChanged = descriptionPanel.displayName.text !== profileStore.displayName + const bioChanged = descriptionPanel.bio.text.trim() !== profileStore.bio.trim() + + root.profileStore.saveProfileIdentityChanges( + displayNameChanged ? descriptionPanel.displayName.text : undefined, + bioChanged ? descriptionPanel.bio.text.trim() : undefined, + imageChanged ? { + source : profileHeader.icon, + aX: profileHeader.cropRect.x, + aY: profileHeader.cropRect.y, + bX: profileHeader.cropRect.x + profileHeader.cropRect.width, + bY: profileHeader.cropRect.y + profileHeader.cropRect.height + } : undefined + ) profileHeader.icon = Qt.binding(() => { return profileStore.profileLargeImage }) } }