feat(ui): modernização visual Material 3 (v0.3.0)
- Edge-to-edge (enableEdgeToEdge) com Scaffold/insets em todas as telas - Compose BOM 2024.12 -> 2025.06; tipografia e tema com dynamic color - SyncBanner colorido removido -> SyncStatusRow compacto com chips M3 acionáveis (sync agora / abrir config / erro em errorContainer) - Lista: busca por título/conteúdo/tag (filterNotes testado), pull-to-refresh, animateItem, contentType, estado vazio com CTA - Ícones no lugar de emoji (pasta, presença, exclusão, aviso) - Editor: chip de presença acessível; imports limpos - Teste unitário de busca (NoteSearchTest) - versionCode 5 / versionName 0.3.0
This commit is contained in:
1 parent
8561ae2f8b
commit
a8c8a5a998
7 files changed
+296
-127
No files matched your search
@@ -3,30 +3,16 @@ package com.labexposed.notas
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.lifecycle.compose.LifecycleEventEffect
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.labexposed.notas.domain.SyncPhase
|
||||
import com.labexposed.notas.domain.SyncUiState
|
||||
import com.labexposed.notas.ui.EditorScreen
|
||||
import com.labexposed.notas.ui.NotesListScreen
|
||||
import com.labexposed.notas.ui.NotasViewModel
|
||||
@@ -36,6 +22,7 @@ import com.labexposed.notas.ui.theme.NotasTheme
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
NotasTheme { NotasApp_() }
|
||||
}
|
||||
@@ -68,36 +55,3 @@ private fun NotasApp_(vm: NotasViewModel = viewModel()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Banner de status: Offline · Sincronizando · Sincronizado · Erro · pendências. */
|
||||
@Composable
|
||||
fun SyncBanner(state: SyncUiState, modifier: Modifier = Modifier) {
|
||||
val (bg, fg, label) = when (state.phase) {
|
||||
SyncPhase.NotConfigured -> Quinteto(Color(0xFF9A9A9A), Color.White, "Configure o servidor ⚙")
|
||||
SyncPhase.Offline -> Quinteto(Color(0xFF6F6F6F), Color.White, "Offline — ${state.pendingCount} alteração(ões) na fila")
|
||||
SyncPhase.Syncing -> Quinteto(Color(0xFF3584E4), Color.White, "Sincronizando…")
|
||||
SyncPhase.Synced -> {
|
||||
val pend = if (state.pendingCount > 0) " · ${state.pendingCount} pendente(s)" else ""
|
||||
val conf = if (state.conflicts > 0) " · ⚠ ${state.conflicts} conflito(s)" else ""
|
||||
val hora = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT)
|
||||
.format(java.util.Date(state.lastSyncAt))
|
||||
Quinteto(Color(0xFF2EC27E), Color(0xFF073042), "Sincronizado às $hora$pend$conf")
|
||||
}
|
||||
SyncPhase.Error -> Quinteto(Color(0xFFE01B24), Color.White, "Erro: ${state.message}")
|
||||
SyncPhase.Unauthorized -> Quinteto(Color(0xFFE01B24), Color.White, "Token inválido — revise em Servidor")
|
||||
}
|
||||
Surface(color = bg, shape = RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp)) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(label, color = fg, style = MaterialTheme.typography.labelLarge)
|
||||
if (state.dirtyCount > 0 && state.phase == SyncPhase.Synced) {
|
||||
Text("● ${state.dirtyCount} não enviado(s)", color = fg, style = MaterialTheme.typography.labelMedium)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class Quinteto(val bg: Color, val fg: Color, val label: String)
|
||||
Reference in new issue
Block a user