Skip to content

Commit

Permalink
Resolve detekt violations with reduction in complexity (#896)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashdavies authored Mar 13, 2024
1 parent 68abf51 commit b45c8a7
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,52 @@ internal class EventsRemoteMediator(
override suspend fun load(
loadType: LoadType,
state: PagingState<String, DatabaseEvent>,
): MediatorResult {
val loadKey = when (loadType) {
LoadType.APPEND -> state.lastItemOrNull()?.id ?: return endOfPaginationReached()
LoadType.PREPEND -> return endOfPaginationReached()
LoadType.REFRESH -> null
): MediatorResult = when (loadType) {
LoadType.PREPEND -> endOfPaginationReached()
else -> when (val lastItem = state.lastItemOrNull()) {
is DatabaseEvent -> load(loadType, lastItem.id)
else -> endOfPaginationReached()
}
}

val result: List<ApiEvent> = try {
eventsCallable(GetEventsRequest(loadKey))
} catch (exception: SocketTimeoutException) {
return MediatorResult.Error(exception)
} catch (exception: GetEventsError) {
return MediatorResult.Error(exception)
}
private suspend fun load(
loadType: LoadType,
startAt: String?,
): MediatorResult = when (val result = eventsCallable.result(GetEventsRequest(startAt))) {
is CallableResult.Error<*> -> MediatorResult.Error(result.throwable)
is CallableResult.Success -> {
eventsQueries.transaction {
if (loadType == LoadType.REFRESH) eventsQueries.deleteAll()

eventsQueries.transaction {
if (loadType == LoadType.REFRESH) {
eventsQueries.deleteAll()
result.value.forEach {
eventsQueries.insertOrReplace(it.asDatabaseEvent())
}
}

result.forEach {
eventsQueries.insertOrReplace(it.asDatabaseEvent())
}
MediatorResult.Success(result.value.isEmpty())
}

return MediatorResult.Success(
endOfPaginationReached = result.isEmpty(),
)
}

private fun endOfPaginationReached(): MediatorResult {
return MediatorResult.Success(endOfPaginationReached = true)
}
}

private suspend fun GetEventsCallable.result(
request: GetEventsRequest,
): CallableResult<List<ApiEvent>> = try {
CallableResult.Success(invoke(request))
} catch (exception: SocketTimeoutException) {
CallableResult.Error(exception)
} catch (exception: GetEventsError) {
CallableResult.Error(exception)
}

private sealed interface CallableResult<out T> {
data class Error<out T>(val throwable: Throwable) : CallableResult<T>
data class Success<out T>(val value: T) : CallableResult<T>
}

private fun ApiEvent.asDatabaseEvent(): DatabaseEvent = DatabaseEvent(
id = id, name = name, website = website, location = location,
status = status, online = online, dateStart = dateStart,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.ktor.http.HttpHeaders
import kotlinx.datetime.Clock
import kotlinx.serialization.Serializable
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.seconds

private const val CUSTOM_EXCHANGE_URL_TEMPLATE =
"https://firebaseappcheck.googleapis.com/v1/projects/%s/apps/%s:exchangeCustomToken"
Expand Down Expand Up @@ -56,12 +57,13 @@ internal fun AppCheckGenerator(
setBody(mapOf("customToken" to customToken))
}.body<CustomTokenResponse>()

val ttlMillis = result.ttl
val ttlSeconds = result.ttl
.substring(0, result.ttl.length - 1)
.toLong() * 1000
.toLong()
.seconds

return mapper(
/* ttlMillis */ ttlMillis,
/* ttlMillis */ ttlSeconds.inWholeMilliseconds,
/* token */ result.token,
)
}
Expand Down
2 changes: 2 additions & 0 deletions app-launcher/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ kotlin {
androidMain.dependencies {
implementation(projects.appLauncher.common)
implementation(projects.httpClient)
implementation(projects.platformScaffold)
implementation(projects.platformSupport)

implementation(libs.androidx.activity.compose)
implementation(libs.androidx.core.splashscreen)
implementation(libs.google.android.material)
implementation(libs.ktor.client.core)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
package io.ashdavies.playground

import android.app.Activity
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import com.slack.circuit.foundation.CircuitCompositionLocals
import com.slack.circuit.foundation.NavigableCircuitContent
import com.slack.circuit.foundation.rememberCircuitNavigator
import com.slack.circuit.overlay.ContentWithOverlays
import io.ashdavies.content.enableStrictMode
import io.ashdavies.content.isDebuggable
import io.ashdavies.http.LocalHttpClient
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.request.header
import java.security.MessageDigest
import java.util.Locale

internal class LauncherActivity : ComposeActivity(content = {
@Composable
private fun Activity.LauncherApp() {
CompositionLocalProvider(
LocalHttpClient provides LocalHttpClient.current.config {
install(DefaultRequest) {
Expand All @@ -26,9 +35,7 @@ internal class LauncherActivity : ComposeActivity(content = {
}
},
) {
val circuit = remember { CircuitConfig(applicationContext) }

CircuitCompositionLocals(circuit) {
CircuitCompositionLocals(remember { CircuitConfig(applicationContext) }) {
ContentWithOverlays {
LauncherContent(LocalContext.current) {
val backStack = rememberSaveableBackStack(intent.getStringExtra("route"))
Expand All @@ -41,7 +48,16 @@ internal class LauncherActivity : ComposeActivity(content = {
}
}
}
},)
}

internal class LauncherActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
enableStrictMode(isDebuggable())
setContent { LauncherApp() }
}
}

@Suppress("DEPRECATION")
private fun getSignature(packageManager: PackageManager, packageName: String): String {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
Expand All @@ -36,6 +36,9 @@ import io.ashdavies.parcelable.Parcelize
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.painterResource

private val LauncherAspectRatio: Float
get() = 1024f / 500f

@Parcelize
internal object LauncherScreen : Parcelable, Screen {

Expand Down Expand Up @@ -88,7 +91,7 @@ private fun LauncherTopAppBar(modifier: Modifier = Modifier) {
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Filled.ArrowForward,
imageVector = Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = null,
)
}
Expand Down Expand Up @@ -118,7 +121,7 @@ private fun LauncherItem(
painter = imagePainter,
contentDescription = item.title,
modifier = Modifier
.aspectRatio(1024f / 500f)
.aspectRatio(LauncherAspectRatio)
.fillMaxWidth(),
contentScale = ContentScale.Crop,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,34 @@ private class LauncherCommand : CliktCommand() {

val route: String? by option(help = "The initial route to navigate to")

override fun run() {
application {
Window(
onCloseRequest = ::exitApplication,
state = rememberWindowState(size = DpSize(450.dp, 975.dp)),
title = commandName,
) {
val circuit = remember { CircuitConfig(PlatformContext.Default) }
override fun run() = application {
Window(
onCloseRequest = ::exitApplication,
state = rememberWindowState(size = DpSize(450.dp, 975.dp)),
title = commandName,
) {
val circuit = remember { CircuitConfig(PlatformContext.Default) }

CircuitCompositionLocals(circuit) {
CompositionLocalProvider(
LocalHttpClient provides LocalHttpClient.current.config {
install(DefaultRequest) {
header("User-Agent", System.getProperty("os.name"))
header("X-API-Key", BuildConfig.BROWSER_API_KEY)
}
},
) {
LauncherContent(PlatformContext.Default) {
val backStack = rememberSaveableBackStack(route)

NavigableCircuitContent(
navigator = rememberCircuitNavigator(backStack, ::exitApplication),
backStack = backStack,
decoration = KeyNavigationDecoration(
decoration = circuit.defaultNavDecoration,
onBackInvoked = backStack::pop,
),
)
CircuitCompositionLocals(circuit) {
CompositionLocalProvider(
LocalHttpClient provides LocalHttpClient.current.config {
install(DefaultRequest) {
header("User-Agent", System.getProperty("os.name"))
header("X-API-Key", BuildConfig.BROWSER_API_KEY)
}
},
) {
LauncherContent(PlatformContext.Default) {
val backStack = rememberSaveableBackStack(route)

NavigableCircuitContent(
navigator = rememberCircuitNavigator(backStack, ::exitApplication),
backStack = backStack,
decoration = KeyNavigationDecoration(
decoration = circuit.defaultNavDecoration,
onBackInvoked = backStack::pop,
),
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.time.Duration.Companion.minutes

private const val DEFAULT_LIMIT = 50

private val DefaultHttpConfig: HttpClientConfig<out HttpClientEngineConfig>.() -> Unit = {
install(ContentNegotiation, ContentNegotiation.Config::json)
Expand All @@ -53,7 +56,7 @@ internal class ApplicationTest {
contentType(ContentType.Application.Json)
}.body<AppCheckToken>()

assertEquals(3_600_000, token.ttlMillis)
assertEquals(60.minutes.inWholeMilliseconds, token.ttlMillis)

val verify = client.put("/firebase/token:verify") {
header(HttpHeaders.AppCheckToken, token.token)
Expand All @@ -65,7 +68,7 @@ internal class ApplicationTest {
contentType(ContentType.Application.Json)
}.body<List<Event>>()

assertEquals(50, events.size)
assertEquals(DEFAULT_LIMIT, events.size)
}
}

Expand Down
5 changes: 5 additions & 0 deletions detekt-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ naming:
InvalidPackageDeclaration:
active: false
FunctionNaming:
excludes: ['**/*Test.kt']
functionPattern: '[\w]+'
MatchingDeclarationName:
excludes:
- '**/*.android.kt'
- '**/*.jvm.kt'
style:
MagicNumber:
ignorePropertyDeclaration: true
10 changes: 6 additions & 4 deletions http-client/src/commonMain/kotlin/io/ashdavies/http/Result.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

@PublishedApi
internal class LoadingException(val progress: Float) : Exception()

public val Result<*>.isLoading: Boolean
get() = exceptionOrNull() is LoadingException

public fun <T> Result.Companion.loading(progress: Float = 0f): Result<T> = failure(LoadingException(progress))
@PublishedApi
internal class LoadingException(val progress: Float) : Exception()

public fun <T> Result.Companion.loading(progress: Float = 0f): Result<T> {
return failure(LoadingException(progress))
}

@OptIn(ExperimentalContracts::class)
public inline fun <T> Result<T>.onLoading(action: (progress: Float) -> Unit): Result<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ package io.ashdavies.content

import android.app.Activity
import android.content.ContextWrapper
import android.content.pm.ApplicationInfo

private val PlatformContext.activity: Activity
get() = requireNotNull(findActivity()) { "Could not find activity!" }

public fun PlatformContext.isDebuggable(): Boolean {
return applicationInfo.flags != 0 and ApplicationInfo.FLAG_DEBUGGABLE
}

public actual fun PlatformContext.reportFullyDrawn() {
activity.reportFullyDrawn()
}
Expand Down
Loading

0 comments on commit b45c8a7

Please sign in to comment.