@@ -25,7 +25,9 @@ import androidx.compose.material.icons.filled.CloudUpload
2525import androidx.compose.material.icons.filled.Delete
2626import androidx.compose.material.icons.filled.History
2727import androidx.compose.material.icons.filled.Shield
28+ import androidx.compose.material.icons.filled.Sync
2829import androidx.compose.material3.Button
30+ import androidx.compose.material3.ButtonDefaults
2931import androidx.compose.material3.Card
3032import androidx.compose.material3.CardDefaults
3133import androidx.compose.material3.CircularProgressIndicator
@@ -43,6 +45,7 @@ import androidx.compose.material3.SnackbarHostState
4345import androidx.compose.material3.Switch
4446import androidx.compose.material3.Tab
4547import androidx.compose.material3.Text
48+ import androidx.compose.material3.TextButton
4649import androidx.compose.material3.TopAppBar
4750import androidx.compose.runtime.Composable
4851import androidx.compose.runtime.LaunchedEffect
@@ -53,6 +56,7 @@ import androidx.compose.runtime.remember
5356import androidx.compose.runtime.setValue
5457import androidx.compose.ui.Alignment
5558import androidx.compose.ui.Modifier
59+ import androidx.compose.ui.graphics.Color
5660import androidx.compose.ui.platform.LocalUriHandler
5761import androidx.compose.ui.text.style.TextOverflow
5862import androidx.compose.ui.unit.dp
@@ -64,6 +68,9 @@ import java.text.SimpleDateFormat
6468import java.util.Date
6569import java.util.Locale
6670
71+ private const val REPO_URL = " https://github.com/jhonsu01/OpenCallShield"
72+ private const val KOFI_URL = " https://ko-fi.com/V7V81LV7GX"
73+
6774@OptIn(ExperimentalMaterial3Api ::class )
6875@Composable
6976fun MainScreen (
@@ -119,7 +126,7 @@ fun MainScreen(
119126 }
120127 when (selectedTab) {
121128 0 -> ProtectionTab (state, viewModel, onRequestRole, spamNumbers.size, blockedCalls.size)
122- 1 -> SpamListTab (spamNumbers, authState, viewModel)
129+ 1 -> SpamListTab (spamNumbers, authState, state.syncing, viewModel)
123130 2 -> HistoryTab (blockedCalls, spamNumbers, viewModel)
124131 else -> AccountTab (authState, viewModel)
125132 }
@@ -135,6 +142,7 @@ private fun ProtectionTab(
135142 spamCount : Int ,
136143 blockedCount : Int
137144) {
145+ val uriHandler = LocalUriHandler .current
138146 Column (
139147 Modifier
140148 .fillMaxSize()
@@ -220,6 +228,29 @@ private fun ProtectionTab(
220228 Text (" Sincronizar ahora" )
221229 }
222230 }
231+
232+ HorizontalDivider ()
233+
234+ // Donaciones (Ko-fi)
235+ Button (
236+ onClick = { uriHandler.openUri(KOFI_URL ) },
237+ modifier = Modifier .fillMaxWidth(),
238+ colors = ButtonDefaults .buttonColors(containerColor = Color (0xFF29ABE0 ))
239+ ) {
240+ Text (" ☕ Apoyame en Ko-fi" )
241+ }
242+ Text (
243+ " OpenCallShield es gratuito y open source. Si te resulta util, considera apoyarlo." ,
244+ style = MaterialTheme .typography.bodySmall
245+ )
246+
247+ // Pie: enlace al repositorio
248+ TextButton (
249+ onClick = { uriHandler.openUri(REPO_URL ) },
250+ modifier = Modifier .fillMaxWidth()
251+ ) {
252+ Text (" OpenCallShield - ver el proyecto en GitHub" )
253+ }
223254 }
224255}
225256
@@ -246,6 +277,7 @@ private fun SettingRow(
246277private fun SpamListTab (
247278 numbers : List <SpamNumber >,
248279 authState : AuthUiState ,
280+ syncing : Boolean ,
249281 viewModel : MainViewModel
250282) {
251283 var input by remember { mutableStateOf(" " ) }
@@ -283,6 +315,28 @@ private fun SpamListTab(
283315 )
284316 }
285317
318+ Spacer (Modifier .size(8 .dp))
319+ OutlinedButton (
320+ onClick = { viewModel.syncNow() },
321+ modifier = Modifier .fillMaxWidth(),
322+ enabled = ! syncing
323+ ) {
324+ if (syncing) {
325+ CircularProgressIndicator (Modifier .size(18 .dp), strokeWidth = 2 .dp)
326+ Spacer (Modifier .width(8 .dp))
327+ Text (" Sincronizando..." )
328+ } else {
329+ Icon (Icons .Filled .Sync , contentDescription = null )
330+ Spacer (Modifier .width(8 .dp))
331+ Text (" Sincronizar base publica" )
332+ }
333+ }
334+ Text (
335+ " Sincroniza primero la base publica: los numeros que ya esten en ella no se " +
336+ " vuelven a reportar. Asi solo aportas numeros nuevos y evitas duplicados." ,
337+ style = MaterialTheme .typography.bodySmall
338+ )
339+
286340 Spacer (Modifier .size(12 .dp))
287341 if (numbers.isEmpty()) {
288342 EmptyState (" Aun no hay numeros reportados." )
0 commit comments