From b264f87b14af0de38dd35e0481d06a95bfb9ba92 Mon Sep 17 00:00:00 2001 From: Raihan Ariq <202310715297@mhs.ubharajaya.ac.id> Date: Thu, 18 Dec 2025 15:09:39 +0700 Subject: [PATCH] Memperbaiki Bug tidak tersimpan --- Readme.md | 3 +- .../java/com/example/notesai/MainActivity.kt | 161 +++++++++--------- 2 files changed, 85 insertions(+), 79 deletions(-) diff --git a/Readme.md b/Readme.md index d00af08..44a58c6 100644 --- a/Readme.md +++ b/Readme.md @@ -149,7 +149,8 @@ * Dark/Light theme toggle (ok) * AI Agent Catatan * Fungsi AI (Upload File) -* Markdown Parser +* Markdown Parser (ok) +* Sematkan Category --- diff --git a/app/src/main/java/com/example/notesai/MainActivity.kt b/app/src/main/java/com/example/notesai/MainActivity.kt index 1a2b0cc..5a1a9f3 100644 --- a/app/src/main/java/com/example/notesai/MainActivity.kt +++ b/app/src/main/java/com/example/notesai/MainActivity.kt @@ -6,21 +6,16 @@ import androidx.activity.compose.setContent import androidx.compose.animation.* import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import java.util.UUID import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import com.example.notesai.data.local.DataStoreManager @@ -37,55 +32,36 @@ import com.example.notesai.presentation.screens.starred.StarredNotesScreen import com.example.notesai.presentation.screens.trash.TrashScreen import com.example.notesai.data.model.Note import com.example.notesai.data.model.Category -import com.example.notesai.util.updateWhere -import kotlinx.coroutines.delay import com.example.notesai.util.AppColors - -// File: MainActivity.kt (Bagian Theme Setup) -// Ganti MaterialTheme di setContent dengan ini: - import com.example.notesai.util.Constants import kotlinx.coroutines.launch - class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme( colorScheme = darkColorScheme( - // Primary colors primary = AppColors.Primary, onPrimary = Color.White, primaryContainer = AppColors.PrimaryContainer, onPrimaryContainer = Color.White, - - // Secondary colors secondary = AppColors.Secondary, onSecondary = Color.White, secondaryContainer = AppColors.SecondaryVariant, onSecondaryContainer = Color.White, - - // Background colors background = AppColors.Background, onBackground = AppColors.OnBackground, - - // Surface colors surface = AppColors.Surface, onSurface = AppColors.OnSurface, surfaceVariant = AppColors.SurfaceVariant, onSurfaceVariant = AppColors.OnSurfaceVariant, - - // Error colors error = AppColors.Error, onError = Color.White, - - // Other outline = AppColors.Border, outlineVariant = AppColors.Divider ), typography = Typography( - // Improve typography for better readability displayLarge = MaterialTheme.typography.displayLarge.copy( fontWeight = FontWeight.Bold ), @@ -120,6 +96,7 @@ fun NotesApp() { val context = LocalContext.current val dataStoreManager = remember { DataStoreManager(context) } val scope = rememberCoroutineScope() + val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current var categories by remember { mutableStateOf(listOf()) } var notes by remember { mutableStateOf(listOf()) } @@ -133,66 +110,85 @@ fun NotesApp() { var showSearch by remember { mutableStateOf(false) } var showFullScreenNote by remember { mutableStateOf(false) } var fullScreenNote by remember { mutableStateOf(null) } - - // Theme state var isDarkTheme by remember { mutableStateOf(true) } - // Load theme preference - SET THEME HERE + // Guard flags to prevent race conditions + var isDataLoaded by remember { mutableStateOf(false) } + + // Load theme preference LaunchedEffect(Unit) { dataStoreManager.themeFlow.collect { theme -> isDarkTheme = theme == "dark" - AppColors.setTheme(isDarkTheme) // SET GLOBAL THEME + AppColors.setTheme(isDarkTheme) } } - // Load data dari DataStore + // Load categories ONCE LaunchedEffect(Unit) { - try { - dataStoreManager.categoriesFlow.collect { loadedCategories -> + dataStoreManager.categoriesFlow.collect { loadedCategories -> + if (!isDataLoaded) { + android.util.Log.d("NotesApp", "Loading ${loadedCategories.size} categories") categories = loadedCategories } - } catch (e: Exception) { - e.printStackTrace() } } + // Load notes ONCE LaunchedEffect(Unit) { - try { - dataStoreManager.notesFlow.collect { loadedNotes -> + dataStoreManager.notesFlow.collect { loadedNotes -> + if (!isDataLoaded) { + android.util.Log.d("NotesApp", "Loading ${loadedNotes.size} notes") notes = loadedNotes + isDataLoaded = true // Mark as loaded } - } catch (e: Exception) { - e.printStackTrace() } } - // Simpan categories dengan debounce - LaunchedEffect(categories.size) { - if (categories.isNotEmpty()) { - delay(500) - try { + // Save categories when changed + LaunchedEffect(categories) { + if (isDataLoaded && categories.isNotEmpty()) { + android.util.Log.d("NotesApp", "Saving ${categories.size} categories") + scope.launch { dataStoreManager.saveCategories(categories) - } catch (e: Exception) { - e.printStackTrace() } } } - // Simpan notes dengan debounce - LaunchedEffect(notes.size) { - if (notes.isNotEmpty()) { - delay(500) - try { + // Save notes when changed + LaunchedEffect(notes) { + if (isDataLoaded && notes.isNotEmpty()) { + android.util.Log.d("NotesApp", "Saving ${notes.size} notes") + scope.launch { dataStoreManager.saveNotes(notes) - } catch (e: Exception) { - e.printStackTrace() } } } - // Provide theme colors + // Save on lifecycle events + DisposableEffect(lifecycleOwner) { + val observer = androidx.lifecycle.LifecycleEventObserver { _, event -> + if (event == androidx.lifecycle.Lifecycle.Event.ON_PAUSE || + event == androidx.lifecycle.Lifecycle.Event.ON_STOP) { + android.util.Log.d("NotesApp", "Lifecycle ${event.name}: Saving data") + scope.launch { + if (categories.isNotEmpty()) { + dataStoreManager.saveCategories(categories) + } + if (notes.isNotEmpty()) { + dataStoreManager.saveNotes(notes) + } + } + } + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } + Box(modifier = Modifier.fillMaxSize()) { Scaffold( + containerColor = AppColors.Background, topBar = { if (!showFullScreenNote && currentScreen != "ai") { ModernTopBar( @@ -222,7 +218,12 @@ fun NotesApp() { floatingActionButton = { AnimatedVisibility( visible = currentScreen == "main" && !showFullScreenNote, - enter = scaleIn() + fadeIn(), + enter = scaleIn( + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessLow + ) + ) + fadeIn(), exit = scaleOut() + fadeOut() ) { FloatingActionButton( @@ -235,9 +236,18 @@ fun NotesApp() { } }, containerColor = AppColors.Primary, - contentColor = Color.White + contentColor = Color.White, + elevation = FloatingActionButtonDefaults.elevation( + defaultElevation = 8.dp, + pressedElevation = 12.dp + ), + modifier = Modifier.size(64.dp) ) { - Icon(Icons.Default.Add, contentDescription = "Add") + Icon( + Icons.Default.Add, + contentDescription = if (selectedCategory != null) "Tambah Catatan" else "Tambah Kategori", + modifier = Modifier.size(28.dp) + ) } } }, @@ -252,8 +262,7 @@ fun NotesApp() { onAIClick = { currentScreen = "ai" } ) } - }, - containerColor = AppColors.Background // SET BACKGROUND HERE + } ) { padding -> Box(modifier = Modifier.fillMaxSize()) { if (showFullScreenNote && fullScreenNote != null) { @@ -306,7 +315,7 @@ fun NotesApp() { ) { when (currentScreen) { "main" -> MainScreen( - categories = categories.filter { !it.isDeleted }, // TAMBAHKAN FILTER INI + categories = categories.filter { !it.isDeleted }, notes = notes, selectedCategory = selectedCategory, searchQuery = searchQuery, @@ -322,12 +331,10 @@ fun NotesApp() { } }, onCategoryDelete = { category -> - // UBAH: Jangan filter, tapi set isDeleted = true categories = categories.map { if (it.id == category.id) it.copy(isDeleted = true) else it } - // Note di dalam kategori juga di-delete notes = notes.map { if (it.categoryId == category.id) it.copy(isDeleted = true) else it @@ -352,7 +359,7 @@ fun NotesApp() { "trash" -> TrashScreen( notes = notes.filter { it.isDeleted }, - categories = categories, // Pass semua categories (sudah ada yang isDeleted) + categories = categories, onRestoreNote = { note -> notes = notes.map { if (it.id == note.id) it.copy(isDeleted = false, isArchived = false) @@ -363,28 +370,24 @@ fun NotesApp() { notes = notes.filter { it.id != note.id } }, onRestoreCategory = { category -> - // Restore kategori categories = categories.map { if (it.id == category.id) it.copy(isDeleted = false) else it } - // Restore semua note di dalam kategori notes = notes.map { if (it.categoryId == category.id) it.copy(isDeleted = false, isArchived = false) else it } }, onDeleteCategoryPermanent = { category -> - // Hapus kategori permanen categories = categories.filter { it.id != category.id } - // Hapus semua note di dalam kategori permanen notes = notes.filter { it.categoryId != category.id } } ) "starred" -> StarredNotesScreen( notes = notes, - categories = categories.filter { !it.isDeleted }, // FILTER + categories = categories.filter { !it.isDeleted }, onNoteClick = { note -> fullScreenNote = note showFullScreenNote = true @@ -401,7 +404,7 @@ fun NotesApp() { "archive" -> ArchiveScreen( notes = notes.filter { it.isArchived && !it.isDeleted }, - categories = categories.filter { !it.isDeleted }, // FILTER + categories = categories.filter { !it.isDeleted }, onRestore = { note -> notes = notes.map { if (it.id == note.id) it.copy(isArchived = false) @@ -417,7 +420,7 @@ fun NotesApp() { ) "ai" -> AIHelperScreen( - categories = categories.filter { !it.isDeleted }, // FILTER + categories = categories.filter { !it.isDeleted }, notes = notes.filter { !it.isDeleted } ) } @@ -462,13 +465,11 @@ fun NotesApp() { categoryId = selectedCategory!!.id, title = title, description = description, - content = "" // Content kosong, akan diisi di EditableFullScreenNoteView + content = "" ) } showNoteDialog = false editingNote = null - showNoteDialog = false - editingNote = null }, onDelete = if (editingNote != null) { { @@ -485,11 +486,16 @@ fun NotesApp() { } } - // Drawer with theme toggle + // Drawer with Animation AnimatedVisibility( visible = drawerState, - enter = fadeIn() + slideInHorizontally { -it }, - exit = fadeOut() + slideOutHorizontally { -it } + enter = fadeIn() + slideInHorizontally( + initialOffsetX = { -it } + ), + exit = fadeOut() + slideOutHorizontally( + targetOffsetX = { -it } + ), + modifier = Modifier.zIndex(100f) ) { DrawerMenu( currentScreen = currentScreen, @@ -504,7 +510,7 @@ fun NotesApp() { }, onThemeToggle = { isDarkTheme = !isDarkTheme - AppColors.setTheme(isDarkTheme) // UPDATE THEME + AppColors.setTheme(isDarkTheme) scope.launch { dataStoreManager.saveTheme(if (isDarkTheme) "dark" else "light") } @@ -512,5 +518,4 @@ fun NotesApp() { ) } } -} - +} \ No newline at end of file