parent
6e6cf05d11
commit
131ebabe85
14 changed files with 344 additions and 142 deletions
|
|
@ -35,11 +35,11 @@ import com.keylesspalace.tusky.databinding.ActivityDraftsBinding
|
|||
import com.keylesspalace.tusky.db.DraftEntity
|
||||
import com.keylesspalace.tusky.db.DraftsAlert
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||
import com.keylesspalace.tusky.util.parseAsMastodonHtml
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
import javax.inject.Inject
|
||||
|
||||
class DraftsActivity : BaseActivity(), DraftActionListener {
|
||||
|
|
@ -131,7 +131,7 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
|
|||
|
||||
Log.w(TAG, "failed loading reply information", throwable)
|
||||
|
||||
if (throwable is HttpException && throwable.code() == 404) {
|
||||
if (throwable.isHttpNotFound()) {
|
||||
// the original status to which a reply was drafted has been deleted
|
||||
// let's open the ComposeActivity without reply information
|
||||
Toast.makeText(context, getString(R.string.drafts_post_reply_removed), Toast.LENGTH_LONG).show()
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ import com.keylesspalace.tusky.di.ViewModelFactory
|
|||
import com.keylesspalace.tusky.entity.Filter
|
||||
import com.keylesspalace.tusky.entity.FilterKeyword
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -282,7 +282,7 @@ class EditFilterActivity : BaseActivity() {
|
|||
finish()
|
||||
},
|
||||
{ throwable ->
|
||||
if (throwable is HttpException && throwable.code() == 404) {
|
||||
if (throwable.isHttpNotFound()) {
|
||||
api.deleteFilterV1(filter.id).fold(
|
||||
{
|
||||
finish()
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import com.keylesspalace.tusky.appstore.EventHub
|
|||
import com.keylesspalace.tusky.entity.Filter
|
||||
import com.keylesspalace.tusky.entity.FilterKeyword
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.HttpException
|
||||
import javax.inject.Inject
|
||||
|
||||
class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub: EventHub) : ViewModel() {
|
||||
|
|
@ -108,7 +108,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
|||
},
|
||||
{ throwable ->
|
||||
return (
|
||||
throwable is HttpException && throwable.code() == 404 &&
|
||||
throwable.isHttpNotFound() &&
|
||||
// Endpoint not found, fall back to v1 api
|
||||
createFilterV1(contexts, expiresInSeconds)
|
||||
)
|
||||
|
|
@ -141,7 +141,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
|||
return results.none { it.isFailure }
|
||||
},
|
||||
{ throwable ->
|
||||
if (throwable is HttpException && throwable.code() == 404) {
|
||||
if (throwable.isHttpNotFound()) {
|
||||
// Endpoint not found, fall back to v1 api
|
||||
if (updateFilterV1(contexts, expiresInSeconds)) {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import com.keylesspalace.tusky.appstore.EventHub
|
|||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
|
||||
import com.keylesspalace.tusky.entity.Filter
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
import javax.inject.Inject
|
||||
|
||||
class FiltersViewModel @Inject constructor(
|
||||
|
|
@ -38,14 +38,13 @@ class FiltersViewModel @Inject constructor(
|
|||
this@FiltersViewModel._state.value = State(filters, LoadingState.LOADED)
|
||||
},
|
||||
{ throwable ->
|
||||
if (throwable is HttpException && throwable.code() == 404) {
|
||||
if (throwable.isHttpNotFound()) {
|
||||
api.getFiltersV1().fold(
|
||||
{ filters ->
|
||||
this@FiltersViewModel._state.value = State(filters.map { it.toFilter() }, LoadingState.LOADED)
|
||||
},
|
||||
{ throwable ->
|
||||
{ _ ->
|
||||
// TODO log errors (also below)
|
||||
|
||||
this@FiltersViewModel._state.value = _state.value.copy(loadingState = LoadingState.ERROR_OTHER)
|
||||
}
|
||||
)
|
||||
|
|
@ -68,7 +67,7 @@ class FiltersViewModel @Inject constructor(
|
|||
}
|
||||
},
|
||||
{ throwable ->
|
||||
if (throwable is HttpException && throwable.code() == 404) {
|
||||
if (throwable.isHttpNotFound()) {
|
||||
api.deleteFilterV1(filter.id).fold(
|
||||
{
|
||||
this@FiltersViewModel._state.value = State(this@FiltersViewModel._state.value.filters.filter { it.id != filter.id }, LoadingState.LOADED)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.keylesspalace.tusky.db.EmojisEntity
|
|||
import com.keylesspalace.tusky.db.InstanceInfoEntity
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
|
@ -63,27 +64,31 @@ class InstanceInfoRepository @Inject constructor(
|
|||
{ instance ->
|
||||
val instanceEntity = InstanceInfoEntity(
|
||||
instance = instanceName,
|
||||
maximumTootCharacters = instance.configuration?.statuses?.maxCharacters ?: instance.maxTootChars,
|
||||
maxPollOptions = instance.configuration?.polls?.maxOptions ?: instance.pollConfiguration?.maxOptions,
|
||||
maxPollOptionLength = instance.configuration?.polls?.maxCharactersPerOption ?: instance.pollConfiguration?.maxOptionChars,
|
||||
minPollDuration = instance.configuration?.polls?.minExpiration ?: instance.pollConfiguration?.minExpiration,
|
||||
maxPollDuration = instance.configuration?.polls?.maxExpiration ?: instance.pollConfiguration?.maxExpiration,
|
||||
charactersReservedPerUrl = instance.configuration?.statuses?.charactersReservedPerUrl,
|
||||
maximumTootCharacters = instance.configuration.statuses.maxCharacters,
|
||||
maxPollOptions = instance.configuration.polls.maxOptions,
|
||||
maxPollOptionLength = instance.configuration.polls.maxCharactersPerOption,
|
||||
minPollDuration = instance.configuration.polls.minExpirationSeconds,
|
||||
maxPollDuration = instance.configuration.polls.maxExpirationSeconds,
|
||||
charactersReservedPerUrl = instance.configuration.statuses.charactersReservedPerUrl,
|
||||
version = instance.version,
|
||||
videoSizeLimit = instance.configuration?.mediaAttachments?.videoSizeLimit ?: instance.uploadLimit,
|
||||
imageSizeLimit = instance.configuration?.mediaAttachments?.imageSizeLimit ?: instance.uploadLimit,
|
||||
imageMatrixLimit = instance.configuration?.mediaAttachments?.imageMatrixLimit,
|
||||
maxMediaAttachments = instance.configuration?.statuses?.maxMediaAttachments ?: instance.maxMediaAttachments,
|
||||
videoSizeLimit = instance.configuration.mediaAttachments.videoSizeLimitBytes.toInt(),
|
||||
imageSizeLimit = instance.configuration.mediaAttachments.imageSizeLimitBytes.toInt(),
|
||||
imageMatrixLimit = instance.configuration.mediaAttachments.imagePixelCountLimit.toInt(),
|
||||
maxMediaAttachments = instance.configuration.statuses.maxMediaAttachments,
|
||||
maxFields = instance.pleroma?.metadata?.fieldLimits?.maxFields,
|
||||
maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength,
|
||||
maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength
|
||||
maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength,
|
||||
)
|
||||
dao.upsert(instanceEntity)
|
||||
instanceEntity
|
||||
},
|
||||
{ throwable ->
|
||||
Log.w(TAG, "failed to instance, falling back to cache and default values", throwable)
|
||||
dao.getInstanceInfo(instanceName)
|
||||
if (throwable.isHttpNotFound()) {
|
||||
getInstanceInfoV1()
|
||||
} else {
|
||||
Log.w(TAG, "failed to instance, falling back to cache and default values", throwable)
|
||||
dao.getInstanceInfo(instanceName)
|
||||
}
|
||||
}
|
||||
).let { instanceInfo: InstanceInfoEntity? ->
|
||||
InstanceInfo(
|
||||
|
|
@ -100,11 +105,42 @@ class InstanceInfoRepository @Inject constructor(
|
|||
maxFields = instanceInfo?.maxFields ?: DEFAULT_MAX_ACCOUNT_FIELDS,
|
||||
maxFieldNameLength = instanceInfo?.maxFieldNameLength,
|
||||
maxFieldValueLength = instanceInfo?.maxFieldValueLength,
|
||||
version = instanceInfo?.version
|
||||
version = instanceInfo?.version,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getInstanceInfoV1(): InstanceInfoEntity? = withContext(Dispatchers.IO) {
|
||||
api.getInstanceV1()
|
||||
.fold(
|
||||
{ instance ->
|
||||
val instanceEntity = InstanceInfoEntity(
|
||||
instance = instanceName,
|
||||
maximumTootCharacters = instance.configuration?.statuses?.maxCharacters ?: instance.maxTootChars,
|
||||
maxPollOptions = instance.configuration?.polls?.maxOptions ?: instance.pollConfiguration?.maxOptions,
|
||||
maxPollOptionLength = instance.configuration?.polls?.maxCharactersPerOption ?: instance.pollConfiguration?.maxOptionChars,
|
||||
minPollDuration = instance.configuration?.polls?.minExpiration ?: instance.pollConfiguration?.minExpiration,
|
||||
maxPollDuration = instance.configuration?.polls?.maxExpiration ?: instance.pollConfiguration?.maxExpiration,
|
||||
charactersReservedPerUrl = instance.configuration?.statuses?.charactersReservedPerUrl,
|
||||
version = instance.version,
|
||||
videoSizeLimit = instance.configuration?.mediaAttachments?.videoSizeLimit ?: instance.uploadLimit,
|
||||
imageSizeLimit = instance.configuration?.mediaAttachments?.imageSizeLimit ?: instance.uploadLimit,
|
||||
imageMatrixLimit = instance.configuration?.mediaAttachments?.imageMatrixLimit,
|
||||
maxMediaAttachments = instance.configuration?.statuses?.maxMediaAttachments ?: instance.maxMediaAttachments,
|
||||
maxFields = instance.pleroma?.metadata?.fieldLimits?.maxFields,
|
||||
maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength,
|
||||
maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength,
|
||||
)
|
||||
dao.upsert(instanceEntity)
|
||||
instanceEntity
|
||||
},
|
||||
{ throwable ->
|
||||
Log.w(TAG, "failed to instance, falling back to cache and default values", throwable)
|
||||
dao.getInstanceInfo(instanceName)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "InstanceInfoRepo"
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
|
@ -36,11 +37,25 @@ class LoginWebViewViewModel @Inject constructor(
|
|||
if (this.domain == null) {
|
||||
this.domain = domain
|
||||
viewModelScope.launch {
|
||||
api.getInstance(domain).fold({ instance ->
|
||||
instanceRules.value = instance.rules?.map { rule -> rule.text }.orEmpty()
|
||||
}, { throwable ->
|
||||
Log.w("LoginWebViewViewModel", "failed to load instance info", throwable)
|
||||
})
|
||||
api.getInstance().fold(
|
||||
{ instance ->
|
||||
instanceRules.value = instance.rules.map { rule -> rule.text }
|
||||
},
|
||||
{ throwable ->
|
||||
if (throwable.isHttpNotFound()) {
|
||||
api.getInstanceV1(domain).fold(
|
||||
{ instance ->
|
||||
instanceRules.value = instance.rules?.map { rule -> rule.text }.orEmpty()
|
||||
},
|
||||
{ throwable ->
|
||||
Log.w("LoginWebViewViewModel", "failed to load instance info", throwable)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Log.w("LoginWebViewViewModel", "failed to load instance info", throwable)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ import com.keylesspalace.tusky.network.FilterModel
|
|||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
import com.keylesspalace.tusky.usecase.TimelineCases
|
||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
|
||||
abstract class TimelineViewModel(
|
||||
private val timelineCases: TimelineCases,
|
||||
|
|
@ -281,7 +281,7 @@ abstract class TimelineViewModel(
|
|||
invalidate()
|
||||
},
|
||||
{ throwable ->
|
||||
if (throwable is HttpException && throwable.code() == 404) {
|
||||
if (throwable.isHttpNotFound()) {
|
||||
// Fallback to client-side filter code
|
||||
val filters = api.getFiltersV1().getOrElse {
|
||||
Log.e(TAG, "Failed to fetch filters", it)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import com.keylesspalace.tusky.entity.Status
|
|||
import com.keylesspalace.tusky.network.FilterModel
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.usecase.TimelineCases
|
||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||
import com.keylesspalace.tusky.util.toViewData
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import kotlinx.coroutines.Job
|
||||
|
|
@ -47,7 +48,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
import javax.inject.Inject
|
||||
|
||||
class ViewThreadViewModel @Inject constructor(
|
||||
|
|
@ -391,7 +391,7 @@ class ViewThreadViewModel @Inject constructor(
|
|||
updateStatuses()
|
||||
},
|
||||
{ throwable ->
|
||||
if (throwable is HttpException && throwable.code() == 404) {
|
||||
if (throwable.isHttpNotFound()) {
|
||||
val filters = api.getFiltersV1().getOrElse {
|
||||
Log.w(TAG, "Failed to fetch filters", it)
|
||||
return@launch
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue