refactor: delete unused files

This commit is contained in:
2026-06-02 15:16:21 +02:00
parent ff6523fd01
commit 441a258b10
2 changed files with 0 additions and 355 deletions
@@ -1,245 +0,0 @@
package com.bitcointxoko.gudariwallet.ui
import android.Manifest
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.NotificationsOff
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
// ─── Permission state model ───────────────────────────────────────────────────
sealed class NotificationPermissionState {
data object NeverAsked : NotificationPermissionState()
data object Rationale : NotificationPermissionState() // asked once, denied
data object PermanentlyDenied: NotificationPermissionState() // "don't ask again"
data object Granted : NotificationPermissionState()
}
// ─── Prefs helper ─────────────────────────────────────────────────────────────
/**
* Persists a flag so we can distinguish between:
* - "never asked" → show the first-time request screen
* - "asked and denied" → show rationale
* - "permanently denied"→ send user to system settings
*/
object PermissionPrefs {
private const val PREFS_NAME = "lnbits_prefs"
private const val KEY = "notification_permission_requested"
fun hasRequestedBefore(context: Context): Boolean =
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
.getBoolean(KEY, false)
fun markRequested(context: Context) =
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
.edit().putBoolean(KEY, true).apply()
}
// ─── Main composable ──────────────────────────────────────────────────────────
/**
* Handles the full POST_NOTIFICATIONS runtime permission flow.
*
* On Android 12 and below this permission doesn't exist — we skip straight
* to onPermissionGranted since notifications are always allowed.
*
* @param hasRequestedBefore Read from PermissionPrefs — distinguishes
* "never asked" from "permanently denied"
* @param onPermissionGranted Called when permission is granted
* @param onPermissionDenied Called when user skips or permanently denies
* @param onRequestedFirstTime Called after the first system prompt fires —
* persists the flag via PermissionPrefs
*/
@Composable
fun NotificationPermissionScreen(
hasRequestedBefore: Boolean,
onPermissionGranted: () -> Unit,
onPermissionDenied: () -> Unit,
onRequestedFirstTime: () -> Unit
) {
// Android 12 and below — POST_NOTIFICATIONS doesn't exist, skip entirely
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
LaunchedEffect(Unit) { onPermissionGranted() }
return
}
val context = LocalContext.current
// Tracks whether the system already granted the permission
// (e.g. user granted it in a previous session)
var permissionState by remember {
mutableStateOf<NotificationPermissionState>(
when {
!hasRequestedBefore -> NotificationPermissionState.NeverAsked
else -> NotificationPermissionState.Rationale
}
)
}
// The system permission launcher
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
permissionState = if (granted) {
NotificationPermissionState.Granted
} else {
// If we've already asked before and they denied again → permanent denial
if (hasRequestedBefore) NotificationPermissionState.PermanentlyDenied
else NotificationPermissionState.Rationale
}
onRequestedFirstTime()
}
// React to state changes
LaunchedEffect(permissionState) {
when (permissionState) {
is NotificationPermissionState.Granted -> onPermissionGranted()
else -> { /* handled by UI below */ }
}
}
// ── UI ────────────────────────────────────────────────────────────────────
when (permissionState) {
is NotificationPermissionState.NeverAsked,
is NotificationPermissionState.Rationale -> {
PermissionRequestCard(
onAllow = {
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
},
onSkip = onPermissionDenied
)
}
is NotificationPermissionState.PermanentlyDenied -> {
PermanentlyDeniedCard(
onOpenSettings = {
// Send user to the app's system notification settings
context.startActivity(
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", context.packageName, null)
}
)
},
onSkip = onPermissionDenied
)
}
is NotificationPermissionState.Granted -> {
// LaunchedEffect above handles this — show nothing
}
}
}
// ─── Sub-composables ──────────────────────────────────────────────────────────
@Composable
private fun PermissionRequestCard(
onAllow: () -> Unit,
onSkip: () -> Unit
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(32.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = Icons.Default.Notifications,
contentDescription = null,
modifier = Modifier.size(72.dp),
tint = MaterialTheme.colorScheme.primary
)
Spacer(Modifier.height(24.dp))
Text(
text = "Stay notified of payments",
style = MaterialTheme.typography.headlineSmall,
textAlign = TextAlign.Center
)
Spacer(Modifier.height(12.dp))
Text(
text = "Allow notifications so you know the moment a Lightning payment arrives, even when the app is in the background.",
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(Modifier.height(40.dp))
Button(
onClick = onAllow,
modifier = Modifier.fillMaxWidth()
) {
Text("Allow Notifications")
}
Spacer(Modifier.height(12.dp))
TextButton(
onClick = onSkip,
modifier = Modifier.fillMaxWidth()
) {
Text("Not Now")
}
}
}
@Composable
private fun PermanentlyDeniedCard(
onOpenSettings: () -> Unit,
onSkip: () -> Unit
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(32.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = Icons.Default.NotificationsOff,
contentDescription = null,
modifier = Modifier.size(72.dp),
tint = MaterialTheme.colorScheme.error
)
Spacer(Modifier.height(24.dp))
Text(
text = "Notifications are blocked",
style = MaterialTheme.typography.headlineSmall,
textAlign = TextAlign.Center
)
Spacer(Modifier.height(12.dp))
Text(
text = "You've permanently denied notifications. To receive payment alerts, enable them in system settings.",
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(Modifier.height(40.dp))
Button(
onClick = onOpenSettings,
modifier = Modifier.fillMaxWidth()
) {
Text("Open Settings")
}
Spacer(Modifier.height(12.dp))
TextButton(
onClick = onSkip,
modifier = Modifier.fillMaxWidth()
) {
Text("Skip")
}
}
}
@@ -1,110 +0,0 @@
package com.bitcointxoko.gudariwallet.ui
import androidx.compose.animation.core.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
/**
* Shown when an invoice has been paid.
* Displays a pulsing checkmark, the amount received, and the memo.
*
* @param amountSats Amount received in satoshis
* @param memo Invoice memo
* @param onDone Called when the user taps "Done" — navigates back to Home
* @param onReceiveMore Called when the user taps "Receive More" — resets the screen
*/
@Composable
fun PaymentSuccessScreen(
amountSats: Long,
memo: String,
onDone: () -> Unit,
onReceiveMore: () -> Unit
) {
// Pulsing scale animation on the checkmark icon
val infiniteTransition = rememberInfiniteTransition(label = "pulse")
val scale by infiniteTransition.animateFloat(
initialValue = 1f,
targetValue = 1.08f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 700, easing = EaseInOut),
repeatMode = RepeatMode.Reverse
),
label = "checkScale"
)
Column(
modifier = Modifier
.fillMaxSize()
.padding(32.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// ── Animated checkmark ────────────────────────────────────────────
Icon(
imageVector = Icons.Default.CheckCircle,
contentDescription = "Payment received",
tint = MaterialTheme.colorScheme.tertiary, // green role
modifier = Modifier
.size(96.dp)
.scale(scale)
)
Spacer(Modifier.height(32.dp))
// ── Title ─────────────────────────────────────────────────────────
Text(
text = "Payment Received!",
style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onBackground
)
Spacer(Modifier.height(16.dp))
// ── Amount ────────────────────────────────────────────────────────
Text(
text = "+$amountSats sats",
style = MaterialTheme.typography.displaySmall,
color = MaterialTheme.colorScheme.tertiary,
textAlign = TextAlign.Center
)
// ── Memo ──────────────────────────────────────────────────────────
if (memo.isNotBlank()) {
Spacer(Modifier.height(8.dp))
Text(
text = memo,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center
)
}
Spacer(Modifier.height(48.dp))
// ── Actions ───────────────────────────────────────────────────────
Button(
onClick = onDone,
modifier = Modifier.fillMaxWidth()
) {
Text("Done")
}
Spacer(Modifier.height(12.dp))
OutlinedButton(
onClick = onReceiveMore,
modifier = Modifier.fillMaxWidth()
) {
Text("Receive More")
}
}
}