refactor: migrate image loading to Coil3 (#82)

* update dependency declarations

* update build scripts

* add getTempDir to FileSystemManager

* define ImageLoaderProvider

* refactor ImagePreloadManager

* refactor CustomImage

* update DI and utilities

* update usages in UI components

* update imports for ImagePreloadManager

* cleanup main application class

* add new image transformer for markdown rendering
This commit is contained in:
akesi seli 2024-11-05 16:19:29 +01:00 committed by GitHub
parent 2996c3ea96
commit 325a0dbd03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
51 changed files with 344 additions and 347 deletions

View File

@ -1,20 +1,16 @@
package com.livefast.eattrash.raccoonforlemmy.android package com.livefast.eattrash.raccoonforlemmy.android
import android.app.Application import android.app.Application
import coil.ImageLoader
import coil.ImageLoaderFactory
import com.livefast.eattrash.raccoonforlemmy.core.utils.debug.CrashReportConfiguration import com.livefast.eattrash.raccoonforlemmy.core.utils.debug.CrashReportConfiguration
import com.livefast.eattrash.raccoonforlemmy.core.utils.debug.CrashReportWriter import com.livefast.eattrash.raccoonforlemmy.core.utils.debug.CrashReportWriter
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.getCoilImageLoader
import com.livefast.eattrash.raccoonforlemmy.di.sharedHelperModule import com.livefast.eattrash.raccoonforlemmy.di.sharedHelperModule
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
class MainApplication : class MainApplication : Application() {
Application(),
ImageLoaderFactory {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -40,6 +36,4 @@ class MainApplication :
} }
} }
} }
override fun newImageLoader(): ImageLoader = getCoilImageLoader(this)
} }

View File

@ -30,7 +30,6 @@ kotlin {
sourceSets { sourceSets {
val androidMain by getting { val androidMain by getting {
dependencies { dependencies {
implementation(libs.coil.compose)
implementation(libs.exoplayer) implementation(libs.exoplayer)
implementation(libs.exoplayer.dash) implementation(libs.exoplayer.dash)
implementation(libs.exoplayer.ui) implementation(libs.exoplayer.ui)
@ -44,9 +43,11 @@ kotlin {
implementation(compose.material3) implementation(compose.material3)
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
implementation(projects.core.utils) implementation(libs.coil.compose)
implementation(projects.core.appearance) implementation(projects.core.appearance)
implementation(projects.core.l10n) implementation(projects.core.l10n)
implementation(projects.core.utils)
} }
} }
val commonTest by getting { val commonTest by getting {
@ -54,18 +55,19 @@ kotlin {
implementation(kotlin("test")) implementation(kotlin("test"))
} }
} }
val iosMain by getting {
dependencies {
implementation(libs.kamel)
}
}
} }
} }
android { android {
namespace = "com.livefast.eattrash.raccoonforlemmy.core.commonui.components" namespace = "com.livefast.eattrash.raccoonforlemmy.core.commonui.components"
compileSdk = libs.versions.android.targetSdk.get().toInt() compileSdk =
libs.versions.android.targetSdk
.get()
.toInt()
defaultConfig { defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt() minSdk =
libs.versions.android.minSdk
.get()
.toInt()
} }
} }

View File

