Coverage Summary for Class: AboutScreenKt (com.georgeci.moneysurfer.feature.settings.about)

Class Class, % Method, % Branch, % Line, % Instruction, %
AboutScreenKt 0% (0/1) 0% (0/17) 0% (0/52) 0% (0/143) 0% (0/1657)


 package com.georgeci.moneysurfer.feature.settings.about
 
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 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
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Text
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.draw.shadow
 import androidx.compose.ui.platform.LocalUriHandler
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.georgeci.moneysurfer.uikit.components.base.SurferPaneTopBar
 import com.georgeci.moneysurfer.uikit.components.settings.SurferSettingsChevron
 import com.georgeci.moneysurfer.uikit.components.settings.SurferSettingsGroup
 import com.georgeci.moneysurfer.uikit.components.settings.SurferSettingsRow
 import com.georgeci.moneysurfer.uikit.icons.SurferIcons
 import com.georgeci.moneysurfer.uikit.modifier.surferContentContainer
 import com.georgeci.moneysurfer.uikit.modifier.surferSafeInsets
 import com.georgeci.moneysurfer.uikit.modifier.surferTestTagAsId
 import com.georgeci.moneysurfer.uikit.semantics.SurferSemantics
 import com.georgeci.moneysurfer.uikit.theme.AppTheme
 import com.georgeci.moneysurfer.utils.HandleSideEffect
 import moneysurfer.feature.settings.generated.resources.Res
 import moneysurfer.feature.settings.generated.resources.settings_about_brand
 import moneysurfer.feature.settings.generated.resources.settings_about_chip_license
 import moneysurfer.feature.settings.generated.resources.settings_about_chip_made_by
 import moneysurfer.feature.settings.generated.resources.settings_about_contact_supporting
 import moneysurfer.feature.settings.generated.resources.settings_about_contact_title
 import moneysurfer.feature.settings.generated.resources.settings_about_copyright
 import moneysurfer.feature.settings.generated.resources.settings_about_github_supporting
 import moneysurfer.feature.settings.generated.resources.settings_about_github_title
 import moneysurfer.feature.settings.generated.resources.settings_about_licenses_supporting
 import moneysurfer.feature.settings.generated.resources.settings_about_licenses_title
 import moneysurfer.feature.settings.generated.resources.settings_about_privacy_supporting
 import moneysurfer.feature.settings.generated.resources.settings_about_privacy_title
 import moneysurfer.feature.settings.generated.resources.settings_about_section_help
 import moneysurfer.feature.settings.generated.resources.settings_about_section_legal
 import moneysurfer.feature.settings.generated.resources.settings_about_terms_title
 import moneysurfer.feature.settings.generated.resources.settings_about_title
 import moneysurfer.feature.settings.generated.resources.settings_about_version_format
 import moneysurfer.uikit.generated.resources.uikit_app_icon
 import org.jetbrains.compose.resources.painterResource
 import org.jetbrains.compose.resources.stringResource
 import org.koin.compose.viewmodel.koinViewModel
 import moneysurfer.uikit.generated.resources.Res as UikitRes
 
 @Composable
 fun AboutScreen(
     onNavigateBack: () -> Unit,
     onNavigateToLicenses: () -> Unit,
     viewModel: AboutViewModel = koinViewModel(),
 ) {
     val state by viewModel.collectAsStateWithLifecycle()
     val uriHandler = LocalUriHandler.current
 
     viewModel.HandleSideEffect { effect ->
         when (effect) {
             AboutEffect.NavigateBack -> onNavigateBack()
             is AboutEffect.OpenUrl -> uriHandler.openUri(effect.url)
             is AboutEffect.OpenEmail -> uriHandler.openUri("mailto:${effect.address}")
             AboutEffect.NavigateToLicenses -> onNavigateToLicenses()
             AboutEffect.OpenStoreListing,
             AboutEffect.OpenRegionPicker,
             AboutEffect.NavigateToDiagnostic,
             -> Unit
         }
     }
 
     AboutContent(state = state, onEvent = viewModel::onEvent)
 }
 
 object AboutTestTags {
     const val Root = "about:root"
 }
 
 @Composable
 private fun AboutContent(
     state: AboutState,
     onEvent: (AboutEvent) -> Unit,
 ) {
     Scaffold(
         modifier = Modifier
             .surferSafeInsets()
             .testTag(AboutTestTags.Root)
             .surferTestTagAsId(),
         containerColor = AppTheme.materialColors.surface,
         topBar = {
             SurferPaneTopBar(
                 title = stringResource(Res.string.settings_about_title),
                 onBack = { onEvent(AboutEvent.OnBackClick) },
                 colors = TopAppBarDefaults.topAppBarColors(
                     containerColor = AppTheme.materialColors.surface,
                     titleContentColor = AppTheme.materialColors.onSurface,
                 ),
             )
         },
     ) { padding ->
         Column(
             modifier = Modifier
                 .fillMaxSize()
                 .surferContentContainer()
                 .padding(top = padding.calculateTopPadding())
                 .verticalScroll(rememberScrollState()),
         ) {
             AppIdentityHero(version = state.appVersion)
 
             GitHubLinkRow(onClick = { onEvent(AboutEvent.OnGitHubClick) })
 
             Spacer(Modifier.height(20.dp))
 
             SurferSettingsGroup(title = stringResource(Res.string.settings_about_section_legal)) {
                 SurferSettingsRow(
                     icon = SurferIcons.Receipt,
                     title = stringResource(Res.string.settings_about_terms_title),
                     onClick = { onEvent(AboutEvent.OnTermsClick) },
                     trailing = { SurferSettingsChevron() },
                 )
                 SurferSettingsRow(
                     icon = SurferIcons.Shield,
                     title = stringResource(Res.string.settings_about_privacy_title),
                     supportingText = stringResource(Res.string.settings_about_privacy_supporting),
                     onClick = { onEvent(AboutEvent.OnPrivacyClick) },
                     trailing = { SurferSettingsChevron() },
                 )
                 SurferSettingsRow(
                     icon = SurferIcons.Code,
                     title = stringResource(Res.string.settings_about_licenses_title),
                     supportingText = stringResource(Res.string.settings_about_licenses_supporting),
                     onClick = { onEvent(AboutEvent.OnLicensesClick) },
                     trailing = { SurferSettingsChevron() },
                 )
             }
 
             SurferSettingsGroup(title = stringResource(Res.string.settings_about_section_help)) {
                 SurferSettingsRow(
                     icon = SurferIcons.Mail,
                     title = stringResource(Res.string.settings_about_contact_title),
                     supportingText = stringResource(Res.string.settings_about_contact_supporting),
                     onClick = { onEvent(AboutEvent.OnContactClick) },
                     trailing = { SurferSettingsChevron() },
                 )
             }
 
             Spacer(Modifier.height(8.dp))
             Text(
                 text = stringResource(Res.string.settings_about_copyright),
                 style = AppTheme.typography.bodySmall,
                 color = AppTheme.materialColors.onSurfaceVariant,
                 textAlign = TextAlign.Center,
                 modifier = Modifier
                     .fillMaxWidth()
                     .padding(horizontal = 32.dp, vertical = 20.dp),
             )
             Spacer(Modifier.height(padding.calculateBottomPadding() + 28.dp))
         }
     }
 }
 
 @Composable
 private fun AppIdentityHero(version: String) {
     val colors = AppTheme.materialColors
     Column(
         modifier = Modifier
             .fillMaxWidth()
             .padding(start = 24.dp, end = 24.dp, top = 8.dp, bottom = 28.dp),
         horizontalAlignment = Alignment.CenterHorizontally,
         verticalArrangement = Arrangement.spacedBy(10.dp),
     ) {
         Image(
             painter = painterResource(UikitRes.drawable.uikit_app_icon),
             contentDescription = SurferSemantics.Decorative,
             modifier = Modifier
                 .size(72.dp)
                 .shadow(elevation = 8.dp, shape = RoundedCornerShape(20.dp))
                 .clip(RoundedCornerShape(20.dp)),
         )
         Text(
             text = stringResource(Res.string.settings_about_brand),
             style = AppTheme.typography.titleLarge,
             color = colors.onSurface,
         )
         Text(
             text = stringResource(Res.string.settings_about_version_format, version),
             style = AppTheme.typography.bodyMedium,
             color = colors.onSurfaceVariant,
         )
         Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
             HeroChip(text = stringResource(Res.string.settings_about_chip_made_by))
             HeroChip(text = stringResource(Res.string.settings_about_chip_license))
         }
     }
 }
 
 @Composable
 private fun GitHubLinkRow(onClick: () -> Unit) {
     val colors = AppTheme.materialColors
     Row(
         modifier = Modifier
             .fillMaxWidth()
             .padding(horizontal = 16.dp)
             .clip(RoundedCornerShape(16.dp))
             .background(colors.surfaceContainerHighest)
             .clickable(onClick = onClick)
             .padding(horizontal = 16.dp, vertical = 14.dp),
         verticalAlignment = Alignment.CenterVertically,
         horizontalArrangement = Arrangement.spacedBy(12.dp),
     ) {
         Icon(
             imageVector = SurferIcons.Info,
             contentDescription = SurferSemantics.Decorative,
             tint = colors.onSurfaceVariant,
         )
         Column(modifier = Modifier.fillMaxWidth()) {
             Text(
                 text = stringResource(Res.string.settings_about_github_title),
                 style = AppTheme.typography.titleSmall,
                 color = colors.onSurface,
             )
             Text(
                 text = stringResource(Res.string.settings_about_github_supporting),
                 style = AppTheme.typography.bodySmall,
                 color = colors.onSurfaceVariant,
             )
         }
     }
 }
 
 @Composable
 private fun HeroChip(text: String) {
     val colors = AppTheme.materialColors
     Box(
         modifier = Modifier
             .clip(RoundedCornerShape(999.dp))
             .background(colors.surfaceContainerHighest)
             .padding(horizontal = 10.dp, vertical = 4.dp),
     ) {
         Text(
             text = text,
             style = AppTheme.typography.labelMedium,
             color = colors.onSurfaceVariant,
         )
     }
 }