Personal account notes (#1978)

* add personal notes to AccountActivity

* use RxJava instead of plain okhttp calls

* make AccountViewModel rx aware

* hide note input until data is loaded
main
Konrad Pozniak 4 years ago committed by Alibek Omarov
parent 0d644e8fe3
commit 9ea9926469
  1. 21
      app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt
  2. 10
      app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt
  3. 1
      app/src/main/java/com/keylesspalace/tusky/entity/Relationship.kt
  4. 73
      app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt
  5. 34
      app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt
  6. 52
      app/src/main/java/com/keylesspalace/tusky/network/TimelineCases.kt
  7. 146
      app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountViewModel.kt
  8. 32
      app/src/main/res/layout/activity_account.xml
  9. 2
      app/src/main/res/values/strings.xml

@ -25,6 +25,7 @@ import android.graphics.Color
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffColorFilter
import android.os.Bundle import android.os.Bundle
import android.text.Editable
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
@ -142,6 +143,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
if (viewModel.isSelf) { if (viewModel.isSelf) {
updateButtons() updateButtons()
saveNoteInfo.hide()
} else {
saveNoteInfo.visibility = View.INVISIBLE
} }
} }
@ -348,8 +352,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
viewModel.accountFieldData.observe(this, Observer<List<Either<IdentityProof, Field>>> { viewModel.accountFieldData.observe(this, Observer<List<Either<IdentityProof, Field>>> {
accountFieldAdapter.fields = it accountFieldAdapter.fields = it
accountFieldAdapter.notifyDataSetChanged() accountFieldAdapter.notifyDataSetChanged()
}) })
viewModel.noteSaved.observe(this) {
saveNoteInfo.visible(it, View.INVISIBLE)
}
} }
/** /**
@ -636,9 +642,22 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
subscribing = relation.subscribing subscribing = relation.subscribing
} }
accountNoteTextInputLayout.visible(relation.note != null)
accountNoteTextInputLayout.editText?.setText(relation.note)
// add the listener late to avoid it firing on the first change
accountNoteTextInputLayout.editText?.removeTextChangedListener(noteWatcher)
accountNoteTextInputLayout.editText?.addTextChangedListener(noteWatcher)
updateButtons() updateButtons()
} }
private val noteWatcher = object: DefaultTextWatcher() {
override fun afterTextChanged(s: Editable) {
viewModel.noteChanged(s.toString())
}
}
private fun updateFollowButton() { private fun updateFollowButton() {
if (viewModel.isSelf) { if (viewModel.isSelf) {
accountFollowButton.setText(R.string.action_edit_own_profile) accountFollowButton.setText(R.string.action_edit_own_profile)

@ -100,7 +100,7 @@ class ReportViewModel @Inject constructor(
val ids = listOf(accountId) val ids = listOf(accountId)
muteStateMutable.value = Loading() muteStateMutable.value = Loading()
blockStateMutable.value = Loading() blockStateMutable.value = Loading()
mastodonApi.relationshipsObservable(ids) mastodonApi.relationships(ids)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
@ -129,9 +129,9 @@ class ReportViewModel @Inject constructor(
fun toggleMute() { fun toggleMute() {
val alreadyMuted = muteStateMutable.value?.data == true val alreadyMuted = muteStateMutable.value?.data == true
if (alreadyMuted) { if (alreadyMuted) {
mastodonApi.unmuteAccountObservable(accountId) mastodonApi.unmuteAccount(accountId)
} else { } else {
mastodonApi.muteAccountObservable(accountId) mastodonApi.muteAccount(accountId)
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -154,9 +154,9 @@ class ReportViewModel @Inject constructor(
fun toggleBlock() { fun toggleBlock() {
val alreadyBlocked = blockStateMutable.value?.data == true val alreadyBlocked = blockStateMutable.value?.data == true
if (alreadyBlocked) { if (alreadyBlocked) {
mastodonApi.unblockAccountObservable(accountId) mastodonApi.unblockAccount(accountId)
} else { } else {
mastodonApi.blockAccountObservable(accountId) mastodonApi.blockAccount(accountId)
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

@ -27,4 +27,5 @@ data class Relationship (
@SerializedName("showing_reblogs") val showingReblogs: Boolean, @SerializedName("showing_reblogs") val showingReblogs: Boolean,
val subscribing: Boolean? = null, // Pleroma extension val subscribing: Boolean? = null, // Pleroma extension
@SerializedName("domain_blocking") val blockingDomain: Boolean @SerializedName("domain_blocking") val blockingDomain: Boolean
val note: String? // nullable for backward compatibility / feature detection
) )

@ -49,7 +49,6 @@ import retrofit2.Response
import java.io.IOException import java.io.IOException
import javax.inject.Inject import javax.inject.Inject
class AccountListFragment : BaseFragment(), AccountActionListener, Injectable { class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
@Inject @Inject
@ -113,28 +112,18 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
} }
} }
override fun onMute(mute: Boolean, id: String, position: Int) { override fun onMute(mute: Boolean, id: String, position: Int, notifications: Boolean) {
val callback = object : Callback<Relationship> { if (!mute) {
override fun onResponse(call: Call<Relationship>, response: Response<Relationship>) {
if (response.isSuccessful) {
onMuteSuccess(mute, id, position)
} else {
onMuteFailure(mute, id)
}
}
override fun onFailure(call: Call<Relationship>, t: Throwable) {
onMuteFailure(mute, id)
}
}
val call = if (!mute) {
api.unmuteAccount(id) api.unmuteAccount(id)
} else { } else {
api.muteAccount(id) api.muteAccount(id)
} }
callList.add(call) .autoDispose(from(this))
call.enqueue(callback) .subscribe({
onMuteSuccess(mute, id, position, notifications)
}, {
onMuteFailure(mute, id, notifications)
})
} }
private fun onMuteSuccess(muted: Boolean, id: String, position: Int) { private fun onMuteSuccess(muted: Boolean, id: String, position: Int) {
@ -164,27 +153,17 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
} }
override fun onBlock(block: Boolean, id: String, position: Int) { override fun onBlock(block: Boolean, id: String, position: Int) {
val cb = object : Callback<Relationship> { if (!block) {
override fun onResponse(call: Call<Relationship>, response: Response<Relationship>) {
if (response.isSuccessful) {
onBlockSuccess(block, id, position)
} else {
onBlockFailure(block, id)
}
}
override fun onFailure(call: Call<Relationship>, t: Throwable) {
onBlockFailure(block, id)
}
}
val call = if (!block) {
api.unblockAccount(id) api.unblockAccount(id)
} else { } else {
api.blockAccount(id) api.blockAccount(id)
} }
callList.add(call) .autoDispose(from(this))
call.enqueue(cb) .subscribe({
onBlockSuccess(block, id, position)
}, {
onBlockFailure(block, id)
})
} }
private fun onBlockSuccess(blocked: Boolean, id: String, position: Int) { private fun onBlockSuccess(blocked: Boolean, id: String, position: Int) {
@ -366,6 +345,28 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
} }
} }
<<<<<<< HEAD
=======
private fun fetchRelationships(ids: List<String>) {
api.relationships(ids)
.autoDispose(from(this))
.subscribe(::onFetchRelationshipsSuccess) {
onFetchRelationshipsFailure(ids)
}
}
private fun onFetchRelationshipsSuccess(relationships: List<Relationship>) {
val mutesAdapter = adapter as MutesAdapter
val mutingNotificationsMap = HashMap<String, Boolean>()
relationships.map { mutingNotificationsMap.put(it.id, it.mutingNotifications) }
mutesAdapter.updateMutingNotificationsMap(mutingNotificationsMap)
}
private fun onFetchRelationshipsFailure(ids: List<String>) {
Log.e(TAG, "Fetch failure for relationships of accounts: $ids")
}
>>>>>>> ce973ea7... Personal account notes (#1978)
private fun onFetchAccountsFailure(throwable: Throwable) { private fun onFetchAccountsFailure(throwable: Throwable) {
fetching = false fetching = false
Log.e(TAG, "Fetch failure", throwable) Log.e(TAG, "Fetch failure", throwable)

@ -271,7 +271,7 @@ interface MastodonApi {
@GET("api/v1/accounts/{id}") @GET("api/v1/accounts/{id}")
fun account( fun account(
@Path("id") accountId: String @Path("id") accountId: String
): Call<Account> ): Single<Account>
/** /**
* Method to fetch statuses for the specified account. * Method to fetch statuses for the specified account.
@ -310,42 +310,43 @@ interface MastodonApi {
fun followAccount( fun followAccount(
@Path("id") accountId: String, @Path("id") accountId: String,
@Field("reblogs") showReblogs: Boolean @Field("reblogs") showReblogs: Boolean
): Call<Relationship> ): Single<Relationship>
@POST("api/v1/accounts/{id}/unfollow") @POST("api/v1/accounts/{id}/unfollow")
fun unfollowAccount( fun unfollowAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Call<Relationship> ): Single<Relationship>
@POST("api/v1/accounts/{id}/block") @POST("api/v1/accounts/{id}/block")
fun blockAccount( fun blockAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Call<Relationship> ): Single<Relationship>
@POST("api/v1/accounts/{id}/unblock") @POST("api/v1/accounts/{id}/unblock")
fun unblockAccount( fun unblockAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Call<Relationship> ): Single<Relationship>
@POST("api/v1/accounts/{id}/mute") @POST("api/v1/accounts/{id}/mute")
fun muteAccount( fun muteAccount(
@Path("id") accountId: String @Path("id") accountId: String,
): Call<Relationship> @Field("notifications") notifications: Boolean? = null
): Single<Relationship>
@POST("api/v1/accounts/{id}/unmute") @POST("api/v1/accounts/{id}/unmute")
fun unmuteAccount( fun unmuteAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Call<Relationship> ): Single<Relationship>
@GET("api/v1/accounts/relationships") @GET("api/v1/accounts/relationships")
fun relationships( fun relationships(
@Query("id[]") accountIds: List<String> @Query("id[]") accountIds: List<String>
): Call<List<Relationship>> ): Single<List<Relationship>>
@GET("api/v1/accounts/{id}/identity_proofs") @GET("api/v1/accounts/{id}/identity_proofs")
fun identityProofs( fun identityProofs(
@Path("id") accountId: String @Path("id") accountId: String
): Call<List<IdentityProof>> ): Single<List<IdentityProof>>
@POST("api/v1/pleroma/accounts/{id}/subscribe") @POST("api/v1/pleroma/accounts/{id}/subscribe")
fun subscribeAccount( fun subscribeAccount(
@ -519,6 +520,7 @@ interface MastodonApi {
@Field("choices[]") choices: List<Int> @Field("choices[]") choices: List<Int>
): Single<Poll> ): Single<Poll>
<<<<<<< HEAD
@POST("api/v1/accounts/{id}/block") @POST("api/v1/accounts/{id}/block")
fun blockAccountObservable( fun blockAccountObservable(
@Path("id") accountId: String @Path("id") accountId: String
@ -554,6 +556,8 @@ interface MastodonApi {
@Query("id[]") accountIds: List<String> @Query("id[]") accountIds: List<String>
): Single<List<Relationship>> ): Single<List<Relationship>>
=======
>>>>>>> ce973ea7... Personal account notes (#1978)
@FormUrlEncoded @FormUrlEncoded
@POST("api/v1/reports") @POST("api/v1/reports")
fun reportObservable( fun reportObservable(
@ -673,8 +677,18 @@ interface MastodonApi {
@Path("id") accountId: String @Path("id") accountId: String
): Single<Chat> ): Single<Chat>
<<<<<<< HEAD
@GET("api/v1/pleroma/chats/{id}") @GET("api/v1/pleroma/chats/{id}")
fun getChat( fun getChat(
@Path("id") chatId: String @Path("id") chatId: String
): Single<Chat> ): Single<Chat>
=======
@FormUrlEncoded
@POST("api/v1/accounts/{id}/note")
fun updateAccountNote(
@Path("id") accountId: String,
@Field("comment") note: String
): Single<Relationship>
>>>>>>> ce973ea7... Personal account notes (#1978)
} }

@ -15,17 +15,14 @@
package com.keylesspalace.tusky.network package com.keylesspalace.tusky.network
import android.util.Log
import com.keylesspalace.tusky.appstore.* import com.keylesspalace.tusky.appstore.*
import com.keylesspalace.tusky.entity.DeletedStatus import com.keylesspalace.tusky.entity.DeletedStatus
import com.keylesspalace.tusky.entity.Poll import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.Relationship
import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.entity.Status
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo import io.reactivex.rxkotlin.addTo
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.lang.IllegalStateException import java.lang.IllegalStateException
import android.util.Log import android.util.Log
@ -96,36 +93,37 @@ class TimelineCasesImpl(
} }
} }
override fun mute(id: String) { override fun muteConversation(status: Status, mute: Boolean): Single<Status> {
val call = mastodonApi.muteAccount(id)
call.enqueue(object : Callback<Relationship> {
override fun onResponse(call: Call<Relationship>, response: Response<Relationship>) {}
override fun onFailure(call: Call<Relationship>, t: Throwable) {}
})
eventHub.dispatch(MuteEvent(id, true))
}
override fun muteStatus(status: Status, mute: Boolean) {
val id = status.actionableId val id = status.actionableId
(if (mute) { val call = if (mute) {
mastodonApi.muteStatus(id) mastodonApi.muteConversation(id)
} else { } else {
mastodonApi.unmuteStatus(id) mastodonApi.unmuteConversation(id)
}).subscribe( { status -> }
eventHub.dispatch(MuteStatusEvent(status.id, mute)) return call.doAfterSuccess {
}, {}).addTo(this.cancelDisposable) eventHub.dispatch(MuteConversationEvent(status.id, mute))
}
} }
override fun block(id: String) { override fun mute(id: String, notifications: Boolean) {
val call = mastodonApi.blockAccount(id) mastodonApi.muteAccount(id, notifications)
call.enqueue(object : Callback<Relationship> { .subscribe({
override fun onResponse(call: Call<Relationship>, response: Response<Relationship>) {} eventHub.dispatch(MuteEvent(id))
}, { t ->
override fun onFailure(call: Call<Relationship>, t: Throwable) {} Log.w("Failed to mute account", t)
}) })
.addTo(cancelDisposable)
}
override fun block(id: String) {
mastodonApi.blockAccount(id)
.subscribe({
eventHub.dispatch(BlockEvent(id)) eventHub.dispatch(BlockEvent(id))
}, { t ->
Log.w("Failed to block account", t)
})
.addTo(cancelDisposable)
} }
override fun delete(id: String): Single<DeletedStatus> { override fun delete(id: String): Single<DeletedStatus> {

@ -2,7 +2,6 @@ package com.keylesspalace.tusky.viewmodel
import android.util.Log import android.util.Log
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.keylesspalace.tusky.appstore.* import com.keylesspalace.tusky.appstore.*
import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Account
@ -11,21 +10,25 @@ import com.keylesspalace.tusky.entity.IdentityProof
import com.keylesspalace.tusky.entity.Relationship import com.keylesspalace.tusky.entity.Relationship
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.util.*
import io.reactivex.Single
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class AccountViewModel @Inject constructor( class AccountViewModel @Inject constructor(
private val mastodonApi: MastodonApi, private val mastodonApi: MastodonApi,
private val eventHub: EventHub, private val eventHub: EventHub,
private val accountManager: AccountManager private val accountManager: AccountManager
) : ViewModel() { ) : RxAwareViewModel() {
val accountData = MutableLiveData<Resource<Account>>() val accountData = MutableLiveData<Resource<Account>>()
val relationshipData = MutableLiveData<Resource<Relationship>>() val relationshipData = MutableLiveData<Resource<Relationship>>()
val noteSaved = MutableLiveData<Boolean>()
private val identityProofData = MutableLiveData<List<IdentityProof>>() private val identityProofData = MutableLiveData<List<IdentityProof>>()
val accountFieldData = combineOptionalLiveData(accountData, identityProofData) { accountRes, identityProofs -> val accountFieldData = combineOptionalLiveData(accountData, identityProofData) { accountRes, identityProofs ->
@ -33,48 +36,40 @@ class AccountViewModel @Inject constructor(
.plus(accountRes?.data?.fields.orEmpty().map { Either.Right<IdentityProof, Field>(it) }) .plus(accountRes?.data?.fields.orEmpty().map { Either.Right<IdentityProof, Field>(it) })
} }
val isRefreshing = MutableLiveData<Boolean>().apply { value = false }
private var isDataLoading = false
lateinit var accountId: String
var isSelf = false
private val callList: MutableList<Call<*>> = mutableListOf() private var noteDisposable: Disposable? = null
private val disposable: Disposable = eventHub.events
init {
eventHub.events
.subscribe { event -> .subscribe { event ->
if (event is ProfileEditedEvent && event.newProfileData.id == accountData.value?.data?.id) { if (event is ProfileEditedEvent && event.newProfileData.id == accountData.value?.data?.id) {
accountData.postValue(Success(event.newProfileData)) accountData.postValue(Success(event.newProfileData))
} }
}.autoDispose()
} }
val isRefreshing = MutableLiveData<Boolean>().apply { value = false }
private var isDataLoading = false
lateinit var accountId: String
var isSelf = false
private fun obtainAccount(reload: Boolean = false) { private fun obtainAccount(reload: Boolean = false) {
if (accountData.value == null || reload) { if (accountData.value == null || reload) {
isDataLoading = true isDataLoading = true
accountData.postValue(Loading()) accountData.postValue(Loading())
val call = mastodonApi.account(accountId) mastodonApi.account(accountId)
call.enqueue(object : Callback<Account> { .subscribe({ account ->
override fun onResponse(call: Call<Account>, accountData.postValue(Success(account))
response: Response<Account>) {
if (response.isSuccessful) {
accountData.postValue(Success(response.body()))
} else {
accountData.postValue(Error())
}
isDataLoading = false isDataLoading = false
isRefreshing.postValue(false) isRefreshing.postValue(false)
} }, {t ->
override fun onFailure(call: Call<Account>, t: Throwable) {
Log.w(TAG, "failed obtaining account", t) Log.w(TAG, "failed obtaining account", t)
accountData.postValue(Error()) accountData.postValue(Error())
isDataLoading = false isDataLoading = false
isRefreshing.postValue(false) isRefreshing.postValue(false)
}
}) })
.autoDispose()
callList.add(call)
} }
} }
@ -83,51 +78,27 @@ class AccountViewModel @Inject constructor(
relationshipData.postValue(Loading()) relationshipData.postValue(Loading())
val ids = listOf(accountId) mastodonApi.relationships(listOf(accountId))
val call = mastodonApi.relationships(ids) .subscribe({ relationships ->
call.enqueue(object : Callback<List<Relationship>> { relationshipData.postValue(Success(relationships[0]))
override fun onResponse(call: Call<List<Relationship>>, }, { t ->
response: Response<List<Relationship>>) {
val relationships = response.body()
if (response.isSuccessful && relationships != null && relationships.getOrNull(0) != null) {
val relationship = relationships[0]
relationshipData.postValue(Success(relationship))
} else {
relationshipData.postValue(Error())
}
}
override fun onFailure(call: Call<List<Relationship>>, t: Throwable) {
Log.w(TAG, "failed obtaining relationships", t) Log.w(TAG, "failed obtaining relationships", t)
relationshipData.postValue(Error()) relationshipData.postValue(Error())
}
}) })
.autoDispose()
callList.add(call)
} }
} }
private fun obtainIdentityProof(reload: Boolean = false) { private fun obtainIdentityProof(reload: Boolean = false) {
if (identityProofData.value == null || reload) { if (identityProofData.value == null || reload) {
val call = mastodonApi.identityProofs(accountId) mastodonApi.identityProofs(accountId)
call.enqueue(object : Callback<List<IdentityProof>> { .subscribe({ proofs ->
override fun onResponse(call: Call<List<IdentityProof>>,
response: Response<List<IdentityProof>>) {
val proofs = response.body()
if (response.isSuccessful && proofs != null ) {
identityProofData.postValue(proofs) identityProofData.postValue(proofs)
} else { }, { t ->
identityProofData.postValue(emptyList())
}
}
override fun onFailure(call: Call<List<IdentityProof>>, t: Throwable) {
Log.w(TAG, "failed obtaining identity proofs", t) Log.w(TAG, "failed obtaining identity proofs", t)
}
}) })
.autoDispose()
callList.add(call)
} }
} }
@ -237,11 +208,17 @@ class AccountViewModel @Inject constructor(
relationshipData.postValue(Loading(newRelation)) relationshipData.postValue(Loading(newRelation))
} }
val callback = object : Callback<Relationship> { when (relationshipAction) {
override fun onResponse(call: Call<Relationship>, RelationShipAction.FOLLOW -> mastodonApi.followAccount(accountId, parameter ?: true)
response: Response<Relationship>) { RelationShipAction.UNFOLLOW -> mastodonApi.unfollowAccount(accountId)
val relationship = response.body() RelationShipAction.BLOCK -> mastodonApi.blockAccount(accountId)
if (response.isSuccessful && relationship != null) { RelationShipAction.UNBLOCK -> mastodonApi.unblockAccount(accountId)
RelationShipAction.MUTE -> mastodonApi.muteAccount(accountId, parameter ?: true)
RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(accountId)
RelationShipAction.SUBSCRIBE -> mastodonApi.subscribeAccount(accountId)
RelationShipAction.UNSUBSCRIBE -> mastodonApi.unsubscribeAccount(accountId)
}.subscribe(
{ relationship ->
relationshipData.postValue(Success(relationship)) relationshipData.postValue(Success(relationship))
when (relationshipAction) { when (relationshipAction) {
@ -252,38 +229,35 @@ class AccountViewModel @Inject constructor(
else -> { else -> {
} }
} }
},
} else { {
relationshipData.postValue(Error(relation)) relationshipData.postValue(Error(relation))
} }
)
.autoDispose()
} }
override fun onFailure(call: Call<Relationship>, t: Throwable) { fun noteChanged(newNote: String) {
relationshipData.postValue(Error(relation)) noteSaved.postValue(false)
} noteDisposable?.dispose()
noteDisposable = Single.timer(1500, TimeUnit.MILLISECONDS)
.flatMap {
mastodonApi.updateAccountNote(accountId, newNote)
} }
.doOnSuccess {
val call = when (relationshipAction) { noteSaved.postValue(true)
RelationShipAction.FOLLOW -> mastodonApi.followAccount(accountId, showReblogs)
RelationShipAction.UNFOLLOW -> mastodonApi.unfollowAccount(accountId)
RelationShipAction.BLOCK -> mastodonApi.blockAccount(accountId)
RelationShipAction.UNBLOCK -> mastodonApi.unblockAccount(accountId)
RelationShipAction.MUTE -> mastodonApi.muteAccount(accountId)
RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(accountId)
RelationShipAction.SUBSCRIBE -> mastodonApi.subscribeAccount(accountId)
RelationShipAction.UNSUBSCRIBE -> mastodonApi.unsubscribeAccount(accountId)
} }
.delay(4, TimeUnit.SECONDS)
call.enqueue(callback) .subscribe({
callList.add(call) noteSaved.postValue(false)
}, {
Log.e(TAG, "Error updating note", it)
})
} }
override fun onCleared() { override fun onCleared() {
callList.forEach { super.onCleared()
it.cancel() noteDisposable?.dispose()
}
disposable.dispose()
} }
fun refresh() { fun refresh() {

@ -218,16 +218,43 @@
app:barrierDirection="bottom" app:barrierDirection="bottom"
app:constraint_referenced_ids="accountAdminTextView,accountModeratorTextView,accountFollowsYouTextView,accountBadgeTextView" /> app:constraint_referenced_ids="accountAdminTextView,accountModeratorTextView,accountFollowsYouTextView,accountBadgeTextView" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/accountNoteTextInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/labelBarrier"
tools:visibility="visible">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/account_note_hint" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/saveNoteInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/account_note_saved"
android:textColor="@color/tusky_blue"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/accountNoteTextInputLayout" />
<androidx.emoji.widget.EmojiTextView <androidx.emoji.widget.EmojiTextView
android:id="@+id/accountNoteTextView" android:id="@+id/accountNoteTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hyphenationFrequency="full" android:hyphenationFrequency="full"
android:lineSpacingMultiplier="1.1" android:lineSpacingMultiplier="1.1"
android:paddingTop="10dp" android:paddingTop="2dp"
android:textColor="?android:textColorTertiary" android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:layout_constraintTop_toBottomOf="@id/labelBarrier" app:layout_constraintTop_toBottomOf="@id/saveNoteInfo"
tools:text="This is a test description. Descriptions can be quite looooong." /> tools:text="This is a test description. Descriptions can be quite looooong." />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
@ -294,6 +321,7 @@
android:text="@string/title_statuses" android:text="@string/title_statuses"
android:textColor="@color/account_tab_font_color" android:textColor="@color/account_tab_font_color"
android:textSize="?attr/status_text_medium" /> android:textSize="?attr/status_text_medium" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout

@ -573,5 +573,7 @@
<string name="pref_title_show_cards_in_timelines">Show link previews in timelines</string> <string name="pref_title_show_cards_in_timelines">Show link previews in timelines</string>
<string name="pref_title_confirm_reblogs">Show confirmation dialog before boosting</string> <string name="pref_title_confirm_reblogs">Show confirmation dialog before boosting</string>
<string name="pref_title_hide_top_toolbar">Hide the title of the top toolbar</string> <string name="pref_title_hide_top_toolbar">Hide the title of the top toolbar</string>
<string name="account_note_hint">Your private note about this account</string>
<string name="account_note_saved">Saved!</string>
</resources> </resources>

Loading…
Cancel
Save