Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dc40467
Rename test files
hueachilles Aug 18, 2025
55efea9
Refactor org invite and transfer view state
hueachilles Aug 21, 2025
e936ef8
Keep visual state consistent while transfering orgs
hueachilles Aug 25, 2025
4cf9dbc
Update transfer org state in correct sequence
hueachilles Aug 25, 2025
7b1303c
Show organization in account profile
hueachilles Aug 25, 2025
fb22b03
Bump version
hueachilles Aug 25, 2025
a23781b
Update translation
hueachilles Aug 25, 2025
e27dd8d
Retain state on successful org transfer for seamless transition
hueachilles Aug 25, 2025
36c41d3
Apply consistent styles to top level views
hueachilles Aug 26, 2025
b0d0be4
Show info form when user chooses to request access to org
hueachilles Aug 26, 2025
8eeacd6
Bump version
hueachilles Aug 26, 2025
39b9236
Correct background color based on system theme
hueachilles Aug 27, 2025
e99b70a
Offset download update icon
hueachilles Aug 27, 2025
89e4520
Sync Incident changed or invalidated Worksites from backend
hueachilles Sep 2, 2025
459fd1c
Update recently viewed Worksite Incident as changed
hueachilles Sep 2, 2025
add5eb4
Updates from cross development
hueachilles Sep 2, 2025
0d00537
Sync data properly on first install and account change
hueachilles Sep 2, 2025
f4274b0
Log additional information when deleting invalidated Cases
hueachilles Sep 3, 2025
b7ace4b
Fix app update available icon positioning
hueachilles Sep 3, 2025
0a44f9e
Update icon color
hueachilles Sep 3, 2025
efd8737
Cache Incident Worksites consistently
hueachilles Sep 4, 2025
2db0993
Page through Cases when caching delta
hueachilles Sep 12, 2025
4f36128
Fix (caching) delta Cases endpoint with correct parameters
hueachilles Sep 12, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ import androidx.navigation.NavController
import com.crisiscleanup.core.designsystem.component.CrisisCleanupBackground
import com.crisiscleanup.core.designsystem.component.CrisisCleanupTextButton
import com.crisiscleanup.core.designsystem.theme.listItemSpacedBy
import com.crisiscleanup.sandbox.navigation.ASYNC_IMAGE_ROUTE
import com.crisiscleanup.sandbox.navigation.ROW_BADGE_ROUTE
import com.crisiscleanup.sandbox.navigation.SandboxNavHost
import com.crisiscleanup.sandbox.navigation.navigateToAsyncImage
import com.crisiscleanup.sandbox.navigation.navigateToBottomNav
import com.crisiscleanup.sandbox.navigation.navigateToCheckboxes
import com.crisiscleanup.sandbox.navigation.navigateToChips
import com.crisiscleanup.sandbox.navigation.navigateToMultiImage
import com.crisiscleanup.sandbox.navigation.navigateToRowBadge
import com.crisiscleanup.sandbox.navigation.navigateToSingleImage

@Composable
Expand Down Expand Up @@ -66,7 +67,7 @@ fun SandboxApp(
SandboxNavHost(
appState.navController,
appState::onBack,
ASYNC_IMAGE_ROUTE,
ROW_BADGE_ROUTE,
)
}
}
Expand Down Expand Up @@ -102,6 +103,9 @@ fun RootRoute(navController: NavController) {
CrisisCleanupTextButton(text = "Async image") {
navController.navigateToAsyncImage()
}
CrisisCleanupTextButton(text = "Row badge") {
navController.navigateToRowBadge()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.crisiscleanup.sandbox.ui.BottomNavRoute
import com.crisiscleanup.sandbox.ui.CheckboxesRoute
import com.crisiscleanup.sandbox.ui.ChipsRoute
import com.crisiscleanup.sandbox.ui.MultiImageRoute
import com.crisiscleanup.sandbox.ui.RowBadgeView
import com.crisiscleanup.sandbox.ui.SingleImageRoute

const val ROOT_ROUTE = "root"
Expand All @@ -20,6 +21,7 @@ private const val BOTTOM_NAV_ROUTE = "bottom-nav"
const val SINGLE_IMAGE_ROUTE = "single-image"
const val MULTI_IMAGE_ROUTE = "multi-image"
const val ASYNC_IMAGE_ROUTE = "async-image"
const val ROW_BADGE_ROUTE = "row-badge"

fun NavController.navigateToBottomNav() {
this.navigate(BOTTOM_NAV_ROUTE)
Expand All @@ -45,6 +47,10 @@ fun NavController.navigateToAsyncImage() {
this.navigate(ASYNC_IMAGE_ROUTE)
}

fun NavController.navigateToRowBadge() {
this.navigate(ROW_BADGE_ROUTE)
}

@Composable
fun SandboxNavHost(
navController: NavHostController,
Expand Down Expand Up @@ -82,5 +88,9 @@ fun SandboxNavHost(
composable(ASYNC_IMAGE_ROUTE) {
AsyncImageView()
}

composable(ROW_BADGE_ROUTE) {
RowBadgeView()
}
}
}
116 changes: 116 additions & 0 deletions app-sandbox/src/main/java/com/crisiscleanup/sandbox/ui/RowBadgeView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.crisiscleanup.sandbox.ui

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
private fun RowScope.BadgedText(
alignment: Alignment,
text: String,
) {
BadgedBox(
{
Badge(
Modifier
.align(alignment)
.size(20.dp),
containerColor = Color.Red,
) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
)
}
},
Modifier
.background(Color.LightGray)
.weight(1f),
) {
Text(
text,
Modifier
.align(Alignment.CenterStart)
.background(Color.Yellow),
)
}
}

@Composable
private fun RowBadge(
alignment: Alignment,
text: String,
buttonText: String = "Press me",
) {
Row(
Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
BadgedText(alignment, text)
Button({}) {
Text(buttonText)
}
}
}

@Composable
private fun ReverseRowBadge(
alignment: Alignment,
text: String,
buttonText: String = "Press me",
) {
Row(
Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Button({}) {
Text(buttonText)
}
BadgedText(alignment, text)
}
}

@Composable
fun RowBadgeView() {
Column(
Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
) {
RowBadge(Alignment.TopStart, "Top start")
RowBadge(Alignment.TopCenter, "Top center")
RowBadge(Alignment.TopEnd, "Top end")
RowBadge(Alignment.BottomEnd, "Bottom end")
RowBadge(Alignment.BottomCenter, "Bottom center")
RowBadge(Alignment.BottomStart, "Bottom start")
ReverseRowBadge(Alignment.TopStart, "Top start")
ReverseRowBadge(Alignment.TopCenter, "Top center")
ReverseRowBadge(Alignment.TopEnd, "Top end")
ReverseRowBadge(Alignment.BottomEnd, "Bottom end")
ReverseRowBadge(Alignment.BottomCenter, "Bottom center")
ReverseRowBadge(Alignment.BottomStart, "Bottom start")
}
}
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ plugins {

android {
defaultConfig {
val buildVersion = 268
val buildVersion = 277
applicationId = "com.crisiscleanup"
versionCode = buildVersion
versionName = "0.9.${buildVersion - 168}"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/crisiscleanup/CrisisCleanupAppEnv.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class CrisisCleanupAppEnv @Inject constructor(
val apiUrl = settingsProvider.apiBaseUrl
return when {
apiUrl.startsWith("https://api.dev.crisiscleanup.io") -> "Dev"
apiUrl.startsWith("https://api.staging.crisiscleanup.io") -> "Staging"
apiUrl.startsWith("https://crisiscleanup-3-api-staging.up.railway.app") -> "Staging"
apiUrl.startsWith("https://api.crisiscleanup.org") -> "Production"
else -> "Local?"
}
Expand Down
28 changes: 2 additions & 26 deletions app/src/main/java/com/crisiscleanup/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
package com.crisiscleanup

import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -24,7 +21,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.metrics.performance.JankStats
import com.crisiscleanup.MainActivityViewState.Loading
import com.crisiscleanup.MainActivityViewState.Success
import com.crisiscleanup.core.common.NetworkMonitor
import com.crisiscleanup.core.common.PermissionManager
import com.crisiscleanup.core.common.PhoneNumberPicker
Expand All @@ -38,7 +34,6 @@ import com.crisiscleanup.core.data.repository.EndOfLifeRepository
import com.crisiscleanup.core.data.repository.LanguageTranslationsRepository
import com.crisiscleanup.core.data.repository.LocalAppMetricsRepository
import com.crisiscleanup.core.designsystem.theme.CrisisCleanupTheme
import com.crisiscleanup.core.model.data.DarkThemeConfig
import com.crisiscleanup.sync.initializers.scheduleSyncWorksites
import com.crisiscleanup.ui.CrisisCleanupApp
import com.crisiscleanup.ui.rememberCrisisCleanupAppState
Expand Down Expand Up @@ -118,18 +113,15 @@ class MainActivity : ComponentActivity() {
}

setContent {
val darkTheme = shouldUseDarkTheme(viewState)
val darkTheme = isSystemInDarkTheme()

val windowSizeClass = calculateWindowSizeClass(this)
val appState = rememberCrisisCleanupAppState(
networkMonitor = networkMonitor,
windowSizeClass = windowSizeClass,
)

enableEdgeToEdge(
statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT),
navigationBarStyle = SystemBarStyle.dark(Color.TRANSPARENT),
)
enableEdgeToEdge()

CompositionLocalProvider {
CrisisCleanupTheme(
Expand Down Expand Up @@ -217,19 +209,3 @@ class MainActivity : ComponentActivity() {
}
}
}

/**
* Returns `true` if dark theme should be used, as a function of the [viewState] and the
* current system context.
*/
@Composable
private fun shouldUseDarkTheme(
viewState: MainActivityViewState,
): Boolean = when (viewState) {
Loading -> isSystemInDarkTheme()
is Success -> when (viewState.userData.darkThemeConfig) {
DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme()
DarkThemeConfig.LIGHT -> false
DarkThemeConfig.DARK -> true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,17 @@ class CrisisCleanupInterceptorProvider @Inject constructor(
.build()
}

private fun isExpiredToken(response: Response, logPaths: String): Pair<Boolean, Response> {
private fun isExpiredToken(response: Response): Pair<Boolean, Response> {
if (response.code == 401) {
return Pair(true, response)
}
response.body?.let { responseBody ->
response.body.let { responseBody ->
val body = responseBody.string()
val errors = json.parseNetworkErrors(body)
val bodyCopy = body.toResponseBody(responseBody.contentType())
val copyResponse = response.newBuilder().body(bodyCopy).build()
return Pair(errors.hasExpiredToken, copyResponse)
}
// TODO If body is null from above wouldn't response need to close (and rebuild)?
logger.logCapture("Token was not expired and body was null for $logPaths. Incoming exception?")
return Pair(false, response)
}

private val invalidRefreshTokenErrorMessages = setOf(
Expand Down Expand Up @@ -169,10 +166,7 @@ class CrisisCleanupInterceptorProvider @Inject constructor(
private fun tryAuthRequest(chain: Interceptor.Chain, request: Request): Response {
val response = chain.proceed(request)

val (isExpired, nextResponse) = isExpiredToken(
response,
request.pathsForLog,
)
val (isExpired, nextResponse) = isExpiredToken(response)
if (isExpired) {
logger.logCapture("Expired token trying refresh ${request.pathsForLog}")
runBlocking {
Expand Down
Loading
Loading