Notification policy (#4768)

This was so much work wow. I think it works pretty well and is the best
compromise between all the alternative we considered. Yes the
pull-to-refreh on the notifications works slightly different now when
the new bar is visible, but I don't think there is a way around that.

Things I plan to do later, i.e. not as part of this PR or release:
- Cache the notification policy summary for better offline behavior and
less view shifting when it loads
- try to reduce some of the code duplications that are now in there
- if there is user demand, add a "legacy mode" setting where this
feature is disabled even if the server would support it

closes #4331
closes #4550 as won't do
closes #4712 as won't do

<img
src="https://github.com/user-attachments/assets/de322d3c-3775-41e7-be57-28ab7fbaecdf"
width="240"/> <img
src="https://github.com/user-attachments/assets/1ce958a4-4f15-484c-a337-5ad93f36046c"
width="240"/> <img
src="https://github.com/user-attachments/assets/98b0482b-1c05-4c99-a371-f7f4d8a69abd"
width="240"/>
This commit is contained in:
Konrad Pozniak 2024-12-03 18:46:50 +01:00 committed by GitHub
commit cd57352cbd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 2401 additions and 97 deletions

View file

@ -0,0 +1,74 @@
package com.keylesspalace.tusky.usecase
import at.connyduck.calladapter.networkresult.NetworkResult
import at.connyduck.calladapter.networkresult.fold
import at.connyduck.calladapter.networkresult.onSuccess
import com.keylesspalace.tusky.entity.NotificationPolicy
import com.keylesspalace.tusky.network.MastodonApi
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import retrofit2.HttpException
class NotificationPolicyUsecase @Inject constructor(
private val api: MastodonApi
) {
private val _state: MutableStateFlow<NotificationPolicyState> = MutableStateFlow(NotificationPolicyState.Loading)
val state: StateFlow<NotificationPolicyState> = _state.asStateFlow()
suspend fun getNotificationPolicy() {
_state.value.let { state ->
if (state is NotificationPolicyState.Loaded) {
_state.value = state.copy(refreshing = true)
} else {
_state.value = NotificationPolicyState.Loading
}
}
api.notificationPolicy().fold(
{ policy ->
_state.value = NotificationPolicyState.Loaded(refreshing = false, policy = policy)
},
{ t ->
if (t is HttpException && t.code() == 404) {
_state.value = NotificationPolicyState.Unsupported
} else {
_state.value = NotificationPolicyState.Error(t)
}
}
)
}
suspend fun updatePolicy(
forNotFollowing: String? = null,
forNotFollowers: String? = null,
forNewAccounts: String? = null,
forPrivateMentions: String? = null,
forLimitedAccounts: String? = null
): NetworkResult<NotificationPolicy> {
return api.updateNotificationPolicy(
forNotFollowing = forNotFollowing,
forNotFollowers = forNotFollowers,
forNewAccounts = forNewAccounts,
forPrivateMentions = forPrivateMentions,
forLimitedAccounts = forLimitedAccounts
).onSuccess { notificationPolicy ->
_state.value = NotificationPolicyState.Loaded(false, notificationPolicy)
}
}
}
sealed interface NotificationPolicyState {
data object Loading : NotificationPolicyState
data object Unsupported : NotificationPolicyState
data class Error(
val throwable: Throwable
) : NotificationPolicyState
data class Loaded(
val refreshing: Boolean,
val policy: NotificationPolicy
) : NotificationPolicyState
}