feat(legal): ExternalLinks utility, Privacy/Terms links in Settings + Paywall screens (batch 12)

This commit is contained in:
null 2026-06-17 01:32:05 -05:00
parent f3bad90ec6
commit 4ede77f067
3 changed files with 100 additions and 16 deletions

View File

@ -1,8 +1,23 @@
package app.closer.core.navigation package app.closer.core.navigation
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.Toast
object ExternalLinks { object ExternalLinks {
const val PRIVACY_POLICY = "https://couplesconnect.app/privacy" const val PRIVACY_POLICY = "https://closer.app/privacy"
const val TERMS_OF_SERVICE = "https://couplesconnect.app/terms" const val TERMS_OF_SERVICE = "https://closer.app/terms"
const val SUBSCRIPTION_TERMS = "https://couplesconnect.app/subscription-terms" const val SUBSCRIPTION_TERMS = "https://closer.app/subscription-terms"
const val SUPPORT = "https://couplesconnect.app/support" const val SUPPORT = "https://couplesconnect.app/support"
fun openUrl(context: Context, url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
val resolved = intent.resolveActivity(context.packageManager)
if (resolved != null) {
context.startActivity(intent)
} else {
Toast.makeText(context, "No browser app found", Toast.LENGTH_SHORT).show()
}
}
} }

View File

@ -48,7 +48,6 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@ -83,7 +82,6 @@ fun PaywallScreen(
) { ) {
val uiState by viewModel.uiState.collectAsState() val uiState by viewModel.uiState.collectAsState()
val context = LocalContext.current val context = LocalContext.current
val uriHandler = LocalUriHandler.current
var showThankYou by remember { mutableStateOf(false) } var showThankYou by remember { mutableStateOf(false) }
LaunchedEffect(uiState.purchaseState) { LaunchedEffect(uiState.purchaseState) {
@ -148,7 +146,7 @@ fun PaywallScreen(
onRestore = { viewModel.restore() } onRestore = { viewModel.restore() }
) )
LegalLinks(uriHandler = uriHandler) LegalLinks()
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
} }
@ -370,21 +368,31 @@ private fun ActionButtons(
@Composable @Composable
private fun LegalLinks( private fun LegalLinks(
uriHandler: androidx.compose.ui.platform.UriHandler,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Row( val context = LocalContext.current
Column(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally) horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) { ) {
TextButton(onClick = { uriHandler.openUri(ExternalLinks.PRIVACY_POLICY) }) { Row(
Text("Privacy", style = MaterialTheme.typography.labelSmall, color = Color(0xFF9B8AA6)) horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally)
) {
TextButton(onClick = { ExternalLinks.openUrl(context, ExternalLinks.PRIVACY_POLICY) }) {
Text("Privacy Policy", style = MaterialTheme.typography.labelSmall, color = Color(0xFF9B8AA6))
}
TextButton(onClick = { ExternalLinks.openUrl(context, ExternalLinks.TERMS_OF_SERVICE) }) {
Text("Terms of Service", style = MaterialTheme.typography.labelSmall, color = Color(0xFF9B8AA6))
}
} }
TextButton(onClick = { uriHandler.openUri(ExternalLinks.TERMS_OF_SERVICE) }) {
Text("Terms", style = MaterialTheme.typography.labelSmall, color = Color(0xFF9B8AA6)) TextButton(onClick = { ExternalLinks.openUrl(context, ExternalLinks.SUBSCRIPTION_TERMS) }) {
} Text(
TextButton(onClick = { uriHandler.openUri(ExternalLinks.SUBSCRIPTION_TERMS) }) { "Subscription terms apply",
Text("Subscription terms", style = MaterialTheme.typography.labelSmall, color = Color(0xFF9B8AA6)) style = MaterialTheme.typography.labelSmall,
color = Color(0xFF9B8AA6)
)
} }
} }
} }

View File

@ -54,7 +54,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import android.content.Context
import androidx.compose.ui.platform.LocalContext
import app.closer.core.navigation.AppRoute import app.closer.core.navigation.AppRoute
import app.closer.core.navigation.ExternalLinks
import app.closer.ui.settings.SettingsDanger import app.closer.ui.settings.SettingsDanger
import app.closer.ui.settings.SettingsInk import app.closer.ui.settings.SettingsInk
import app.closer.ui.settings.SettingsMuted import app.closer.ui.settings.SettingsMuted
@ -349,6 +352,35 @@ fun SettingsScreen(
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
// Legal
Text(
text = "Legal",
style = MaterialTheme.typography.labelLarge,
color = SettingsMuted,
modifier = Modifier.padding(horizontal = 4.dp, vertical = 4.dp)
)
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(containerColor = SettingsCard)
) {
val context = LocalContext.current
Column {
SettingsLegalRow(
label = "Privacy Policy",
onClick = { ExternalLinks.openUrl(context, ExternalLinks.PRIVACY_POLICY) }
)
Divider(modifier = Modifier.padding(horizontal = 16.dp), thickness = 0.5.dp)
SettingsLegalRow(
label = "Terms of Service",
onClick = { ExternalLinks.openUrl(context, ExternalLinks.TERMS_OF_SERVICE) }
)
}
}
Spacer(Modifier.height(8.dp))
// Account lifecycle — separated from legal links // Account lifecycle — separated from legal links
Card( Card(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -422,3 +454,32 @@ private fun SettingsRow(
) )
} }
} }
@Composable
private fun SettingsLegalRow(
label: String,
onClick: () -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 14.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = label,
style = MaterialTheme.typography.bodyLarge,
color = SettingsInk,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Icon(
Icons.AutoMirrored.Filled.ArrowForwardIos,
contentDescription = null,
modifier = Modifier.size(14.dp),
tint = SettingsMuted
)
}
}