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
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.Toast
object ExternalLinks {
const val PRIVACY_POLICY = "https://couplesconnect.app/privacy"
const val TERMS_OF_SERVICE = "https://couplesconnect.app/terms"
const val SUBSCRIPTION_TERMS = "https://couplesconnect.app/subscription-terms"
const val PRIVACY_POLICY = "https://closer.app/privacy"
const val TERMS_OF_SERVICE = "https://closer.app/terms"
const val SUBSCRIPTION_TERMS = "https://closer.app/subscription-terms"
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.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
@ -83,7 +82,6 @@ fun PaywallScreen(
) {
val uiState by viewModel.uiState.collectAsState()
val context = LocalContext.current
val uriHandler = LocalUriHandler.current
var showThankYou by remember { mutableStateOf(false) }
LaunchedEffect(uiState.purchaseState) {
@ -148,7 +146,7 @@ fun PaywallScreen(
onRestore = { viewModel.restore() }
)
LegalLinks(uriHandler = uriHandler)
LegalLinks()
Spacer(modifier = Modifier.height(8.dp))
}
@ -370,21 +368,31 @@ private fun ActionButtons(
@Composable
private fun LegalLinks(
uriHandler: androidx.compose.ui.platform.UriHandler,
modifier: Modifier = Modifier
) {
Row(
val context = LocalContext.current
Column(
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) }) {
Text("Privacy", style = MaterialTheme.typography.labelSmall, color = Color(0xFF9B8AA6))
Row(
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 = { uriHandler.openUri(ExternalLinks.SUBSCRIPTION_TERMS) }) {
Text("Subscription terms", style = MaterialTheme.typography.labelSmall, color = Color(0xFF9B8AA6))
TextButton(onClick = { ExternalLinks.openUrl(context, ExternalLinks.SUBSCRIPTION_TERMS) }) {
Text(
"Subscription terms apply",
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.unit.dp
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.ExternalLinks
import app.closer.ui.settings.SettingsDanger
import app.closer.ui.settings.SettingsInk
import app.closer.ui.settings.SettingsMuted
@ -349,6 +352,35 @@ fun SettingsScreen(
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
Card(
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
)
}
}