Skip to content

Commit

Permalink
✨ [#44] Details Page
Browse files Browse the repository at this point in the history
  • Loading branch information
kioba committed Feb 16, 2025
1 parent f4f7608 commit 753343c
Show file tree
Hide file tree
Showing 15 changed files with 392 additions and 116 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ dependencies {
implementation(libs.androidX.compose.activity)
implementation(projects.feature.feed)
implementation(projects.platform.domain)
implementation(libs.navigation.compose)

testImplementation(libs.test.mockK)
testImplementation(libs.test.junit4)
Expand Down
25 changes: 18 additions & 7 deletions app/src/main/kotlin/dev/kioba/quack/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@ import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import dev.kioba.feature.feed.ui.FeedPage
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import dev.kioba.feature.feed.ui.FeedDestination
import dev.kioba.feature.feed.ui.feedComposable

internal class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge(
navigationBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT),
)
super.onCreate(savedInstanceState)
setContent { FeedPage() }
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge(
navigationBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT),
)
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = FeedDestination,
) {
feedComposable()
}
}
}
}
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ plugins {
alias(libs.plugins.jetbrainsCompose) apply false
alias(libs.plugins.composeCompiler) apply false
alias(libs.plugins.ksp) apply false
alias(libs.plugins.kotlin.android) apply false
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public fun Avatar(
error = placeholder,
contentDescription = contentDescription,
modifier = modifier
.size(getSize(size))
.size(avatarSize(size))
.clip(CircleShape)
)
}
Expand All @@ -42,7 +42,7 @@ public enum class AvatarSize {
Medium;
}

private fun getSize(
public fun avatarSize(
size: AvatarSize,
): Dp =
when (size) {
Expand Down
78 changes: 78 additions & 0 deletions feature/details/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.composeCompiler)
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.screenshot)
alias(libs.plugins.serialization)
}

