From 0ef31add87b840dc134ad6976354d413617f9b0e Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 27 Nov 2023 15:33:23 -0500 Subject: [PATCH] Update dependencies, fix ui, and more --- .github/workflows/build.yml | 14 +- .idea/.name | 2 +- build.gradle.kts | 20 -- genesis/app/build.gradle.kts | 11 +- .../kotlin/xyz/genesisapp/genesis/app/App.kt | 13 +- .../genesisapp/genesis/app/data/DataStore.kt | 17 ++ .../genesis/app/di/genesisClientModule.kt | 2 +- .../genesis/app/ui/components/User/Avatar.kt | 34 ++- .../client/messaging/ChannelsScreen.kt | 24 +- .../ui/screens/client/messaging/ChatScreen.kt | 2 +- .../ui/screens/client/messaging/GuildsTab.kt | 286 +++++++++--------- genesis/common/build.gradle.kts | 21 +- .../genesisapp/common/compose/koin/Module.kt | 10 + .../genesisapp/common/compose/koin/http.kt | 16 + .../xyz/genesisapp/common/fytix/EventBus.kt | 7 +- genesis/discord/api/build.gradle.kts | 5 - .../xyz/genesisapp/discord/api/types/Asset.kt | 20 +- genesis/discord/client/build.gradle.kts | 11 +- .../discord/client/GenesisClient.kt | 2 + .../discord/client/entities/Asset.kt | 48 +++ .../discord/client/entities/guild/Channel.kt | 13 +- .../discord/client/entities/guild/Guild.kt | 30 +- .../discord/client/entities/guild/Message.kt | 40 ++- .../discord/client/entities/guild/User.kt | 7 +- .../discord/client/gateway/GatewayClient.kt | 31 +- .../discord/client/gateway/handlers/Ready.kt | 10 +- genesis/genesisApi/build.gradle.kts | 8 - gradle/libs.versions.toml | 21 +- 28 files changed, 417 insertions(+), 308 deletions(-) create mode 100644 genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/compose/koin/Module.kt create mode 100644 genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/compose/koin/http.kt create mode 100644 genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/Asset.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 632b06c..6dbaf8d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,37 +27,37 @@ jobs: uses: actions/cache@v3 with: path: appAndroid/build - key: ${{ runner.os }}-appAndroid-${{ hashFiles('appAndroid/**') }} + key: ${{ runner.os }}-appAndroid-${{ hashFiles('appAndroid/**', 'build.gradle.kts', 'gradle.properties', 'gradle/libs.versions.toml') }} - name: Cache appDesktop uses: actions/cache@v3 with: path: appDesktop/build - key: ${{ runner.os }}-appDesktop-${{ hashFiles('appDesktop/**') }} + key: ${{ runner.os }}-appDesktop-${{ hashFiles('appDesktop/**', 'build.gradle.kts', 'gradle.properties', 'gradle/libs.versions.toml') }} - name: Cache genesisApp uses: actions/cache@v3 with: path: genesis/app/build - key: ${{ runner.os }}-genesis-app-${{ hashFiles('genesis/app/**') }} + key: ${{ runner.os }}-genesis-app-${{ hashFiles('genesis/app/**', 'build.gradle.kts', 'gradle.properties', 'gradle/libs.versions.toml') }} - name: Cache genesisCommon uses: actions/cache@v3 with: path: genesis/common/build - key: ${{ runner.os }}-genesis-common-${{ hashFiles('genesis/common/**') }} + key: ${{ runner.os }}-genesis-common-${{ hashFiles('genesis/common/**', 'build.gradle.kts', 'gradle.properties', 'gradle/libs.versions.toml') }} - name: Cache genesisDiscordApi uses: actions/cache@v3 with: path: genesis/discord/api/build - key: ${{ runner.os }}-genesis-discord-api-${{ hashFiles('genesis/discord/api/**') }} + key: ${{ runner.os }}-genesis-discord-api-${{ hashFiles('genesis/discord/api/**', 'build.gradle.kts', 'gradle.properties', 'gradle/libs.versions.toml') }} - name: Cache genesisDiscordClient uses: actions/cache@v3 with: path: genesis/discord/client/build - key: ${{ runner.os }}-genesis-discord-client-${{ hashFiles('genesis/discord/client/**') }} + key: ${{ runner.os }}-genesis-discord-client-${{ hashFiles('genesis/discord/client/**', 'build.gradle.kts', 'gradle.properties', 'gradle/libs.versions.toml') }} - name: Cache genesisApi uses: actions/cache@v3 with: path: genesis/genesisApi/build - key: ${{ runner.os }}-genesis-genesisApi-${{ hashFiles('genesis/genesisApi/**') }} + key: ${{ runner.os }}-genesis-genesisApi-${{ hashFiles('genesis/genesisApi/**', 'build.gradle.kts', 'gradle.properties', 'gradle/libs.versions.toml') }} diff --git a/.idea/.name b/.idea/.name index 7286a83..40fc7f7 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -__Icons.kt \ No newline at end of file +Genesis \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index be2208d..0c0d457 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,24 +29,4 @@ buildscript { // classpath(libs.moko.resources.generator) } } -allprojects { - val iosSpec = object : Spec { - override fun isSatisfiedBy(element: Task?): Boolean { - return System.getenv("GITHUB_ACTIONS") == "true" || System.getenv("ENABLE_IOS") == "true" - } - - } - -// tasks.withType(KotlinCompile::class).all { -// kotlinOptions { -// freeCompilerArgs += "-Xexpect-actual-classes" -// } -// } - - tasks.matching { - name.contains("FrameworkIos") - }.forEach { - it.setOnlyIf(iosSpec) - } -} true // Needed to make the Suppress annotation work for the plugins block \ No newline at end of file diff --git a/genesis/app/build.gradle.kts b/genesis/app/build.gradle.kts index ff43a7c..f223777 100644 --- a/genesis/app/build.gradle.kts +++ b/genesis/app/build.gradle.kts @@ -49,7 +49,7 @@ kotlin { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") - implementation("media.kamel:kamel-image:0.8.2") + implementation(libs.kamel) implementation(project(":genesis:common")) @@ -68,15 +68,6 @@ kotlin { implementation(libs.ktor.client.okhttp) } } - val iosArm64Main by getting - val iosMain by creating { - dependsOn(commonMain) - iosArm64Main.dependsOn(this) - dependencies { - implementation(libs.ktor.client.darwin) - } - - } val desktopMain by getting { dependencies { implementation(compose.desktop.common) diff --git a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/App.kt b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/App.kt index a6f5991..34b2b99 100644 --- a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/App.kt +++ b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/App.kt @@ -5,17 +5,18 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import io.github.aakira.napier.Antilog -import io.github.aakira.napier.DebugAntilog import io.github.aakira.napier.Napier -import org.jetbrains.compose.resources.ExperimentalResourceApi import org.koin.compose.KoinApplication import org.koin.compose.getKoin +import xyz.genesisapp.common.compose.koin.uninitModule import xyz.genesisapp.common.preferences.PreferencesManager import xyz.genesisapp.discord.client.GenesisClient import xyz.genesisapp.discord.client.enum.LogLevel @@ -24,12 +25,12 @@ import xyz.genesisapp.genesis.app.di.dataStoreModule import xyz.genesisapp.genesis.app.di.genesisApiModule import xyz.genesisapp.genesis.app.di.genesisClientModule import xyz.genesisapp.genesis.app.di.httpModule +import xyz.genesisapp.genesis.app.di.platformHttpEngineFactory import xyz.genesisapp.genesis.app.di.preferencesModule import xyz.genesisapp.genesis.app.theme.LocalContextColors import xyz.genesisapp.genesis.app.theme.ThemeProvider import xyz.genesisapp.genesis.app.ui.screens.RootScreen -@OptIn(ExperimentalResourceApi::class) @Composable fun App() { Napier.base(getAntiLog()) @@ -40,7 +41,9 @@ fun App() { httpModule(), genesisClientModule(), genesisApiModule(), - dataStoreModule() + dataStoreModule(), + + uninitModule(httpFactory = platformHttpEngineFactory) ) }) { val koin = getKoin() diff --git a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/data/DataStore.kt b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/data/DataStore.kt index 7ef8470..1d71389 100644 --- a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/data/DataStore.kt +++ b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/data/DataStore.kt @@ -69,4 +69,21 @@ class DataStore() : EventBus("DataStore") { fun compositionOnClientTabSelect(block: Event.(ClientTab) -> Unit) = compositionLocalOn("CLIENT_TAB_SELECT", block) + /** + * Explicit typing for [emit] function, event is [Boolean] + */ + fun toggleGuilds() = emit("GUILDS_TOGGLE", true) + + /** + * Explicit typing for [on] function, event is [Boolean] + */ + fun onToggleGuilds(block: Event.(Boolean) -> Unit) = + on("GUILDS_TOGGLE", block) + + /** + * Explicit typing for [compositionLocalOn] function, event is [Boolean] + */ + @Composable + fun compositionOnToggleGuilds(block: Event.(Boolean) -> Unit) = + compositionLocalOn("GUILDS_TOGGLE", block) } \ No newline at end of file diff --git a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/di/genesisClientModule.kt b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/di/genesisClientModule.kt index 217e788..2c1e32a 100644 --- a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/di/genesisClientModule.kt +++ b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/di/genesisClientModule.kt @@ -5,5 +5,5 @@ import xyz.genesisapp.discord.client.GenesisClient fun genesisClientModule() = module { - single { GenesisClient(get()) } + single { GenesisClient(get(), enableAssetCache = true) } } \ No newline at end of file diff --git a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/components/User/Avatar.kt b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/components/User/Avatar.kt index d76964f..8dd794f 100644 --- a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/components/User/Avatar.kt +++ b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/components/User/Avatar.kt @@ -6,40 +6,52 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp +import io.github.aakira.napier.Napier import io.kamel.image.KamelImage -import io.kamel.image.asyncPainterResource import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource -import xyz.genesisapp.discord.api.types.AssetType -import xyz.genesisapp.discord.api.types.toUrl import xyz.genesisapp.discord.client.entities.guild.User @OptIn(ExperimentalResourceApi::class) @Composable fun Avatar(author: User, modifier: Modifier = Modifier) { + val scope = rememberCoroutineScope() Box( modifier = modifier .width(32.dp) .height(32.dp) .clip(shape = CircleShape) ) { + val defaultAvatar = painterResource("images/defaults/default_avatar_${author.id % 5}.png") if (author.avatar != null) { KamelImage( - resource = asyncPainterResource( - author.avatar!!.toUrl( - AssetType.Avatar, - author.id, - 128 - ) - ), + resource = author.avatar!!.render(128), contentDescription = "Avatar", + onLoading = { + Image( + painter = defaultAvatar, + contentDescription = "Avatar Loading", + ) + }, + onFailure = { + Napier.e( + "Avatar for ${author.displayName} failed to load", + it, + "Avatar Component" + ) + Image( + painter = defaultAvatar, + contentDescription = "Avatar Failed to load", + ) + } ) } else { Image( - painter = painterResource("images/default/default_avatar_${author.id % 5}.png"), + painter = defaultAvatar, contentDescription = "Avatar", ) } diff --git a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/ChannelsScreen.kt b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/ChannelsScreen.kt index 1cf371c..7ce2faa 100644 --- a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/ChannelsScreen.kt +++ b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/ChannelsScreen.kt @@ -38,15 +38,12 @@ import io.github.aakira.napier.Napier import io.kamel.core.ExperimentalKamelApi import io.kamel.image.KamelImage import io.kamel.image.KamelImageBox -import io.kamel.image.asyncPainterResource import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource import org.koin.compose.getKoin import xyz.genesisapp.common.preferences.PreferencesManager -import xyz.genesisapp.discord.api.types.AssetType import xyz.genesisapp.discord.api.types.Snowflake import xyz.genesisapp.discord.api.types.timestamp -import xyz.genesisapp.discord.api.types.toUrl import xyz.genesisapp.discord.client.GenesisClient import xyz.genesisapp.discord.client.entities.guild.Channel import xyz.genesisapp.discord.client.entities.guild.Guild @@ -89,12 +86,8 @@ fun Channel(channel: Channel, select: (Channel) -> Unit) { ) { if (channel.icon != null) { KamelImage( - resource = asyncPainterResource( - channel.icon!!.toUrl( - AssetType.Avatar, - channel.id, - 128 - ) + resource = channel.icon!!.render( + 128 ), contentDescription = channel.name, ) @@ -175,11 +168,10 @@ class ChannelsScreen( mutableStateOf(currentChannel) } - Events( - dataStore.events.quietRegister("GUILD_SELECT") { - navigator.push(ChannelsScreen(genesisClient.guilds[it], guild.id)) - } - ) + dataStore.compositionOnGuildSelect { + navigator.push(ChannelsScreen(genesisClient.guilds[it]!!, guild.id)) + } + Scaffold( modifier = Modifier @@ -263,9 +255,7 @@ class ChannelsScreen( .height(48.dp) if (guild.banner != null) { KamelImageBox( - resource = asyncPainterResource( - guild.banner!!.toUrl(AssetType.Banner, guild.id, 480), - ), + resource = guild.banner!!.render(480), modifier = modifier, onFailure = { Text(guild.name) diff --git a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/ChatScreen.kt b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/ChatScreen.kt index 07531fd..341991b 100644 --- a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/ChatScreen.kt +++ b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/ChatScreen.kt @@ -151,7 +151,7 @@ class ChatScreen( AnimatedVisibility(visible = dataStore.mobileUi) { Button( onClick = { - dataStore.events.emit("GUILDS_TOGGLE", true) + dataStore.toggleGuilds() }, modifier = Modifier.align(alignment = Alignment.CenterVertically) ) { diff --git a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/GuildsTab.kt b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/GuildsTab.kt index 419e485..4f3c20b 100644 --- a/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/GuildsTab.kt +++ b/genesis/app/src/commonMain/kotlin/xyz/genesisapp/genesis/app/ui/screens/client/messaging/GuildsTab.kt @@ -35,31 +35,28 @@ import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import cafe.adriel.voyager.navigator.Navigator +import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.TabOptions import io.kamel.image.KamelImage -import io.kamel.image.asyncPainterResource import kotlinx.coroutines.launch import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource import org.koin.compose.getKoin import xyz.genesisapp.common.preferences.PreferencesManager import xyz.genesisapp.discord.api.domain.user.GuildFolder -import xyz.genesisapp.discord.api.types.AssetType import xyz.genesisapp.discord.api.types.Snowflake -import xyz.genesisapp.discord.api.types.toUrl import xyz.genesisapp.discord.client.GenesisClient import xyz.genesisapp.discord.entities.guild.ChannelType import xyz.genesisapp.genesis.app.data.DataStore import xyz.genesisapp.genesis.app.ui.components.icons.Icons import xyz.genesisapp.genesis.app.ui.components.icons.icons.Empty -import xyz.genesisapp.genesis.app.ui.screens.EventTab import kotlin.math.roundToInt enum class GuildIconType { DM, GUILD, FOLDER } -internal object GuildsTab : EventTab() { +internal object GuildsTab : Tab { override val options: TabOptions @Composable @@ -96,14 +93,13 @@ internal object GuildsTab : EventTab() { val scope = rememberCoroutineScope() val swipeableState = rememberSwipeableState(0) - Events( - dataStore.events.quietRegister("GUILDS_TOGGLE") { - scope.launch { - if (swipeableState.currentValue == 0) swipeableState.animateTo(1) - else swipeableState.animateTo(0) - } + dataStore.compositionOnToggleGuilds { + scope.launch { + if (swipeableState.currentValue == 0) swipeableState.animateTo(1) + else swipeableState.animateTo(0) } - ) + } + fun chooseGuild(id: Snowflake) { lastGuild = currentGuild @@ -118,170 +114,166 @@ internal object GuildsTab : EventTab() { ) } + val width = 500.dp - Row { - var rerenderBool by remember { mutableStateOf(true) } - fun rerender() { - rerenderBool = false - } - if (rerenderBool) { - LazyColumn( - modifier = Modifier - .fillMaxHeight() - .width(64.dp) - ) { - item(key = "DMS") { - val modifier = Modifier - .size( - 48.dp, - 48.dp - ) - .clickable { - chooseGuild(0L) - } - - Box( - modifier = if (currentGuild.toInt() == 0) { - modifier.clip(RoundedCornerShape(2.dp)) - } else { - modifier.clip(CircleShape) - } - ) { - Image( - painterResource(if (useDiscordIcon) "images/img_logo.png" else "icons/genesis.png"), - contentDescription = "DMs", - modifier = Modifier - .size(24.dp) - .align(Alignment.Center) - ) - } - } - - val notHitGuilds = genesisClient.guilds.keys.toMutableList() - notHitGuilds.remove(0L) - genesisClient.userSettings!!.guildFolders.forEach { folder -> - folder.guildIds.forEach { guildId -> - notHitGuilds.remove(guildId.toLong()) - } - } - - if (notHitGuilds.size > 0) { - genesisClient.userSettings!!.guildFolders.add( - GuildFolder( - id = null, - name = "Other", - color = null, - guildIds = notHitGuilds.map { it.toString() }, - collapsed = false - ) - ) - } - + Box( + modifier = Modifier.swipeable( + state = swipeableState, + anchors = mapOf(0f to 0, width.value to 1), + thresholds = { _, _ -> FractionalThreshold(0.3f) }, + orientation = Orientation.Horizontal + ) + ) { + Row { + var rerenderBool by remember { mutableStateOf(true) } + fun rerender() { + rerenderBool = false + } + if (rerenderBool) { + LazyColumn( + modifier = Modifier + .fillMaxHeight() + .width(64.dp) + ) { + item(key = "DMS") { + val modifier = Modifier + .size( + 48.dp, + 48.dp + ) + .clickable { + chooseGuild(0L) + } - items( - items = genesisClient.userSettings!!.guildFolders, - key = { it.id ?: it.guildIds.first() } - ) { sortedFolder -> - if (sortedFolder.guildIds.isEmpty()) return@items - if (sortedFolder.guildIds.size == 1) { - sortedFolder.collapsed = false - } else { Box( - modifier = Modifier - .size( - 48.dp, - 48.dp - ) - .clickable { - sortedFolder.collapsed = !sortedFolder.collapsed - rerender() - } + modifier = if (currentGuild.toInt() == 0) { + modifier.clip(RoundedCornerShape(2.dp)) + } else { + modifier.clip(CircleShape) + } ) { Image( - painter = painterResource("images/folder.png"), - contentDescription = sortedFolder.name, - colorFilter = ColorFilter.tint( - Color( - sortedFolder.color ?: 0x5865f2 - ).copy(alpha = 0.5f) - ), + painterResource(if (useDiscordIcon) "images/img_logo.png" else "icons/genesis.png"), + contentDescription = "DMs", modifier = Modifier .size(24.dp) .align(Alignment.Center) ) } + } + + val notHitGuilds = genesisClient.guilds.keys.toMutableList() + notHitGuilds.remove(0L) + genesisClient.userSettings!!.guildFolders.forEach { folder -> + folder.guildIds.forEach { guildId -> + notHitGuilds.remove(guildId.toLong()) + } + } + if (notHitGuilds.size > 0) { + genesisClient.userSettings!!.guildFolders.add( + GuildFolder( + id = null, + name = "Other", + color = null, + guildIds = notHitGuilds.map { it.toString() }, + collapsed = false + ) + ) } - if (!sortedFolder.collapsed) { - sortedFolder.guildIds.forEach forEach2@{ guildId -> - val guild = genesisClient.guilds[guildId.toLong()] - if (guild === null) return@forEach2 - val modifier = Modifier - .size( - 48.dp, - 48.dp - ) - .clickable { - chooseGuild(guildId.toLong()) - } + + + items( + items = genesisClient.userSettings!!.guildFolders, + key = { it.id ?: it.guildIds.first() } + ) { sortedFolder -> + if (sortedFolder.guildIds.isEmpty()) return@items + if (sortedFolder.guildIds.size == 1) { + sortedFolder.collapsed = false + } else { Box( - modifier = if (currentGuild == guildId.toLong()) { - modifier.clip(RoundedCornerShape(2.dp)) - } else { - modifier.clip(CircleShape) - } + modifier = Modifier + .size( + 48.dp, + 48.dp + ) + .clickable { + sortedFolder.collapsed = !sortedFolder.collapsed + rerender() + } ) { - if (guild.icon !== null) KamelImage( - resource = asyncPainterResource( - guild.icon!!.toUrl( - AssetType.Icon, guild.id, - 128 - ) + Image( + painter = painterResource("images/folder.png"), + contentDescription = sortedFolder.name, + colorFilter = ColorFilter.tint( + Color( + sortedFolder.color ?: 0x5865f2 + ).copy(alpha = 0.5f) ), - contentDescription = guild.name - ) else { - var str = "" - guild.name.split(" ").forEach { word -> - str += word[0] + modifier = Modifier + .size(24.dp) + .align(Alignment.Center) + ) + } + + } + if (!sortedFolder.collapsed) { + sortedFolder.guildIds.forEach forEach2@{ guildId -> + val guild = genesisClient.guilds[guildId.toLong()] + if (guild === null) return@forEach2 + val modifier = Modifier + .size( + 48.dp, + 48.dp + ) + .clickable { + chooseGuild(guildId.toLong()) + } + + Box( + modifier = if (currentGuild == guildId.toLong()) { + modifier.clip(RoundedCornerShape(2.dp)) + } else { + modifier.clip(CircleShape) } + ) { + if (guild.icon !== null) KamelImage( + resource = guild.icon!!.render(128), + contentDescription = guild.name + ) else { + var str = "" + guild.name.split(" ").forEach { word -> + str += word[0] + } - Text(str) + Text(str) + } } } } } } - } - } else rerenderBool = true - Navigator(ChannelsScreen(genesisClient.guilds[currentGuild], lastGuild)) - } - val guild = genesisClient.guilds[currentGuild]!! + } else rerenderBool = true + Navigator(ChannelsScreen(genesisClient.guilds[currentGuild], lastGuild)) + } + val guild = genesisClient.guilds[currentGuild]!! - val firstChannel = when (currentGuild) { - 0L -> guild.channels.first().id - else -> guild.channels.find { it.type == ChannelType.GUILD_TEXT }!!.id - } + val firstChannel = when (currentGuild) { + 0L -> guild.channels.first().id + else -> guild.channels.find { it.type == ChannelType.GUILD_TEXT }!!.id + } - var currentChannel by prefs.preference( - "client.currentChannel", - firstChannel - ) + var currentChannel by prefs.preference( + "client.currentChannel", + firstChannel + ) - if (guild.channels.find { it.id == currentChannel } == null) currentChannel = - firstChannel + if (guild.channels.find { it.id == currentChannel } == null) currentChannel = + firstChannel - val width = 500.dp - Box( - modifier = Modifier.swipeable( - state = swipeableState, - anchors = mapOf(0f to 0, width.value to 1), - thresholds = { _, _ -> FractionalThreshold(0.3f) }, - orientation = Orientation.Horizontal - ) - ) { Box( modifier = Modifier.offset { IntOffset( diff --git a/genesis/common/build.gradle.kts b/genesis/common/build.gradle.kts index 8369c79..d43eb11 100644 --- a/genesis/common/build.gradle.kts +++ b/genesis/common/build.gradle.kts @@ -22,6 +22,15 @@ kotlin { dependencies { implementation(libs.serialization.json) compileOnly(compose.runtime) + @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) + compileOnly(compose.components.resources) + compileOnly(compose.foundation) + + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.negotiation) + + compileOnly(libs.koin.core) + compileOnly(libs.koin.compose) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") @@ -31,21 +40,13 @@ kotlin { } val androidMain by getting { dependencies { - - } - } - val iosArm64Main by getting - val iosMain by creating { - dependsOn(commonMain) - iosArm64Main.dependsOn(this) - - languageSettings { - + implementation(libs.ktor.client.okhttp) } } val desktopMain by getting { dependencies { implementation(libs.jvm.gson) + implementation(libs.ktor.client.okhttp) } } } diff --git a/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/compose/koin/Module.kt b/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/compose/koin/Module.kt new file mode 100644 index 0000000..0459f20 --- /dev/null +++ b/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/compose/koin/Module.kt @@ -0,0 +1,10 @@ +package xyz.genesisapp.common.compose.koin + +import io.ktor.client.engine.HttpClientEngineFactory +import org.koin.dsl.module + +fun uninitModule( + httpFactory: HttpClientEngineFactory<*> +) = module { + single { httpModule(httpFactory) } +} \ No newline at end of file diff --git a/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/compose/koin/http.kt b/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/compose/koin/http.kt new file mode 100644 index 0000000..35f06fd --- /dev/null +++ b/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/compose/koin/http.kt @@ -0,0 +1,16 @@ +package xyz.genesisapp.common.compose.koin + +import androidx.compose.runtime.Composable +import io.ktor.client.HttpClient +import io.ktor.client.engine.HttpClientEngineFactory +import org.koin.compose.getKoin +import org.koin.core.qualifier.named +import org.koin.dsl.module + + +internal fun httpModule(factory: HttpClientEngineFactory<*>) = module { + single(named("uninitCommonComposeHttp")) { HttpClient(factory) } +} + +@Composable +internal fun getHttpClient() = getKoin().get(named("uninitCommonComposeHttp")) \ No newline at end of file diff --git a/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/fytix/EventBus.kt b/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/fytix/EventBus.kt index 7ea64b8..dc25d8e 100644 --- a/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/fytix/EventBus.kt +++ b/genesis/common/src/commonMain/kotlin/xyz/genesisapp/common/fytix/EventBus.kt @@ -47,13 +47,18 @@ open class EventBus(val composableId: String = "") { if (!busses.containsKey(event)) return // TODO: Default ("UNKNOWN" event) event bus & "ALL" event CoroutineScope((Dispatchers.Default)).launch { val listeners = busses[event]!! - for (listener in listeners) { + val iterator = listeners.iterator() + while (iterator.hasNext()) { + val listener = iterator.next() + + withContext(Dispatchers.Default) { listener.onEvent(object : Event { override val type: String = event override val data: A = data }) } + } } } diff --git a/genesis/discord/api/build.gradle.kts b/genesis/discord/api/build.gradle.kts index 0ba5542..619f300 100644 --- a/genesis/discord/api/build.gradle.kts +++ b/genesis/discord/api/build.gradle.kts @@ -35,11 +35,6 @@ kotlin { } } - val iosArm64Main by getting - val iosMain by creating { - dependsOn(commonMain) - iosArm64Main.dependsOn(this) - } val desktopMain by getting { dependencies { diff --git a/genesis/discord/api/src/commonMain/kotlin/xyz/genesisapp/discord/api/types/Asset.kt b/genesis/discord/api/src/commonMain/kotlin/xyz/genesisapp/discord/api/types/Asset.kt index 611cdbd..174fe30 100644 --- a/genesis/discord/api/src/commonMain/kotlin/xyz/genesisapp/discord/api/types/Asset.kt +++ b/genesis/discord/api/src/commonMain/kotlin/xyz/genesisapp/discord/api/types/Asset.kt @@ -24,13 +24,19 @@ fun Asset.toUrl( parentId: Snowflake? = null, size: Int? = null ): String { - var url = "https://cdn.discordapp.com/${type.value}/" - if (parentId !== null) { - url += "$parentId/" + val builder = StringBuilder() + .append("https://cdn.discordapp.com/") + .append(type.value) + .append("/") + parentId?.let { + builder.append(it) + .append("/") } - url += extension - if (size !== null) { - url += "?size=$size" + builder.append(".") + .append(extension) + size?.let { + builder.append("?size=") + .append(it) } - return url + return builder.toString() } \ No newline at end of file diff --git a/genesis/discord/client/build.gradle.kts b/genesis/discord/client/build.gradle.kts index 3e70eac..d479fb9 100644 --- a/genesis/discord/client/build.gradle.kts +++ b/genesis/discord/client/build.gradle.kts @@ -26,6 +26,9 @@ kotlin { implementation(libs.ktor.client.logging) implementation(libs.ktor.serialization.json) compileOnly(compose.runtime) + compileOnly(compose.foundation) + + compileOnly(libs.kamel) implementation(libs.napier) @@ -42,14 +45,6 @@ kotlin { implementation(libs.ktor.client.okhttp) } } - val iosArm64Main by getting - val iosMain by creating { - dependsOn(commonMain) - iosArm64Main.dependsOn(this) - dependencies { - implementation(libs.ktor.client.darwin) - } - } val desktopMain by getting { dependencies { implementation(libs.ktor.client.okhttp) diff --git a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/GenesisClient.kt b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/GenesisClient.kt index 78e4acb..9cdd162 100644 --- a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/GenesisClient.kt +++ b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/GenesisClient.kt @@ -24,6 +24,7 @@ import xyz.genesisapp.discord.entities.guild.GuildMember @OptIn(ExperimentalDiscordApi::class) class GenesisClient( httpClientEngineFactory: HttpClientEngineFactory<*>, + enableAssetCache: Boolean = false ) : EventBus("GenesisClient") { lateinit var user: DomainMe // ClientUser lateinit var normalUser: User @@ -35,6 +36,7 @@ class GenesisClient( var guilds = mutableStateMapOf() var channels = mutableStateMapOf() + var users = mutableStateMapOf() var guildMembers = mutableStateMapOf() var userSettings: UserSettings? = null diff --git a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/Asset.kt b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/Asset.kt new file mode 100644 index 0000000..b08c985 --- /dev/null +++ b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/Asset.kt @@ -0,0 +1,48 @@ +package xyz.genesisapp.discord.client.entities + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import io.kamel.core.Resource +import io.kamel.core.isSuccess +import io.kamel.image.asyncPainterResource +import xyz.genesisapp.discord.api.types.Asset +import xyz.genesisapp.discord.api.types.AssetType +import xyz.genesisapp.discord.api.types.Snowflake +import xyz.genesisapp.discord.client.GenesisClient + +class Asset( + val genesisClient: GenesisClient, + val hash: Asset, + val type: AssetType, + val parentId: Snowflake? = null, +) { + private val sizeCache: MutableMap> = mutableMapOf() + + + fun toUrl(extension: String = "png", size: Int? = null): String { + val builder = StringBuilder() + .append("https://cdn.discordapp.com/") + .append(type.value) + .append("/") + parentId?.let { + builder.append(it) + .append("/") + } + builder.append(hash) + .append(".") + .append(extension) + size?.let { + builder.append("?size=") + .append(it) + } + return builder.toString() + } + + @Composable + fun render(size: Int, extension: String = "png"): Resource { + if (!sizeCache.keys.contains(size) || !sizeCache[size]!!.isSuccess) { + sizeCache[size] = asyncPainterResource(toUrl(extension, size)) + } + return sizeCache[size]!! + } +} \ No newline at end of file diff --git a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Channel.kt b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Channel.kt index 817eb43..f7a0a47 100644 --- a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Channel.kt +++ b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Channel.kt @@ -12,9 +12,10 @@ import xyz.genesisapp.common.fytix.Ok import xyz.genesisapp.common.getTimeInMillis import xyz.genesisapp.discord.api.domain.DomainMessage import xyz.genesisapp.discord.api.domain.UtcDateTime -import xyz.genesisapp.discord.api.types.Asset +import xyz.genesisapp.discord.api.types.AssetType import xyz.genesisapp.discord.api.types.Snowflake import xyz.genesisapp.discord.client.GenesisClient +import xyz.genesisapp.discord.client.entities.Asset import xyz.genesisapp.discord.client.enum.LogLevel import xyz.genesisapp.discord.entities.guild.ApiChannel import xyz.genesisapp.discord.entities.guild.ChannelType @@ -147,7 +148,6 @@ class Channel( ) var message = Message.fromApiMessage(domainMessage, genesisClient) message.isSent = false - message.author = genesisClient.normalUser addMessage(message) delay(3000L) return when (val res = genesisClient.rest.sendMessage(id, domainMessage)) { @@ -185,7 +185,14 @@ class Channel( type = apiChannel.type!!, recipients = apiChannel.recipients?.map { User.fromApiUser(it, genesisClient) } ?.toMutableList() ?: mutableListOf(), - icon = apiChannel.icon, + icon = apiChannel.icon?.let { + Asset( + genesisClient, + it, + AssetType.Avatar, + apiChannel.recipients?.get(0)!!.id + ) + }, lastMessageId = apiChannel.lastMessageId ) } diff --git a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Guild.kt b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Guild.kt index fcc24a6..6cf891b 100644 --- a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Guild.kt +++ b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Guild.kt @@ -1,8 +1,9 @@ package xyz.genesisapp.discord.client.entities.guild -import xyz.genesisapp.discord.api.types.Asset +import xyz.genesisapp.discord.api.types.AssetType import xyz.genesisapp.discord.api.types.Snowflake import xyz.genesisapp.discord.client.GenesisClient +import xyz.genesisapp.discord.client.entities.Asset import xyz.genesisapp.discord.entities.guild.ApiGuild import xyz.genesisapp.discord.entities.guild.ApiRole import xyz.genesisapp.discord.entities.guild.GuildMember @@ -75,13 +76,34 @@ class Guild( afkChannelId = apiGuild.afkChannelId, afkTimeout = apiGuild.afkTimeout, - icon = apiGuild.icon, + icon = apiGuild.icon?.let { + Asset( + genesisClient, + it, + AssetType.Icon, + apiGuild.id.toLong() + ) + }, ownerId = apiGuild.ownerId, - splash = apiGuild.splash, + splash = apiGuild.splash?.let { + Asset( + genesisClient, + it, + AssetType.Banner, + apiGuild.id.toLong() + ) + }, discoverySplash = apiGuild.discoverySplash, - banner = apiGuild.banner, + banner = apiGuild.banner?.let { + Asset( + genesisClient, + it, + AssetType.Banner, + apiGuild.id.toLong() + ) + }, systemChannelId = apiGuild.systemChannelId, diff --git a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Message.kt b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Message.kt index a315583..d2a0011 100644 --- a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Message.kt +++ b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/Message.kt @@ -11,25 +11,39 @@ class Message( var id: Snowflake, var channelId: Snowflake, var content: String = "", - var author: User, var isSent: Boolean = true, var nonce: Long = getTimeInMillis() + UtcDateTime.DISCORD_EPOCH * 1000000, - + var authorId: Snowflake, var embeds: List = listOf(), var attachments: List = listOf(), ) { + val author: User + get() = genesisClient.users[authorId]!! + companion object { - fun fromApiMessage(domainMessage: DomainMessage, genesisClient: GenesisClient) = Message( - genesisClient, - id = domainMessage.id ?: domainMessage.nonce!!, - channelId = domainMessage.channelId!!, - content = domainMessage.content ?: "", - author = User.fromApiUser(domainMessage.author!!, genesisClient), - nonce = domainMessage.nonce ?: (getTimeInMillis() + UtcDateTime.DISCORD_EPOCH * 1000000), - embeds = domainMessage.embeds?.map { Embed.fromApiMessageEmbed(it) } ?: listOf(), - attachments = domainMessage.attachments?.map { Attachment.fromApiMessageAttachment(it) } - ?: listOf(), - ) + fun fromApiMessage(domainMessage: DomainMessage, genesisClient: GenesisClient): Message { + domainMessage.author?.let { + if (!genesisClient.users.containsKey(it.id)) genesisClient.users[it.id] = + User.fromApiUser(it, genesisClient) + } + + return Message( + genesisClient, + id = domainMessage.id ?: domainMessage.nonce!!, + channelId = domainMessage.channelId!!, + content = domainMessage.content ?: "", + authorId = domainMessage.author!!.id, + nonce = domainMessage.nonce + ?: (getTimeInMillis() + UtcDateTime.DISCORD_EPOCH * 1000000), + embeds = domainMessage.embeds?.map { Embed.fromApiMessageEmbed(it) } ?: listOf(), + attachments = domainMessage.attachments?.map { + Attachment.fromApiMessageAttachment( + it + ) + } + ?: listOf(), + ) + } } } \ No newline at end of file diff --git a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/User.kt b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/User.kt index 247f224..5798f17 100644 --- a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/User.kt +++ b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/entities/guild/User.kt @@ -4,9 +4,10 @@ import kotlinx.serialization.json.JsonObject import xyz.genesisapp.discord.api.domain.user.ApiUser import xyz.genesisapp.discord.api.domain.user.PremiumType import xyz.genesisapp.discord.api.domain.user.UserFlags -import xyz.genesisapp.discord.api.types.Asset +import xyz.genesisapp.discord.api.types.AssetType import xyz.genesisapp.discord.api.types.Snowflake import xyz.genesisapp.discord.client.GenesisClient +import xyz.genesisapp.discord.client.entities.Asset class User( val accentColor: Int? = null, @@ -49,9 +50,9 @@ class User( genesisClient: GenesisClient ): User = User( accentColor = apiUser.accentColor, - avatar = apiUser.avatar, + avatar = apiUser.avatar?.let { Asset(genesisClient, it, AssetType.Avatar, apiUser.id) }, avatarDecorationData = apiUser.avatarDecorationData, - banner = apiUser.banner, + banner = apiUser.banner?.let { Asset(genesisClient, it, AssetType.Banner, apiUser.id) }, bannerColor = apiUser.bannerColor, bio = apiUser.bio, discriminator = apiUser.discriminator, diff --git a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/gateway/GatewayClient.kt b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/gateway/GatewayClient.kt index bed14a3..e4c82dc 100644 --- a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/gateway/GatewayClient.kt +++ b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/gateway/GatewayClient.kt @@ -31,12 +31,13 @@ import xyz.genesisapp.discord.client.gateway.handlers.initGatewayHandlers import xyz.genesisapp.discord.client.gateway.serializers.GatewaySerializer import xyz.genesisapp.discord.client.gateway.types.GatewayEvent import xyz.genesisapp.discord.client.gateway.types.events.LastMessages +import xyz.genesisapp.discord.client.gateway.types.events.Ready class GatewayClient( engineFactory: HttpClientEngineFactory<*>, val parentClient: GenesisClient, val eventBus: EventBus = parentClient, -) : EventBus() { +) : EventBus("GatewayClient") { private val http = HttpClient(engineFactory) { install(WebSockets) { contentConverter = KotlinxWebsocketSerializationConverter(Json) @@ -107,19 +108,19 @@ class GatewayClient( var gatewayUrl = "wss://gateway.discord.gg/?v=9&encoding=json" var seq: Int? = null var retries = 0 -// once("READY") { ready -> -// resumePacket = GatewayEvent( -// 6, -// null, -// null, -// GatewayResume( -// token = token, -// sessionId = ready.sessionId, -// seq = seq!! -// ) -// ) -// gatewayUrl = ready.resumeGatewayUrl -// } + once("READY") { ready -> + resumePacket = GatewayEvent( + 6, + null, + null, + GatewayResume( + token = token, + sessionId = ready.sessionId, + seq = seq!! + ) + ) + gatewayUrl = ready.resumeGatewayUrl + } scope.launch { while (!isDisconnecting) { if (retries > 10) { @@ -161,7 +162,6 @@ class GatewayClient( Napier.d("Received event $event", null, "Gateway") emit(event, json.d) - emit("${event}Raw", text) } catch (e: Exception) { val blacklist = listOf( "Unknown opcode", @@ -187,6 +187,7 @@ class GatewayClient( } } } catch (_: Exception) { + retries++ } } isDisconnecting = false diff --git a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/gateway/handlers/Ready.kt b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/gateway/handlers/Ready.kt index f65ca1d..6f2700a 100644 --- a/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/gateway/handlers/Ready.kt +++ b/genesis/discord/client/src/commonMain/kotlin/xyz/genesisapp/discord/client/gateway/handlers/Ready.kt @@ -37,9 +37,13 @@ fun gatewayReadyHandler(genesisClient: GenesisClient, gateway: GatewayClient) { "Gateway" ) val user = genesisClient.rest.getUser(me.getOrNull()!!.id) - if (user.isOk()) genesisClient.normalUser = - User.fromApiUser(user.getOrNull()!!, genesisClient) - else if (genesisClient.logLevel >= LogLevel.ERROR) Napier.e( + if (user.isOk()) { + val userRes = User.fromApiUser(user.getOrNull()!!, genesisClient) + genesisClient.normalUser = + userRes + genesisClient.users[userRes.id] = userRes + + } else if (genesisClient.logLevel >= LogLevel.ERROR) Napier.e( "Error getting user: ${user.errorOrNull()}", null, "Gateway" diff --git a/genesis/genesisApi/build.gradle.kts b/genesis/genesisApi/build.gradle.kts index a98cc60..4180e79 100644 --- a/genesis/genesisApi/build.gradle.kts +++ b/genesis/genesisApi/build.gradle.kts @@ -37,14 +37,6 @@ kotlin { implementation(libs.ktor.client.okhttp) } } - val iosArm64Main by getting - val iosMain by creating { - dependsOn(commonMain) - iosArm64Main.dependsOn(this) - dependencies { - implementation(libs.ktor.client.darwin) - } - } val desktopMain by getting { dependencies { implementation(libs.ktor.client.okhttp) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e8b7f01..2d7223e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,19 +1,22 @@ [versions] -kotlin = "1.9.10" -serialization = "1.6.0" +kotlin = "1.9.20" +serialization = "1.6.1" android = "8.1.2" -compose = "1.5.2" +compose = "1.5.10" moko-resources = "0.23.0" -koin = "3.5.0" -koin-compose = "1.1.0" +koin = "3.5.2-RC1" +koin-compose = "1.1.1-RC1" voyager = "1.0.0-rc10" -ktor = "2.3.5" +ktor = "2.3.6" napier = "2.6.1" +gson = "2.10.1" +kprefs = "0.12.1" +kamel = "0.8.2" [libraries] serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" } serialization-protobuf = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-protobuf", version.ref = "serialization" } -serialization-preferences = { group = "net.edwardday.serialization", name = "kprefs", version = "0.12.1" } +serialization-preferences = { group = "net.edwardday.serialization", name = "kprefs", version.ref = "kprefs" } moko-resources-common = { group = "dev.icerock.moko", name = "resources", version.ref = "moko-resources" } moko-resources-compose = { group = "dev.icerock.moko", name = "resources-compose", version.ref = "moko-resources" } @@ -30,7 +33,7 @@ voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", vers voyager-koin = { module = "cafe.adriel.voyager:voyager-koin", version.ref = "voyager" } voyager-core = { module = "cafe.adriel.voyager:voyager-core", version.ref = "voyager" } -jvm-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } +jvm-gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" } @@ -42,6 +45,8 @@ ktor-serialization-json = { group = "io.ktor", name = "ktor-serialization-kotlin napier = { group = "io.github.aakira", name = "napier", version.ref = "napier" } +kamel = { group = "media.kamel", name = "kamel-image", version.ref = "kamel" } + [plugins] android-application = { id = "com.android.application", version.ref = "android" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }