Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#541] Integrate the Twitter Jetpack Compose Rules Detekt plugin #556

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sample-compose/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ dependencies {

implementation("org.jetbrains.kotlin:kotlin-stdlib:${Versions.KOTLIN_VERSION}")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.KOTLINX_COROUTINES_VERSION}")
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:${Versions.KOTLIN_COLLECTIONS_IMMUTABLE_VERSION}")

kapt("com.google.dagger:hilt-compiler:${Versions.HILT_VERSION}")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,22 @@ import co.nimblehq.sample.compose.ui.models.UiModel
import co.nimblehq.sample.compose.ui.showToast
import co.nimblehq.sample.compose.ui.theme.ComposeTheme
import com.google.accompanist.permissions.*
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.*

@Composable
fun HomeScreen(
isResultOk: Boolean = false,
viewModel: HomeViewModel = hiltViewModel(),
navigator: (destination: BaseDestination) -> Unit,
Comment on lines +30 to 32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

isResultOk: Boolean = false,
) {
val context = LocalContext.current
viewModel.error.collectAsEffect { e -> e.showToast(context) }
viewModel.navigator.collectAsEffect { destination -> navigator(destination) }

val isLoading: IsLoading by viewModel.isLoading.collectAsStateWithLifecycle()
val uiModels: List<UiModel> by viewModel.uiModels.collectAsStateWithLifecycle()
val uiModels: ImmutableList<UiModel> by viewModel.uiModels.collectAsStateWithLifecycle()
val isFirstTimeLaunch: Boolean by viewModel.isFirstTimeLaunch.collectAsStateWithLifecycle()

LaunchedEffect(isFirstTimeLaunch) {
Expand Down Expand Up @@ -85,7 +87,7 @@ private fun CameraPermission() {

@Composable
private fun HomeScreenContent(
uiModels: List<UiModel>,
uiModels: ImmutableList<UiModel>,
isLoading: IsLoading,
onItemClick: (UiModel) -> Unit,
onItemLongClick: (UiModel) -> Unit,
Expand Down Expand Up @@ -116,7 +118,7 @@ private fun HomeScreenContent(
private fun HomeScreenPreview() {
ComposeTheme {
HomeScreenContent(
uiModels = listOf(UiModel("1", "name1"), UiModel("2", "name2"), UiModel("3", "name3")),
uiModels = persistentListOf(UiModel("1", "name1"), UiModel("2", "name2"), UiModel("3", "name3")),
isLoading = false,
onItemClick = {},
onItemLongClick = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import co.nimblehq.sample.compose.ui.models.toUiModel
import co.nimblehq.sample.compose.ui.screens.main.MainDestination
import co.nimblehq.sample.compose.util.DispatchersProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
Expand All @@ -26,7 +29,7 @@ class HomeViewModel @Inject constructor(
private val dispatchersProvider: DispatchersProvider,
) : BaseViewModel() {

private val _uiModels = MutableStateFlow<List<UiModel>>(emptyList())
private val _uiModels = MutableStateFlow<ImmutableList<UiModel>>(persistentListOf())
val uiModels = _uiModels.asStateFlow()

private val _isFirstTimeLaunch = MutableStateFlow(false)
Expand All @@ -37,7 +40,7 @@ class HomeViewModel @Inject constructor(
.injectLoading()
.onEach { result ->
val uiModels = result.map { it.toUiModel() }
_uiModels.emit(uiModels)
_uiModels.emit(uiModels.toImmutableList())
}
.flowOn(dispatchersProvider.io)
.catch { e -> _error.emit(e) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import co.nimblehq.sample.compose.ui.models.UiModel
import co.nimblehq.sample.compose.ui.theme.ComposeTheme
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf

@Composable
fun ItemList(
uiModels: List<UiModel>,
uiModels: ImmutableList<UiModel>,
onItemClick: (UiModel) -> Unit,
onItemLongClick: (UiModel) -> Unit,
modifier: Modifier = Modifier,
Expand All @@ -33,7 +35,7 @@ fun ItemList(
private fun ItemListPreview() {
ComposeTheme {
ItemList(
uiModels = listOf(UiModel("1", "name1"), UiModel("2", "name2"), UiModel("3", "name3")),
uiModels = persistentListOf(UiModel("1", "name1"), UiModel("2", "name2"), UiModel("3", "name3")),
onItemClick = {},
onItemLongClick = {}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ import co.nimblehq.sample.compose.ui.theme.ComposeTheme

@Composable
fun SecondScreen(
id: String,
viewModel: SecondViewModel = hiltViewModel(),
navigator: (destination: BaseDestination) -> Unit,
Comment on lines 26 to 27
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the viewModel param with the default value be located at the end as well?

Suggested change
viewModel: SecondViewModel = hiltViewModel(),
navigator: (destination: BaseDestination) -> Unit,
navigator: (destination: BaseDestination) -> Unit,
viewModel: SecondViewModel = hiltViewModel(),

id: String,
) {
SecondScreenContent(
id = id,
Expand All @@ -37,11 +37,15 @@ fun SecondScreen(
@Composable
private fun SecondScreenContent(
id: String,
modifier: Modifier = Modifier,
onUpdateClick: () -> Unit,
Comment on lines 39 to 41
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please cross-check with some config from https://github.com/appKODE/detekt-rules-compose, which forces us to order the Modifier with the default value at the end. I think that's a good convention to apply as well 🤔

) {
Scaffold(topBar = {
AppBar(R.string.second_title_bar)
}) { paddingValues ->
Scaffold(
topBar = {
AppBar(R.string.second_title_bar)
},
modifier = modifier,
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,23 @@ import co.nimblehq.sample.compose.ui.theme.ComposeTheme

@Composable
fun ThirdScreen(
model: UiModel?,
viewModel: ThirdViewModel = hiltViewModel(),
navigator: (destination: BaseDestination) -> Unit,
Comment on lines +23 to 25
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, params with default values to the bottom.

model: UiModel?,
) {
ThirdScreenContent(data = model)
}

@Composable
fun ThirdScreenContent(data: UiModel?) {
fun ThirdScreenContent(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must be private

data: UiModel?,
modifier: Modifier = Modifier,
) {
Scaffold(
topBar = {
AppBar(title = R.string.third_title_bar)
}
},
modifier = modifier,
) { paddingValues ->
Box(
modifier = Modifier
Expand All @@ -50,7 +54,7 @@ fun ThirdScreenContent(data: UiModel?) {

@Preview
@Composable
fun ThirdScreenPreview() {
private fun ThirdScreenPreview() {
ComposeTheme {
ThirdScreenContent(data = UiModel("1", "name1"))
}
Expand Down
4 changes: 4 additions & 0 deletions sample-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ plugins {
id("org.jetbrains.kotlinx.kover").version(Versions.KOVER_VERSION)
}

dependencies {
detektPlugins("io.nlopez.compose.rules:detekt:0.3.3")
}

allprojects {
repositories {
google()
Expand Down
1 change: 1 addition & 0 deletions sample-compose/buildSrc/src/main/java/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ object Versions {

const val KOTLIN_VERSION = "1.8.21"
const val KOTLINX_COROUTINES_VERSION = "1.7.1"
const val KOTLIN_COLLECTIONS_IMMUTABLE_VERSION = "0.3.6"
const val KOVER_VERSION = "0.7.3"

const val MOSHI_VERSION = "1.12.0"
Expand Down
46 changes: 46 additions & 0 deletions sample-compose/detekt-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ style:
MagicNumber:
active: true
ignoreNumbers: [ '-1', '0', '1', '2' ]
ignoreAnnotated: [ 'Preview' ]
ignoreHashCodeFunction: false
ignorePropertyDeclaration: true
ignoreConstantDeclaration: true
Expand Down Expand Up @@ -347,3 +348,48 @@ style:
active: false
WildcardImport:
active: false

Compose:
CompositionLocalAllowlist:
active: true
allowedCompositionLocals: LocalAppColors,LocalAppDimensions,LocalAppShapes,LocalAppStyles,LocalAppTypography
ContentEmitterReturningValues:
active: true
DefaultsVisibility:
active: true
ModifierClickableOrder:
active: true
ModifierComposable:
active: true
ModifierMissing:
active: true
ModifierNaming:
active: true
ModifierNotUsedAtRoot:
active: true
ModifierReused:
active: true
ModifierWithoutDefault:
active: true
MultipleEmitters:
active: true
MutableParams:
active: true
ComposableNaming:
active: true
ComposableParamOrder:
active: true
PreviewAnnotationNaming:
active: true
PreviewPublic:
active: true
RememberMissing:
active: true
RememberContentMissing:
active: true
UnstableCollections:
active: true
ViewModelForwarding:
active: true
ViewModelInjection:
active: true
4 changes: 4 additions & 0 deletions template-compose/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ dependencies {
kapt(COMPILER)
}

with(Dependencies.Kotlin) {
implementation(COLLECTIONS_IMMUTABLE)
}

with(Dependencies.Log) {
implementation(TIMBER)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import co.nimblehq.template.compose.ui.models.UiModel
import co.nimblehq.template.compose.ui.showToast
import co.nimblehq.template.compose.ui.theme.AppTheme.dimensions
import co.nimblehq.template.compose.ui.theme.ComposeTheme
import kotlinx.collections.immutable.*
import timber.log.Timber

@Composable
Expand All @@ -33,7 +34,7 @@ fun HomeScreen(
viewModel.error.collectAsEffect { e -> e.showToast(context) }
viewModel.navigator.collectAsEffect { destination -> navigator(destination) }

val uiModels: List<UiModel> by viewModel.uiModels.collectAsStateWithLifecycle()
val uiModels: ImmutableList<UiModel> by viewModel.uiModels.collectAsStateWithLifecycle()

HomeScreenContent(
title = stringResource(id = R.string.app_name),
Expand All @@ -44,7 +45,7 @@ fun HomeScreen(
@Composable
private fun HomeScreenContent(
title: String,
uiModels: List<UiModel>
uiModels: ImmutableList<UiModel>
) {
Column(
modifier = Modifier.fillMaxSize(),
Expand All @@ -67,7 +68,7 @@ private fun HomeScreenPreview() {
ComposeTheme {
HomeScreenContent(
title = stringResource(id = R.string.app_name),
uiModels = listOf(UiModel(1), UiModel(2), UiModel(3))
uiModels = persistentListOf(UiModel(1), UiModel(2), UiModel(3))
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import co.nimblehq.template.compose.ui.models.UiModel
import co.nimblehq.template.compose.ui.models.toUiModel
import co.nimblehq.template.compose.util.DispatchersProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.*
import kotlinx.coroutines.flow.*
import javax.inject.Inject

Expand All @@ -16,15 +17,15 @@ class HomeViewModel @Inject constructor(
useCase: UseCase,
) : BaseViewModel() {

private val _uiModels = MutableStateFlow<List<UiModel>>(emptyList())
private val _uiModels = MutableStateFlow<ImmutableList<UiModel>>(persistentListOf())
val uiModels = _uiModels.asStateFlow()

init {
useCase()
.injectLoading()
.onEach { result ->
val uiModels = result.map { it.toUiModel() }
_uiModels.emit(uiModels)
_uiModels.emit(uiModels.toImmutableList())
}
.flowOn(dispatchersProvider.io)
.catch { e -> _error.emit(e) }
Expand Down
4 changes: 4 additions & 0 deletions template-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ plugins {
id(Plugins.KOVER) version Versions.KOVER
}

dependencies {
detektPlugins(Plugins.DETEKT_RULES)
}

tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}
Expand Down
2 changes: 2 additions & 0 deletions template-compose/buildSrc/src/main/java/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ object Dependencies {

object Kotlin {
const val COROUTINES_CORE = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.KOTLIN_COROUTINES}"
const val COLLECTIONS_IMMUTABLE =
"org.jetbrains.kotlinx:kotlinx-collections-immutable:${Versions.KOTLIN_COLLECTIONS_IMMUTABLE}"
}

object Log {
Expand Down
1 change: 1 addition & 0 deletions template-compose/buildSrc/src/main/java/Plugins.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ object Plugins {
const val JAVA_LIBRARY = "java-library"

const val DETEKT = "io.gitlab.arturbosch.detekt"
const val DETEKT_RULES = "io.nlopez.compose.rules:detekt:0.3.3"

const val HILT_ANDROID = "com.google.dagger.hilt.android"

Expand Down
1 change: 1 addition & 0 deletions template-compose/buildSrc/src/main/java/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ object Versions {

const val KOTEST = "5.6.2"
const val KOTLIN = "1.9.10"
const val KOTLIN_COLLECTIONS_IMMUTABLE = "0.3.6"
const val KOTLIN_COROUTINES = "1.7.1"
const val KOVER = "0.7.3"

Expand Down
Loading
Loading