Merge pull request #1 from Lakoja/3486-separate-diff-from-encode
3486 separate diff from encode
This commit is contained in:
commit
a58f29fa57
|
@ -100,6 +100,14 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
private val currentProfileData
|
||||
get() = ProfileData(
|
||||
displayName = binding.displayNameEditText.text.toString(),
|
||||
note = binding.noteEditText.text.toString(),
|
||||
locked = binding.lockedCheckBox.isChecked,
|
||||
fields = accountFieldEditAdapter.getFieldData()
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@ -206,14 +214,14 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
}
|
||||
|
||||
val onBackCallback = object : OnBackPressedCallback(enabled = true) {
|
||||
override fun handleOnBackPressed() = checkForPotentialUnsavedChanges()
|
||||
override fun handleOnBackPressed() = checkForUnsavedChanges()
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, onBackCallback)
|
||||
}
|
||||
|
||||
fun checkForPotentialUnsavedChanges() {
|
||||
if (hasUnsavedChanges()) {
|
||||
fun checkForUnsavedChanges() {
|
||||
if (viewModel.hasUnsavedChanges(currentProfileData)) {
|
||||
showUnsavedChangesDialog()
|
||||
} else {
|
||||
finish()
|
||||
|
@ -223,18 +231,10 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (!isFinishing) {
|
||||
viewModel.updateProfile(profileData)
|
||||
viewModel.updateProfile(currentProfileData)
|
||||
}
|
||||
}
|
||||
|
||||
private val profileData
|
||||
get() = ProfileData(
|
||||
displayName = binding.displayNameEditText.text.toString(),
|
||||
note = binding.noteEditText.text.toString(),
|
||||
locked = binding.lockedCheckBox.isChecked,
|
||||
fields = accountFieldEditAdapter.getFieldData()
|
||||
)
|
||||
|
||||
private fun observeImage(
|
||||
liveData: LiveData<Uri>,
|
||||
imageView: ImageView,
|
||||
|
@ -308,7 +308,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun save() = viewModel.save(profileData)
|
||||
private fun save() = viewModel.save(currentProfileData)
|
||||
|
||||
private fun onSaveFailure(msg: String?) {
|
||||
val errorMsg = msg ?: getString(R.string.error_media_upload_sending)
|
||||
|
@ -328,8 +328,6 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
private fun hasUnsavedChanges() = viewModel.hasUnsavedChanges(profileData)
|
||||
|
||||
private suspend fun launchAlertDialog() = AlertDialog.Builder(this)
|
||||
.setTitle(getString(R.string.title_edit_profile_save_changes_prompt))
|
||||
.setMessage(getString(R.string.message_edit_profile_save_changes_prompt))
|
||||
|
|
|
@ -42,7 +42,6 @@ import kotlinx.coroutines.flow.shareIn
|
|||
import kotlinx.coroutines.launch
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.File
|
||||
|
@ -76,7 +75,7 @@ class EditProfileViewModel @Inject constructor(
|
|||
val instanceData: Flow<InstanceInfo> = instanceInfoRepo::getInstanceInfo.asFlow()
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
|
||||
private var oldProfileData: Account? = null
|
||||
private var apiProfileAccount: Account? = null
|
||||
|
||||
fun obtainProfile() = viewModelScope.launch {
|
||||
if (profileData.value == null || profileData.value is Error) {
|
||||
|
@ -84,7 +83,7 @@ class EditProfileViewModel @Inject constructor(
|
|||
|
||||
mastodonApi.accountVerifyCredentials().fold(
|
||||
{ profile ->
|
||||
oldProfileData = profile
|
||||
apiProfileAccount = profile
|
||||
profileData.postValue(Success(profile))
|
||||
},
|
||||
{
|
||||
|
@ -113,21 +112,42 @@ class EditProfileViewModel @Inject constructor(
|
|||
|
||||
saveData.value = Loading()
|
||||
|
||||
val encoded = encodeChangedProfileFields(newProfileData)
|
||||
if (encoded.allFieldsAreNull()) {
|
||||
// if nothing has changed, there is no need to make a network request
|
||||
val diff = getProfileDiff(apiProfileAccount, newProfileData)
|
||||
if (!diff.hasChanges()) {
|
||||
// if nothing has changed, there is no need to make an api call
|
||||
saveData.postValue(Success())
|
||||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
var avatarFileBody: MultipartBody.Part? = null
|
||||
diff.avatarFile?.let {
|
||||
avatarFileBody = MultipartBody.Part.createFormData("avatar", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull()))
|
||||
}
|
||||
|
||||
var headerFileBody: MultipartBody.Part? = null
|
||||
diff.headerFile?.let {
|
||||
headerFileBody = MultipartBody.Part.createFormData("header", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull()))
|
||||
}
|
||||
|
||||
mastodonApi.accountUpdateCredentials(
|
||||
encoded.displayName, encoded.note, encoded.locked, encoded.avatar, encoded.header,
|
||||
encoded.field1?.first, encoded.field1?.second, encoded.field2?.first, encoded.field2?.second, encoded.field3?.first, encoded.field3?.second, encoded.field4?.first, encoded.field4?.second
|
||||
diff.displayName?.toRequestBody(MultipartBody.FORM),
|
||||
diff.note?.toRequestBody(MultipartBody.FORM),
|
||||
diff.locked?.toString()?.toRequestBody(MultipartBody.FORM),
|
||||
avatarFileBody,
|
||||
headerFileBody,
|
||||
diff.field1?.first?.toRequestBody(MultipartBody.FORM),
|
||||
diff.field1?.second?.toRequestBody(MultipartBody.FORM),
|
||||
diff.field2?.first?.toRequestBody(MultipartBody.FORM),
|
||||
diff.field2?.second?.toRequestBody(MultipartBody.FORM),
|
||||
diff.field3?.first?.toRequestBody(MultipartBody.FORM),
|
||||
diff.field3?.second?.toRequestBody(MultipartBody.FORM),
|
||||
diff.field4?.first?.toRequestBody(MultipartBody.FORM),
|
||||
diff.field4?.second?.toRequestBody(MultipartBody.FORM)
|
||||
).fold(
|
||||
{ newProfileData ->
|
||||
{ newAccountData ->
|
||||
saveData.postValue(Success())
|
||||
eventHub.dispatch(ProfileEditedEvent(newProfileData))
|
||||
eventHub.dispatch(ProfileEditedEvent(newAccountData))
|
||||
},
|
||||
{ throwable ->
|
||||
saveData.postValue(Error(errorMessage = throwable.getServerErrorMessage()))
|
||||
|
@ -151,63 +171,61 @@ class EditProfileViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
internal fun hasUnsavedChanges(newProfileData: ProfileData): Boolean {
|
||||
val encoded = encodeChangedProfileFields(newProfileData)
|
||||
// If all fields are null, there are no changes.
|
||||
return !encoded.allFieldsAreNull()
|
||||
val diff = getProfileDiff(apiProfileAccount, newProfileData)
|
||||
|
||||
return diff.hasChanges()
|
||||
}
|
||||
|
||||
private fun encodeChangedProfileFields(newProfileData: ProfileData): EncodedProfileData {
|
||||
val displayName = if (oldProfileData?.displayName == newProfileData.displayName) {
|
||||
private fun getProfileDiff(oldProfileAccount: Account?, newProfileData: ProfileData): DiffProfileData {
|
||||
val displayName = if (oldProfileAccount?.displayName == newProfileData.displayName) {
|
||||
null
|
||||
} else {
|
||||
newProfileData.displayName.toRequestBody(MultipartBody.FORM)
|
||||
newProfileData.displayName
|
||||
}
|
||||
|
||||
val note = if (oldProfileData?.source?.note == newProfileData.note) {
|
||||
val note = if (oldProfileAccount?.source?.note == newProfileData.note) {
|
||||
null
|
||||
} else {
|
||||
newProfileData.note.toRequestBody(MultipartBody.FORM)
|
||||
newProfileData.note
|
||||
}
|
||||
|
||||
val locked = if (oldProfileData?.locked == newProfileData.locked) {
|
||||
val locked = if (oldProfileAccount?.locked == newProfileData.locked) {
|
||||
null
|
||||
} else {
|
||||
newProfileData.locked.toString().toRequestBody(MultipartBody.FORM)
|
||||
newProfileData.locked
|
||||
}
|
||||
|
||||
val avatar = if (avatarData.value != null) {
|
||||
val avatarBody = getCacheFileForName(AVATAR_FILE_NAME).asRequestBody("image/png".toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("avatar", randomAlphanumericString(12), avatarBody)
|
||||
val avatarFile = if (avatarData.value != null) {
|
||||
getCacheFileForName(AVATAR_FILE_NAME)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val header = if (headerData.value != null) {
|
||||
val headerBody = getCacheFileForName(HEADER_FILE_NAME).asRequestBody("image/png".toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("header", randomAlphanumericString(12), headerBody)
|
||||
val headerFile = if (headerData.value != null) {
|
||||
getCacheFileForName(HEADER_FILE_NAME)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// when one field changed, all have to be sent or they unchanged ones would get overridden
|
||||
val fieldsUnchanged = oldProfileData?.source?.fields == newProfileData.fields
|
||||
val field1 = calculateFieldToUpdate(newProfileData.fields.getOrNull(0), fieldsUnchanged)
|
||||
val field2 = calculateFieldToUpdate(newProfileData.fields.getOrNull(1), fieldsUnchanged)
|
||||
val field3 = calculateFieldToUpdate(newProfileData.fields.getOrNull(2), fieldsUnchanged)
|
||||
val field4 = calculateFieldToUpdate(newProfileData.fields.getOrNull(3), fieldsUnchanged)
|
||||
val allFieldsUnchanged = oldProfileAccount?.source?.fields == newProfileData.fields
|
||||
val field1 = calculateFieldToUpdate(newProfileData.fields.getOrNull(0), allFieldsUnchanged)
|
||||
val field2 = calculateFieldToUpdate(newProfileData.fields.getOrNull(1), allFieldsUnchanged)
|
||||
val field3 = calculateFieldToUpdate(newProfileData.fields.getOrNull(2), allFieldsUnchanged)
|
||||
val field4 = calculateFieldToUpdate(newProfileData.fields.getOrNull(3), allFieldsUnchanged)
|
||||
|
||||
return EncodedProfileData(
|
||||
displayName, note, locked, field1, field2, field3, field4, header, avatar
|
||||
return DiffProfileData(
|
||||
displayName, note, locked, field1, field2, field3, field4, headerFile, avatarFile
|
||||
)
|
||||
}
|
||||
|
||||
private fun calculateFieldToUpdate(newField: StringField?, fieldsUnchanged: Boolean): Pair<RequestBody, RequestBody>? {
|
||||
private fun calculateFieldToUpdate(newField: StringField?, fieldsUnchanged: Boolean): Pair<String, String>? {
|
||||
if (fieldsUnchanged || newField == null) {
|
||||
return null
|
||||
}
|
||||
return Pair(
|
||||
newField.name.toRequestBody(MultipartBody.FORM),
|
||||
newField.value.toRequestBody(MultipartBody.FORM)
|
||||
newField.name,
|
||||
newField.value
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -215,19 +233,19 @@ class EditProfileViewModel @Inject constructor(
|
|||
return File(application.cacheDir, filename)
|
||||
}
|
||||
|
||||
private data class EncodedProfileData(
|
||||
val displayName: RequestBody?,
|
||||
val note: RequestBody?,
|
||||
val locked: RequestBody?,
|
||||
val field1: Pair<RequestBody, RequestBody>?,
|
||||
val field2: Pair<RequestBody, RequestBody>?,
|
||||
val field3: Pair<RequestBody, RequestBody>?,
|
||||
val field4: Pair<RequestBody, RequestBody>?,
|
||||
val header: MultipartBody.Part?,
|
||||
val avatar: MultipartBody.Part?
|
||||
private data class DiffProfileData(
|
||||
val displayName: String?,
|
||||
val note: String?,
|
||||
val locked: Boolean?,
|
||||
val field1: Pair<String, String>?,
|
||||
val field2: Pair<String, String>?,
|
||||
val field3: Pair<String, String>?,
|
||||
val field4: Pair<String, String>?,
|
||||
val headerFile: File?,
|
||||
val avatarFile: File?
|
||||
) {
|
||||
fun allFieldsAreNull() = displayName == null && note == null && locked == null &&
|
||||
avatar == null && header == null && field1 == null && field2 == null &&
|
||||
field3 == null && field4 == null
|
||||
fun hasChanges() = displayName != null || note != null || locked != null ||
|
||||
avatarFile != null || headerFile != null || field1 != null || field2 != null ||
|
||||
field3 != null || field4 != null
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue