Moves URL Previews into suspend functions

This commit is contained in:
Vitor Pamplona
2023-08-04 10:47:01 -04:00
parent e7defa14b9
commit ba2902b725
4 changed files with 19 additions and 24 deletions
@@ -2,33 +2,35 @@ package com.vitorpamplona.amethyst.model
import android.util.LruCache import android.util.LruCache
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.previews.BahaUrlPreview import com.vitorpamplona.amethyst.service.previews.BahaUrlPreview
import com.vitorpamplona.amethyst.service.previews.IUrlPreviewCallback import com.vitorpamplona.amethyst.service.previews.IUrlPreviewCallback
import com.vitorpamplona.amethyst.service.previews.UrlInfoItem import com.vitorpamplona.amethyst.service.previews.UrlInfoItem
import com.vitorpamplona.amethyst.ui.components.UrlPreviewState import com.vitorpamplona.amethyst.ui.components.UrlPreviewState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Stable @Stable
object UrlCachedPreviewer { object UrlCachedPreviewer {
var cache = LruCache<String, UrlPreviewState>(100) var cache = LruCache<String, UrlPreviewState>(100)
private set private set
fun previewInfo(url: String, onReady: (UrlPreviewState) -> Unit) { suspend fun previewInfo(
checkNotInMainThread() url: String,
onReady: suspend (UrlPreviewState) -> Unit
) = withContext(Dispatchers.IO) {
cache[url]?.let { cache[url]?.let {
onReady(it) onReady(it)
return return@withContext
} }
BahaUrlPreview( BahaUrlPreview(
url, url,
object : IUrlPreviewCallback { object : IUrlPreviewCallback {
override fun onComplete(urlInfo: UrlInfoItem) { override suspend fun onComplete(urlInfo: UrlInfoItem) = withContext(Dispatchers.IO) {
cache[url]?.let { cache[url]?.let {
if (it is UrlPreviewState.Loaded || it is UrlPreviewState.Empty) { if (it is UrlPreviewState.Loaded || it is UrlPreviewState.Empty) {
onReady(it) onReady(it)
return return@withContext
} }
} }
@@ -42,10 +44,10 @@ object UrlCachedPreviewer {
onReady(state) onReady(state)
} }
override fun onFailed(throwable: Throwable) { override suspend fun onFailed(throwable: Throwable) = withContext(Dispatchers.IO) {
cache[url]?.let { cache[url]?.let {
onReady(it) onReady(it)
return return@withContext
} }
val state = UrlPreviewState.Error(throwable.message ?: "Error Loading url preview") val state = UrlPreviewState.Error(throwable.message ?: "Error Loading url preview")
@@ -1,25 +1,21 @@
package com.vitorpamplona.amethyst.service.previews package com.vitorpamplona.amethyst.service.previews
import android.net.Uri import android.net.Uri
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.util.* import java.util.*
class BahaUrlPreview(val url: String, var callback: IUrlPreviewCallback?) { class BahaUrlPreview(val url: String, var callback: IUrlPreviewCallback?) {
val scope = CoroutineScope(Job() + Dispatchers.IO)
private val imageExtensionArray = arrayOf(".gif", ".png", ".jpg", ".jpeg", ".bmp", ".webp") private val imageExtensionArray = arrayOf(".gif", ".png", ".jpg", ".jpeg", ".bmp", ".webp")
fun fetchUrlPreview(timeOut: Int = 30000) { suspend fun fetchUrlPreview(timeOut: Int = 30000) = withContext(Dispatchers.IO) {
val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> try {
callback?.onFailed(throwable)
}
scope.launch(exceptionHandler) {
fetch(timeOut) fetch(timeOut)
} catch (t: Throwable) {
callback?.onFailed(t)
} }
} }
private suspend fun fetch(timeOut: Int = 30000) { private suspend fun fetch(timeOut: Int = 30000) {
checkNotInMainThread()
lateinit var urlInfoItem: UrlInfoItem lateinit var urlInfoItem: UrlInfoItem
if (checkIsImageUrl()) { if (checkIsImageUrl()) {
urlInfoItem = UrlInfoItem(url = url, image = url) urlInfoItem = UrlInfoItem(url = url, image = url)
@@ -43,7 +39,6 @@ class BahaUrlPreview(val url: String, var callback: IUrlPreviewCallback?) {
} }
fun cleanUp() { fun cleanUp() {
scope.cancel()
callback = null callback = null
} }
} }
@@ -1,6 +1,6 @@
package com.vitorpamplona.amethyst.service.previews package com.vitorpamplona.amethyst.service.previews
interface IUrlPreviewCallback { interface IUrlPreviewCallback {
fun onComplete(urlInfo: UrlInfoItem) suspend fun onComplete(urlInfo: UrlInfoItem)
fun onFailed(throwable: Throwable) suspend fun onFailed(throwable: Throwable)
} }
@@ -30,7 +30,7 @@ fun UrlPreview(url: String, urlText: String, accountViewModel: AccountViewModel)
} else { } else {
var urlPreviewState by remember(url) { var urlPreviewState by remember(url) {
mutableStateOf( mutableStateOf(
UrlCachedPreviewer.cache.get(url)?.let { it } ?: UrlPreviewState.Loading UrlCachedPreviewer.cache.get(url) ?: UrlPreviewState.Loading
) )
} }
@@ -39,9 +39,7 @@ fun UrlPreview(url: String, urlText: String, accountViewModel: AccountViewModel)
LaunchedEffect(url) { LaunchedEffect(url) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
UrlCachedPreviewer.previewInfo(url) { UrlCachedPreviewer.previewInfo(url) {
launch(Dispatchers.Main) { urlPreviewState = it
urlPreviewState = it
}
} }
} }
} }