@ -1,104 +0,0 @@
package com.livefast.eattrash.raccoonforlemmy.core.commonui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter
import com.livefast.eattrash.raccoonforlemmy.core.l10n.messages.LocalStrings
@Composable
actual fun CustomImage(
modifier: Modifier,
url: String,
autoload: Boolean,
blurred: Boolean,
loadButtonContent: @Composable (() -> Unit)?,
contentDescription: String?,
quality: FilterQuality,
contentScale: ContentScale,
alignment: Alignment,
contentAlignment: Alignment,
alpha: Float,
colorFilter: ColorFilter?,
onLoading: @Composable (BoxScope.(Float?) -> Unit)?,
onFailure: @Composable (BoxScope.(Throwable) -> Unit)?,
) {
var shouldBeRendered by remember(autoload) { mutableStateOf(autoload) }
var painterState: AsyncImagePainter.State by remember {
mutableStateOf(AsyncImagePainter.State.Empty)
}
Box(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
if (shouldBeRendered) {
AsyncImage(
modifier =
Modifier
.fillMaxSize()
.blur(radius = if (blurred) 60.dp else 0.dp),
model = url,
contentDescription = contentDescription,
filterQuality = quality,
contentScale = contentScale,
alignment = alignment,
alpha = alpha,
colorFilter = colorFilter,
onLoading = {
painterState = it
},
onError = {
painterState = it
},
onSuccess = {
painterState = it
},
)
when (val state = painterState) {
AsyncImagePainter.State.Empty -> Unit
is AsyncImagePainter.State.Error -> {
onFailure?.invoke(this, state.result.throwable)
}
is AsyncImagePainter.State.Loading -> {
onLoading?.invoke(this, null)
}
else -> Unit
}
} else {
Button(
onClick = {
shouldBeRendered = true
},
) {
if (loadButtonContent != null) {
loadButtonContent.invoke()
} else {
Text(
text = LocalStrings.current.buttonLoad,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}
}
}

View File

@ -1,20 +1,35 @@
package com.livefast.eattrash.raccoonforlemmy.core.commonui.components package com.livefast.eattrash.raccoonforlemmy.core.commonui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.compose.AsyncImagePainter
import com.livefast.eattrash.raccoonforlemmy.core.l10n.messages.LocalStrings
import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.getImageLoaderProvider
@Composable @Composable
expect fun CustomImage( fun CustomImage(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
url: String, url: String,
autoload: Boolean = true,
blurred: Boolean = false, blurred: Boolean = false,
autoload: Boolean,
loadButtonContent: @Composable (() -> Unit)? = null, loadButtonContent: @Composable (() -> Unit)? = null,
contentDescription: String? = null, contentDescription: String? = null,
quality: FilterQuality = FilterQuality.Medium, quality: FilterQuality = FilterQuality.Medium,
@ -25,4 +40,74 @@ expect fun CustomImage(
colorFilter: ColorFilter? = null, colorFilter: ColorFilter? = null,
onLoading: @Composable (BoxScope.(Float?) -> Unit)? = null, onLoading: @Composable (BoxScope.(Float?) -> Unit)? = null,
onFailure: @Composable (BoxScope.(Throwable) -> Unit)? = null, onFailure: @Composable (BoxScope.(Throwable) -> Unit)? = null,
) onSuccess: @Composable (BoxScope.() -> Unit)? = null,
) {
val imageLoaderProvider = remember { getImageLoaderProvider() }
var painterState: AsyncImagePainter.State by remember {
mutableStateOf(AsyncImagePainter.State.Empty)
}
var shouldBeRendered by remember(autoload) { mutableStateOf(autoload) }
Box(
modifier = modifier,
contentAlignment = contentAlignment,
) {
if (shouldBeRendered) {
AsyncImage(
modifier =
Modifier
.fillMaxSize()
.blur(radius = if (blurred) 60.dp else 0.dp),
model = url,
contentDescription = contentDescription,
filterQuality = quality,
contentScale = contentScale,
alignment = alignment,
alpha = alpha,
colorFilter = colorFilter,
onState = {
painterState = it
},
imageLoader = imageLoaderProvider.provideImageLoader(),
)
} else {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Button(
onClick = {
shouldBeRendered = true
},
) {
if (loadButtonContent != null) {
loadButtonContent.invoke()
} else {
Text(
text = LocalStrings.current.buttonLoad,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}
}
when (val state = painterState) {
AsyncImagePainter.State.Empty -> Unit
is AsyncImagePainter.State.Error -> {
onFailure?.invoke(this, state.result.throwable)
}
is AsyncImagePainter.State.Loading -> {
onLoading?.invoke(this, null)
}
is AsyncImagePainter.State.Success -> {
onSuccess?.invoke(this)
}
else -> Unit
}
}
}

View File

@ -1,86 +0,0 @@
package com.livefast.eattrash.raccoonforlemmy.core.commonui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.livefast.eattrash.raccoonforlemmy.core.l10n.messages.LocalStrings
import io.kamel.image.KamelImage
import io.kamel.image.asyncPainterResource
@Composable
actual fun CustomImage(
modifier: Modifier,
url: String,
autoload: Boolean,
blurred: Boolean,
loadButtonContent: @Composable (() -> Unit)?,
contentDescription: String?,
quality: FilterQuality,
contentScale: ContentScale,
alignment: Alignment,
contentAlignment: Alignment,
alpha: Float,
colorFilter: ColorFilter?,
onLoading: @Composable (BoxScope.(Float?) -> Unit)?,
onFailure: @Composable (BoxScope.(Throwable) -> Unit)?,
) {
var shouldBeRendered by remember(autoload) { mutableStateOf(autoload) }
Box(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
if (shouldBeRendered) {
val painterResource =
asyncPainterResource(
data = url,
filterQuality = quality,
)
KamelImage(
modifier =
Modifier
.fillMaxSize()
.blur(radius = if (blurred) 60.dp else 0.dp),
resource = painterResource,
contentDescription = contentDescription,
contentScale = contentScale,
alignment = alignment,
contentAlignment = contentAlignment,
alpha = alpha,
colorFilter = colorFilter,
onLoading = onLoading,
onFailure = onFailure,
)
} else {
Button(
onClick = {
shouldBeRendered = true
},
) {
if (loadButtonContent != null) {
loadButtonContent()
} else {
Text(
text = LocalStrings.current.buttonLoad,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}
}
}

View File

@ -101,6 +101,7 @@ fun CommunityAndCreatorInfo(
onDoubleClick = onDoubleClick ?: {}, onDoubleClick = onDoubleClick ?: {},
), ),
url = communityIcon, url = communityIcon,
autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
@ -133,6 +134,7 @@ fun CommunityAndCreatorInfo(
), ),
url = creatorAvatar, url = creatorAvatar,
quality = FilterQuality.Low, quality = FilterQuality.Low,
autoload = autoLoadImages,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
} else { } else {

View File

@ -64,6 +64,7 @@ fun CommunityHeader(
CustomImage( CustomImage(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
url = banner, url = banner,
autoload = autoLoadImages,
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
contentDescription = null, contentDescription = null,
) )
@ -107,6 +108,7 @@ fun CommunityHeader(
}, },
), ),
url = communityIcon, url = communityIcon,
autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
@ -181,7 +183,7 @@ fun CommunityHeader(
IconButton( IconButton(
modifier = Modifier.padding(end = Spacing.s).size(iconSize), modifier = Modifier.padding(end = Spacing.s).size(iconSize),
onClick = { onClick = {
onInfo?.invoke() onInfo.invoke()
}, },
) { ) {
Icon( Icon(

View File

@ -41,7 +41,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomDrop
import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomImage import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomImage
import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.PlaceholderImage import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.PlaceholderImage
import com.livefast.eattrash.raccoonforlemmy.core.utils.compose.buildAnnotatedStringWithHighlights import com.livefast.eattrash.raccoonforlemmy.core.utils.compose.buildAnnotatedStringWithHighlights
import com.livefast.eattrash.raccoonforlemmy.core.utils.compose.onClick
import com.livefast.eattrash.raccoonforlemmy.core.utils.toLocalDp import com.livefast.eattrash.raccoonforlemmy.core.utils.toLocalDp
import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.CommunityModel import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.CommunityModel
import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.readableHandle import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.readableHandle
@ -93,6 +92,7 @@ fun CommunityItem(
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)), .clip(RoundedCornerShape(iconSize / 2)),
url = communityIcon, url = communityIcon,
autoload = autoLoadImages,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
} else { } else {

View File

@ -128,6 +128,7 @@ fun InboxReplySubtitle(
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)), .clip(RoundedCornerShape(iconSize / 2)),
url = creatorAvatar, url = creatorAvatar,
autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
@ -166,6 +167,7 @@ fun InboxReplySubtitle(
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)), .clip(RoundedCornerShape(iconSize / 2)),
url = communityIcon, url = communityIcon,
autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )

View File

@ -34,7 +34,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomDrop
import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomImage import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomImage
import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.PlaceholderImage import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.PlaceholderImage
import com.livefast.eattrash.raccoonforlemmy.core.persistence.data.MultiCommunityModel import com.livefast.eattrash.raccoonforlemmy.core.persistence.data.MultiCommunityModel
import com.livefast.eattrash.raccoonforlemmy.core.utils.compose.onClick
import com.livefast.eattrash.raccoonforlemmy.core.utils.toLocalDp import com.livefast.eattrash.raccoonforlemmy.core.utils.toLocalDp
@Composable @Composable
@ -67,6 +66,7 @@ fun MultiCommunityItem(
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)), .clip(RoundedCornerShape(iconSize / 2)),
url = communityIcon, url = communityIcon,
autoload = autoLoadImages,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
} else { } else {

View File

@ -60,6 +60,7 @@ fun SettingsImageInfo(
CustomImage( CustomImage(
modifier = imageModifier, modifier = imageModifier,
url = url, url = url,
autoload = true,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = contentScale, contentScale = contentScale,
) )

View File

@ -67,6 +67,7 @@ fun UserHeader(
CustomImage( CustomImage(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
url = banner, url = banner,
autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
) )
@ -107,6 +108,7 @@ fun UserHeader(
}, },
), ),
url = userAvatar, url = userAvatar,
autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
@ -209,7 +211,7 @@ fun UserHeader(
IconButton( IconButton(
modifier = Modifier.padding(end = Spacing.s).size(iconSize), modifier = Modifier.padding(end = Spacing.s).size(iconSize),
onClick = { onClick = {
onInfo?.invoke() onInfo.invoke()
}, },
) { ) {
Icon( Icon(

View File

@ -38,7 +38,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomDrop
import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomImage import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.CustomImage
import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.PlaceholderImage import com.livefast.eattrash.raccoonforlemmy.core.commonui.components.PlaceholderImage
import com.livefast.eattrash.raccoonforlemmy.core.utils.compose.buildAnnotatedStringWithHighlights import com.livefast.eattrash.raccoonforlemmy.core.utils.compose.buildAnnotatedStringWithHighlights
import com.livefast.eattrash.raccoonforlemmy.core.utils.compose.onClick
import com.livefast.eattrash.raccoonforlemmy.core.utils.toLocalDp import com.livefast.eattrash.raccoonforlemmy.core.utils.toLocalDp
import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.UserModel import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.UserModel
import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.readableHandle import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.readableHandle
@ -81,6 +80,7 @@ fun UserItem(
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)), .clip(RoundedCornerShape(iconSize / 2)),
url = avatar, url = avatar,
autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )

View File

@ -30,7 +30,7 @@ kotlin {
sourceSets { sourceSets {
val androidMain by getting { val androidMain by getting {
dependencies { dependencies {
implementation(libs.multiplatform.markdown.renderer.coil2) implementation(libs.multiplatform.markdown.renderer.coil3)
} }
} }
val commonMain by getting { val commonMain by getting {
@ -41,6 +41,7 @@ kotlin {
api(libs.multiplatform.markdown.renderer) api(libs.multiplatform.markdown.renderer)
api(libs.multiplatform.markdown.renderer.m3) api(libs.multiplatform.markdown.renderer.m3)
api(libs.multiplatform.markdown.renderer.coil3)
implementation(projects.core.l10n) implementation(projects.core.l10n)
implementation(projects.core.commonui.components) implementation(projects.core.commonui.components)
@ -57,8 +58,14 @@ kotlin {
android { android {
namespace = "com.livefast.eattrash.raccoonforlemmy.core.markdown" namespace = "com.livefast.eattrash.raccoonforlemmy.core.markdown"
compileSdk = libs.versions.android.targetSdk.get().toInt() compileSdk =
libs.versions.android.targetSdk
.get()
.toInt()
defaultConfig { defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt() minSdk =
libs.versions.android.minSdk
.get()
.toInt()
} }
} }

View File

@ -1,6 +0,0 @@
package com.livefast.eattrash.raccoonforlemmy.core.markdown
import com.mikepenz.markdown.coil2.Coil2ImageTransformerImpl
import com.mikepenz.markdown.model.ImageTransformer
actual fun provideImageTransformer(): ImageTransformer = Coil2ImageTransformerImpl

View File

@ -1,5 +1,6 @@
package com.livefast.eattrash.raccoonforlemmy.core.markdown package com.livefast.eattrash.raccoonforlemmy.core.markdown
import com.mikepenz.markdown.coil3.Coil3ImageTransformerImpl
import com.mikepenz.markdown.model.ImageTransformer import com.mikepenz.markdown.model.ImageTransformer
expect fun provideImageTransformer(): ImageTransformer internal fun provideImageTransformer(): ImageTransformer = Coil3ImageTransformerImpl

View File

@ -1,6 +0,0 @@
package com.livefast.eattrash.raccoonforlemmy.core.markdown
import com.mikepenz.markdown.model.ImageTransformer
import com.mikepenz.markdown.model.NoOpImageTransformerImpl
actual fun provideImageTransformer(): ImageTransformer = NoOpImageTransformerImpl()

View File

@ -36,6 +36,8 @@ kotlin {
implementation(libs.koin.core) implementation(libs.koin.core)
implementation(libs.ktor.cio) implementation(libs.ktor.cio)
implementation(libs.coil)
implementation(libs.coil.network.ktor)
implementation(project.dependencies.platform(libs.kotlincrypto.bom)) implementation(project.dependencies.platform(libs.kotlincrypto.bom))
implementation(libs.kotlincrypto.md5) implementation(libs.kotlincrypto.md5)
@ -48,7 +50,6 @@ kotlin {
implementation(libs.androidx.activity) implementation(libs.androidx.activity)
implementation(libs.androidx.browser) implementation(libs.androidx.browser)
implementation(libs.ktor.android) implementation(libs.ktor.android)
implementation(libs.coil)
implementation(libs.coil.gif) implementation(libs.coil.gif)
} }
} }
@ -71,8 +72,14 @@ kotlin {
android { android {
namespace = "com.livefast.eattrash.raccoonforlemmy.core.utils" namespace = "com.livefast.eattrash.raccoonforlemmy.core.utils"
compileSdk = libs.versions.android.targetSdk.get().toInt() compileSdk =
libs.versions.android.targetSdk
.get()
.toInt()
defaultConfig { defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt() minSdk =
libs.versions.android.minSdk
.get()
.toInt()
} }
} }

View File

@ -12,8 +12,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.utils.fs.DefaultFileSystemMana
import com.livefast.eattrash.raccoonforlemmy.core.utils.fs.FileSystemManager import com.livefast.eattrash.raccoonforlemmy.core.utils.fs.FileSystemManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.DefaultGalleryHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.DefaultGalleryHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.GalleryHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.GalleryHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.DefaultImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.network.DefaultNetworkManager import com.livefast.eattrash.raccoonforlemmy.core.utils.network.DefaultNetworkManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.network.NetworkManager import com.livefast.eattrash.raccoonforlemmy.core.utils.network.NetworkManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.share.DefaultShareHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.share.DefaultShareHelper
@ -24,15 +22,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.DefaultHapticFee
import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback
import org.koin.dsl.module import org.koin.dsl.module
actual val imagePreloadModule =
module {
single<ImagePreloadManager> {
DefaultImagePreloadManager(
context = get(),
)
}
}
actual val networkModule = actual val networkModule =
module { module {
single<NetworkManager> { single<NetworkManager> {

View File

@ -5,6 +5,8 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect import androidx.compose.runtime.SideEffect
import okio.FileSystem
import okio.Path
import org.koin.java.KoinJavaComponent import org.koin.java.KoinJavaComponent
import java.io.InputStreamReader import java.io.InputStreamReader
import java.io.OutputStreamWriter import java.io.OutputStreamWriter
@ -55,6 +57,8 @@ class DefaultFileSystemManager(
launcher.launch(name) launcher.launch(name)
} }
} }
override fun getTempDir(): Path = FileSystem.SYSTEM_TEMPORARY_DIRECTORY
} }
actual fun getFileSystemManager(): FileSystemManager { actual fun getFileSystemManager(): FileSystemManager {

View File

@ -0,0 +1,33 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imageload
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import coil3.decode.Decoder
import coil3.gif.AnimatedImageDecoder
import coil3.gif.GifDecoder
import org.koin.java.KoinJavaComponent
actual fun ByteArray.toComposeImageBitmap(): ImageBitmap = BitmapFactory.decodeByteArray(this, 0, size).asImageBitmap()
actual fun IntArray.toComposeImageBitmap(
width: Int,
height: Int,
): ImageBitmap = Bitmap.createBitmap(this, width, height, Bitmap.Config.ARGB_8888).asImageBitmap()
actual fun getNativeDecoders(): List<Decoder.Factory> =
buildList {
if (Build.VERSION.SDK_INT >= 28) {
AnimatedImageDecoder.Factory()
} else {
GifDecoder.Factory()
}
}
actual fun getImageLoaderProvider(): ImageLoaderProvider {
val inject = KoinJavaComponent.inject<ImageLoaderProvider>(ImageLoaderProvider::class.java)
val res by inject
return res
}

View File

@ -1,25 +0,0 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload
import android.content.Context
import coil.annotation.ExperimentalCoilApi
import coil.imageLoader
import coil.memory.MemoryCache
import coil.request.ImageRequest
class DefaultImagePreloadManager(
private val context: Context,
) : ImagePreloadManager {
override fun preload(url: String) {
val request =
ImageRequest.Builder(context)
.data(url)
.build()
context.imageLoader.enqueue(request)
}
@OptIn(ExperimentalCoilApi::class)
override fun remove(url: String) {
context.imageLoader.memoryCache?.remove(MemoryCache.Key(url))
context.imageLoader.diskCache?.remove(url)
}
}

View File

@ -1,32 +0,0 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload
import android.content.Context
import android.os.Build
import coil.ImageLoader
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.disk.DiskCache
import coil.memory.MemoryCache
fun getCoilImageLoader(context: Context): ImageLoader =
ImageLoader.Builder(context)
.components {
if (Build.VERSION.SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
}
.memoryCache {
MemoryCache.Builder(context)
.maxSizePercent(0.25)
.build()
}
.diskCache {
DiskCache.Builder()
.directory(context.cacheDir.resolve("image_cache"))
.maxSizePercent(0.02)
.build()
}
.crossfade(true)
.build()

View File

@ -2,8 +2,6 @@ package com.livefast.eattrash.raccoonforlemmy.core.utils.di
import org.koin.core.module.Module import org.koin.core.module.Module
expect val imagePreloadModule: Module
expect val networkModule: Module expect val networkModule: Module
expect val appIconModule: Module expect val appIconModule: Module

View File

@ -1,5 +1,9 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.di package com.livefast.eattrash.raccoonforlemmy.core.utils.di
import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.DefaultImageLoaderProvider
import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.DefaultImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImageLoaderProvider
import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.zombiemode.DefaultZombieModeHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.zombiemode.DefaultZombieModeHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.zombiemode.ZombieModeHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.zombiemode.ZombieModeHelper
import org.koin.dsl.module import org.koin.dsl.module
@ -9,4 +13,17 @@ val utilsModule =
factory<ZombieModeHelper> { factory<ZombieModeHelper> {
DefaultZombieModeHelper() DefaultZombieModeHelper()
} }
single<ImageLoaderProvider> {
DefaultImageLoaderProvider(
context = get(),
fileSystemManager = get(),
)
}
single<ImagePreloadManager> {
DefaultImagePreloadManager(
context = get(),
imageLoaderProvider = get(),
)
}
} }

View File

@ -1,6 +1,7 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.fs package com.livefast.eattrash.raccoonforlemmy.core.utils.fs
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import okio.Path
interface FileSystemManager { interface FileSystemManager {
val isSupported: Boolean val isSupported: Boolean
@ -18,6 +19,8 @@ interface FileSystemManager {
data: String, data: String,
callback: (Boolean) -> Unit, callback: (Boolean) -> Unit,
) )
fun getTempDir(): Path
} }
expect fun getFileSystemManager(): FileSystemManager expect fun getFileSystemManager(): FileSystemManager

View File

@ -0,0 +1,39 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imageload
import coil3.ImageLoader
import coil3.PlatformContext
import coil3.disk.DiskCache
import coil3.memory.MemoryCache
import coil3.request.crossfade
import com.livefast.eattrash.raccoonforlemmy.core.utils.fs.FileSystemManager
internal class DefaultImageLoaderProvider(
private val context: PlatformContext,
private val fileSystemManager: FileSystemManager,
) : ImageLoaderProvider {
private val imageLoader by lazy {
ImageLoader
.Builder(context)
.components {
val decoders = getNativeDecoders()
for (decoder in decoders) {
add(decoder)
}
}.memoryCache {
MemoryCache
.Builder()
.maxSizePercent(context, 0.25)
.build()
}.diskCache {
val path = fileSystemManager.getTempDir()
DiskCache
.Builder()
.directory(path)
.maxSizePercent(0.02)
.build()
}.crossfade(true)
.build()
}
override fun provideImageLoader(): ImageLoader = imageLoader
}

View File

@ -0,0 +1,26 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imageload
import coil3.PlatformContext
import coil3.memory.MemoryCache
import coil3.request.ImageRequest
internal class DefaultImagePreloadManager(
private val context: PlatformContext,
private val imageLoaderProvider: ImageLoaderProvider,
) : ImagePreloadManager {
override fun preload(url: String) {
val imageLoader = imageLoaderProvider.provideImageLoader()
val request =
ImageRequest
.Builder(context)
.data(url)
.build()
imageLoader.enqueue(request)
}
override fun remove(url: String) {
val imageLoader = imageLoaderProvider.provideImageLoader()
imageLoader.memoryCache?.remove(MemoryCache.Key(url))
imageLoader.diskCache?.remove(url)
}
}

View File

@ -0,0 +1,7 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imageload
import coil3.ImageLoader
interface ImageLoaderProvider {
fun provideImageLoader(): ImageLoader
}

View File

@ -1,4 +1,4 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload package com.livefast.eattrash.raccoonforlemmy.core.utils.imageload
interface ImagePreloadManager { interface ImagePreloadManager {
fun preload(url: String) fun preload(url: String)

View File

@ -0,0 +1,15 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imageload
import androidx.compose.ui.graphics.ImageBitmap
import coil3.decode.Decoder
expect fun ByteArray.toComposeImageBitmap(): ImageBitmap
expect fun IntArray.toComposeImageBitmap(
width: Int,
height: Int,
): ImageBitmap
expect fun getNativeDecoders(): List<Decoder.Factory>
expect fun getImageLoaderProvider(): ImageLoaderProvider

View File

@ -12,8 +12,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.utils.fs.DefaultFileSystemMana
import com.livefast.eattrash.raccoonforlemmy.core.utils.fs.FileSystemManager import com.livefast.eattrash.raccoonforlemmy.core.utils.fs.FileSystemManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.DefaultGalleryHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.DefaultGalleryHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.GalleryHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.GalleryHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.DefaultImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.network.DefaultNetworkManager import com.livefast.eattrash.raccoonforlemmy.core.utils.network.DefaultNetworkManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.network.NetworkManager import com.livefast.eattrash.raccoonforlemmy.core.utils.network.NetworkManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.share.DefaultShareHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.share.DefaultShareHelper
@ -24,13 +22,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.DefaultHapticFee
import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback
import org.koin.dsl.module import org.koin.dsl.module
actual val imagePreloadModule =
module {
single<ImagePreloadManager> {
DefaultImagePreloadManager()
}
}
actual val networkModule = actual val networkModule =
module { module {
single<NetworkManager> { DefaultNetworkManager() } single<NetworkManager> { DefaultNetworkManager() }

View File

@ -1,6 +1,8 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.fs package com.livefast.eattrash.raccoonforlemmy.core.utils.fs
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import okio.FileSystem
import okio.Path
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
@ -24,6 +26,8 @@ class DefaultFileSystemManager : FileSystemManager {
) { ) {
callback(false) callback(false)
} }
override fun getTempDir(): Path = FileSystem.SYSTEM_TEMPORARY_DIRECTORY
} }
object FileSystemManagerDiHelper : KoinComponent { object FileSystemManagerDiHelper : KoinComponent {

View File

@ -0,0 +1,33 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imageload
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asComposeImageBitmap
import androidx.compose.ui.graphics.toComposeImageBitmap
import coil3.decode.Decoder
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.ColorAlphaType
import org.jetbrains.skia.ColorType
import org.jetbrains.skia.Image
import org.jetbrains.skia.ImageInfo
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
actual fun ByteArray.toComposeImageBitmap(): ImageBitmap = Image.makeFromEncoded(this).toComposeImageBitmap()
actual fun IntArray.toComposeImageBitmap(
width: Int,
height: Int,
): ImageBitmap {
val bmp = Bitmap()
val info = ImageInfo(width, height, ColorType.RGBA_8888, ColorAlphaType.PREMUL)
bmp.installPixels(info, map { it.toByte() }.toByteArray(), info.minRowBytes)
return bmp.asComposeImageBitmap()
}
actual fun getNativeDecoders(): List<Decoder.Factory> = emptyList()
actual fun getImageLoaderProvider(): ImageLoaderProvider = ImageUtilsDiHelper.imageLoaderProvider
internal object ImageUtilsDiHelper : KoinComponent {
val imageLoaderProvider: ImageLoaderProvider by inject()
}

View File

@ -1,11 +0,0 @@
package com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload
class DefaultImagePreloadManager() : ImagePreloadManager {
override fun preload(url: String) {
// no-op
}
override fun remove(url: String) {
// no-op
}
}

View File

@ -8,7 +8,7 @@ androidx-media3 = "1.4.1"
androidx-splashscreen = "1.0.1" androidx-splashscreen = "1.0.1"
androidx-work = "2.9.1" androidx-work = "2.9.1"
android-gradle = "8.5.2" android-gradle = "8.5.2"
coil = "2.7.0" coil = "3.0.0"
colorpicker = "1.1.2" colorpicker = "1.1.2"
compose = "1.7.0" compose = "1.7.0"
detekt = "1.23.7" detekt = "1.23.7"
@ -47,16 +47,16 @@ androidx-security-crypto = { module = "androidx.security:security-crypto", versi
androidx-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-splashscreen" } androidx-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-splashscreen" }
androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" } androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" }
kamel = { module = "media.kamel:kamel-image", version.ref = "kamel" } coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
coil = { module = "io.coil-kt:coil", version.ref = "coil" } coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } coil-compose = { module = "io.coil-kt.coil3:coil-compose-core", version.ref = "coil" }
coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil" }
compose-colorpicker = { module = "com.github.skydoves:colorpicker-compose", version.ref = "colorpicker" } compose-colorpicker = { module = "com.github.skydoves:colorpicker-compose", version.ref = "colorpicker" }
multiplatform-markdown-renderer = { module = "com.mikepenz:multiplatform-markdown-renderer", version.ref = "multiplatform-markdown-renderer" } multiplatform-markdown-renderer = { module = "com.mikepenz:multiplatform-markdown-renderer", version.ref = "multiplatform-markdown-renderer" }
multiplatform-markdown-renderer-m3 = { module = "com.mikepenz:multiplatform-markdown-renderer-m3", version.ref = "multiplatform-markdown-renderer" } multiplatform-markdown-renderer-m3 = { module = "com.mikepenz:multiplatform-markdown-renderer-m3", version.ref = "multiplatform-markdown-renderer" }
multiplatform-markdown-renderer-coil2 = { module = "com.mikepenz:multiplatform-markdown-renderer-coil2", version.ref = "multiplatform-markdown-renderer" } multiplatform-markdown-renderer-coil3= { module = "com.mikepenz:multiplatform-markdown-renderer-coil3", version.ref = "multiplatform-markdown-renderer" }
multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" } multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" }

View File

@ -17,7 +17,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.utils.di.customTabsModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.fileSystemModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.fileSystemModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.galleryHelperModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.galleryHelperModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.hapticFeedbackModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.hapticFeedbackModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.imagePreloadModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.networkModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.networkModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.shareHelperModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.shareHelperModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.utilsModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.utilsModule
@ -79,7 +78,6 @@ val sharedHelperModule =
repositoryModule, repositoryModule,
utilsModule, utilsModule,
domainInboxModule, domainInboxModule,
imagePreloadModule,
networkModule, networkModule,
coreNavigationModule, coreNavigationModule,
lemmyUiModule, lemmyUiModule,

View File

@ -81,12 +81,13 @@ internal fun RowScope.TabNavigationItem(
val iconSize = IconSize.m val iconSize = IconSize.m
CustomImage( CustomImage(
url = customIconUrl,
modifier = modifier =
Modifier Modifier
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)) .clip(RoundedCornerShape(iconSize / 2))
.then(pointerInputModifier), .then(pointerInputModifier),
url = customIconUrl,
autoload = true,
) )
} else { } else {
Icon( Icon(

View File

@ -17,7 +17,6 @@ import com.livefast.eattrash.raccoonforlemmy.core.utils.di.customTabsModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.fileSystemModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.fileSystemModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.galleryHelperModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.galleryHelperModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.hapticFeedbackModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.hapticFeedbackModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.imagePreloadModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.networkModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.networkModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.shareHelperModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.shareHelperModule
import com.livefast.eattrash.raccoonforlemmy.core.utils.di.utilsModule import com.livefast.eattrash.raccoonforlemmy.core.utils.di.utilsModule
@ -79,7 +78,6 @@ fun initKoin() {
repositoryModule, repositoryModule,
domainInboxModule, domainInboxModule,
utilsModule, utilsModule,
imagePreloadModule,
networkModule, networkModule,
coreNavigationModule, coreNavigationModule,
lemmyUiModule, lemmyUiModule,

View File

@ -47,6 +47,7 @@ fun AcknoledgementItem(
.clip(RoundedCornerShape(iconSize / 2)), .clip(RoundedCornerShape(iconSize / 2)),
contentDescription = null, contentDescription = null,
url = url, url = url,
autoload = true,
) )
} }
} else { } else {

View File

@ -14,7 +14,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.Communi
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.CommunitySortRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.CommunitySortRepository
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.FavoriteCommunityRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.FavoriteCommunityRepository
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback
import com.livefast.eattrash.raccoonforlemmy.core.utils.zombiemode.ZombieModeHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.zombiemode.ZombieModeHelper

View File

@ -45,6 +45,7 @@ internal fun DrawerCommunityItem(
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)), .clip(RoundedCornerShape(iconSize / 2)),
url = url, url = url,
autoload = autoLoadImages,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
} else { } else {

View File

@ -8,7 +8,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.architecture.DefaultMviModel
import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter
import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback
import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.ApiConfigurationRepository import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.ApiConfigurationRepository
import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.IdentityRepository import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.IdentityRepository
@ -390,7 +390,10 @@ class ExploreViewModel(
val results = paginationManager.loadNextPage() val results = paginationManager.loadNextPage()
if (uiState.value.autoLoadImages) { if (uiState.value.autoLoadImages) {
results.forEach { res -> results.forEach { res ->
(res as? SearchResult.Post)?.model?.imageUrl?.takeIf { it.isNotEmpty() } (res as? SearchResult.Post)
?.model
?.imageUrl
?.takeIf { it.isNotEmpty() }
?.also { url -> ?.also { url ->
imagePreloadManager.preload(url) imagePreloadManager.preload(url)
} }

View File

@ -11,7 +11,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.architecture.DefaultMviModel
import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter
import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback
import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.IdentityRepository import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.IdentityRepository
import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.CommentModel import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.CommentModel

View File

@ -175,6 +175,7 @@ private fun ModlogHeader(
url = creatorAvatar, url = creatorAvatar,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
autoload = autoLoadImages,
) )
} }
Text( Text(
@ -197,7 +198,6 @@ private fun ModlogFooter(
onOpen: (() -> Unit)? = null, onOpen: (() -> Unit)? = null,
onOptionSelected: ((OptionId) -> Unit)? = null, onOptionSelected: ((OptionId) -> Unit)? = null,
) { ) {
val buttonModifier = Modifier.size(IconSize.l).padding(3.dp)
var optionsExpanded by remember { mutableStateOf(false) } var optionsExpanded by remember { mutableStateOf(false) }
var optionsOffset by remember { mutableStateOf(Offset.Zero) } var optionsOffset by remember { mutableStateOf(Offset.Zero) }
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha) val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)

View File

@ -11,7 +11,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCent
import com.livefast.eattrash.raccoonforlemmy.core.persistence.data.MultiCommunityModel import com.livefast.eattrash.raccoonforlemmy.core.persistence.data.MultiCommunityModel
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.MultiCommunityRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.MultiCommunityRepository
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback
import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.IdentityRepository import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.IdentityRepository

View File

@ -160,8 +160,8 @@ class MultiCommunityEditorScreen(
value = uiState.name, value = uiState.name,
keyboardOptions = keyboardOptions =
KeyboardOptions( KeyboardOptions(
autoCorrectEnabled = false,
keyboardType = KeyboardType.Text, keyboardType = KeyboardType.Text,
autoCorrect = false,
imeAction = ImeAction.Next, imeAction = ImeAction.Next,
), ),
onValueChange = { value -> onValueChange = { value ->
@ -227,6 +227,7 @@ class MultiCommunityEditorScreen(
}, },
), ),
url = url, url = url,
autoload = uiState.autoLoadImages,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
) )
} }

View File

@ -9,7 +9,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.architecture.DefaultMviModel
import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter
import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback
import com.livefast.eattrash.raccoonforlemmy.core.utils.zombiemode.ZombieModeHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.zombiemode.ZombieModeHelper

View File

@ -171,6 +171,7 @@ private fun ReportHeader(
url = creatorAvatar, url = creatorAvatar,
quality = FilterQuality.Low, quality = FilterQuality.Low,
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
autoload = autoLoadImages,
) )
} }
Text( Text(
@ -193,7 +194,6 @@ private fun ReportFooter(
onOpenResolve: (() -> Unit)? = null, onOpenResolve: (() -> Unit)? = null,
onOptionSelected: ((OptionId) -> Unit)? = null, onOptionSelected: ((OptionId) -> Unit)? = null,
) { ) {
val buttonModifier = Modifier.size(IconSize.l).padding(3.dp)
var optionsExpanded by remember { mutableStateOf(false) } var optionsExpanded by remember { mutableStateOf(false) }
var optionsOffset by remember { mutableStateOf(Offset.Zero) } var optionsOffset by remember { mutableStateOf(Offset.Zero) }
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha) val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)

View File

@ -12,7 +12,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.commonui.lemmyui.UserDetailSec
import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter
import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent
import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback
import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.ApiConfigurationRepository import com.livefast.eattrash.raccoonforlemmy.domain.identity.repository.ApiConfigurationRepository

View File

@ -9,7 +9,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.Setting
import com.livefast.eattrash.raccoonforlemmy.core.utils.datetime.epochMillis import com.livefast.eattrash.raccoonforlemmy.core.utils.datetime.epochMillis
import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.GalleryHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.GalleryHelper
import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.download import com.livefast.eattrash.raccoonforlemmy.core.utils.gallery.download
import com.livefast.eattrash.raccoonforlemmy.core.utils.imagepreload.ImagePreloadManager import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager
import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers