Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.groundplatform.android.ui.tos

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand All @@ -37,13 +38,18 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import ground_android.core.ui.generated.resources.Res
import ground_android.core.ui.generated.resources.retry
import org.groundplatform.android.R
import org.groundplatform.android.ui.common.ExcludeFromJacocoGeneratedReport
import org.groundplatform.android.ui.components.Toolbar
import org.groundplatform.ui.theme.AppTheme
import org.jetbrains.compose.resources.stringResource

@Composable
fun TermsOfServiceScreen(
Expand All @@ -69,6 +75,7 @@ fun TermsOfServiceScreen(
onAgreeCheckedChange = { viewModel.setAgreeCheckboxChecked(it) },
onAgreeClick = { viewModel.onAgreeButtonClicked() },
onBackClick = onNavigateUp,
onRetry = { viewModel.onRetryClicked() },
termsContent = termsContent,
)
}
Expand All @@ -79,6 +86,7 @@ private fun TermsOfServiceContent(
onAgreeCheckedChange: (Boolean) -> Unit,
onAgreeClick: () -> Unit,
onBackClick: () -> Unit,
onRetry: () -> Unit,
termsContent: @Composable (String) -> Unit,
) {
Scaffold(
Expand Down Expand Up @@ -113,6 +121,20 @@ private fun TermsOfServiceContent(
}
}
}
is TosUiState.Error -> {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResource(R.string.load_tos_failed),
textAlign = TextAlign.Center,
)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = onRetry) { Text(text = stringResource(Res.string.retry)) }
}
}
}
}
}
Expand Down Expand Up @@ -145,34 +167,56 @@ private fun AgreeSection(
@Composable
@ExcludeFromJacocoGeneratedReport
private fun TermsOfServiceContentPreview() {
TermsOfServiceContent(
uiState =
TosUiState.Success(
tosHtml = "Sample Terms of Service content.",
agreeChecked = false,
isViewOnly = false,
),
onAgreeCheckedChange = {},
onAgreeClick = {},
onBackClick = {},
termsContent = { Text("Sample Terms") },
)
AppTheme {
TermsOfServiceContent(
uiState =
TosUiState.Success(
tosHtml = "Sample Terms of Service content.",
agreeChecked = false,
isViewOnly = false,
),
onAgreeCheckedChange = {},
onAgreeClick = {},
onBackClick = {},
onRetry = {},
termsContent = { Text("Sample Terms") },
)
}
}

@Preview(showBackground = true)
@Composable
@ExcludeFromJacocoGeneratedReport
private fun TermsOfServiceContentIsViewOnlyPreview() {
TermsOfServiceContent(
uiState =
TosUiState.Success(
tosHtml = "Sample Terms of Service content.",
agreeChecked = false,
isViewOnly = true,
),
onAgreeCheckedChange = {},
onAgreeClick = {},
onBackClick = {},
termsContent = { Text("Sample Terms") },
)
AppTheme {
TermsOfServiceContent(
uiState =
TosUiState.Success(
tosHtml = "Sample Terms of Service content.",
agreeChecked = false,
isViewOnly = true,
),
onAgreeCheckedChange = {},
onAgreeClick = {},
onBackClick = {},
onRetry = {},
termsContent = { Text("Sample Terms") },
)
}
}

@Preview(showBackground = true)
@Composable
@ExcludeFromJacocoGeneratedReport
private fun TermsOfServiceContentErrorPreview() {
AppTheme {
TermsOfServiceContent(
uiState = TosUiState.Error(isViewOnly = true),
onAgreeCheckedChange = {},
onAgreeClick = {},
onBackClick = {},
onRetry = {},
termsContent = { Text("Terms of service") },
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ sealed interface TosUiState {
val agreeChecked: Boolean,
override val isViewOnly: Boolean,
) : TosUiState

data class Error(override val isViewOnly: Boolean) : TosUiState
}

sealed interface TosEvent {
Expand All @@ -68,7 +70,7 @@ constructor(
private val _uiState = MutableStateFlow<TosUiState>(TosUiState.Loading(isViewOnly))
val uiState: StateFlow<TosUiState> = _uiState.asStateFlow()

private val _events = Channel<TosEvent>()
private val _events = Channel<TosEvent>(Channel.BUFFERED)
val events = _events.receiveAsFlow()

init {
Expand All @@ -92,8 +94,13 @@ constructor(
}
}

fun onRetryClicked() {
loadTermsOfService()
}

private fun loadTermsOfService() {
viewModelScope.launch {
_uiState.value = TosUiState.Loading(isViewOnly)
try {
val tos = termsOfServiceRepository.getTermsOfService()?.text ?: ""
val flavor = CommonMarkFlavourDescriptor()
Expand All @@ -107,8 +114,12 @@ constructor(
if (!e.isExpectedFailure()) {
Timber.e(e, "Failed to load Terms of Service")
}
_events.send(TosEvent.LoadError)
authManager.signOut()
if (isViewOnly) {
_uiState.value = TosUiState.Error(isViewOnly)
} else {
_events.send(TosEvent.LoadError)
authManager.signOut()
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import ground_android.core.ui.generated.resources.Res
import ground_android.core.ui.generated.resources.retry
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.groundplatform.android.R
Expand Down Expand Up @@ -141,6 +143,25 @@ class TermsOfServiceScreenTest {
assert(errorOccurred)
}

@Test
fun `View-only load failure shows error with retry and recovers on retry`() = runTest {
setupViewModel(Result.failure(Exception("Failed to load")), isViewOnly = true)
setScreenContent()

composeTestRule.waitUntil(timeoutMillis = 5000) {
viewModel.uiState.value is TosUiState.Error
}
composeTestRule.onNodeWithText(getString(Res.string.retry)).assertIsDisplayed()

fakeRepository.termsOfService = Result.success(TEST_TOS)
composeTestRule.onNodeWithText(getString(Res.string.retry)).performClick()

composeTestRule.waitUntil(timeoutMillis = 5000) {
viewModel.uiState.value is TosUiState.Success
}
composeTestRule.onNode(hasText(TEST_TOS_TEXT, substring = true)).assertIsDisplayed()
}

@Test
fun `Toolbar is displayed`() = runTest {
setupViewModel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

Expand Down Expand Up @@ -102,9 +103,9 @@ class TermsOfServiceViewModelTest {
}

@Test
fun `Load failure triggers LoadError event and signs out`() = runTest {
fun `Load failure signs out when not view-only`() = runTest {
fakeRepository.termsOfService = Result.failure(Exception("Failed to load"))
setupViewModel()
setupViewModel(isViewOnly = false)

viewModel.events.test {
advanceUntilIdle()
Expand All @@ -115,6 +116,29 @@ class TermsOfServiceViewModelTest {
verify(authManager).signOut()
}

@Test
fun `Load failure shows Error state, emits LoadError, and does not sign out when view-only`() =
runTest {
fakeRepository.termsOfService = Result.failure(Exception("Failed to load"))
setupViewModel(isViewOnly = true)

assertThat(viewModel.uiState.value).isEqualTo(TosUiState.Error(isViewOnly = true))
verify(authManager, never()).signOut()
}

@Test
fun `onRetryClicked reloads terms and recovers from Error state`() = runTest {
fakeRepository.termsOfService = Result.failure(Exception("Failed to load"))
setupViewModel(isViewOnly = true)
assertThat(viewModel.uiState.value).isEqualTo(TosUiState.Error(isViewOnly = true))

fakeRepository.termsOfService = Result.success(TEST_TOS)
viewModel.onRetryClicked()
advanceUntilIdle()

assertSuccessState(isViewOnly = true, TERMS_TEXT)
}

companion object {
private const val TERMS_TEXT = "Terms content"
private val TEST_TOS = TermsOfService("1", TERMS_TEXT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@
<string name="survey">Encuesta</string>
<string name="job">Trabajo</string>
<string name="pdf_data_collector">Recolector de datos</string>
<string name="retry">Reintentar</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@
<string name="survey">Enquête</string>
<string name="job">Mission</string>
<string name="pdf_data_collector">Collecteur de données</string>
<string name="retry">Réessayer</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@
<string name="survey">ការស្ទង់មតិ</string>
<string name="job">ការងារ</string>
<string name="pdf_data_collector">អ្នកប្រមូលទិន្នន័យ</string>
<string name="retry">ព្យាយាមឡើងវិញ</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@
<string name="survey">ແບບສຳຫຼວດ</string>
<string name="job">ພາລະກິດ</string>
<string name="pdf_data_collector">ຜູ້ເກັບຂໍ້ມູນ</string>
<string name="retry">ລອງໃໝ່</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@
<string name="survey">Inquérito</string>
<string name="job">Tarefa</string>
<string name="pdf_data_collector">Coletor de dados</string>
<string name="retry">Tentar novamente</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@
<string name="survey">แบบสำรวจ</string>
<string name="job">งาน</string>
<string name="pdf_data_collector">ผู้เก็บข้อมูล</string>
<string name="retry">ลองใหม่</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@
<string name="survey">Khảo sát</string>
<string name="job">Công việc</string>
<string name="pdf_data_collector">Người thu thập dữ liệu</string>
<string name="retry">Thử lại</string>
</resources>
1 change: 1 addition & 0 deletions core/ui/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@
<string name="survey">Survey</string>
<string name="job">Job</string>
<string name="pdf_data_collector">Data collector</string>
<string name="retry">Retry</string>
</resources>
Loading