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
This commit is contained in:
Andrey Bocharnikov 2024-05-02 19:14:55 +04:00 committed by Jonathan Rainville
parent 1884f6e033
commit 688238fdd3
6 changed files with 61 additions and 46 deletions

View File

@ -45,7 +45,7 @@ method onProfileShowcasePreferencesSaveSucceeded*(self: AccessInterface) {.base.
method onProfileShowcasePreferencesSaveFailed*(self: AccessInterface) {.base.} = method onProfileShowcasePreferencesSaveFailed*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") 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") raise newException(ValueError, "No implementation available")
method saveProfileShowcasePreferences*(self: AccessInterface, showcase: ShowcaseSaveData) {.base.} = method saveProfileShowcasePreferences*(self: AccessInterface, showcase: ShowcaseSaveData) {.base.} =

View File

@ -1,4 +1,5 @@
import json, strutils, sequtils import json, strutils, sequtils
import std/options
include app_service/common/json_utils include app_service/common/json_utils
include app_service/common/utils include app_service/common/utils
@ -30,10 +31,12 @@ type IdentityImage* = ref object of RootObj
bX*: int bX*: int
bY*: int bY*: int
type IdentitySaveData* = ref object of RootObj # Struct that contains the profile changes
displayName*: string # if field is none, it means that the user doesn't want to change it
bio*: string type IdentityChangesSaveData* = ref object of RootObj
image*: IdentityImage displayName*: Option[string]
bio*: Option[string]
image*: Option[IdentityImage]
proc toShowcaseSaveEntry*(jsonObj: JsonNode): ShowcaseSaveEntry = proc toShowcaseSaveEntry*(jsonObj: JsonNode): ShowcaseSaveEntry =
result = ShowcaseSaveEntry() result = ShowcaseSaveEntry()
@ -78,11 +81,11 @@ proc toIdentityImage*(jsonObj: JsonNode): IdentityImage =
discard jsonObj.getProp("bX", result.bX) discard jsonObj.getProp("bX", result.bX)
discard jsonObj.getProp("bY", result.bY) discard jsonObj.getProp("bY", result.bY)
proc toIdentitySaveData*(jsonObj: JsonNode): IdentitySaveData = proc toIdentityChangesSaveData*(jsonObj: JsonNode): IdentityChangesSaveData =
result = IdentitySaveData() result = IdentityChangesSaveData()
discard jsonObj.getProp("displayName", result.displayName) if jsonObj{"displayName"} != nil and jsonObj{"displayName"}.kind != JNull:
discard jsonObj.getProp("bio", result.bio) 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: if jsonObj{"image"} != nil and jsonObj{"image"}.kind != JNull:
result.image = jsonObj{"image"}.toIdentityImage() result.image = some(jsonObj{"image"}.toIdentityImage())
else:
result.image = nil

View File

@ -116,13 +116,24 @@ proc deleteIdentityImage*(self: Module): bool =
let keyUid = singletonInstance.userProfile.getKeyUid() let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.deleteIdentityImage(keyUid) self.controller.deleteIdentityImage(keyUid)
method saveProfileIdentity*(self: Module, identity: IdentitySaveData) = method saveProfileIdentityChanges*(self: Module, identityChanges: IdentityChangesSaveData) =
var ok = self.controller.setDisplayName(identity.displayName) var ok = true
ok = ok and self.controller.setBio(identity.bio)
if identity.image != nil: # Update only the fields that have changed
ok = ok and self.storeIdentityImage(identity.image) if identityChanges.displayName.isSome:
else: ok = self.controller.setDisplayName(identityChanges.displayName.get)
ok = ok and self.deleteIdentityImage()
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: if ok:
self.view.emitProfileIdentitySaveSucceededSignal() self.view.emitProfileIdentitySaveSucceededSignal()
else: else:

View File

@ -204,10 +204,10 @@ QtObject:
QtProperty[QVariant] showcasePreferencesSocialLinksModel: QtProperty[QVariant] showcasePreferencesSocialLinksModel:
read = getShowcasePreferencesSocialLinksModel read = getShowcasePreferencesSocialLinksModel
proc saveProfileIdentity(self: View, profileData: string) {.slot.} = proc saveProfileIdentityChanges(self: View, profileDataChanges: string) {.slot.} =
let profileDataObj = profileData.parseJson let profileDataChangesObj = profileDataChanges.parseJson
let identityInfo = profileDataObj.toIdentitySaveData() let identityChangesInfo = profileDataChangesObj.toIdentityChangesSaveData()
self.delegate.saveProfileIdentity(identityInfo) self.delegate.saveProfileIdentityChanges(identityChangesInfo)
proc saveProfileShowcasePreferences(self: View, profileData: string) {.slot.} = proc saveProfileShowcasePreferences(self: View, profileData: string) {.slot.} =
let profileDataObj = profileData.parseJson let profileDataObj = profileData.parseJson

View File

@ -77,21 +77,14 @@ QtObject {
globalUtils.copyToClipboard(value) globalUtils.copyToClipboard(value)
} }
// Identity related: function saveProfileIdentityChanges(displayName, bio, imageInfo) {
function saveProfileIdentity(displayName, bio, source, aX, aY, bX, bY) { const changes = Object.assign({},
var identityInfo = { displayName !== undefined && { displayName },
"displayName": displayName, bio !== undefined && { bio },
"bio": bio, imageInfo !== undefined && { image: imageInfo })
"image": source ? {
"source": source, const json = JSON.stringify(changes)
"aX": aX, root.profileModule.saveProfileIdentityChanges(json)
"aY": aY,
"bX": bX,
"bY": bY
} : null
}
let json = JSON.stringify(identityInfo)
root.profileModule.saveProfileIdentity(json)
} }
function getProfileShowcaseEntriesLimit() { function getProfileShowcaseEntriesLimit() {

View File

@ -231,15 +231,23 @@ SettingsContentBase {
root.profileStore.saveProfileShowcasePreferences(showcaseModels.buildJSONModelsCurrentState()) root.profileStore.saveProfileShowcasePreferences(showcaseModels.buildJSONModelsCurrentState())
} }
// Identity info // Identity info. Update only those fields that have changed
if (isIdentityTabDirty) { if (isIdentityTabDirty) {
root.profileStore.saveProfileIdentity(descriptionPanel.displayName.text, const imageChanged = profileHeader.icon !== profileStore.profileLargeImage
descriptionPanel.bio.text.trim(), const displayNameChanged = descriptionPanel.displayName.text !== profileStore.displayName
profileHeader.icon, const bioChanged = descriptionPanel.bio.text.trim() !== profileStore.bio.trim()
profileHeader.cropRect.x,
profileHeader.cropRect.y, root.profileStore.saveProfileIdentityChanges(
(profileHeader.cropRect.x + profileHeader.cropRect.width), displayNameChanged ? descriptionPanel.displayName.text : undefined,
(profileHeader.cropRect.y + profileHeader.cropRect.height)) 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 }) profileHeader.icon = Qt.binding(() => { return profileStore.profileLargeImage })
} }
} }