android {
namespace = "dev.kioba.feature.details"

compileSdk = libs.versions.compileSdk
.get()
.toInt()

defaultConfig {
minSdk = 21

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
@Suppress("UnstableApiUsage")
experimentalProperties["android.experimental.enableScreenshotTest"] = true

@Suppress("UnstableApiUsage")
testOptions {
screenshotTests {
imageDifferenceThreshold = 0.05f // 5%
}

}
}

kotlin {
explicitApi()
}

dependencies {
// implementation(projects.designSystem)
// implementation(projects.domain.post.api)
// implementation(projects.domain.post.fakes)
// implementation(projects.domain.user.api)
// implementation(projects.domain.user.fakes)
implementation(projects.platform.androidCompose)
implementation(projects.platform.androidDatabase)
implementation(projects.platform.database)
implementation(projects.platform.domain)
implementation(projects.platform.network)
implementation(projects.platform.test)

implementation(libs.bundles.compose)
implementation(libs.navigation.compose)
implementation(platform(libs.androidX.compose.bom))

implementation(libs.arrow.core)

implementation(libs.architecture.anchor)
implementation(libs.kotlinX.coroutines.core)
implementation(libs.kotlinX.coroutines.android)
implementation(libs.kotlinx.serialization.json)

testImplementation(libs.test.mockK)
testImplementation(libs.test.junit4)
testImplementation(libs.architecture.anchorTest)
androidTestImplementation(libs.androidTest.androidXTest.core)
androidTestImplementation(libs.androidTest.androidXTest.junit)
androidTestImplementation(libs.androidTest.testRunner)
androidTestImplementation(libs.androidTest.espresso)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.kioba.feature.details

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("dev.kioba.feature.details.test", appContext.packageName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dev.kioba.feature.details.data

import dev.kioba.anchor.Anchor
import dev.kioba.anchor.Effect
import dev.kioba.anchor.RememberAnchorScope
import dev.kioba.feature.details.model.DetailsViewState
import dev.kioba.platform.database.DatabaseScope
import dev.kioba.platform.domain.EffectContext
import dev.kioba.platform.domain.buildEffectContext
import dev.kioba.platform.network.NetworkScope
import kotlinx.coroutines.flow.Flow


internal typealias DetailsAnchor = Anchor<DetailsEffects, DetailsViewState>

public class DetailsEffects(
databaseScope: DatabaseScope,
networkScope: NetworkScope,
) : EffectContext by buildEffectContext(databaseScope, networkScope), Effect

internal fun RememberAnchorScope.detailsAnchor(
effectsScope: DetailsEffects,
): DetailsAnchor =
create(
initialState = ::DetailsViewState,
effectScope = { effectsScope },
init = DetailsAnchor::init,
) {
listen(::onCreated)
}

fun onCreated(any: Any): Flow<*> {
TODO("Not yet implemented")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dev.kioba.feature.details.model

import dev.kioba.anchor.ViewState

internal data class DetailsViewState(
val isLoading: Boolean = true,
): ViewState
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package dev.kioba.feature.details.ui

import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import dev.kioba.anchor.compose.RememberAnchor
import dev.kioba.android.database.buildDatabaseScope
import dev.kioba.feature.details.data.DetailsEffects
import dev.kioba.feature.details.data.detailsAnchor
import dev.kioba.platform.network.buildNetworkScope
import kotlinx.serialization.Serializable


private fun DetailsEffects(
context: Context,
): DetailsEffects =
DetailsEffects(
networkScope = buildNetworkScope(),
databaseScope = buildDatabaseScope(context),
)

public fun NavGraphBuilder.feedComposable(
) {
composable<DetailsDestination> { stack -> stack.DetailsPage() }
}

@Serializable
public data object DetailsDestination

@Composable
private fun NavBackStackEntry.DetailsPage() {
val context = LocalContext.current
RememberAnchor(
scope = { detailsAnchor(DetailsEffects(context)) },
) { state -> DetailsUi(state = state) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.kioba.feature.details

import org.junit.Test

import org.junit.Assert.*

/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
12 changes: 7 additions & 5 deletions feature/feed/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
plugins {
id("com.android.library")
kotlin("android")
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.composeCompiler)
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.screenshot)
alias(libs.plugins.serialization)
}

android {
Expand Down Expand Up @@ -52,15 +53,16 @@ dependencies {
implementation(projects.platform.network)
implementation(projects.platform.test)

implementation(platform(libs.androidX.compose.bom))
implementation(libs.bundles.compose)
implementation(libs.androidX.fragment)
implementation(libs.navigation.compose)
implementation(platform(libs.androidX.compose.bom))

implementation(libs.arrow.core)

implementation(libs.architecture.anchor)
implementation(libs.kotlinX.coroutines.core)
implementation(libs.kotlinX.coroutines.android)
implementation(libs.kotlinx.serialization.json)

testImplementation(libs.test.mockK)
testImplementation(libs.test.junit4)
Expand Down
19 changes: 16 additions & 3 deletions feature/feed/src/main/kotlin/dev/kioba/feature/feed/ui/FeedPage.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
package dev.kioba.feature.feed.ui

import android.content.Context
import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import dev.kioba.anchor.compose.RememberAnchor
import dev.kioba.android.database.buildDatabaseScope
import dev.kioba.feature.feed.data.FeedEffects
import dev.kioba.feature.feed.data.feedAnchor
import dev.kioba.platform.network.buildNetworkScope
import kotlinx.serialization.Serializable

private fun FeedEffects(context: Context): FeedEffects =
FeedEffects(
networkScope = buildNetworkScope(),
databaseScope = buildDatabaseScope(context = context)
)

public fun NavGraphBuilder.feedComposable(
) {
composable<FeedDestination> { stack -> stack.FeedPage() }
}

@Serializable
public data object FeedDestination

@Composable
public fun ComponentActivity.FeedPage() {
private fun NavBackStackEntry.FeedPage() {
val context = LocalContext.current
RememberAnchor(
scope = { feedAnchor(FeedEffects(this@FeedPage)) },
scope = { feedAnchor(FeedEffects(context)) },
) { state -> FeedUi(state = state) }
}
Loading

0 comments on commit 753343c

Please sign in to comment.