8770fbe986
* Convert ComposeActivity to Kotlin * More ComposeActivity cleanups * Move ComposeActivity to it's own package * Remove ComposeActivity.IntentBuilder * Re-do part of the media downsizing/uploading * Add sending of status to ViewModel, draft media descriptions * Allow uploading video, update description after uploading * Enable camera, enable upload cancelling * Cleanup of ComposeActivity * Extract CaptionDialog, extract ComposeActivity methods * Fix handling of redrafted media * Add initial state and media uploading out of Activity * Change ComposeOptions.mentionedUsernames to be Set rather than List We probably don't want repeated usernames when we are writing a post and Set provides such guarantee for free plus it tells it to the callers. The only disadvantage is lack of order but it shouldn't be a problem. * Add combineOptionalLiveData. Add docs. It it useful for nullable LiveData's. I think we cannot differentiate between value not being set and value being null so I just added the variant without null check. * Add poll support to Compose. * cleanup code * move more classes into compose package * cleanup code * fix button behavior * add error handling for media upload * add caching for instance data again * merge develop * fix scheduled toots * delete unused string * cleanup ComposeActivity * fix restoring media from drafts * make media upload code a little bit clearer * cleanup autocomplete search code * avoid duplicate object creation in SavedTootActivity * perf: avoid unnecessary work when initializing ComposeActivity * add license header to new files * use small toot button on bigger displays * fix ComposeActivityTest * fix bad merge * use Singles.zip instead of Single.zip
93 lines
3.5 KiB
Kotlin
93 lines
3.5 KiB
Kotlin
/* Copyright 2019 Tusky Contributors
|
|
*
|
|
* This file is a part of Tusky.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
* Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
|
* see <http://www.gnu.org/licenses>. */
|
|
|
|
package com.keylesspalace.tusky.util
|
|
|
|
import androidx.lifecycle.*
|
|
import io.reactivex.BackpressureStrategy
|
|
import io.reactivex.Observable
|
|
import io.reactivex.Single
|
|
|
|
inline fun <X, Y> LiveData<X>.map(crossinline mapFunction: (X) -> Y): LiveData<Y> =
|
|
Transformations.map(this) { input -> mapFunction(input) }
|
|
|
|
inline fun <X, Y> LiveData<X>.switchMap(
|
|
crossinline switchMapFunction: (X) -> LiveData<Y>
|
|
): LiveData<Y> = Transformations.switchMap(this) { input -> switchMapFunction(input) }
|
|
|
|
inline fun <X> LiveData<X>.filter(crossinline predicate: (X) -> Boolean): LiveData<X> {
|
|
val liveData = MediatorLiveData<X>()
|
|
liveData.addSource(this) { value ->
|
|
if (predicate(value)) {
|
|
liveData.value = value
|
|
}
|
|
}
|
|
return liveData
|
|
}
|
|
|
|
fun LifecycleOwner.withLifecycleContext(body: LifecycleContext.() -> Unit) =
|
|
LifecycleContext(this).apply(body)
|
|
|
|
class LifecycleContext(val lifecycleOwner: LifecycleOwner) {
|
|
inline fun <T> LiveData<T>.observe(crossinline observer: (T) -> Unit) =
|
|
this.observe(lifecycleOwner, Observer { observer(it) })
|
|
|
|
/**
|
|
* Just hold a subscription,
|
|
*/
|
|
fun <T> LiveData<T>.subscribe() =
|
|
this.observe(lifecycleOwner, Observer { })
|
|
}
|
|
|
|
/**
|
|
* Invokes @param [combiner] when value of both @param [a] and @param [b] are not null. Returns
|
|
* [LiveData] with value set to the result of calling [combiner] with value of both.
|
|
* Important! You still need to observe to the returned [LiveData] for [combiner] to be invoked.
|
|
*/
|
|
fun <A, B, R> combineLiveData(a: LiveData<A>, b: LiveData<B>, combiner: (A, B) -> R): LiveData<R> {
|
|
val liveData = MediatorLiveData<R>()
|
|
liveData.addSource(a) {
|
|
if (a.value != null && b.value != null) {
|
|
liveData.value = combiner(a.value!!, b.value!!)
|
|
}
|
|
}
|
|
liveData.addSource(b) {
|
|
if (a.value != null && b.value != null) {
|
|
liveData.value = combiner(a.value!!, b.value!!)
|
|
}
|
|
}
|
|
return liveData
|
|
}
|
|
|
|
/**
|
|
* Returns [LiveData] with value set to the result of calling [combiner] with value of [a] and [b]
|
|
* after either changes. Doesn't check if either has value.
|
|
* Important! You still need to observe to the returned [LiveData] for [combiner] to be invoked.
|
|
*/
|
|
fun <A, B, R> combineOptionalLiveData(a: LiveData<A>, b: LiveData<B>, combiner: (A?, B?) -> R): LiveData<R> {
|
|
val liveData = MediatorLiveData<R>()
|
|
liveData.addSource(a) {
|
|
liveData.value = combiner(a.value, b.value)
|
|
}
|
|
liveData.addSource(b) {
|
|
liveData.value = combiner(a.value, b.value)
|
|
}
|
|
return liveData
|
|
}
|
|
|
|
fun <T> Single<T>.toLiveData() = LiveDataReactiveStreams.fromPublisher(this.toFlowable())
|
|
fun <T> Observable<T>.toLiveData(
|
|
backpressureStrategy: BackpressureStrategy = BackpressureStrategy.LATEST
|
|
) = LiveDataReactiveStreams.fromPublisher(this.toFlowable(BackpressureStrategy.LATEST)) |