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.} =
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.} =

View File

@ -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())

View File

@ -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:

View File

@ -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

View File

@ -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() {

View File

@ -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 })
}
}