Skip to content

Commit

Permalink
refactor:
Browse files Browse the repository at this point in the history
 - small update checker.
 - fix errors on many screens
 * I am quite confused as to how the API developers are thinking. changing data structures on endpoints used by not only my app, but theirs too. making both unusable for many students. I question myself a lot of times. do they know anything about actual good practices to follow when making any changes?
  • Loading branch information
abdallahmehiz committed Jan 31, 2025
1 parent 7aa03a3 commit 984ce23
Show file tree
Hide file tree
Showing 39 changed files with 380 additions and 145 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ plugins {
alias(libs.plugins.sqldelight) apply false
alias(libs.plugins.detekt) apply false
alias(libs.plugins.gms) apply false
alias(libs.plugins.kotlin.android) apply false
}

buildscript {
Expand Down
6 changes: 3 additions & 3 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import com.codingfeline.buildkonfig.compiler.FieldSpec.Type.STRING
import io.gitlab.arturbosch.detekt.Detekt
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
Expand Down Expand Up @@ -53,6 +52,7 @@ kotlin {
implementation(project(":i18n"))
implementation(project(":domain"))
implementation(project(":core"))
implementation(project(":core:update-check"))

implementation(compose.ui)
implementation(compose.runtime)
Expand Down Expand Up @@ -88,8 +88,8 @@ kotlin {
}
}

val appVersionName = "0.1.7"
val appVersionCode = 12
val appVersionName = libs.versions.app.version.name.get()
val appVersionCode = libs.versions.app.version.code.get().toInt()
val appPackageName = "mehiz.abdallah.progres"

android {
Expand Down
88 changes: 38 additions & 50 deletions composeApp/src/commonMain/kotlin/App.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.SlideTransition
Expand All @@ -31,6 +26,7 @@ import org.koin.compose.koinInject
import org.koin.core.context.loadKoinModules
import org.koin.dsl.module
import preferences.BasePreferences
import presentation.AppStateBanners
import presentation.theme.AppTheme
import ui.home.HomeScreen
import ui.onboarding.LoginScreen
Expand All @@ -40,36 +36,14 @@ fun App(onReady: () -> Unit, modifier: Modifier = Modifier) {
val preferences = koinInject<BasePreferences>()
val revealCanvasState = rememberRevealCanvasState()
val toasterState = rememberToasterState()
loadKoinModules(
module {
single { revealCanvasState }
single { toasterState }
},
)
AppTheme {
RevealCanvas(revealCanvasState) {
Column(modifier) {
Surface(color = MaterialTheme.colorScheme.surfaceDim) { ConnectivityStatusBar() }
Navigator(screen = if (preferences.isLoggedIn.get()) HomeScreen else LoginScreen) {
onReady()
SlideTransition(it)
}
}
Toaster(toasterState, richColors = true)
}
}
}

@Composable
fun ConnectivityStatusBar(
modifier: Modifier = Modifier,
) {
val appUpdateChecker = koinInject<AppUpdateCheck>()
val isThereAnUpdate by appUpdateChecker.isThereAnUpdate.collectAsState()
val state = rememberConnectivityState().apply { startMonitoring() }
val httpConnectivity = Connectivity(
options = HttpConnectivityOptions(
urls = listOf(stringResource(MR.strings.progres_api_url)),
pollingIntervalMs = 10 * 1000,
timeoutMs = 10 * 1000
timeoutMs = 10 * 1000,
),
)
httpConnectivity.start()
Expand All @@ -79,24 +53,38 @@ fun ConnectivityStatusBar(
httpConnectivity.stop()
httpConnectivity.start()
}
Box(modifier = modifier.windowInsetsPadding(WindowInsets.statusBars))
if (isHttpMonitoring && state.isMonitoring) {
Row(
modifier
.fillMaxWidth()
.animateContentSize()
.background(MaterialTheme.colorScheme.errorContainer),
horizontalArrangement = Arrangement.Center,
) {
if (state.isDisconnected || httpStatus.isDisconnected) {
Text(
stringResource(
if (state.isDisconnected) MR.strings.connectivity_no_internet else MR.strings.connectivity_no_progres,
),
color = MaterialTheme.colorScheme.onErrorContainer,
modifier = Modifier.windowInsetsPadding(WindowInsets.statusBars),
loadKoinModules(
module {
single { revealCanvasState }
single { toasterState }
},
)
var isThereABannerVisible by remember { mutableStateOf(false) }
LaunchedEffect(isThereAnUpdate, state.isConnected, httpStatus, isThereABannerVisible) {
isThereABannerVisible = isThereAnUpdate || !state.isConnected || httpStatus !is Connectivity.Status.Connected
}
AppTheme {
RevealCanvas(revealCanvasState) {
Column(modifier) {
AppStateBanners(
newUpdate = isThereAnUpdate,
noInternet = !state.isConnected && state.isMonitoring,
cantReach = httpStatus !is Connectivity.Status.Connected && isHttpMonitoring,
modifier = Modifier.fillMaxWidth(),
)
Navigator(screen = if (preferences.isLoggedIn.get()) HomeScreen else LoginScreen) {
onReady()
SlideTransition(
it,
modifier = if (isThereABannerVisible) {
Modifier.consumeWindowInsets(WindowInsets.statusBars)
} else {
Modifier
},
)
}
}
Toaster(toasterState, richColors = true)
}
}
}
4 changes: 3 additions & 1 deletion composeApp/src/commonMain/kotlin/di/InitKoin.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package di

import UpdateCheckerModule
import mehiz.abdallah.progres.domain.di.DomainModule
import org.koin.dsl.module
import utils.CredentialManager
Expand All @@ -14,6 +15,7 @@ fun initKoin(
PreferencesModule(datastorePath),
DomainModule,
ScreenModelsModule,
ApplicationModule(credentialManager, platformUtils)
ApplicationModule(credentialManager, platformUtils),
UpdateCheckerModule,
)
}
6 changes: 0 additions & 6 deletions composeApp/src/commonMain/kotlin/preferences/KVaultKeys.kt

This file was deleted.

131 changes: 131 additions & 0 deletions composeApp/src/commonMain/kotlin/presentation/AppBanners.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
Copyright © 2015 Javier Tomás
Copyright © 2024 Mihon Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package presentation

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.SubcomposeLayout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
import androidx.compose.ui.util.fastMaxBy
import dev.icerock.moko.resources.compose.stringResource
import mehiz.abdallah.progres.i18n.MR
import org.koin.compose.koinInject
import utils.PlatformUtils

@Composable
fun AppStateBanners(
newUpdate: Boolean,
noInternet: Boolean,
cantReach: Boolean,
modifier: Modifier = Modifier,
) {
val density = LocalDensity.current
val mainInsets = WindowInsets.statusBars
val mainInsetsTop = mainInsets.getTop(density)
SubcomposeLayout(modifier = modifier) { constraints ->
val noInternetPlaceable = subcompose(0) {
AnimatedVisibility(
visible = noInternet || cantReach,
enter = expandVertically(),
exit = shrinkVertically(),
) {
NoInternet(
modifier = Modifier.windowInsetsPadding(mainInsets),
noInternet = noInternet,
)
}
}.fastMap { it.measure(constraints) }
val noInternetHeight = noInternetPlaceable.fastMaxBy { it.height }?.height ?: 0

val newUpdatePlaceable = subcompose(1) {
AnimatedVisibility(
visible = newUpdate,
enter = expandVertically(),
exit = shrinkVertically(),
) {
val top = (mainInsetsTop - noInternetHeight).coerceAtLeast(0)
NewUpdateBanner(
modifier = Modifier.windowInsetsPadding(WindowInsets(top = top)),
)
}
}.fastMap { it.measure(constraints) }
val newUpdateHeight = newUpdatePlaceable.fastMaxBy { it.height }?.height ?: 0

layout(constraints.maxWidth, noInternetHeight + newUpdateHeight) {
noInternetPlaceable.fastForEach {
it.place(0, 0)
}
newUpdatePlaceable.fastForEach {
it.place(0, noInternetHeight)
}
}
}
}

@Composable
private fun NewUpdateBanner(modifier: Modifier = Modifier) {
// I don't care about injecting platform utils into this small composable.
val platformUtils = koinInject<PlatformUtils>()
Text(
text = stringResource(MR.strings.new_update_available),
modifier = Modifier
.background(MaterialTheme.colorScheme.tertiary)
.fillMaxWidth()
.clickable { platformUtils.openURI(platformUtils.getString(MR.strings.repository_url) + "/releases/latest") }
.padding(4.dp)
.then(modifier),
color = MaterialTheme.colorScheme.onTertiary,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.labelMedium,
)
}

@Composable
private fun NoInternet(
noInternet: Boolean,
modifier: Modifier = Modifier,
) {
Text(
stringResource(
if (noInternet) MR.strings.connectivity_no_internet else MR.strings.connectivity_no_progres,
),
color = MaterialTheme.colorScheme.onErrorContainer,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.labelMedium,
modifier = Modifier
.background(MaterialTheme.colorScheme.errorContainer)
.fillMaxWidth()
.padding(4.dp)
.then(modifier),
)
}
2 changes: 0 additions & 2 deletions composeApp/src/commonMain/kotlin/ui/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
Expand Down Expand Up @@ -175,7 +174,6 @@ object HomeScreen : Screen {
Icon(Icons.Rounded.Settings, null)
}
},
windowInsets = WindowInsets(0.dp),
)
},
) { paddingValues ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -103,7 +102,6 @@ object AccommodationScreen : Screen {
Icon(Icons.AutoMirrored.Rounded.ArrowBack, null)
}
},
windowInsets = WindowInsets(0.dp),
)
},
) { paddingValues ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -60,7 +59,6 @@ object BacInfoScreen : Screen {
Icon(Icons.AutoMirrored.Rounded.ArrowBack, null)
}
},
windowInsets = WindowInsets(0.dp)
)
},
) { paddingValues ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
Expand Down Expand Up @@ -107,7 +106,6 @@ object CCGradesScreen : Screen {
Icon(Icons.AutoMirrored.Rounded.ArrowBack, null)
}
},
windowInsets = WindowInsets(0.dp)
)
},
) { paddingValues ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -98,7 +97,6 @@ object DischargeScreen : Screen {
Icon(Icons.AutoMirrored.Rounded.ArrowBack, null)
}
},
windowInsets = WindowInsets(0.dp),
)
},
) { paddingValues ->
Expand Down
Loading

0 comments on commit 984ce23

Please sign in to comment.