Skip to content

Commit

Permalink
Merge pull request #170 from mbakgun/feature/webapp
Browse files Browse the repository at this point in the history
feature/webapp
  • Loading branch information
mbakgun authored Feb 11, 2024
2 parents eb615d7 + 7b8eca2 commit e10d817
Show file tree
Hide file tree
Showing 26 changed files with 243 additions and 67 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ iosApp/iosApp.xcworkspace/*
iosApp/iosApp.xcodeproj/*
!iosApp/iosApp.xcodeproj/project.pbxproj
shared/shared.podspec
/kotlin-js-store/yarn.lock
13 changes: 12 additions & 1 deletion README-de.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Build & Ship](https://github.com/mbakgun/midjourney-images-compose-multiplatform/actions/workflows/main.yml/badge.svg)](https://github.com/mbakgun/midjourney-images-compose-multiplatform/actions/workflows/main.yml)
#### Vorgestellt auf [Google Dev Bibliothek](https://devlibrary.withgoogle.com/products/android/repos/mbakgun-midjourney-images-compose-multiplatform)

Diese Anwendung wurde entwickelt, um die Bilder von MidJourney anzuzeigen. Die Anwendung wurde mit Compose Multiplatform entwickelt. Die Anwendung läuft auf den Plattformen Android, iOS, Wear OS, Android Automotive, Android TV
Diese Anwendung wurde entwickelt, um die Bilder von MidJourney anzuzeigen. Die Anwendung wurde mit Compose Multiplatform entwickelt. Die Anwendung läuft auf den Plattformen Android, iOS, Web, Wear OS, Android Automotive, Android TV

<p align="center"><img src="image-assets/1.gif" alt="kmm-compose-header" /><br><br></p>
Die Anwendung wurde im MVVM-Konzept mit Kotlin und Jetpack Compose entwickelt. Es wurden Netzwerkanforderungszustände, Endlos-Pagination, Bildladeprozesse und Bildcaching durchgeführt.
Expand Down Expand Up @@ -95,6 +95,17 @@ Dieses Projekt kann für Windows, Debian und MacOS erstellt werden.

<img src="image-assets/desktop.gif" alt="desktop-compose"/>

## Web Application

Diese Projekt kann für Web(JS) erstellt werden.
[Demo](https://mj.akgns.com/compose)

```
./gradlew :compose-web:jsBrowserDevelopmentRun
```

<img src="image-assets/web.gif" alt="web-compose"/>

## Tests

Die Anwendung verfügt über Compose UI-Tests, Maestro UI-Tests und Unit-Tests. Die Unit-Tests sind unter dem Common-Paket mit Fake-Daten geschrieben. Die UI-Tests sind unter dem androidTest-Paket geschrieben. Die Maestro-Tests sind unter dem [Maestro-Paket](https://github.com/mbakgun/midjourney-images-compose-multiplatform/tree/master/.maestro) geschrieben.
Expand Down
12 changes: 11 additions & 1 deletion README-tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#### [Google Dev](https://devlibrary.withgoogle.com/products/android/repos/mbakgun-midjourney-images-compose-multiplatform) Öne Çıkanlar

Bu uygulama, çoklu platform desteği ile MidJourney'ın oluşturduğu resimleri göstermek için geliştirilmiştir. Uygulama,
Compose Multiplatform ile geliştirilmiştir. Uygulama, Android, iOS, Wear OS, Android Automotive, Android TV platformlarında çalışmaktadır.
Compose Multiplatform ile geliştirilmiştir. Uygulama, Android, iOS, Web, Wear OS, Android Automotive, Android TV platformlarında çalışmaktadır.

<p align="center"><img src="image-assets/1.gif" alt="kmm-compose-header" /><br><br></p>
Kotlin ve Jetpack Compose kullanılarak MVVM konseptinde geliştirtirildi. Network request state'leri, endless pagination, image loading ve image caching işlemleri yapılmıştır.
Expand Down Expand Up @@ -98,6 +98,16 @@ Bu proje Windows, Debian ve MacOS için oluşturulabilir.

<img src="image-assets/desktop.gif" alt="desktop-compose"/>

## Web Application

Bu proje Web(JS) için oluşturulabilir.
[Demo](https://mj.akgns.com/compose)

```
./gradlew :compose-web:jsBrowserDevelopmentRun
```
<img src="image-assets/web.gif" alt="web-compose"/>

## Test

Uygulama compose ui test,maestro ui test ve unit testlere sahiptir. Unit testler common paket altında, fake data ile yazılmıştır. UI testler ise
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Build & Ship](https://github.com/mbakgun/midjourney-images-compose-multiplatform/actions/workflows/main.yml/badge.svg)](https://github.com/mbakgun/midjourney-images-compose-multiplatform/actions/workflows/main.yml)
#### Featured in [Google dev library](https://devlibrary.withgoogle.com/products/android/repos/mbakgun-midjourney-images-compose-multiplatform)

This application is developed to display the images created by MidJourney. The application is developed with Compose Multiplatform and works on Android, iOS, Wear OS, Android Automotive, Android TV platforms.
This application is developed to display the images created by MidJourney. The application is developed with Compose Multiplatform and works on Android, iOS, Web, Wear OS, Android Automotive, Android TV platforms.

<p align="center"><img src="image-assets/1.gif" alt="kmm-compose-header" /><br><br></p>
Application developed in the MVVM concept using Kotlin and Jetpack Compose. Network request states, endless pagination, image loading, and image caching processes were performed.
Expand Down Expand Up @@ -95,6 +95,16 @@ This project can be built for Windows, Debian, and MacOS.

<img src="image-assets/desktop.gif" alt="desktop-compose"/>

## Web Application

This project can be for the Compose for Web(JS).
[Demo](https://mj.akgns.com/compose)

```
./gradlew :compose-web:jsBrowserDevelopmentRun
```
<img src="image-assets/web.gif" alt="web-compose"/>

## Testing

The application has Compose UI tests, Maestro UI tests, and unit tests. The unit tests are written under the common package with fake data. The UI tests are written under the androidTest package. The Maestro tests are written under the [maestro package](https://github.com/mbakgun/midjourney-images-compose-multiplatform/tree/master/.maestro).
Expand Down
5 changes: 4 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ android.targetSdk=34
android.minSdk=24

#Desktop
compose.desktop.packaging.checkJdkVendor=false
compose.desktop.packaging.checkJdkVendor=false

#Web
org.jetbrains.compose.experimental.jscanvas.enabled=true
7 changes: 3 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
androidxUiTest = "1.6.1"
koin = "3.5.3"
ktorClient = "2.3.8"
kotlinxCoroutines = "1.7.3"
kotlinxCoroutines = "1.8.0-RC2"

[plugins]
detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.23.5" }
Expand All @@ -20,6 +20,7 @@ koin = { module = "io.insert-koin:koin-android", version.ref="koin" }
koinCore = { module = "io.insert-koin:koin-core", version.ref="koin" }
koinTest = { module = "io.insert-koin:koin-test", version="3.5.3" }
ktorClient = { module = "io.ktor:ktor-client-android", version.ref="ktorClient" }
ktorClientJs = { module = "io.ktor:ktor-client-js", version.ref="ktorClient" }
ktorClientContentNegotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref="ktorClient" }
ktorClientCore = { module = "io.ktor:ktor-client-core", version.ref="ktorClient" }
ktorClientIos = { module = "io.ktor:ktor-client-ios", version.ref="ktorClient" }
Expand All @@ -31,6 +32,4 @@ kotlinxCoroutinesCore = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-cor
kotlinxCoroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref="kotlinxCoroutines" }
kotlinxSerializationCore = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version="1.6.2" }
multiplatformSettings = { module = "com.russhwolf:multiplatform-settings-no-arg", version="1.1.1" }



okio-fakefilesystem = { module = "com.squareup.okio:okio-fakefilesystem", version = "3.7.0" }
Binary file added image-assets/web.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ include(":shared")
include(":wearApp")
include(":televisionApp")
include(":automotiveApp")
include(":webApp")

pluginManagement {
repositories {
Expand Down
59 changes: 37 additions & 22 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ kotlin {
iosArm64()
iosSimulatorArm64()

js(IR) {
browser()
binaries.executable()
}

cocoapods {
version = "1.0.0"
summary = "MidJourney Shared Module"
Expand All @@ -29,33 +34,33 @@ kotlin {

sourceSets {
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.materialIconsExtended)
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.materialIconsExtended)

//sharedVm
api(libs.kmmViewmodelCore)
//sharedVm
api(libs.kmmViewmodelCore)

//di
api(libs.koinCore)
//di
api(libs.koinCore)

//network
implementation(libs.ktorClientCore)
implementation(libs.ktorClientJson)
implementation(libs.ktorClientLogging)
implementation(libs.ktorClientContentNegotiation)
implementation(libs.ktorSerializationKotlinxJson)
implementation(libs.kotlinxSerializationCore)
//network
implementation(libs.ktorClientCore)
implementation(libs.ktorClientJson)
implementation(libs.ktorClientLogging)
implementation(libs.ktorClientContentNegotiation)
implementation(libs.ktorSerializationKotlinxJson)
implementation(libs.kotlinxSerializationCore)

//imageloading
implementation(libs.imageLoader)
//imageLoading
implementation(libs.imageLoader)

//coroutines
implementation(libs.kotlinxCoroutinesCore)
//coroutines
implementation(libs.kotlinxCoroutinesCore)

//local
implementation(libs.multiplatformSettings)
//local
implementation(libs.multiplatformSettings)
}

androidMain.dependencies {
Expand All @@ -68,7 +73,12 @@ kotlin {
}

jvmMain.dependencies {
implementation(libs.ktorClientJvm)
implementation(libs.ktorClientJvm)
}

jsMain.dependencies {
implementation(libs.okio.fakefilesystem)
implementation(libs.ktorClientJs)
}

commonTest.dependencies {
Expand Down Expand Up @@ -121,3 +131,8 @@ kotlin {
}
}
}

compose.experimental {
web.application {
}
}
6 changes: 3 additions & 3 deletions shared/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
<SmellBaseline>
<ManuallySuppressedIssues></ManuallySuppressedIssues>
<CurrentIssues>
<ID>FunctionNaming:GridStateExt.kt$@Composable fun LazyGridState.OnBottomReached( loadMore: () -&gt; Unit )</ID>
<ID>FunctionNaming:GridStateExt.kt$@Composable fun LazyStaggeredGridState.OnBottomReached( loadMore: () -&gt; Unit )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun DraggableThemeSelection( useDarkTheme: Boolean, onClick: (Boolean) -&gt; Unit )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun EmptyScreen( onRefresh: () -&gt; Unit )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun ErrorScreen( onRefresh: () -&gt; Unit )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun MjImagesList( images: MjImages, state: LazyStaggeredGridState, onLoadMore: () -&gt; Unit, showPreviewDialog: (imageUrl: String) -&gt; Unit, )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun PlatformSpecificMjImagesGrid( state: LazyStaggeredGridState, images: MjImages, showPreviewDialog: (imageUrl: String) -&gt; Unit, onLoadMore: () -&gt; Unit, modifier: Modifier = Modifier, )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun PreviewDialog( imageUrl: String, onDismissed: () -&gt; Unit, )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun PreviewImage(imageUrl: String)</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun ScrollToTopButton( onClick: () -&gt; Unit, modifier: Modifier = Modifier )</ID>
Expand All @@ -32,15 +30,17 @@
<ID>MagicNumber:Colors.kt$0xFFFFFFFF</ID>
<ID>MagicNumber:ImageLoader.kt$0.25</ID>
<ID>MagicNumber:ImageLoader.kt$1024</ID>
<ID>MagicNumber:ImageLoader.kt$256L</ID>
<ID>MagicNumber:ImageLoader.kt$32</ID>
<ID>MagicNumber:ImageLoader.kt$512</ID>
<ID>MagicNumber:ImageLoader.kt$512L</ID>
<ID>MagicNumber:MjImagesApp.kt$.2f</ID>
<ID>MagicNumber:MjImagesApp.kt$.8f</ID>
<ID>MagicNumber:MjImagesApp.kt$180</ID>
<ID>MagicNumber:MjImagesApp.kt$230f</ID>
<ID>MagicNumber:MjImagesApp.kt$24f</ID>
<ID>UnusedPrivateProperty:build.gradle.kts$val androidInstrumentedTest by getting { dependencies { implementation(libs.androidxUiTestJunit4) implementation(libs.androidxUiTestManifest) } }</ID>
<ID>UnusedPrivateProperty:build.gradle.kts$val androidMain by getting { dependencies { implementation(project(":shared")) api(libs.androidxActivityCompose) api(libs.androidxAppcompat) api(libs.androidxCoreKtx) } }</ID>
<ID>UnusedPrivateProperty:build.gradle.kts$val jsMain by getting { dependencies { implementation(compose.runtime) implementation(compose.foundation) implementation(project(":shared")) } }</ID>
<ID>UnusedPrivateProperty:build.gradle.kts$val jvmMain by getting { dependencies { implementation(project(":shared")) implementation(compose.desktop.currentOs) } }</ID>
<ID>UseCheckOrError:ImageLoader.kt$throw IllegalStateException("Unsupported operating system")</ID>
</CurrentIssues>
Expand Down
9 changes: 9 additions & 0 deletions shared/src/androidMain/kotlin/util/ImageProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package util

import androidx.compose.ui.unit.dp

internal actual fun getImageProvider(): ImageProvider =
ImageProvider(
columnCount = 2,
itemHeight = 180.dp
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import data.source.MjImagesDataSource
import kotlinx.coroutines.withContext
import util.DispatcherProvider

class MjImagesLocalDataSource constructor(
class MjImagesLocalDataSource(
private val settings: Settings,
private val dispatcherProvider: DispatcherProvider,
) : MjImagesDataSource.Local {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import data.source.remote.model.MjImagesResponse
import kotlinx.coroutines.withContext
import util.DispatcherProvider

class MjImagesRemoteDataSource constructor(
class MjImagesRemoteDataSource(
private val service: MjImagesService,
private val dispatcherProvider: DispatcherProvider,
) : MjImagesDataSource.Remote {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import domain.model.MjImages
import kotlinx.coroutines.withContext
import util.DispatcherProvider

class MjImagesMapper constructor(
class MjImagesMapper(
private val dispatcherProvider: DispatcherProvider,
) {

Expand Down
6 changes: 4 additions & 2 deletions shared/src/commonMain/kotlin/ui/MjImagesApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ import kotlinx.coroutines.launch
import ui.theme.AppTheme
import util.OnBottomReached
import util.generateImageLoader
import util.getImageProvider

@OptIn(ExperimentalMaterialApi::class)
@Composable
Expand Down Expand Up @@ -189,7 +190,7 @@ fun MjImagesList(
) {
LazyVerticalStaggeredGrid(
state = state.apply { OnBottomReached(onLoadMore::invoke) },
columns = StaggeredGridCells.Fixed(2),
columns = StaggeredGridCells.Fixed(getImageProvider().columnCount),
modifier = Modifier.fillMaxSize()
.semantics { contentDescription = "imagesGrid" }
.testTag("imagesGrid"),
Expand All @@ -213,7 +214,8 @@ fun MjImageItem(
showPreviewDialog: (imageUrl: String) -> Unit,
) {
val uriHandler = LocalUriHandler.current
val height = remember { derivedStateOf((180 * image.ratio)::dp) }
val height =
remember { derivedStateOf((getImageProvider().itemHeight.value * image.ratio)::dp) }

Surface(
modifier = Modifier
Expand Down
7 changes: 7 additions & 0 deletions shared/src/commonMain/kotlin/util/ImageProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package util

import androidx.compose.ui.unit.Dp

data class ImageProvider(val columnCount: Int, val itemHeight: Dp)

internal expect fun getImageProvider(): ImageProvider
29 changes: 0 additions & 29 deletions shared/src/iosMain/kotlin/util/GridStateExt.kt

This file was deleted.

9 changes: 9 additions & 0 deletions shared/src/iosMain/kotlin/util/ImageProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package util

import androidx.compose.ui.unit.dp

internal actual fun getImageProvider(): ImageProvider =
ImageProvider(
columnCount = 2,
itemHeight = 180.dp
)
12 changes: 12 additions & 0 deletions shared/src/jsMain/kotlin/util/DispatcherProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package util

import kotlinx.coroutines.Dispatchers

internal actual fun getDispatcherProvider(): DispatcherProvider = JsDispatcherProvider()

private class JsDispatcherProvider : DispatcherProvider {
override val main = Dispatchers.Main
override val io = Dispatchers.Default
override val default = Dispatchers.Default
override val unconfined = Dispatchers.Unconfined
}
23 changes: 23 additions & 0 deletions shared/src/jsMain/kotlin/util/ImageLoader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package util

import com.seiko.imageloader.ImageLoader
import com.seiko.imageloader.component.setupDefaultComponents
import okio.FileSystem
import okio.fakefilesystem.FakeFileSystem

actual fun generateImageLoader(): ImageLoader {
return ImageLoader {
components {
setupDefaultComponents()
}
interceptor {
memoryCacheConfig {
maxSizeBytes(512 * 1024 * 1024) // 512MB
}
diskCacheConfig(FakeFileSystem().apply { emulateUnix() }) {
directory(FileSystem.SYSTEM_TEMPORARY_DIRECTORY)
maxSizeBytes(256L * 1024 * 1024) // 256MB
}
}
}
}
Loading

0 comments on commit e10d817

Please sign in to comment.