diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..81cf63d
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+RoomJetpackCompose
\ No newline at end of file
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 66b01d7..b86273d 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,8 +1,6 @@
-
-
-
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml
new file mode 100644
index 0000000..4ea72a9
--- /dev/null
+++ b/.idea/copilot.data.migration.agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml
new file mode 100644
index 0000000..7ef04e2
--- /dev/null
+++ b/.idea/copilot.data.migration.ask.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml
new file mode 100644
index 0000000..1f2ea11
--- /dev/null
+++ b/.idea/copilot.data.migration.ask2agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml
new file mode 100644
index 0000000..8648f94
--- /dev/null
+++ b/.idea/copilot.data.migration.edit.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index 30cdd63..0bdd9c5 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,21 +4,14 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 910c7a2..c4a282e 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -52,6 +52,9 @@
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 4226042..0fc2901 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,3 @@
-
+
diff --git a/README.md b/README.md
index 69aad03..98cbea4 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ It's an app built with [Kotlin][1] that shows how to perform CRUD operations in
Below you can find the docs for each tehnology that is used in this app:
## Firebase Products:
-* [Firebase Authentication][2]
+* [Firebase Authentication](https://firebase.google.com/docs/auth)
## Android Architecture Components:
* [ViewModel][5]
@@ -56,6 +56,7 @@ The code in this project is licensed under the Apache License 2.0.
* This is not an officially supported Google product.
[1]: https://kotlinlang.org/
+[2]: https://firebase.google.com/docs/auth
[3]: https://developer.android.com/topic/libraries/architecture
[5]: https://developer.android.com/topic/libraries/architecture/viewmodel
[6]: https://developer.android.com/training/dependency-injection/hilt-android
@@ -65,3 +66,104 @@ The code in this project is licensed under the Apache License 2.0.
[10]: https://medium.com/firebase-tips-tricks/how-to-read-data-from-room-using-kotlin-flow-in-jetpack-compose-7a720dec35f5
[12]: https://developer.android.com/guide/navigation
[13]: https://developer.android.com/training/data-storage/room
+
+---
+
+## Guía rápida: uso de `BaseScreen`
+
+`BaseScreen` centraliza el layout de pantallas en Compose y expone slots para que agregues tus propios componentes sin repetir estructura.
+
+- Props principales:
+ - `title: String?` – título simple para el TopBar (si no pasás `topBar`).
+ - `topBar: @Composable (() -> Unit)?` – TopBar custom (usa `AppTopBar` o el que quieras).
+ - `header: @Composable (() -> Unit)?` – Cabecera opcional sobre el contenido (chips, filtros, etc.).
+ - `content: @Composable (PaddingValues) -> Unit` – Contenido principal. Recibe el padding superior ya calculado.
+ - `bottomBar: @Composable (() -> Unit)?` – Barra inferior (por ejemplo `BottomNavigationBar`).
+ - `fab: @Composable (() -> Unit)?` – Floating Action Button opcional.
+ - `centerContent: Boolean` – Centra vertical y horizontalmente el contenido cuando es `true`.
+
+- Estilos y comportamiento incorporados:
+ - Respeta el área segura de status bar para evitar solapes con la cámara/recortes.
+ - Pinta el fondo con `MaterialTheme.colorScheme.surface` dentro de un contenedor con bordes superiores curvos.
+ - Mantiene paddings y radios desde `ui.theme.Dimens`.
+
+- Ejemplo mínimo:
+
+```kotlin
+@Composable
+fun ExampleScreen() {
+ BaseScreen(
+ title = "Example",
+ bottomBar = { BottomNavigationBar() },
+ ) { _ ->
+ // Tu contenido
+ Column(Modifier.fillMaxSize().padding(Dimens.paddingLarge)) {
+ Text("Hola BaseScreen")
+ }
+ }
+}
+```
+
+- Ejemplo con TopBar custom + Header + FAB:
+
+```kotlin
+@Composable
+fun WithHeaderAndFab() {
+ BaseScreen(
+ topBar = { AppTopBar(title = "Dashboard") },
+ header = {
+ Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ FilterChip(label = "All")
+ FilterChip(label = "Income")
+ FilterChip(label = "Expense")
+ }
+ },
+ fab = { AddActionFab(onClick = { /* do something */ }) },
+ bottomBar = { BottomNavigationBar() }
+ ) { innerPadding ->
+ LazyColumn(contentPadding = innerPadding) {
+ items(100) { index -> Text("Item #$index") }
+ }
+ }
+}
+```
+
+### Ejemplo centrado (empty state)
+
+```kotlin
+@Composable
+fun EmptyState() {
+ BaseScreen(title = "No Data", centerContent = true) { _ ->
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Icon(Icons.Default.Info, contentDescription = null)
+ Spacer(Modifier.height(8.dp))
+ Text("Todavía no hay elementos")
+ }
+ }
+}
+```
+
+### Ejemplo con lista seccionada (como Notification)
+
+```kotlin
+@Composable
+fun SectionList(sections: List) {
+ BaseScreen(title = "Sectioned") { _ ->
+ LazyColumn(
+ modifier = Modifier.fillMaxSize().padding(Dimens.paddingMedium),
+ verticalArrangement = Arrangement.spacedBy(Dimens.paddingMedium),
+ contentPadding = PaddingValues(bottom = Dimens.paddingLarge)
+ ) {
+ sections.forEach { section ->
+ item(section.title) { Text(section.title) }
+ items(section.items) { item -> ItemRow(item) }
+ }
+ }
+ }
+}
+```
+
+- Recomendaciones:
+ - Cuando uses `LazyColumn`, pasá `contentPadding = PaddingValues(bottom = Dimens.paddingLarge)` si tenés navbar para evitar que el último ítem quede tapado.
+ - Si ves que el título se corta por la cámara/notch, el `BaseScreen` ya aplica `statusBarsPadding()` al TopBar.
+ - Para pantallas centradas (empty states, loaders): `centerContent = true`.
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 110a8c3..7a54de1 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -46,11 +46,21 @@ dependencies {
implementation(libs.hilt.navigation.compose)
//Hilt
implementation(libs.hilt)
+ implementation(libs.material3)
+ implementation(libs.foundation)
+ implementation(libs.ui)
+ implementation(libs.lifecycle.viewmodel.ktx)
+ implementation(libs.runtime)
ksp(libs.hilt.compiler)
//Room
implementation(libs.room.runtime)
implementation(libs.room.ktx)
ksp(libs.room.compiler)
+ //Coil
+ implementation(libs.coil.compose)
+ // Retrofit
+ implementation(libs.retrofit)
+ implementation(libs.converter.gson)
//Serialization
implementation(libs.serialization)
//Tests
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/HiltTestRunner.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/HiltTestRunner.kt
deleted file mode 100644
index 18f5ebc..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/HiltTestRunner.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package ro.alexmamo.roomjetpackcompose
-
-import android.app.Application
-import android.content.Context
-import androidx.test.runner.AndroidJUnitRunner
-import dagger.hilt.android.testing.HiltTestApplication
-
-class HiltTestRunner : AndroidJUnitRunner() {
- override fun newApplication(
- cl: ClassLoader?,
- className: String?,
- context: Context?
- ): Application {
- return super.newApplication(cl, HiltTestApplication::class.java.name, context)
- }
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/data/dao/BookDaoTest.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/data/dao/BookDaoTest.kt
deleted file mode 100644
index ed8202e..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/data/dao/BookDaoTest.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.data.dao
-
-import android.content.Context
-import androidx.test.core.app.ApplicationProvider
-import com.google.common.truth.Truth
-import dagger.hilt.android.testing.HiltAndroidRule
-import dagger.hilt.android.testing.HiltAndroidTest
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import ro.alexmamo.roomjetpackcompose.data.network.BookDb
-import ro.alexmamo.roomjetpackcompose.utils.getBookTest
-import ro.alexmamo.roomjetpackcompose.utils.getUpdatedBookTest
-import java.io.IOException
-import javax.inject.Inject
-
-@HiltAndroidTest
-class BookDaoTest() {
- @get:Rule
- var hiltRule = HiltAndroidRule(this)
-
- @Inject
- lateinit var bookDao: BookDao
- @Inject
- lateinit var bookDb: BookDb
-
- val context = ApplicationProvider.getApplicationContext()
- private val bookTest = getBookTest(context)
- private val updatedBookTest = getUpdatedBookTest(context)
-
- @Before
- fun init() {
- hiltRule.inject()
- }
-
- @Test
- @Throws(Exception::class)
- fun testInsertAndGetBookById() = runTest {
- bookDao.insertBook(bookTest)
- val book = bookDao.getBookById(bookTest.id)
- Truth.assertThat(book).isEqualTo(bookTest)
- }
-
- @Test
- @Throws(Exception::class)
- fun testInsertAndCheckIfBookExistsInBookList() = runTest {
- bookDao.insertBook(bookTest)
- val bookList = bookDao.getBookList().first()
- Truth.assertThat(bookTest).isIn(bookList)
- }
-
- @Test
- @Throws(Exception::class)
- fun testInsertAndCheckTheSizeOfBookList() = runTest {
- bookDao.insertBook(bookTest)
- val bookList = bookDao.getBookList().first()
- Truth.assertThat(bookList.size).isEqualTo(1)
- }
-
- @Test
- @Throws(Exception::class)
- fun testUpdateAndGetBookById() = runTest {
- bookDao.insertBook(bookTest)
- bookDao.updateBook(updatedBookTest)
- val book = bookDao.getBookById(bookTest.id)
- Truth.assertThat(book.title).isEqualTo(updatedBookTest.title)
- }
-
- @Test
- @Throws(Exception::class)
- fun testInsertAndDeleteAndCheckTheSizeOfBookList() = runTest {
- bookDao.insertBook(bookTest)
- bookDao.deleteBook(bookTest)
- val bookList = bookDao.getBookList().first()
- Truth.assertThat(bookList).isEmpty()
- }
-
- @After
- @Throws(IOException::class)
- fun closeDb() {
- bookDb.close()
- }
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/data/repository/FakeBookRepositoryImpl.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/data/repository/FakeBookRepositoryImpl.kt
deleted file mode 100644
index 4d3f459..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/data/repository/FakeBookRepositoryImpl.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.data.repository
-
-import kotlinx.coroutines.flow.flow
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-import ro.alexmamo.roomjetpackcompose.domain.repository.BookRepository
-
-class FakeBookRepositoryImpl() : BookRepository {
- private val bookList = mutableListOf()
-
- override fun getBookList() = flow {
- emit(bookList)
- }
-
- override suspend fun getBookById(id: Int) = bookList.find { book ->
- book.id == id
- }
-
- override suspend fun insertBook(book: Book) {
- bookList.add(book)
- }
-
- override suspend fun updateBook(book: Book) {
- val indexOfFirstBook = bookList.indexOfFirst { firstBook ->
- firstBook.id == book.id
- }
- if (indexOfFirstBook != -1) {
- bookList[indexOfFirstBook] = book
- }
- }
-
- override suspend fun deleteBook(book: Book) {
- bookList.remove(book)
- }
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/di/AppModuleTest.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/di/AppModuleTest.kt
deleted file mode 100644
index fd52429..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/di/AppModuleTest.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.di
-
-import android.content.Context
-import androidx.room.Room
-import androidx.test.core.app.ApplicationProvider
-import dagger.Module
-import dagger.Provides
-import dagger.hilt.components.SingletonComponent
-import dagger.hilt.testing.TestInstallIn
-import ro.alexmamo.roomjetpackcompose.data.network.BookDb
-import ro.alexmamo.roomjetpackcompose.domain.repository.BookRepository
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.BookListViewModel
-import ro.alexmamo.roomjetpackcompose.data.repository.FakeBookRepositoryImpl
-
-@Module
-@TestInstallIn(
- components = [SingletonComponent::class],
- replaces = [AppModule::class]
-)
-class AppModuleTest {
- @Provides
- fun provideBookDb() = Room.inMemoryDatabaseBuilder(
- ApplicationProvider.getApplicationContext(),
- BookDb::class.java
- ).build()
-
- @Provides
- fun provideBookDao(
- bookDb: BookDb
- ) = bookDb.bookDao
-
- @Provides
- fun provideBookRepository(): BookRepository = FakeBookRepositoryImpl()
-
- @Provides
- fun provideBookListViewModel(
- repo: BookRepository
- ) = BookListViewModel(repo)
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/domain/BookRepositoryTest.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/domain/BookRepositoryTest.kt
deleted file mode 100644
index fd7511c..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/domain/BookRepositoryTest.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.domain
-
-import android.content.Context
-import androidx.activity.ComponentActivity
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.test.core.app.ApplicationProvider
-import com.google.common.truth.Truth
-import dagger.hilt.android.testing.HiltAndroidRule
-import dagger.hilt.android.testing.HiltAndroidTest
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import ro.alexmamo.roomjetpackcompose.domain.repository.BookRepository
-import ro.alexmamo.roomjetpackcompose.utils.getBookTest
-import ro.alexmamo.roomjetpackcompose.utils.getUpdatedBookTest
-import javax.inject.Inject
-
-@HiltAndroidTest
-class BookRepositoryTest {
- @get:Rule(order = 0)
- var hiltRule = HiltAndroidRule(this)
-
- @get:Rule(order = 1)
- val composeTestRule = createAndroidComposeRule()
-
- @Inject
- lateinit var fakeRepo: BookRepository
-
- val context = ApplicationProvider.getApplicationContext()
- private val bookTest = getBookTest(context)
- private val updatedBookTest = getUpdatedBookTest(context)
-
- @Before
- fun init() {
- hiltRule.inject()
- }
-
- @Test
- fun testInsertAndGetBookById() = runBlocking {
- fakeRepo.insertBook(bookTest)
- val book = fakeRepo.getBookById(bookTest.id)
- Truth.assertThat(book).isEqualTo(bookTest)
- }
-
- @Test
- fun testInsertAndCheckIfBookExistsInBookList() = runBlocking {
- fakeRepo.insertBook(bookTest)
- val bookList = fakeRepo.getBookList().first()
- Truth.assertThat(bookTest).isIn(bookList)
- }
-
- @Test
- fun testInsertAndCheckTheSizeOfBookList() = runTest {
- fakeRepo.insertBook(bookTest)
- val bookList = fakeRepo.getBookList().first()
- Truth.assertThat(bookList.size).isEqualTo(1)
- }
-
- @Test
- fun testUpdateAndGetBookById() = runTest {
- fakeRepo.insertBook(bookTest)
- fakeRepo.updateBook(updatedBookTest)
- val book = fakeRepo.getBookById(bookTest.id)
- Truth.assertThat(book?.title).isEqualTo(updatedBookTest.title)
- }
-
- @Test
- @Throws(Exception::class)
- fun testInsertAndDeleteAndCheckTheSizeOfBookList() = runTest {
- fakeRepo.insertBook(bookTest)
- fakeRepo.deleteBook(bookTest)
- val bookList = fakeRepo.getBookList().first()
- Truth.assertThat(bookList).isEmpty()
- }
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/navigation/BookNavigationTest.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/navigation/BookNavigationTest.kt
deleted file mode 100644
index f8b7f60..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/navigation/BookNavigationTest.kt
+++ /dev/null
@@ -1,130 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.navigation
-
-import android.content.Context
-import androidx.activity.compose.setContent
-import androidx.annotation.StringRes
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onNodeWithContentDescription
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.test.performClick
-import androidx.navigation.compose.ComposeNavigator
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import androidx.navigation.testing.TestNavHostController
-import androidx.navigation.toRoute
-import androidx.test.core.app.ApplicationProvider
-import com.google.common.truth.Truth
-import dagger.hilt.android.testing.HiltAndroidRule
-import dagger.hilt.android.testing.HiltAndroidTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import ro.alexmamo.roomjetpackcompose.R
-import ro.alexmamo.roomjetpackcompose.domain.model.toBookDetails
-import ro.alexmamo.roomjetpackcompose.presentation.MainActivity
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.BookListScreen
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.BookListViewModel
-import ro.alexmamo.roomjetpackcompose.presentation.book_details.BookDetailsScreen
-import ro.alexmamo.roomjetpackcompose.utils.getBookTest
-import javax.inject.Inject
-
-@HiltAndroidTest
-class BookNavigationTest {
- @get:Rule(order = 0)
- var hiltRule = HiltAndroidRule(this)
-
- @get:Rule(order = 1)
- val composeTestRule = createAndroidComposeRule()
-
- @Inject
- lateinit var fakeViewModel: BookListViewModel
-
- val context = ApplicationProvider.getApplicationContext()
- private val bookTest = getBookTest(context)
-
- lateinit var navController: TestNavHostController
-
- @Before
- fun init() {
- hiltRule.inject()
- }
-
- @Before
- fun setupNavHost() {
- composeTestRule.activity.setContent {
- navController = TestNavHostController(LocalContext.current)
- navController.navigatorProvider.addNavigator(ComposeNavigator())
- fakeViewModel.insertBook(bookTest)
-
- NavHost(
- navController = navController,
- startDestination = BookListScreen
- ) {
- composable {
- BookListScreen(
- viewModel = fakeViewModel,
- navigateToBookDetailsScreen = { book ->
- val bookDetails = book.toBookDetails()
- navController.navigate(bookDetails)
- }
- )
- }
- composable { entry ->
- val bookDetails = entry.toRoute()
- val book = bookDetails.toBook()
- BookDetailsScreen(
- book = book,
- navigateBack = navController::navigateUp
- )
- }
- }
- }
- composeTestRule.waitForIdle()
- }
-
- @Test
- fun testStartDestinationByRoute() {
- val startDestination = navController.graph.startDestinationRoute
- val currentDestination = navController.currentBackStackEntry?.destination?.route
- Truth.assertThat(currentDestination).isEqualTo(startDestination)
- }
-
- @Test
- fun testStartDestinationByText() {
- composeTestRule
- .onNodeWithText(getString(R.string.book_list_screen_title))
- .assertIsDisplayed()
- }
-
- @Test
- fun testNavigationFromBookListScreenToBookDetailsScreen() {
- composeTestRule.apply {
- onNodeWithText(getString(R.string.book_list_screen_title))
- .assertIsDisplayed()
- onNodeWithText(bookTest.title)
- .performClick()
- onNodeWithText(getString(R.string.book_details_screen_title))
- .assertIsDisplayed()
- }
- }
-
- @Test
- fun testNavigationFromBookListScreenToBookDetailsScreenAndBack() {
- composeTestRule.apply {
- onNodeWithText(getString(R.string.book_list_screen_title))
- .assertIsDisplayed()
- onNodeWithText(bookTest.title)
- .performClick()
- onNodeWithText(getString(R.string.book_details_screen_title))
- .assertIsDisplayed()
- onNodeWithContentDescription(getString(R.string.navigate_back))
- .performClick()
- onNodeWithText(getString(R.string.book_list_screen_title))
- .assertIsDisplayed()
- }
- }
-
- private fun getString(@StringRes resId: Int) = composeTestRule.activity.getString(resId)
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListScreenTest.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListScreenTest.kt
deleted file mode 100644
index b07168c..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListScreenTest.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list
-
-import android.content.Context
-import androidx.annotation.StringRes
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onNodeWithContentDescription
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.test.performClick
-import androidx.compose.ui.test.performTextInput
-import androidx.test.core.app.ApplicationProvider
-import dagger.hilt.android.testing.HiltAndroidRule
-import dagger.hilt.android.testing.HiltAndroidTest
-import org.junit.Rule
-import org.junit.Test
-import ro.alexmamo.roomjetpackcompose.R
-import ro.alexmamo.roomjetpackcompose.presentation.MainActivity
-import ro.alexmamo.roomjetpackcompose.utils.getBookTest
-
-@HiltAndroidTest
-class BookListScreenTest {
- @get:Rule(order = 0)
- var hiltRule = HiltAndroidRule(this)
-
- @get:Rule(order = 1)
- val composeTestRule = createAndroidComposeRule()
-
- val context = ApplicationProvider.getApplicationContext()
- private val bookTest = getBookTest(context)
-
- @Test
- fun testBookClickAndNavigationToBookDetailsScreenAndBackToBookListScreen() {
- composeTestRule.apply {
- onNodeWithContentDescription(getString(R.string.open_insert_book_dialog))
- .performClick()
- onNodeWithText(getString(R.string.book_title))
- .performTextInput(bookTest.title)
- onNodeWithText(getString(R.string.book_author))
- .performTextInput(bookTest.author)
- onNodeWithText(getString(R.string.insert_button))
- .performClick()
- onNodeWithText(bookTest.title)
- .performClick()
- onNodeWithText(getString(R.string.book_details_screen_title))
- .assertIsDisplayed()
- onNodeWithText(bookTest.title)
- .assertIsDisplayed()
- onNodeWithText("by ${bookTest.author}")
- .assertIsDisplayed()
- onNodeWithContentDescription(getString(R.string.navigate_back))
- .performClick()
- onNodeWithText(getString(R.string.book_list_screen_title))
- .assertIsDisplayed()
- }
- }
-
- private fun getString(@StringRes resId: Int) = composeTestRule.activity.getString(resId)
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListContentTest.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListContentTest.kt
deleted file mode 100644
index c03501b..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListContentTest.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
-
-import androidx.annotation.StringRes
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import dagger.hilt.android.testing.HiltAndroidRule
-import dagger.hilt.android.testing.HiltAndroidTest
-import org.junit.Rule
-import org.junit.Test
-import ro.alexmamo.roomjetpackcompose.R
-import ro.alexmamo.roomjetpackcompose.presentation.MainActivity
-
-@HiltAndroidTest
-class BookListContentTest {
- @get:Rule(order = 0)
- var hiltRule = HiltAndroidRule(this)
-
- @get:Rule(order = 1)
- val composeTestRule = createAndroidComposeRule()
-
- @Test
- fun testBookListContent() {
- composeTestRule.apply {
- onNodeWithText(getString(R.string.empty_book_list_text))
- .assertIsDisplayed()
- }
- }
-
- private fun getString(@StringRes resId: Int) = composeTestRule.activity.getString(resId)
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListTopBarTest.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListTopBarTest.kt
deleted file mode 100644
index a7a8659..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListTopBarTest.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
-
-import androidx.annotation.StringRes
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import dagger.hilt.android.testing.HiltAndroidRule
-import dagger.hilt.android.testing.HiltAndroidTest
-import org.junit.Rule
-import org.junit.Test
-import ro.alexmamo.roomjetpackcompose.R
-import ro.alexmamo.roomjetpackcompose.presentation.MainActivity
-
-@HiltAndroidTest
-class BookListTopBarTest {
- @get:Rule(order = 0)
- var hiltRule = HiltAndroidRule(this)
-
- @get:Rule(order = 1)
- val composeTestRule = createAndroidComposeRule()
-
- @Test
- fun testBookListTopBar() {
- composeTestRule.apply {
- onNodeWithText(getString(R.string.book_list_screen_title))
- .assertIsDisplayed()
- }
- }
-
- private fun getString(@StringRes resId: Int) = composeTestRule.activity.getString(resId)
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookAlertDialogTest.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookAlertDialogTest.kt
deleted file mode 100644
index 80059a6..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookAlertDialogTest.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
-
-import androidx.annotation.StringRes
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onNodeWithContentDescription
-import dagger.hilt.android.testing.HiltAndroidRule
-import dagger.hilt.android.testing.HiltAndroidTest
-import org.junit.Rule
-import org.junit.Test
-import ro.alexmamo.roomjetpackcompose.R
-import ro.alexmamo.roomjetpackcompose.presentation.MainActivity
-
-@HiltAndroidTest
-class InsertBookAlertDialogTest {
- @get:Rule(order = 0)
- var hiltRule = HiltAndroidRule(this)
-
- @get:Rule(order = 1)
- val composeTestRule = createAndroidComposeRule()
-
- @Test
- fun testInsertBookFloatingActionButton() {
- composeTestRule.apply {
- onNodeWithContentDescription(getString(R.string.open_insert_book_dialog))
- .assertIsDisplayed()
- }
- }
-
- private fun getString(@StringRes resId: Int) = composeTestRule.activity.getString(resId)
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/utils/Utils.kt b/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/utils/Utils.kt
deleted file mode 100644
index a85bbb9..0000000
--- a/app/src/androidTest/java/ro/alexmamo/roomjetpackcompose/utils/Utils.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.utils
-
-import android.content.Context
-import ro.alexmamo.roomjetpackcompose.R
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-
-fun getBookTest(context: Context): Book {
- return Book(
- id = 1,
- title = context.getString(R.string.title_test),
- author = context.getString(R.string.author_test)
- )
-}
-
-fun getUpdatedBookTest(context: Context): Book {
- return getBookTest(context).copy(
- title = context.getString(R.string.new_title_test)
- )
-}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2ea2c7c..57bd175 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
+
Unit,
- imageVector: ImageVector,
- resourceId: Int
+ modifier: Modifier = Modifier,
+ withCircle: Boolean = false,
+ circleSize: Dp = 30.dp,
+ circleColor: Color = MaterialTheme.colorScheme.surface,
+ iconSizeWhenCircle: Dp = 18.dp,
+ iconSizeDefault: Dp = 19.dp,
+ content: @Composable (Modifier) -> Unit
) {
IconButton(
- onClick = onActionIconButtonClick
+ onClick = onActionIconButtonClick,
+ modifier = modifier
) {
- Icon(
- imageVector = imageVector,
- contentDescription = stringResource(
- id = resourceId
- )
- )
+ val iconModifier = if (withCircle) Modifier.size(iconSizeWhenCircle) else Modifier.size(iconSizeDefault)
+ if (withCircle) {
+ Box(
+ modifier = Modifier
+ .size(circleSize)
+ .background(color = circleColor, shape = CircleShape),
+ contentAlignment = Alignment.Center
+ ) {
+ content(iconModifier)
+ }
+ } else {
+ content(iconModifier)
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/BottomNavigationBar.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/BottomNavigationBar.kt
new file mode 100644
index 0000000..a6e3890
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/BottomNavigationBar.kt
@@ -0,0 +1,58 @@
+package ro.alexmamo.roomjetpackcompose.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.runtime.*
+import androidx.compose.ui.unit.dp
+import ro.alexmamo.roomjetpackcompose.R
+
+@Composable
+fun BottomNavigationBar() {
+ var selectedIndex by remember { mutableStateOf(0) }
+
+ val icons = listOf(
+ R.drawable.home,
+ R.drawable.analysis,
+ R.drawable.transactions,
+ R.drawable.expenses,
+ R.drawable.profile
+ )
+
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(100.dp)
+ .background(
+ color = MaterialTheme.colorScheme.surface, // o background, según prefieras
+ shape = RoundedCornerShape(topStart = 70.dp, topEnd = 70.dp)
+ ),
+ contentAlignment = Alignment.Center
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterHorizontally),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ icons.forEachIndexed { index, icon ->
+ NavigationMenuButton(
+ iconRes = icon,
+ selected = index == selectedIndex,
+ onClick = { selectedIndex = index }
+ )
+ }
+ }
+ }
+}
+
+
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/ButtonCategoriesMenuPrimaryAndSecondary.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/ButtonCategoriesMenuPrimaryAndSecondary.kt
new file mode 100644
index 0000000..e9c7c70
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/ButtonCategoriesMenuPrimaryAndSecondary.kt
@@ -0,0 +1,43 @@
+package ro.alexmamo.roomjetpackcompose.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import ro.alexmamo.roomjetpackcompose.ui.theme.LightBlue
+import ro.alexmamo.roomjetpackcompose.ui.theme.OceanBlue
+
+@Composable
+fun ButtonCategoriesMenuPrimaryAndSecondary(
+ iconRes: Int,
+ selected: Boolean,
+ onClick: () -> Unit
+) {
+ val backgroundColor = if (selected) OceanBlue else LightBlue
+ val iconTint = Color.White
+
+ Box(
+ modifier = Modifier
+ .width(105.dp)
+ .height(98.dp)
+ .clip(RoundedCornerShape(26.dp))
+ .background(backgroundColor)
+ .clickable { onClick() },
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ painter = painterResource(id = iconRes),
+ contentDescription = null,
+ tint = iconTint,
+ modifier = Modifier.size(45.dp)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/ButtonsGreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/ButtonsGreen.kt
new file mode 100644
index 0000000..5c66063
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/ButtonsGreen.kt
@@ -0,0 +1,54 @@
+package ro.alexmamo.roomjetpackcompose.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import ro.alexmamo.roomjetpackcompose.ui.theme.CaribbeanGreen
+import ro.alexmamo.roomjetpackcompose.ui.theme.LightGreen
+import ro.alexmamo.roomjetpackcompose.ui.theme.Void
+
+enum class ButtonGreenType {
+ DARK, LIGHT
+}
+
+@Composable
+fun ButtonsGreen(
+ text: String,
+ type: ButtonGreenType,
+ onClick: () -> Unit
+) {
+ val backgroundColor = when (type) {
+ ButtonGreenType.DARK -> CaribbeanGreen
+ ButtonGreenType.LIGHT -> LightGreen
+ }
+
+ val textColor = Void
+
+ Box(
+ modifier = Modifier
+ .width(207.dp)
+ .height(45.dp)
+ .clip(RoundedCornerShape(28.dp))
+ .background(backgroundColor)
+ .clickable { onClick() },
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = text,
+ color = textColor,
+ fontSize = 18.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/EyePassButton.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/EyePassButton.kt
new file mode 100644
index 0000000..d4ffc41
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/EyePassButton.kt
@@ -0,0 +1,40 @@
+package ro.alexmamo.roomjetpackcompose.components
+
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import ro.alexmamo.roomjetpackcompose.R
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import ro.alexmamo.roomjetpackcompose.ui.theme.Void
+
+
+@Composable
+fun EyesPassButton(
+ passwordVisible: Boolean,
+ onToggleVisibility: () -> Unit
+) {
+ val iconRes = if (passwordVisible) R.drawable.eye_on else R.drawable.eye_off
+
+ Box(
+ modifier = Modifier
+ .size(48.dp)
+ .clip(RoundedCornerShape(50.dp))
+ .clickable { onToggleVisibility() },
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ painter = painterResource(id = iconRes),
+ contentDescription = if (passwordVisible) "Ocultar contraseña" else "Mostrar contraseña",
+ tint = Void,
+ modifier = Modifier.size(28.dp)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/LoadingIndicator.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/LoadingIndicator.kt
index 07be205..6cab307 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/LoadingIndicator.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/LoadingIndicator.kt
@@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier
@Composable
fun LoadingIndicator() {
+ // ESTO ESTA PENDIENTE AUN
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/MenuSwitchOnOff.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/MenuSwitchOnOff.kt
new file mode 100644
index 0000000..405df26
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/MenuSwitchOnOff.kt
@@ -0,0 +1,61 @@
+package ro.alexmamo.roomjetpackcompose.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Text
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import ro.alexmamo.roomjetpackcompose.ui.theme.CaribbeanGreen
+
+@Composable
+fun MenuSwitchOnOff(
+ options: List,
+ selectedIndex: Int?,
+ onOptionSelected: (Int) -> Unit
+) {
+ Row(
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .background(color = MaterialTheme.colorScheme.surface, RoundedCornerShape(22.dp))
+ .padding(horizontal = 8.dp, vertical = 6.dp)
+ ) {
+ options.forEachIndexed { index, label ->
+ val isSelected = index == selectedIndex
+ val backgroundColor = if (isSelected) CaribbeanGreen else
+ MaterialTheme.colorScheme.surface
+ val textColor = if (isSelected) MaterialTheme.colorScheme.onPrimary else
+ MaterialTheme.colorScheme.onSecondary
+
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .height(60.dp)
+ .clip(RoundedCornerShape(22.dp))
+ .background(backgroundColor)
+ .clickable { onOptionSelected(index) }
+ .padding(vertical = 10.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = label,
+ color = textColor,
+ fontSize = 15.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/NavigationMenu.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/NavigationMenu.kt
new file mode 100644
index 0000000..0bf7714
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/NavigationMenu.kt
@@ -0,0 +1,50 @@
+package ro.alexmamo.roomjetpackcompose.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import ro.alexmamo.roomjetpackcompose.ui.theme.CaribbeanGreen
+import ro.alexmamo.roomjetpackcompose.ui.theme.LightGreen
+import ro.alexmamo.roomjetpackcompose.ui.theme.Void
+
+@Composable
+fun NavigationMenuButton(
+ iconRes: Int,
+ selected: Boolean,
+ onClick: () -> Unit
+) {
+ val backgroundColor = if (selected) CaribbeanGreen else MaterialTheme.colorScheme.surface
+ val iconTint = if (selected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSecondary
+
+
+
+ Box(
+ modifier = Modifier
+ .height(53.dp)
+ .width(57.dp)
+ .clip(RoundedCornerShape(22.dp))
+ .background(backgroundColor)
+ .clickable { onClick() },
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ painter = painterResource(id = iconRes),
+ contentDescription = null,
+ tint = iconTint,
+ modifier = Modifier.size(28.dp)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/NotificationItem.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/NotificationItem.kt
new file mode 100644
index 0000000..cf6d73a
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/NotificationItem.kt
@@ -0,0 +1,90 @@
+package ro.alexmamo.roomjetpackcompose.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.text.font.FontWeight
+import ro.alexmamo.roomjetpackcompose.ui.theme.CaribbeanGreen
+import ro.alexmamo.roomjetpackcompose.ui.theme.Dimens
+import ro.alexmamo.roomjetpackcompose.ui.theme.Cyprus
+
+@Composable
+fun NotificationItem(
+ iconRes: Int,
+ titleRes: Int,
+ messageRes: Int,
+ timeRes: Int,
+ modifier: Modifier = Modifier
+) {
+ val title = stringResource(id = titleRes)
+ val message = stringResource(id = messageRes)
+ val time = stringResource(id = timeRes)
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(vertical = Dimens.paddingSmall),
+ // alineamos al top para que icon coincida con la línea del título
+ verticalAlignment = Alignment.Top
+ ) {
+ // usar surface como fondo del contenedor del icon para mejor contraste
+ Box(
+ modifier = Modifier
+ .size(44.dp)
+ .clip(RoundedCornerShape(12.dp))
+ .background(CaribbeanGreen),
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ painter = painterResource(id = iconRes),
+ contentDescription = null,
+ tint = Cyprus,
+ modifier = Modifier.size(24.dp)
+ )
+ }
+
+ Spacer(modifier = Modifier.width(Dimens.paddingMedium))
+
+ Column(
+ modifier = Modifier.weight(1f)
+ ) {
+ Text(
+ text = title,
+ style = MaterialTheme.typography.titleMedium.copy(fontSize = 15.sp, fontWeight = FontWeight.Medium),
+ color = MaterialTheme.colorScheme.onSurface,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = message,
+ style = MaterialTheme.typography.bodySmall.copy(fontSize = 14.sp),
+ color = MaterialTheme.colorScheme.onSecondary,
+ maxLines = 2,
+ overflow = TextOverflow.Ellipsis
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = time,
+ style = MaterialTheme.typography.labelSmall,
+ color = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.End
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ }
+ }
+}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/TopBar.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/TopBar.kt
new file mode 100644
index 0000000..2da35c2
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/components/TopBar.kt
@@ -0,0 +1,43 @@
+package ro.alexmamo.roomjetpackcompose.components
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import ro.alexmamo.roomjetpackcompose.ui.theme.Dimens
+
+@Composable
+fun AppTopBar(
+ modifier: Modifier = Modifier,
+ title: String? = null,
+ leftAction: (@Composable () -> Unit)? = null,
+ rightAction: (@Composable () -> Unit)? = null
+) {
+ Box(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(horizontal = Dimens.paddingLarge, vertical = Dimens.paddingSmall)
+ ) {
+ if (leftAction != null) {
+ Box(modifier = Modifier.align(Alignment.CenterStart)) {
+ leftAction()
+ }
+ }
+
+ if (title != null) {
+ Box(modifier = Modifier.align(Alignment.Center)) {
+ Text(text = title, style = MaterialTheme.typography.titleLarge)
+ }
+ }
+
+ if (rightAction != null) {
+ Box(modifier = Modifier.align(Alignment.CenterEnd)) {
+ rightAction()
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/core/Utils.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/core/Utils.kt
index 197e1f0..c1c80b2 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/core/Utils.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/core/Utils.kt
@@ -9,9 +9,11 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
const val TAG = "AppTag"
-const val BOOK_TABLE = "book_table"
-const val AUTHOR_FIELD = "author"
-const val TITLE_FIELD = "title"
+
+//const val TAG = "AppTag"
+const val TODO_TABLE = "todo_table"
+const val NAME_FIELD = "name"
+const val DESCRIPTION_FIELD = "description"
fun logMessage(
message: String
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/dao/BookDao.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/dao/BookDao.kt
deleted file mode 100644
index 38e20d2..0000000
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/dao/BookDao.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.data.dao
-
-import androidx.room.*
-import androidx.room.OnConflictStrategy.Companion.IGNORE
-import kotlinx.coroutines.flow.Flow
-import ro.alexmamo.roomjetpackcompose.core.BOOK_TABLE
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-
-@Dao
-interface BookDao {
- @Query("SELECT * FROM $BOOK_TABLE ORDER BY id ASC")
- fun getBookList(): Flow>
-
- @Query("SELECT * FROM $BOOK_TABLE WHERE id = :id")
- suspend fun getBookById(id: Int): Book
-
- @Insert(onConflict = IGNORE)
- suspend fun insertBook(book: Book)
-
- @Update
- suspend fun updateBook(book: Book)
-
- @Delete
- suspend fun deleteBook(book: Book)
-}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/dao/TodoDao.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/dao/TodoDao.kt
new file mode 100644
index 0000000..f9d5b7e
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/dao/TodoDao.kt
@@ -0,0 +1,29 @@
+package ro.alexmamo.roomjetpackcompose.data.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy.Companion.IGNORE
+import androidx.room.Query
+import androidx.room.Update
+import kotlinx.coroutines.flow.Flow
+import ro.alexmamo.roomjetpackcompose.core.TODO_TABLE
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+
+@Dao
+interface TodoDao {
+ @Query("SELECT * FROM $TODO_TABLE ORDER BY id ASC")
+ fun getTodoList(): Flow>
+
+ @Query("SELECT * FROM $TODO_TABLE WHERE id = :id")
+ suspend fun getTodoById(id: Int): Todo
+
+ @Insert(onConflict = IGNORE)
+ suspend fun insertTodo(todo: Todo)
+
+ @Update
+ suspend fun updateTodo(todo: Todo)
+
+ @Delete
+ suspend fun deleteTodo(todo: Todo)
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/network/BookDb.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/network/BookDb.kt
deleted file mode 100644
index 94c0738..0000000
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/network/BookDb.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.data.network
-
-import androidx.room.Database
-import androidx.room.RoomDatabase
-import ro.alexmamo.roomjetpackcompose.data.dao.BookDao
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-
-@Database(
- entities = [Book::class],
- version = 1,
- exportSchema = false
-)
-abstract class BookDb : RoomDatabase() {
- abstract val bookDao: BookDao
-}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/network/TodoDb.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/network/TodoDb.kt
new file mode 100644
index 0000000..3cba476
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/network/TodoDb.kt
@@ -0,0 +1,15 @@
+package ro.alexmamo.roomjetpackcompose.data.network
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import ro.alexmamo.roomjetpackcompose.data.dao.TodoDao
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+
+@Database(
+ entities = [Todo::class],
+ version = 3,
+ exportSchema = false
+)
+abstract class TodoDb : RoomDatabase() {
+ abstract val todoDao: TodoDao
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/repository/BookRepositoryImpl.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/repository/BookRepositoryImpl.kt
deleted file mode 100644
index a1e053a..0000000
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/repository/BookRepositoryImpl.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.data.repository
-
-import ro.alexmamo.roomjetpackcompose.data.dao.BookDao
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-import ro.alexmamo.roomjetpackcompose.domain.repository.BookRepository
-
-class BookRepositoryImpl(
- private val bookDao: BookDao
-) : BookRepository {
- override fun getBookList() = bookDao.getBookList()
-
- override suspend fun getBookById(id: Int) = bookDao.getBookById(id)
-
- override suspend fun insertBook(book: Book) = bookDao.insertBook(book)
-
- override suspend fun updateBook(book: Book) = bookDao.updateBook(book)
-
- override suspend fun deleteBook(book: Book) = bookDao.deleteBook(book)
-}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/repository/TodoRepositoryImpl.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/repository/TodoRepositoryImpl.kt
new file mode 100644
index 0000000..b01009d
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/data/repository/TodoRepositoryImpl.kt
@@ -0,0 +1,19 @@
+package ro.alexmamo.roomjetpackcompose.data.repository
+
+import ro.alexmamo.roomjetpackcompose.data.dao.TodoDao
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+import ro.alexmamo.roomjetpackcompose.domain.repository.TodoRepository
+
+class TodoRepositoryImpl(
+ private val todoDao: TodoDao
+) : TodoRepository {
+ override fun getTodoList() = todoDao.getTodoList()
+
+ override suspend fun getTodoById(id: Int) = todoDao.getTodoById(id)
+
+ override suspend fun insertTodo(todo: Todo) = todoDao.insertTodo(todo)
+
+ override suspend fun updateTodo(todo: Todo) = todoDao.updateTodo(todo)
+
+ override suspend fun deleteTodo(todo: Todo) = todoDao.deleteTodo(todo)
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/di/AppModule.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/di/AppModule.kt
index 54a61fe..331bb6a 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/di/AppModule.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/di/AppModule.kt
@@ -8,33 +8,35 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import ro.alexmamo.roomjetpackcompose.R
-import ro.alexmamo.roomjetpackcompose.data.dao.BookDao
-import ro.alexmamo.roomjetpackcompose.data.network.BookDb
-import ro.alexmamo.roomjetpackcompose.data.repository.BookRepositoryImpl
-import ro.alexmamo.roomjetpackcompose.domain.repository.BookRepository
+import ro.alexmamo.roomjetpackcompose.data.dao.TodoDao
+import ro.alexmamo.roomjetpackcompose.data.network.TodoDb
+import ro.alexmamo.roomjetpackcompose.data.repository.TodoRepositoryImpl
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+import ro.alexmamo.roomjetpackcompose.domain.repository.TodoRepository
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
- fun provideBookDb(
+ fun provideTodoDb(
@ApplicationContext
context: Context
) = Room.databaseBuilder(
context,
- BookDb::class.java,
+ TodoDb::class.java,
context.resources.getString(R.string.db_name)
- ).build()
+ ).fallbackToDestructiveMigration().build()
+
@Provides
- fun provideBookDao(
- bookDb: BookDb
- ) = bookDb.bookDao
+ fun provideTodoDao(
+ todoDb: TodoDb
+ ) = todoDb.todoDao
@Provides
- fun provideBookRepository(
- bookDao: BookDao
- ): BookRepository = BookRepositoryImpl(
- bookDao = bookDao
+ fun provideTodoRepository(
+ todoDao: TodoDao
+ ): TodoRepository = TodoRepositoryImpl(
+ todoDao = todoDao
)
}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/model/Book.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/model/Book.kt
deleted file mode 100644
index 49afea0..0000000
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/model/Book.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.domain.model
-
-import androidx.room.Entity
-import androidx.room.PrimaryKey
-import ro.alexmamo.roomjetpackcompose.core.BOOK_TABLE
-import ro.alexmamo.roomjetpackcompose.navigation.BookDetails
-
-@Entity(tableName = BOOK_TABLE)
-data class Book(
- @PrimaryKey(autoGenerate = true)
- val id: Int,
- val title: String,
- val author: String
-)
-
-fun Book.toBookDetails() = BookDetails(
- id = this.id,
- title = this.title,
- author = this.author
-)
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/model/Todo.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/model/Todo.kt
new file mode 100644
index 0000000..02042a8
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/model/Todo.kt
@@ -0,0 +1,20 @@
+package ro.alexmamo.roomjetpackcompose.domain.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import ro.alexmamo.roomjetpackcompose.core.TODO_TABLE
+import ro.alexmamo.roomjetpackcompose.navigation.TodoDetails
+
+@Entity(tableName = TODO_TABLE)
+data class Todo(
+ @PrimaryKey(autoGenerate = true)
+ val id: Int,
+ val name: String,
+ val description: String
+)
+
+fun Todo.toTodoDetails() = TodoDetails(
+ id = this.id,
+ name = this.name,
+ description = this.description
+)
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/repository/BookRepository.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/repository/BookRepository.kt
deleted file mode 100644
index e913c92..0000000
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/repository/BookRepository.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.domain.repository
-
-import kotlinx.coroutines.flow.Flow
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-
-interface BookRepository {
- fun getBookList(): Flow>
-
- suspend fun getBookById(id: Int): Book?
-
- suspend fun insertBook(book: Book)
-
- suspend fun updateBook(book: Book)
-
- suspend fun deleteBook(book: Book)
-}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/repository/TodoRepository.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/repository/TodoRepository.kt
new file mode 100644
index 0000000..f509735
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/domain/repository/TodoRepository.kt
@@ -0,0 +1,16 @@
+package ro.alexmamo.roomjetpackcompose.domain.repository
+
+import kotlinx.coroutines.flow.Flow
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+
+interface TodoRepository {
+ fun getTodoList(): Flow>
+
+ suspend fun getTodoById(id: Int): Todo?
+
+ suspend fun insertTodo(todo: Todo)
+
+ suspend fun updateTodo(todo: Todo)
+
+ suspend fun deleteTodo(todo: Todo)
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/ApiResult.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/ApiResult.kt
new file mode 100644
index 0000000..25a3807
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/ApiResult.kt
@@ -0,0 +1,7 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure
+
+sealed class ApiResult {
+ data class Success(val data: T) : ApiResult()
+ data class Error(val code: Int?, val message: String?) : ApiResult()
+ data class Exception(val exception: Throwable) : ApiResult()
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/RetrofitUtils.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/RetrofitUtils.kt
new file mode 100644
index 0000000..93363fc
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/RetrofitUtils.kt
@@ -0,0 +1,22 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure
+
+import okhttp3.OkHttpClient
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+
+object RetrofitUtils {
+ val okHttpClient = OkHttpClient.Builder()
+ .addInterceptor { chain ->
+ val request = chain.request().newBuilder()
+ .addHeader("x-api-key", "123456789")
+ .build()
+ chain.proceed(request)
+ }
+ .build()
+
+ val retrofit: Retrofit = Retrofit.Builder()
+ .client(okHttpClient)
+ .addConverterFactory(GsonConverterFactory.create())
+ .baseUrl("https://d9811bf4-5e67-4a8c-bdcf-603cbbfc0275.mock.pstmn.io/")
+ .build()
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/SafeApiCall.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/SafeApiCall.kt
new file mode 100644
index 0000000..8aad348
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/SafeApiCall.kt
@@ -0,0 +1,31 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure
+
+import android.util.Log
+import retrofit2.Response
+
+suspend fun safeApiCall(
+ tag: String = "API_CALL",
+ apiCall: suspend () -> Response,
+ map: (T) -> R
+): ApiResult {
+ return try {
+ val response = apiCall()
+ if (response.isSuccessful) {
+ val body = response.body()
+ if (body != null) {
+ Log.d(tag, "✅ Éxito: ${response.code()} -> ${body.toString().take(500)}")
+ ApiResult.Success(map(body))
+ } else {
+ Log.e(tag, "⚠️ Cuerpo nulo en respuesta exitosa (${response.code()})")
+ ApiResult.Error(response.code(), "Cuerpo de respuesta nulo")
+ }
+ } else {
+ val errorBody = response.errorBody()?.string()
+ Log.e(tag, "❌ Error HTTP ${response.code()} -> ${errorBody ?: "Sin cuerpo de error"}")
+ ApiResult.Error(response.code(), errorBody)
+ }
+ } catch (e: Exception) {
+ Log.e(tag, "💥 Excepción: ${e.message}", e)
+ ApiResult.Exception(e)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/Auth.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/Auth.kt
new file mode 100644
index 0000000..40a24ef
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/Auth.kt
@@ -0,0 +1,10 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.auth
+
+import ro.alexmamo.roomjetpackcompose.infraestructure.user.User
+
+
+interface Auth {
+ suspend fun login(data: LoginRequest): Token?
+
+ suspend fun createUser(data: CreateUserRequest): User?
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthApi.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthApi.kt
new file mode 100644
index 0000000..7d0d349
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthApi.kt
@@ -0,0 +1,14 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.auth
+
+import retrofit2.Response
+import retrofit2.http.Body
+import retrofit2.http.POST
+import ro.alexmamo.roomjetpackcompose.infraestructure.user.UserResponse
+
+interface AuthApi {
+ @POST("auth/login")
+ suspend fun login(@Body data: LoginRequest): Response
+
+ @POST("auth/create")
+ suspend fun createUser(@Body data: CreateUserRequest): Response
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthImpl.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthImpl.kt
new file mode 100644
index 0000000..a4f89f7
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthImpl.kt
@@ -0,0 +1,28 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.auth
+
+import ro.alexmamo.roomjetpackcompose.infraestructure.ApiResult
+import ro.alexmamo.roomjetpackcompose.infraestructure.RetrofitUtils
+import ro.alexmamo.roomjetpackcompose.infraestructure.safeApiCall
+import ro.alexmamo.roomjetpackcompose.infraestructure.user.User
+
+class AuthImpl : Auth {
+ private val api = RetrofitUtils.retrofit.create(AuthApi::class.java)
+
+ override suspend fun login(data: LoginRequest): Token? {
+ return when (val result = safeApiCall("login", {api.login(data)}) { res -> res.toModel()
+ }) {
+ is ApiResult.Success -> result.data
+ is ApiResult.Error,
+ is ApiResult.Exception -> null
+ }
+ }
+
+ override suspend fun createUser(data: CreateUserRequest): User? {
+ return when (val result = safeApiCall("createUser", {api.createUser(data)}) { res -> res.toModel()
+ }) {
+ is ApiResult.Success -> result.data
+ is ApiResult.Error,
+ is ApiResult.Exception -> null
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthModels.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthModels.kt
new file mode 100644
index 0000000..2abe342
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthModels.kt
@@ -0,0 +1,16 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.auth
+
+data class Token (
+ val token: String,
+)
+
+data class LoginRequest(
+ val email: String,
+ val password: String
+)
+
+data class CreateUserRequest(
+ val email: String,
+ val password: String,
+ val username: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthResponses.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthResponses.kt
new file mode 100644
index 0000000..102d677
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/auth/AuthResponses.kt
@@ -0,0 +1,12 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.auth
+
+import com.google.gson.annotations.SerializedName
+
+data class TokenResponse(
+ @SerializedName("token") val token: String,
+) {
+ fun toModel(): Token =
+ Token(
+ token = token,
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserApi.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserApi.kt
new file mode 100644
index 0000000..ae8a1bc
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserApi.kt
@@ -0,0 +1,12 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.user
+
+import retrofit2.Response
+import retrofit2.http.Body
+import retrofit2.http.GET
+import retrofit2.http.POST
+import retrofit2.http.Path
+
+interface UserApi {
+ @GET("users/{id}")
+ suspend fun getById(@Path("id") id: Int): Response
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserModels.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserModels.kt
new file mode 100644
index 0000000..23f1267
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserModels.kt
@@ -0,0 +1,30 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.user
+
+data class User(
+ val id: Int,
+ val email: String,
+ val username: String,
+ val password: String,
+ val name: Name,
+ val address: Address,
+ val phone: String,
+ val __v: Int
+)
+
+data class Name(
+ val firstname: String,
+ val lastname: String
+)
+
+data class Address(
+ val geolocation: Geolocation,
+ val city: String,
+ val street: String,
+ val number: Int,
+ val zipcode: String
+)
+
+data class Geolocation(
+ val lat: String,
+ val long: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserResponses.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserResponses.kt
new file mode 100644
index 0000000..5cfa440
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UserResponses.kt
@@ -0,0 +1,65 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.user
+
+import com.google.gson.annotations.SerializedName
+
+data class UserResponse(
+ @SerializedName("id") val id: Int,
+ @SerializedName("email") val email: String,
+ @SerializedName("username") val username: String,
+ @SerializedName("password") val password: String,
+ @SerializedName("name") val name: NameResponse,
+ @SerializedName("address") val address: AddressResponse,
+ @SerializedName("phone") val phone: String,
+ @SerializedName("__v") val v: Int
+) {
+ fun toModel(): User =
+ User(
+ id = id,
+ email = email,
+ username = username,
+ password = password,
+ name = name.toModel(),
+ address = address.toModel(),
+ phone = phone,
+ __v = v
+ )
+}
+
+data class NameResponse(
+ @SerializedName("firstname") val firstname: String,
+ @SerializedName("lastname") val lastname: String
+) {
+ fun toModel(): Name =
+ Name(
+ firstname = firstname,
+ lastname = lastname
+ )
+}
+
+data class AddressResponse(
+ @SerializedName("geolocation") val geolocation: GeolocationResponse,
+ @SerializedName("city") val city: String,
+ @SerializedName("street") val street: String,
+ @SerializedName("number") val number: Int,
+ @SerializedName("zipcode") val zipcode: String
+) {
+ fun toModel(): Address =
+ Address(
+ geolocation = geolocation.toModel(),
+ city = city,
+ street = street,
+ number = number,
+ zipcode = zipcode
+ )
+}
+
+data class GeolocationResponse(
+ @SerializedName("lat") val lat: String,
+ @SerializedName("long") val long: String
+) {
+ fun toModel(): Geolocation =
+ Geolocation(
+ lat = lat,
+ long = long
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/Users.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/Users.kt
new file mode 100644
index 0000000..1f82cad
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/Users.kt
@@ -0,0 +1,6 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.user
+
+
+interface Users {
+ suspend fun getById(id: Int): User?
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UsersImpl.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UsersImpl.kt
new file mode 100644
index 0000000..931a2d9
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/user/UsersImpl.kt
@@ -0,0 +1,21 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.user
+
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import ro.alexmamo.roomjetpackcompose.infraestructure.ApiResult
+import ro.alexmamo.roomjetpackcompose.infraestructure.RetrofitUtils
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.AuthApi
+import ro.alexmamo.roomjetpackcompose.infraestructure.safeApiCall
+
+class UsersImpl : Users {
+ private val api = RetrofitUtils.retrofit.create(UserApi::class.java)
+
+ override suspend fun getById(id: Int): User? {
+ return when (val result = safeApiCall("login", {api.getById(id)}) { res -> res.toModel()
+ }) {
+ is ApiResult.Success -> result.data
+ is ApiResult.Error,
+ is ApiResult.Exception -> null
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/Wallet.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/Wallet.kt
new file mode 100644
index 0000000..dbccea4
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/Wallet.kt
@@ -0,0 +1,5 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.wallet
+
+interface WalletInterface {
+ suspend fun get(): Wallet?
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletApi.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletApi.kt
new file mode 100644
index 0000000..19d6132
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletApi.kt
@@ -0,0 +1,10 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.wallet
+
+import retrofit2.Response
+import retrofit2.http.GET
+import ro.alexmamo.roomjetpackcompose.infraestructure.walletimport.WalletResponse
+
+interface WalletApi {
+ @GET("transactions")
+ suspend fun get(): Response
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletImpl.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletImpl.kt
new file mode 100644
index 0000000..8839747
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletImpl.kt
@@ -0,0 +1,18 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.wallet
+
+import ro.alexmamo.roomjetpackcompose.infraestructure.ApiResult
+import ro.alexmamo.roomjetpackcompose.infraestructure.RetrofitUtils
+import ro.alexmamo.roomjetpackcompose.infraestructure.safeApiCall
+
+class WalletImpl : WalletInterface {
+ private val api = RetrofitUtils.retrofit.create(WalletApi::class.java)
+
+ override suspend fun get(): Wallet? {
+ return when (val result = safeApiCall("get", {api.get()}) { res -> res.toModel()
+ }) {
+ is ApiResult.Success -> result.data
+ is ApiResult.Error,
+ is ApiResult.Exception -> null
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletModels.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletModels.kt
new file mode 100644
index 0000000..a44d967
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletModels.kt
@@ -0,0 +1,19 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.wallet
+
+data class Wallet(
+ val userId: Int,
+ val balance: Double,
+ val income: Double,
+ val expense: Double,
+ val transactions: List
+)
+
+data class Transaction(
+ val transactionId: String,
+ val date: String,
+ val description: String,
+ val amount: Double,
+ val currency: String,
+ val type: String,
+ val subtype: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletResponses.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletResponses.kt
new file mode 100644
index 0000000..f075732
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/infraestructure/wallet/WalletResponses.kt
@@ -0,0 +1,43 @@
+package ro.alexmamo.roomjetpackcompose.infraestructure.walletimport
+
+import com.google.gson.annotations.SerializedName
+import ro.alexmamo.roomjetpackcompose.infraestructure.wallet.Transaction
+import ro.alexmamo.roomjetpackcompose.infraestructure.wallet.Wallet
+
+data class WalletResponse(
+ @SerializedName("user_id") val userId: Int,
+ @SerializedName("balance") val balance: Double,
+ @SerializedName("income") val income: Double,
+ @SerializedName("expense") val expense: Double,
+ @SerializedName("transactions") val transactions: List
+) {
+ fun toModel(): Wallet =
+ Wallet(
+ userId = userId,
+ balance = balance,
+ income = income,
+ expense = expense,
+ transactions = transactions.map { it.toModel() }
+ )
+}
+
+data class TransactionResponse(
+ @SerializedName("transaction_id") val transactionId: String,
+ @SerializedName("date") val date: String,
+ @SerializedName("description") val description: String,
+ @SerializedName("amount") val amount: Double,
+ @SerializedName("currency") val currency: String,
+ @SerializedName("type") val type: String,
+ @SerializedName("subtype") val subtype: String
+) {
+ fun toModel(): Transaction =
+ Transaction(
+ transactionId = transactionId,
+ date = date,
+ description = description,
+ amount = amount,
+ currency = currency,
+ type = type,
+ subtype = subtype
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/navigation/NavGraph.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/navigation/NavGraph.kt
index dfc0cf5..39d962d 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/navigation/NavGraph.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/navigation/NavGraph.kt
@@ -5,9 +5,17 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
-import ro.alexmamo.roomjetpackcompose.domain.model.toBookDetails
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.BookListScreen
-import ro.alexmamo.roomjetpackcompose.presentation.book_details.BookDetailsScreen
+import ro.alexmamo.roomjetpackcompose.domain.model.toTodoDetails
+import ro.alexmamo.roomjetpackcompose.presentation.create_user.CreateUserScreen
+import ro.alexmamo.roomjetpackcompose.presentation.create_user.CreateUserViewModel
+import ro.alexmamo.roomjetpackcompose.presentation.home.HomeScreen
+import ro.alexmamo.roomjetpackcompose.presentation.home.WalletViewModel
+import ro.alexmamo.roomjetpackcompose.presentation.login.LoginScreen
+import ro.alexmamo.roomjetpackcompose.presentation.login.LoginViewModel
+import ro.alexmamo.roomjetpackcompose.presentation.profile.ProfileScreen
+import ro.alexmamo.roomjetpackcompose.presentation.profile.UserViewModel
+import ro.alexmamo.roomjetpackcompose.presentation.todo_details.TodoDetailsScreen
+import ro.alexmamo.roomjetpackcompose.presentation.todo_list.TodoListScreen
@Composable
fun NavGraph(
@@ -15,21 +23,44 @@ fun NavGraph(
) {
NavHost(
navController = navController,
- startDestination = BookListScreen
+ startDestination = HomeScreen
) {
- composable {
- BookListScreen(
- navigateToBookDetailsScreen = { book ->
- val bookDetails = book.toBookDetails()
- navController.navigate(bookDetails)
+
+ val loginViewModel = LoginViewModel()
+ val userViewModel = UserViewModel()
+ val createUserViewModel = CreateUserViewModel()
+ val walletViewModel = WalletViewModel()
+
+ composable {
+ TodoListScreen(
+ navigateToTodoDetailsScreen = { todo ->
+ val todoDetails = todo.toTodoDetails()
+ navController.navigate(todoDetails)
}
)
}
- composable { entry ->
- val bookDetails = entry.toRoute()
- val book = bookDetails.toBook()
- BookDetailsScreen(
- book = book,
+ composable {
+ ProfileScreen(userViewModel)
+ }
+ composable {
+ HomeScreen(walletViewModel)
+ }
+ composable {
+ LoginScreen(loginViewModel, onLoginSuccess = { token ->
+ navController.navigate(UserScreen)
+ })
+ }
+
+ composable {
+ CreateUserScreen(createUserViewModel, onCreateUserSuccess = { user ->
+ navController.navigate(UserScreen)
+ })
+ }
+ composable { entry ->
+ val todoDetails = entry.toRoute()
+ val todo = todoDetails.toTodo()
+ TodoDetailsScreen (
+ todo = todo,
navigateBack = navController::navigateUp
)
}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/navigation/Route.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/navigation/Route.kt
index 47a2301..0b1281a 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/navigation/Route.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/navigation/Route.kt
@@ -1,20 +1,33 @@
package ro.alexmamo.roomjetpackcompose.navigation
import kotlinx.serialization.Serializable
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+
+
+@Serializable
+object TodoListScreen
+
+@Serializable
+object LoginScreen
+
+@Serializable
+object CreateUserScreen
+
+@Serializable
+object HomeScreen
@Serializable
-object BookListScreen
+object UserScreen
@Serializable
-data class BookDetails(
+data class TodoDetails(
val id: Int,
- val title: String,
- val author: String
+ val name: String,
+ val description: String
)
-fun BookDetails.toBook() = Book(
+fun TodoDetails.toTodo() = Todo(
id = this.id,
- title = this.title,
- author = this.author
+ name = this.name,
+ description = this.description
)
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/MainActivity.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/MainActivity.kt
index b4623be..b94568a 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/MainActivity.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/MainActivity.kt
@@ -6,15 +6,18 @@ import androidx.activity.compose.setContent
import androidx.navigation.compose.rememberNavController
import dagger.hilt.android.AndroidEntryPoint
import ro.alexmamo.roomjetpackcompose.navigation.NavGraph
+import ro.alexmamo.roomjetpackcompose.ui.theme.CustomTheme
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
- NavGraph(
- navController = rememberNavController()
- )
+ CustomTheme {
+ NavGraph(
+ navController = rememberNavController()
+ )
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/BookDetailsScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/BookDetailsScreen.kt
deleted file mode 100644
index c3920c4..0000000
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/BookDetailsScreen.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_details
-
-import androidx.compose.material.Scaffold
-import androidx.compose.runtime.Composable
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-import ro.alexmamo.roomjetpackcompose.presentation.book_details.components.BookDetailsContent
-import ro.alexmamo.roomjetpackcompose.presentation.book_details.components.BookDetailsTopBar
-
-@Composable
-fun BookDetailsScreen(
- book: Book,
- navigateBack: () -> Unit
-) {
- Scaffold(
- topBar = {
- BookDetailsTopBar(
- onArrowBackIconClick = navigateBack
- )
- },
- content = { innerPadding ->
- BookDetailsContent(
- innerPadding = innerPadding,
- book = book
- )
- }
- )
-}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListViewModel.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListViewModel.kt
deleted file mode 100644
index f9e932c..0000000
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListViewModel.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-import ro.alexmamo.roomjetpackcompose.domain.model.Response
-import ro.alexmamo.roomjetpackcompose.domain.repository.BookRepository
-import javax.inject.Inject
-
-typealias InsertBookResponse = Response
-typealias UpdateBookResponse = Response
-typealias DeleteBookResponse = Response
-
-@HiltViewModel
-class BookListViewModel @Inject constructor(
- private val repo: BookRepository
-) : ViewModel() {
- val bookListState = repo.getBookList().map { bookList ->
- try {
- Response.Success(bookList)
- } catch (e: Exception) {
- Response.Failure(e)
- }
- }.stateIn(
- scope = viewModelScope,
- started = SharingStarted.WhileSubscribed(5_000),
- initialValue = Response.Loading
- )
-
- private val _insertBookState = MutableStateFlow(Response.Idle)
- val insertBookState: StateFlow = _insertBookState.asStateFlow()
-
- private val _updateBookState = MutableStateFlow(Response.Idle)
- val updateBookState: StateFlow = _updateBookState.asStateFlow()
-
- private val _deleteBookState = MutableStateFlow(Response.Idle)
- val deleteBookState: StateFlow = _deleteBookState.asStateFlow()
-
- fun insertBook(book: Book) = viewModelScope.launch {
- try {
- _insertBookState.value = Response.Loading
- _insertBookState.value = Response.Success(repo.insertBook(book))
- } catch (e: Exception) {
- Response.Failure(e)
- }
- }
-
- fun resetInsertBookState() {
- _insertBookState.value = Response.Idle
- }
-
- fun updateBook(book: Book) = viewModelScope.launch {
- try {
- _updateBookState.value = Response.Loading
- _updateBookState.value = Response.Success(repo.updateBook(book))
- } catch (e: Exception) {
- Response.Failure(e)
- }
- }
-
- fun resetUpdateBookState() {
- _updateBookState.value = Response.Idle
- }
-
- fun deleteBook(book: Book) = viewModelScope.launch {
- try {
- _deleteBookState.value = Response.Loading
- _deleteBookState.value = Response.Success(repo.deleteBook(book))
- } catch (e: Exception) {
- Response.Failure(e)
- }
- }
-
- fun resetDeleteBookState() {
- _deleteBookState.value = Response.Idle
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListContent.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListContent.kt
deleted file mode 100644
index 5bc43a0..0000000
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListContent.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
-
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-
-const val NON_EXISTENT_BOOK_ID = -1
-
-@Composable
-fun BookListContent(
- innerPadding: PaddingValues,
- bookList: List,
- onBookCardClick: (Book) -> Unit,
- onUpdateBook: (Book) -> Unit,
- onEmptyBookField: (String) -> Unit,
- onDeleteBook: (Book) -> Unit,
- onNoBookUpdates: () -> Unit
-) {
- var editBookId by remember { mutableIntStateOf(NON_EXISTENT_BOOK_ID) }
-
- LazyColumn(
- modifier = Modifier.fillMaxSize().padding(innerPadding)
- ) {
- items(
- items = bookList,
- key = { book ->
- book.id
- }
- ) { book ->
- if (editBookId != book.id) {
- BookCard(
- book = book,
- onBookCardClick = {
- onBookCardClick(book)
- },
- onEditBook = {
- editBookId = book.id
- },
- onDeleteBook = {
- onDeleteBook(book)
- editBookId = NON_EXISTENT_BOOK_ID
- }
- )
- } else {
- EditableBookCard(
- book = book,
- onUpdateBook = { updatedBook ->
- onUpdateBook(updatedBook)
- editBookId = NON_EXISTENT_BOOK_ID
- },
- onEmptyBookField = onEmptyBookField,
- onNoBookUpdates = {
- onNoBookUpdates()
- editBookId = NON_EXISTENT_BOOK_ID
- },
- onCancel = {
- editBookId = NON_EXISTENT_BOOK_ID
- }
- )
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/create_user/CreateUserScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/create_user/CreateUserScreen.kt
new file mode 100644
index 0000000..82efc68
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/create_user/CreateUserScreen.kt
@@ -0,0 +1,34 @@
+package ro.alexmamo.roomjetpackcompose.presentation.create_user
+
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.viewmodel.compose.viewModel
+import ro.alexmamo.roomjetpackcompose.infraestructure.user.User
+
+@Composable
+fun CreateUserScreen(
+ viewModel: CreateUserViewModel = viewModel(),
+ onCreateUserSuccess: (User) -> Unit
+) {
+ val uiState by viewModel.uiState.collectAsState()
+
+ Button(onClick = { viewModel.createUser("email", "pass", "user") }) {
+ Text("Crear usuario")
+ }
+
+ when (uiState) {
+ is CreateUserViewModel.UiState.Loading -> Text("Cargando...")
+ is CreateUserViewModel.UiState.Success -> {
+ val user = (uiState as CreateUserViewModel.UiState.Success).user
+ LaunchedEffect(Unit) {
+ onCreateUserSuccess(user)
+ }
+ }
+ is CreateUserViewModel.UiState.Error -> Text("Error: ${(uiState as CreateUserViewModel.UiState.Error).message}")
+ else -> {}
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/create_user/CreateUserViewModel.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/create_user/CreateUserViewModel.kt
new file mode 100644
index 0000000..87a9177
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/create_user/CreateUserViewModel.kt
@@ -0,0 +1,38 @@
+package ro.alexmamo.roomjetpackcompose.presentation.create_user
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.Auth
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.AuthImpl
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.CreateUserRequest
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.Token
+import ro.alexmamo.roomjetpackcompose.infraestructure.user.User
+
+
+class CreateUserViewModel(private val auth: Auth = AuthImpl()) : ViewModel() {
+
+ sealed class UiState {
+ object Idle : UiState()
+ object Loading : UiState()
+ data class Success(val user: User) : UiState()
+ data class Error(val message: String) : UiState()
+ }
+
+ private val _uiState = MutableStateFlow(UiState.Idle)
+ val uiState = _uiState.asStateFlow()
+
+ fun createUser(email: String, password: String, username: String) {
+ viewModelScope.launch {
+ _uiState.value = UiState.Loading
+ val user = auth.createUser(CreateUserRequest(email, password, username))
+ if (user != null) {
+ _uiState.value = UiState.Success(user)
+ } else {
+ _uiState.value = UiState.Error("Error al crear usuario")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/home/HomeScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/home/HomeScreen.kt
new file mode 100644
index 0000000..904de17
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/home/HomeScreen.kt
@@ -0,0 +1,90 @@
+package ro.alexmamo.roomjetpackcompose.presentation.home
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.CircularProgressIndicator
+import androidx.compose.material.Divider
+import androidx.compose.material.Text
+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.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import ro.alexmamo.roomjetpackcompose.infraestructure.wallet.Transaction
+
+@Composable
+fun HomeScreen(viewModel: WalletViewModel = viewModel()) {
+ val uiState by viewModel.uiState.collectAsState()
+
+ LaunchedEffect(Unit) {
+ viewModel.get()
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp),
+ ) {
+ when (uiState) {
+ is WalletViewModel.UiState.Idle -> Text("Esperando acción…")
+
+ is WalletViewModel.UiState.Loading -> Box(
+ Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator()
+ }
+
+ is WalletViewModel.UiState.Success -> {
+ val wallet = (uiState as WalletViewModel.UiState.Success).wallet
+
+ Text(text = "💰 Balance: ${wallet.balance}")
+ Text(text = "Ingresos: ${wallet.income}")
+ Text(text = "Gastos: ${wallet.expense}")
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ Text(
+ text = "Transacciones"
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ LazyColumn {
+ items(wallet.transactions) { tx ->
+ TransactionRow(tx)
+ }
+ }
+ }
+
+ is WalletViewModel.UiState.Error -> Text(
+ text = "Error: ${(uiState as WalletViewModel.UiState.Error).message}"
+ )
+ }
+ }
+}
+
+@Composable
+fun TransactionRow(tx: Transaction) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 8.dp)
+ ) {
+ Text("📄 ${tx.description}")
+ Text("Fecha: ${tx.date}")
+ Text("Monto: ${tx.amount} ${tx.currency}")
+ Text("Tipo: ${tx.type} - ${tx.subtype}")
+ Divider(modifier = Modifier.padding(top = 8.dp))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/home/WalletViewModel.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/home/WalletViewModel.kt
new file mode 100644
index 0000000..b4245f3
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/home/WalletViewModel.kt
@@ -0,0 +1,35 @@
+package ro.alexmamo.roomjetpackcompose.presentation.home
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import ro.alexmamo.roomjetpackcompose.infraestructure.wallet.Wallet
+import ro.alexmamo.roomjetpackcompose.infraestructure.wallet.WalletImpl
+import ro.alexmamo.roomjetpackcompose.infraestructure.wallet.WalletInterface
+
+class WalletViewModel(private val walletInterface: WalletInterface = WalletImpl()) : ViewModel() {
+
+ sealed class UiState {
+ object Idle : UiState()
+ object Loading : UiState()
+ data class Success(val wallet: Wallet) : UiState()
+ data class Error(val message: String) : UiState()
+ }
+
+ private val _uiState = MutableStateFlow(UiState.Idle)
+ val uiState = _uiState.asStateFlow()
+
+ fun get() {
+ viewModelScope.launch {
+ _uiState.value = UiState.Loading
+ val wallet = walletInterface.get()
+ if (wallet != null) {
+ _uiState.value = UiState.Success(wallet)
+ } else {
+ _uiState.value = UiState.Error("Error al traer transacciones")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/layouts/BaseScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/layouts/BaseScreen.kt
new file mode 100644
index 0000000..8565cbb
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/layouts/BaseScreen.kt
@@ -0,0 +1,109 @@
+package ro.alexmamo.roomjetpackcompose.presentation.layouts
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.FabPosition
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.unit.dp
+import ro.alexmamo.roomjetpackcompose.components.AppTopBar
+import ro.alexmamo.roomjetpackcompose.ui.theme.Dimens
+
+/**
+ * BaseScreen:`.
+ * - soporta variante centrada o normal
+ * - slots: topBar, header, content, bottomBar, fab
+ * - mantiene paddings y radios desde `ui.theme.Dimens`
+ */
+
+@Composable
+fun BaseScreen(
+ modifier: Modifier = Modifier,
+ title: String? = null,
+ centerContent: Boolean = false,
+ topBar: (@Composable () -> Unit)? = null,
+ header: (@Composable () -> Unit)? = null,
+ content: @Composable (paddingValues: PaddingValues) -> Unit,
+ bottomBar: (@Composable () -> Unit)? = null,
+ fab: (@Composable () -> Unit)? = null,
+ snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }
+) {
+ Scaffold(
+ modifier = modifier,
+ topBar = {
+ // sirve para qe la camara no recorte el título del top bar.
+ Box(modifier = Modifier.statusBarsPadding()) {
+ when {
+ topBar != null -> topBar()
+ title != null -> AppTopBar(title = title)
+ }
+ }
+ },
+ floatingActionButton = { if (fab != null) fab() },
+ floatingActionButtonPosition = FabPosition.End,
+ bottomBar = { if (bottomBar != null) bottomBar() },
+ snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
+ ) { paddingValues ->
+ val onlyTopPadding = PaddingValues(top = paddingValues.calculateTopPadding())
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(onlyTopPadding)
+ ) {
+ if (header != null) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ horizontal = Dimens.paddingLarge,
+ vertical = Dimens.paddingSmall
+ )
+ ) {
+ header()
+ }
+ }
+
+ // contenid prinicipal
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(
+ RoundedCornerShape(
+ topStart = 56.dp,
+ topEnd = 56.dp
+ )
+ )
+ .background(MaterialTheme.colorScheme.surface)
+ .padding(Dimens.paddingLarge)
+ ) {
+ if (centerContent) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ content(PaddingValues(0.dp))
+ }
+ } else {
+ content(onlyTopPadding)
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/login/LoginScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/login/LoginScreen.kt
new file mode 100644
index 0000000..8033fa8
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/login/LoginScreen.kt
@@ -0,0 +1,34 @@
+package ro.alexmamo.roomjetpackcompose.presentation.login
+
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.viewmodel.compose.viewModel
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.Token
+
+@Composable
+fun LoginScreen(
+ viewModel: LoginViewModel = viewModel(),
+ onLoginSuccess: (Token) -> Unit
+) {
+ val uiState by viewModel.uiState.collectAsState()
+
+ Button(onClick = { viewModel.login("user", "pass") }) {
+ Text("Iniciar sesión")
+ }
+
+ when (uiState) {
+ is LoginViewModel.UiState.Loading -> Text("Cargando...")
+ is LoginViewModel.UiState.Success -> {
+ val token = (uiState as LoginViewModel.UiState.Success).token
+ LaunchedEffect(Unit) {
+ onLoginSuccess(token) // redirigís acá
+ }
+ }
+ is LoginViewModel.UiState.Error -> Text("Error: ${(uiState as LoginViewModel.UiState.Error).message}")
+ else -> {}
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/login/LoginViewModel.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/login/LoginViewModel.kt
new file mode 100644
index 0000000..e7aad8e
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/login/LoginViewModel.kt
@@ -0,0 +1,36 @@
+package ro.alexmamo.roomjetpackcompose.presentation.login
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.Auth
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.AuthImpl
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.LoginRequest
+import ro.alexmamo.roomjetpackcompose.infraestructure.auth.Token
+
+class LoginViewModel(private val auth: Auth = AuthImpl()) : ViewModel() {
+
+ sealed class UiState {
+ object Idle : UiState()
+ object Loading : UiState()
+ data class Success(val token: Token) : UiState()
+ data class Error(val message: String) : UiState()
+ }
+
+ private val _uiState = MutableStateFlow(UiState.Idle)
+ val uiState = _uiState.asStateFlow()
+
+ fun login(username: String, password: String) {
+ viewModelScope.launch {
+ _uiState.value = UiState.Loading
+ val token = auth.login(LoginRequest(username, password))
+ if (token != null) {
+ _uiState.value = UiState.Success(token)
+ } else {
+ _uiState.value = UiState.Error("Error al iniciar sesión")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/notification/NotificationScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/notification/NotificationScreen.kt
new file mode 100644
index 0000000..d682581
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/notification/NotificationScreen.kt
@@ -0,0 +1,111 @@
+package ro.alexmamo.roomjetpackcompose.presentation.notification
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import ro.alexmamo.roomjetpackcompose.R
+import ro.alexmamo.roomjetpackcompose.components.ActionIconButton
+import ro.alexmamo.roomjetpackcompose.components.AppTopBar
+import ro.alexmamo.roomjetpackcompose.components.BottomNavigationBar
+import ro.alexmamo.roomjetpackcompose.components.NotificationItem
+import ro.alexmamo.roomjetpackcompose.presentation.layouts.BaseScreen
+import ro.alexmamo.roomjetpackcompose.ui.theme.Dimens
+import ro.alexmamo.roomjetpackcompose.ui.theme.Honeydew
+
+@Composable
+fun NotificationScreen(vm: NotificationViewModel = viewModel()) {
+ BaseScreen(
+ title = null,
+ topBar = {
+ AppTopBar(
+ title = stringResource(R.string.notification_title),
+ leftAction = {
+ ActionIconButton(
+ onActionIconButtonClick = { /* para atras */ },
+ withCircle = false,
+ content = { mod ->
+ androidx.compose.material.Icon(
+ painter = painterResource(id = R.drawable.arrow_left),
+ contentDescription = stringResource(id = R.string.navigate_back),
+ tint = Honeydew,
+ modifier = mod
+ )
+ }
+ )
+ },
+ rightAction = {
+ ActionIconButton(
+ onActionIconButtonClick = { /* esto la verdad nose qe haria porque ya estamos en notif */ },
+ withCircle = true,
+ circleSize = 30.dp,
+ circleColor = MaterialTheme.colorScheme.surface,
+ content = { mod ->
+ androidx.compose.material.Icon(
+ painter = painterResource(id = R.drawable.notification),
+ contentDescription = stringResource(id = R.string.notification_title),
+ tint = MaterialTheme.colorScheme.onSecondary,
+ modifier = mod
+ )
+ }
+ )
+ }
+ )
+ },
+ bottomBar = { BottomNavigationBar() },
+ content = { _ ->
+ val sections by vm.sections.collectAsState()
+
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(Dimens.paddingMedium),
+ verticalArrangement = Arrangement.spacedBy(Dimens.paddingMedium),
+ contentPadding = PaddingValues(bottom = Dimens.paddingLarge)
+ ) {
+ sections.forEach { section ->
+ item(key = section.titleRes) {
+ val sectionTitle = stringResource(id = section.titleRes)
+ Text(
+ sectionTitle,
+ style = MaterialTheme.typography.titleSmall,
+ color = MaterialTheme.colorScheme.onSecondary
+ )
+ }
+ items(section.items) { item ->
+ NotificationItem(
+ iconRes = item.iconRes,
+ titleRes = item.titleRes,
+ messageRes = item.messageRes,
+ timeRes = item.timeRes
+ )
+
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = Dimens.paddingMedium)
+ .height(2.dp)
+ .background(color = MaterialTheme.colorScheme.background)
+ )
+ }
+ }
+ }
+ }
+ )
+}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/notification/NotificationViewModel.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/notification/NotificationViewModel.kt
new file mode 100644
index 0000000..487594a
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/notification/NotificationViewModel.kt
@@ -0,0 +1,49 @@
+package ro.alexmamo.roomjetpackcompose.presentation.notification
+
+import androidx.lifecycle.ViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import ro.alexmamo.roomjetpackcompose.R
+
+class NotificationViewModel : ViewModel() {
+
+ data class Item(
+ val iconRes: Int,
+ val titleRes: Int,
+ val messageRes: Int,
+ val timeRes: Int
+ )
+
+ data class Section(
+ val titleRes: Int,
+ val items: List-
+ )
+
+ private val _sections = MutableStateFlow(
+ listOf(
+ Section(
+ titleRes = R.string.today_section_title,
+ items = listOf(
+ Item(R.drawable.notification, R.string.notif_reminder_title, R.string.notif_reminder_message, R.string.notification_time_example),
+ Item(R.drawable.star, R.string.notif_new_update_title, R.string.notif_reminder_message, R.string.notification_time_example),
+ )
+ ),
+ Section(
+ titleRes = R.string.yesterday_section_title,
+ items = listOf(
+ Item(R.drawable.dollar, R.string.notif_transactions_title, R.string.notif_transactions_message, R.string.notification_time_example),
+ Item(R.drawable.notification, R.string.notif_reminder_title, R.string.notif_reminder_message, R.string.notification_time_example),
+ )
+ ),
+ Section(
+ titleRes = R.string.thisweekend_section_title,
+ items = listOf(
+ Item(R.drawable.arrow_down, R.string.notif_expense_title, R.string.notif_expense_message, R.string.notification_time_example),
+ Item(R.drawable.transactions, R.string.notif_transactions_title, R.string.notif_transactions_message, R.string.notification_time_example),
+ )
+ ),
+ )
+ )
+
+ val sections: StateFlow
> = _sections
+}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/profile/ProfileScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/profile/ProfileScreen.kt
new file mode 100644
index 0000000..b93c45d
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/profile/ProfileScreen.kt
@@ -0,0 +1,50 @@
+package ro.alexmamo.roomjetpackcompose.presentation.profile
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Text
+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.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+
+@Composable
+fun ProfileScreen(viewModel: UserViewModel = viewModel()) {
+ val uiState by viewModel.uiState.collectAsState()
+
+ LaunchedEffect(Unit) {
+ viewModel.fetchUser(1)
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ when (uiState) {
+ is UserViewModel.UiState.Idle -> Text("Esperando acción…")
+ is UserViewModel.UiState.Loading -> CircularProgressIndicator()
+ is UserViewModel.UiState.Success -> {
+ val user = (uiState as UserViewModel.UiState.Success).user
+ Text("Usuario: ${user.name.firstname} ${user.name.lastname}")
+ Spacer(Modifier.height(8.dp))
+ Text("Email: ${user.email}")
+ Text("Ciudad: ${user.address.city}")
+ }
+ is UserViewModel.UiState.Error -> Text(
+ "Error: ${(uiState as UserViewModel.UiState.Error).message}",
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/profile/UserViewModel.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/profile/UserViewModel.kt
new file mode 100644
index 0000000..a9bb2e8
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/profile/UserViewModel.kt
@@ -0,0 +1,40 @@
+package ro.alexmamo.roomjetpackcompose.presentation.profile
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+
+import ro.alexmamo.roomjetpackcompose.infraestructure.user.User
+import ro.alexmamo.roomjetpackcompose.infraestructure.user.Users
+import ro.alexmamo.roomjetpackcompose.infraestructure.user.UsersImpl
+
+class UserViewModel(
+ private val users: Users = UsersImpl()
+) : ViewModel() {
+
+ sealed class UiState {
+ object Idle : UiState()
+ object Loading : UiState()
+ data class Success(val user: User) : UiState()
+ data class Error(val message: String) : UiState()
+ }
+
+ private val _uiState = MutableStateFlow(UserViewModel.UiState.Idle)
+ val uiState = _uiState.asStateFlow()
+
+ fun fetchUser(id: Int) {
+ viewModelScope.launch {
+ _uiState.value = UserViewModel.UiState.Loading
+ val user = users.getById(id)
+ if (user != null) {
+ _uiState.value = UserViewModel.UiState.Success(user)
+ } else {
+ _uiState.value = UserViewModel.UiState.Error("Error al traer usuario")
+ }
+ }
+ }
+}
+
+private fun Any.launch(function: () -> Unit) {}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/TodoDetailsScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/TodoDetailsScreen.kt
new file mode 100644
index 0000000..3a12231
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/TodoDetailsScreen.kt
@@ -0,0 +1,27 @@
+package ro.alexmamo.roomjetpackcompose.presentation.todo_details
+
+import androidx.compose.material.Scaffold
+import androidx.compose.runtime.Composable
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+import ro.alexmamo.roomjetpackcompose.presentation.todo_details.components.TodoDetailsContent
+import ro.alexmamo.roomjetpackcompose.presentation.todo_details.components.TodoDetailsTopBar
+
+@Composable
+fun TodoDetailsScreen(
+ todo: Todo,
+ navigateBack: () -> Unit
+) {
+ Scaffold(
+ topBar = {
+ TodoDetailsTopBar(
+ onArrowBackIconClick = navigateBack
+ )
+ },
+ content = { innerPadding ->
+ TodoDetailsContent(
+ innerPadding = innerPadding,
+ todo = todo
+ )
+ }
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/components/BookDetailsContent.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/components/TodoDetailsContent.kt
similarity index 53%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/components/BookDetailsContent.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/components/TodoDetailsContent.kt
index 2196534..bd60638 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/components/BookDetailsContent.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/components/TodoDetailsContent.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_details.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_details.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -7,23 +7,23 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.components.AuthorText
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.components.TitleText
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+import ro.alexmamo.roomjetpackcompose.presentation.todo_list.components.DescriptionText
+import ro.alexmamo.roomjetpackcompose.presentation.todo_list.components.NameText
@Composable
-fun BookDetailsContent(
+fun TodoDetailsContent(
innerPadding: PaddingValues,
- book: Book
+ todo: Todo
) {
Column(
modifier = Modifier.fillMaxSize().padding(innerPadding).padding(8.dp)
) {
- TitleText(
- title = book.title
+ NameText(
+ title = todo.name
)
- AuthorText(
- author = book.author
+ DescriptionText(
+ author = todo.description
)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/components/BookDetailsTopBar.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/components/TodoDetailsTopBar.kt
similarity index 51%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/components/BookDetailsTopBar.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/components/TodoDetailsTopBar.kt
index 556af02..570774c 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_details/components/BookDetailsTopBar.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_details/components/TodoDetailsTopBar.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_details.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_details.components
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
@@ -7,25 +7,36 @@ import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ro.alexmamo.roomjetpackcompose.R
+import androidx.compose.material3.Icon
+import androidx.compose.ui.unit.dp
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.foundation.layout.size
import ro.alexmamo.roomjetpackcompose.components.ActionIconButton
@Composable
-fun BookDetailsTopBar(
+fun TodoDetailsTopBar(
onArrowBackIconClick: () -> Unit
) {
TopAppBar (
title = {
Text(
text = stringResource(
- id = R.string.book_details_screen_title
+ id = R.string.todo_details_screen_name
)
)
},
navigationIcon = {
ActionIconButton(
onActionIconButtonClick = onArrowBackIconClick,
- imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
- resourceId = R.string.navigate_back
+ withCircle = false,
+ content = { mod ->
+ Icon(
+ Icons.AutoMirrored.Outlined.ArrowBack,
+ contentDescription = stringResource(id = R.string.navigate_back),
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = mod
+ )
+ }
)
}
)
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListScreen.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/TodoListScreen.kt
similarity index 58%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListScreen.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/TodoListScreen.kt
index 49b8a0f..bc8772d 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/BookListScreen.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/TodoListScreen.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list
import androidx.compose.material.Scaffold
import androidx.compose.material.SnackbarHost
@@ -13,42 +13,43 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import coil.compose.AsyncImage
import ro.alexmamo.roomjetpackcompose.R
import ro.alexmamo.roomjetpackcompose.components.LoadingIndicator
import ro.alexmamo.roomjetpackcompose.core.logMessage
import ro.alexmamo.roomjetpackcompose.core.showSnackbarMessage
import ro.alexmamo.roomjetpackcompose.core.showToastMessage
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
import ro.alexmamo.roomjetpackcompose.domain.model.Response
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.components.BookListContent
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.components.BookListTopBar
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.components.EmptyBookListContent
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.components.InsertBookAlertDialog
-import ro.alexmamo.roomjetpackcompose.presentation.book_list.components.InsertBookFloatingActionButton
+import ro.alexmamo.roomjetpackcompose.presentation.todo_list.components.TodoListContent
+import ro.alexmamo.roomjetpackcompose.presentation.todo_list.components.TodoListTopBar
+import ro.alexmamo.roomjetpackcompose.presentation.todo_list.components.EmptyTodoListContent
+import ro.alexmamo.roomjetpackcompose.presentation.todo_list.components.InsertTodoAlertDialog
+import ro.alexmamo.roomjetpackcompose.presentation.todo_list.components.InsertFloatingActionButton
@Composable
-fun BookListScreen(
- viewModel: BookListViewModel = hiltViewModel(),
- navigateToBookDetailsScreen: (Book) -> Unit
+fun TodoListScreen(
+ viewModel: TodoListViewModel = hiltViewModel(),
+ navigateToTodoDetailsScreen: (Todo) -> Unit
) {
val context = LocalContext.current
val resources = context.resources
val coroutineScope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
- var openInsertBookDialog by remember { mutableStateOf(false) }
- val bookListResponse by viewModel.bookListState.collectAsStateWithLifecycle()
- val insertBookResponse by viewModel.insertBookState.collectAsStateWithLifecycle()
- val updateBookResponse by viewModel.updateBookState.collectAsStateWithLifecycle()
- val deleteBookResponse by viewModel.deleteBookState.collectAsStateWithLifecycle()
+ var openInsertTodoDialog by remember { mutableStateOf(false) }
+ val todoListResponse by viewModel.todoListState.collectAsStateWithLifecycle()
+ val insertTodoResponse by viewModel.insertTodoState.collectAsStateWithLifecycle()
+ val updateTodoResponse by viewModel.updateTodoState.collectAsStateWithLifecycle()
+ val deleteTodoResponse by viewModel.deleteTodoState.collectAsStateWithLifecycle()
Scaffold(
topBar = {
- BookListTopBar()
+ TodoListTopBar()
},
floatingActionButton = {
- InsertBookFloatingActionButton(
- onInsertBookFloatingActionButtonClick = {
- openInsertBookDialog = true
+ InsertFloatingActionButton(
+ onInsertFloatingActionButtonClick = {
+ openInsertTodoDialog = true
}
)
},
@@ -58,43 +59,43 @@ fun BookListScreen(
)
}
) { innerPadding ->
- when(val bookListResponse = bookListResponse) {
+ when(val todoListResponse = todoListResponse) {
is Response.Idle -> {}
is Response.Loading -> LoadingIndicator()
- is Response.Success -> bookListResponse.data.let { bookList ->
- if (bookList.isEmpty()) {
- EmptyBookListContent(
+ is Response.Success -> todoListResponse.data.let { todoList ->
+ if (todoList.isEmpty()) {
+ EmptyTodoListContent(
innerPadding = innerPadding
)
} else {
- BookListContent(
+ TodoListContent(
innerPadding = innerPadding,
- bookList = bookList,
- onBookCardClick = navigateToBookDetailsScreen,
- onUpdateBook = { book ->
- viewModel.updateBook(book)
+ todoList = todoList,
+ onTodoCardClick = navigateToTodoDetailsScreen,
+ onUpdateTodo = { todo ->
+ viewModel.updateTodo(todo)
},
- onEmptyBookField = { bookField ->
+ onEmptyTodoField = { todoField ->
showSnackbarMessage(
coroutineScope = coroutineScope,
snackbarHostState = snackbarHostState,
- message = resources.getString(R.string.empty_book_field_message, bookField)
+ message = resources.getString(R.string.empty_todo_field_message, todoField)
)
},
- onDeleteBook = { bookId ->
- viewModel.deleteBook(bookId)
+ onDeleteTodo = { todoId ->
+ viewModel.deleteTodo(todoId)
},
- onNoBookUpdates = {
+ onNoTodoUpdates = {
showSnackbarMessage(
coroutineScope = coroutineScope,
snackbarHostState = snackbarHostState,
- message = resources.getString(R.string.no_book_updates_message)
+ message = resources.getString(R.string.no_todo_updates_message)
)
}
)
}
}
- is Response.Failure -> bookListResponse.e.message?.let { errorMessage ->
+ is Response.Failure -> todoListResponse.e.message?.let { errorMessage ->
LaunchedEffect(errorMessage) {
logMessage(errorMessage)
showToastMessage(context, errorMessage)
@@ -103,36 +104,36 @@ fun BookListScreen(
}
}
- if (openInsertBookDialog) {
- InsertBookAlertDialog(
- onInsertBook = { book ->
- viewModel.insertBook(book)
+ if (openInsertTodoDialog) {
+ InsertTodoAlertDialog(
+ onInsertTodo = { todo ->
+ viewModel.insertTodo(todo)
},
- onEmptyBookField = { emptyField ->
+ onEmptyTodoField = { emptyField ->
showSnackbarMessage(
coroutineScope = coroutineScope,
snackbarHostState = snackbarHostState,
- message = resources.getString(R.string.empty_book_field_message, emptyField)
+ message = resources.getString(R.string.empty_todo_field_message, emptyField)
)
},
- onInsertBookDialogCancel = {
- openInsertBookDialog = false
+ onInsertTodoDialogCancel = {
+ openInsertTodoDialog = false
}
)
}
- when(val insertBookResponse = insertBookResponse) {
+ when(val insertTodoResponse = insertTodoResponse) {
is Response.Idle -> {}
is Response.Loading -> LoadingIndicator()
is Response.Success -> LaunchedEffect(Unit) {
showSnackbarMessage(
coroutineScope = coroutineScope,
snackbarHostState = snackbarHostState,
- message = resources.getString(R.string.book_action_message, BookAction.ADDED)
+ message = resources.getString(R.string.todo_action_message, TodoAction.ADDED)
)
- viewModel.resetInsertBookState()
+ viewModel.resetInsertTodoState()
}
- is Response.Failure -> insertBookResponse.e.message?.let { errorMessage ->
+ is Response.Failure -> insertTodoResponse.e.message?.let { errorMessage ->
LaunchedEffect(errorMessage) {
logMessage(errorMessage)
showToastMessage(context, errorMessage)
@@ -140,18 +141,18 @@ fun BookListScreen(
}
}
- when(val updateBookResponse = updateBookResponse) {
+ when(val updateTodoResponse = updateTodoResponse) {
is Response.Idle -> {}
is Response.Loading -> LoadingIndicator()
is Response.Success -> LaunchedEffect(Unit) {
showSnackbarMessage(
coroutineScope = coroutineScope,
snackbarHostState = snackbarHostState,
- message = resources.getString(R.string.book_action_message, BookAction.UPDATED)
+ message = resources.getString(R.string.todo_action_message, TodoAction.UPDATED)
)
- viewModel.resetUpdateBookState()
+ viewModel.resetUpdateTodoState()
}
- is Response.Failure -> updateBookResponse.e.message?.let { errorMessage ->
+ is Response.Failure -> updateTodoResponse.e.message?.let { errorMessage ->
LaunchedEffect(errorMessage) {
logMessage(errorMessage)
showToastMessage(context, errorMessage)
@@ -159,18 +160,18 @@ fun BookListScreen(
}
}
- when(val deleteBookResponse = deleteBookResponse) {
+ when(val deleteTodoResponse = deleteTodoResponse) {
is Response.Idle -> {}
is Response.Loading -> LoadingIndicator()
is Response.Success -> LaunchedEffect(Unit) {
showSnackbarMessage(
coroutineScope = coroutineScope,
snackbarHostState = snackbarHostState,
- message = resources.getString(R.string.book_action_message, BookAction.DELETED)
+ message = resources.getString(R.string.todo_action_message, TodoAction.DELETED)
)
- viewModel.resetDeleteBookState()
+ viewModel.resetDeleteTodoState()
}
- is Response.Failure -> deleteBookResponse.e.message?.let { errorMessage ->
+ is Response.Failure -> deleteTodoResponse.e.message?.let { errorMessage ->
LaunchedEffect(errorMessage) {
logMessage(errorMessage)
showToastMessage(context, errorMessage)
@@ -179,7 +180,7 @@ fun BookListScreen(
}
}
-enum class BookAction() {
+enum class TodoAction() {
ADDED,
UPDATED,
DELETED
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/TodoListViewModel.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/TodoListViewModel.kt
new file mode 100644
index 0000000..35bceea
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/TodoListViewModel.kt
@@ -0,0 +1,85 @@
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+import ro.alexmamo.roomjetpackcompose.domain.model.Response
+import ro.alexmamo.roomjetpackcompose.domain.repository.TodoRepository
+import javax.inject.Inject
+
+typealias InsertTodoResponse = Response
+typealias UpdateTodoResponse = Response
+typealias DeleteTodoResponse = Response
+
+@HiltViewModel
+class TodoListViewModel @Inject constructor(
+ private val repo: TodoRepository
+) : ViewModel() {
+ val todoListState = repo.getTodoList().map { todoList ->
+ try {
+ Response.Success(todoList)
+ } catch (e: Exception) {
+ Response.Failure(e)
+ }
+ }.stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(5_000),
+ initialValue = Response.Loading
+ )
+
+ private val _insertTodoState = MutableStateFlow(Response.Idle)
+ val insertTodoState: StateFlow = _insertTodoState.asStateFlow()
+
+ private val _updateTodoState = MutableStateFlow(Response.Idle)
+ val updateTodoState: StateFlow = _updateTodoState.asStateFlow()
+
+ private val _deleteTodoState = MutableStateFlow(Response.Idle)
+ val deleteTodoState: StateFlow = _deleteTodoState.asStateFlow()
+
+ fun insertTodo(todo: Todo) = viewModelScope.launch {
+ try {
+ _insertTodoState.value = Response.Loading
+ _insertTodoState.value = Response.Success(repo.insertTodo(todo))
+ } catch (e: Exception) {
+ Response.Failure(e)
+ }
+ }
+
+ fun resetInsertTodoState() {
+ _insertTodoState.value = Response.Idle
+ }
+
+ fun updateTodo(todo: Todo) = viewModelScope.launch {
+ try {
+ _updateTodoState.value = Response.Loading
+ _updateTodoState.value = Response.Success(repo.updateTodo(todo))
+ } catch (e: Exception) {
+ Response.Failure(e)
+ }
+ }
+
+ fun resetUpdateTodoState() {
+ _updateTodoState.value = Response.Idle
+ }
+
+ fun deleteTodo(todo: Todo) = viewModelScope.launch {
+ try {
+ _deleteTodoState.value = Response.Loading
+ _deleteTodoState.value = Response.Success(repo.deleteTodo(todo))
+ } catch (e: Exception) {
+ Response.Failure(e)
+ }
+ }
+
+ fun resetDeleteTodoState() {
+ _deleteTodoState.value = Response.Idle
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/AuthorText.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/DescriptionText.kt
similarity index 82%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/AuthorText.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/DescriptionText.kt
index df685a3..9bd3a31 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/AuthorText.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/DescriptionText.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@@ -7,7 +7,7 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.sp
@Composable
-fun AuthorText(
+fun DescriptionText(
author: String
) {
Text(
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/AuthorTextField.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/DescriptionTextField.kt
similarity index 56%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/AuthorTextField.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/DescriptionTextField.kt
index daff6db..7423424 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/AuthorTextField.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/DescriptionTextField.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.material.Text
import androidx.compose.material.TextField
@@ -11,22 +11,22 @@ import androidx.compose.ui.res.stringResource
import ro.alexmamo.roomjetpackcompose.R
@Composable
-fun AuthorTextField(
- author: String,
- onUpdateAuthor: (String) -> Unit
+fun DescriptionTextField(
+ description: String,
+ onUpdateDescription: (String) -> Unit
) {
- var author by remember { mutableStateOf(author) }
+ var description by remember { mutableStateOf(description) }
TextField(
- value = author,
- onValueChange = { newAuthor ->
- author = newAuthor
- onUpdateAuthor(newAuthor)
+ value = description,
+ onValueChange = { newDescription ->
+ description = newDescription
+ onUpdateDescription(newDescription)
},
placeholder = {
Text(
text = stringResource(
- id = R.string.book_author
+ id = R.string.todo_description
)
)
}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/EditableBookCard.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/EditableTodoCard.kt
similarity index 59%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/EditableBookCard.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/EditableTodoCard.kt
index 59f8737..6ee71e7 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/EditableBookCard.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/EditableTodoCard.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -18,19 +18,19 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import ro.alexmamo.roomjetpackcompose.R
import ro.alexmamo.roomjetpackcompose.components.ActionButton
-import ro.alexmamo.roomjetpackcompose.core.AUTHOR_FIELD
-import ro.alexmamo.roomjetpackcompose.core.TITLE_FIELD
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
+import ro.alexmamo.roomjetpackcompose.core.DESCRIPTION_FIELD
+import ro.alexmamo.roomjetpackcompose.core.NAME_FIELD
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
@Composable
-fun EditableBookCard(
- book: Book,
- onUpdateBook: (Book) -> Unit,
- onEmptyBookField: (String) -> Unit,
- onNoBookUpdates: () -> Unit,
+fun EditableTodoCard(
+ todo: Todo,
+ onUpdateTodo: (Todo) -> Unit,
+ onEmptyTodoField: (String) -> Unit,
+ onNoTodoUpdates: () -> Unit,
onCancel: () -> Unit
) {
- var updatedBook by remember { mutableStateOf(book) }
+ var updatedTodo by remember { mutableStateOf(todo) }
Card(
modifier = Modifier.fillMaxWidth().padding(
@@ -45,22 +45,22 @@ fun EditableBookCard(
Column(
modifier = Modifier.padding(8.dp)
) {
- TitleTextField(
- title = updatedBook.title,
- onUpdateTitle = { newTitle ->
- updatedBook = updatedBook.copy(
- title = newTitle
+ NameTextField(
+ name = updatedTodo.name,
+ onUpdateName = { newName ->
+ updatedTodo = updatedTodo.copy(
+ name = newName
)
}
)
Spacer(
modifier = Modifier.height(8.dp)
)
- AuthorTextField(
- author = updatedBook.author,
- onUpdateAuthor = { newAuthor ->
- updatedBook = updatedBook.copy(
- author = newAuthor
+ DescriptionTextField(
+ description = updatedTodo.description,
+ onUpdateDescription = { newDescription ->
+ updatedTodo = updatedTodo.copy(
+ description = newDescription
)
}
)
@@ -74,16 +74,16 @@ fun EditableBookCard(
)
ActionButton(
onActionButtonClick = {
- updatedBook.apply {
- if (title.isEmpty()) {
- onEmptyBookField(TITLE_FIELD)
- } else if (author.isEmpty()) {
- onEmptyBookField(AUTHOR_FIELD)
+ updatedTodo.apply {
+ if (name.isEmpty()) {
+ onEmptyTodoField(NAME_FIELD)
+ } else if (description.isEmpty()) {
+ onEmptyTodoField(DESCRIPTION_FIELD)
} else {
- if (updatedBook != book) {
- onUpdateBook(updatedBook)
+ if (updatedTodo != todo) {
+ onUpdateTodo(updatedTodo)
} else {
- onNoBookUpdates()
+ onNoTodoUpdates()
}
}
}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/EmptyBookListContent.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/EmptyTodoListContent.kt
similarity index 84%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/EmptyBookListContent.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/EmptyTodoListContent.kt
index 535ba97..1973634 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/EmptyBookListContent.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/EmptyTodoListContent.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
@@ -13,7 +13,7 @@ import androidx.compose.ui.unit.sp
import ro.alexmamo.roomjetpackcompose.R
@Composable
-fun EmptyBookListContent(
+fun EmptyTodoListContent(
innerPadding: PaddingValues
) {
Box(
@@ -22,7 +22,7 @@ fun EmptyBookListContent(
){
Text(
text = stringResource(
- id = R.string.empty_book_list_text
+ id = R.string.empty_todo_list_text
),
fontSize = 18.sp
)
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookFloatingActionButton.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/InsertFloatingActionButton.kt
similarity index 70%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookFloatingActionButton.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/InsertFloatingActionButton.kt
index 769b49a..a48ae83 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookFloatingActionButton.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/InsertFloatingActionButton.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
@@ -10,17 +10,17 @@ import androidx.compose.ui.res.stringResource
import ro.alexmamo.roomjetpackcompose.R
@Composable
-fun InsertBookFloatingActionButton(
- onInsertBookFloatingActionButtonClick: () -> Unit
+fun InsertFloatingActionButton(
+ onInsertFloatingActionButtonClick: () -> Unit
) {
FloatingActionButton(
backgroundColor = MaterialTheme.colors.primary,
- onClick = onInsertBookFloatingActionButtonClick
+ onClick = onInsertFloatingActionButtonClick
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = stringResource(
- id = R.string.open_insert_book_dialog
+ id = R.string.open_insert_todo_dialog
)
)
}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookAlertDialog.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/InsertTodoAlertDialog.kt
similarity index 53%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookAlertDialog.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/InsertTodoAlertDialog.kt
index 01b4b50..48a1ecb 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/InsertBookAlertDialog.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/InsertTodoAlertDialog.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@@ -15,45 +15,45 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ro.alexmamo.roomjetpackcompose.R
import ro.alexmamo.roomjetpackcompose.components.ActionButton
-import ro.alexmamo.roomjetpackcompose.core.AUTHOR_FIELD
-import ro.alexmamo.roomjetpackcompose.core.TITLE_FIELD
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
+import ro.alexmamo.roomjetpackcompose.core.DESCRIPTION_FIELD
+import ro.alexmamo.roomjetpackcompose.core.NAME_FIELD
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
const val EMPTY_STRING = ""
@Composable
-fun InsertBookAlertDialog(
- onInsertBook: (book: Book) -> Unit,
- onEmptyBookField: (String) -> Unit,
- onInsertBookDialogCancel: () -> Unit,
+fun InsertTodoAlertDialog(
+ onInsertTodo: (todo: Todo) -> Unit,
+ onEmptyTodoField: (String) -> Unit,
+ onInsertTodoDialogCancel: () -> Unit,
) {
- var title by remember { mutableStateOf(EMPTY_STRING) }
- var author by remember { mutableStateOf(EMPTY_STRING) }
+ var name by remember { mutableStateOf(EMPTY_STRING) }
+ var description by remember { mutableStateOf(EMPTY_STRING) }
AlertDialog(
- onDismissRequest = onInsertBookDialogCancel,
+ onDismissRequest = onInsertTodoDialogCancel,
title = {
Text(
text = stringResource(
- id = R.string.insert_book
+ id = R.string.insert_todo
)
)
},
text = {
Column {
- TitleTextField(
- title = title,
- onUpdateTitle = { newTitle ->
- title = newTitle
+ NameTextField(
+ name = name,
+ onUpdateName = { newName ->
+ name = newName
}
)
Spacer(
modifier = Modifier.height(16.dp)
)
- AuthorTextField(
- author = author,
- onUpdateAuthor = { newAuthor ->
- author = newAuthor
+ DescriptionTextField(
+ description = description,
+ onUpdateDescription = { newDescription ->
+ description = newDescription
}
)
}
@@ -61,29 +61,29 @@ fun InsertBookAlertDialog(
confirmButton = {
ActionButton(
onActionButtonClick = {
- if (title.isEmpty()) {
- onEmptyBookField(TITLE_FIELD)
+ if (name.isEmpty()) {
+ onEmptyTodoField(NAME_FIELD)
return@ActionButton
}
- if (author.isEmpty()) {
- onEmptyBookField(AUTHOR_FIELD)
+ if (description.isEmpty()) {
+ onEmptyTodoField(DESCRIPTION_FIELD)
return@ActionButton
}
- onInsertBook(Book(
+ onInsertTodo(Todo(
id = 0,
- title = title,
- author = author
+ name = name,
+ description = description
))
- onInsertBookDialogCancel()
+ onInsertTodoDialogCancel()
},
resourceId = R.string.insert_button
)
},
dismissButton = {
ActionButton(
- onActionButtonClick = onInsertBookDialogCancel,
+ onActionButtonClick = onInsertTodoDialogCancel,
resourceId = R.string.cancel_button
)
- }
+ },
)
}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/TitleText.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/NameText.kt
similarity index 79%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/TitleText.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/NameText.kt
index cbe2c81..97b4650 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/TitleText.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/NameText.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@@ -6,7 +6,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
@Composable
-fun TitleText(
+fun NameText(
title: String
) {
Text(
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/TitleTextField.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/NameTextField.kt
similarity index 70%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/TitleTextField.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/NameTextField.kt
index 5f07134..d078fea 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/TitleTextField.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/NameTextField.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.material.Text
import androidx.compose.material.TextField
@@ -17,13 +17,13 @@ import androidx.compose.ui.text.input.TextFieldValue
import ro.alexmamo.roomjetpackcompose.R
@Composable
-fun TitleTextField(
- title: String,
- onUpdateTitle: (String) -> Unit
+fun NameTextField(
+ name: String,
+ onUpdateName: (String) -> Unit
) {
- var title by remember { mutableStateOf(TextFieldValue(
- text = title,
- selection = TextRange(title.length)
+ var name by remember { mutableStateOf(TextFieldValue(
+ text = name,
+ selection = TextRange(name.length)
)) }
val focusRequester = remember { FocusRequester() }
@@ -33,15 +33,15 @@ fun TitleTextField(
TextField(
modifier = Modifier.focusRequester(focusRequester),
- value = title,
- onValueChange = { newTitle ->
- title = newTitle
- onUpdateTitle(newTitle.text)
+ value = name,
+ onValueChange = { newName ->
+ name = newName
+ onUpdateName(newName.text)
},
placeholder = {
Text(
text = stringResource(
- id = R.string.book_title
+ id = R.string.todo_name
)
)
}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookCard.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoCard.kt
similarity index 51%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookCard.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoCard.kt
index 5cbacca..0d336b2 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookCard.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoCard.kt
@@ -1,6 +1,7 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -13,17 +14,18 @@ import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ro.alexmamo.roomjetpackcompose.R
import ro.alexmamo.roomjetpackcompose.components.ActionIconButton
-import ro.alexmamo.roomjetpackcompose.domain.model.Book
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
@Composable
-fun BookCard(
- book: Book,
- onBookCardClick: () -> Unit,
- onEditBook: () -> Unit,
- onDeleteBook: () -> Unit
+fun TodoCard(
+ todo: Todo,
+ onTodoCardClick: () -> Unit,
+ onEditTodo: () -> Unit,
+ onDeleteTodo: () -> Unit
) {
Card(
modifier = Modifier.fillMaxWidth().padding(
@@ -32,7 +34,7 @@ fun BookCard(
end = 8.dp,
bottom = 4.dp
).clickable {
- onBookCardClick()
+ onTodoCardClick()
},
shape = MaterialTheme.shapes.small,
elevation = 3.dp
@@ -41,25 +43,37 @@ fun BookCard(
modifier = Modifier.fillMaxWidth().padding(8.dp)
) {
Column {
- TitleText(
- title = book.title
+ NameText(
+ title = todo.name
)
- AuthorText(
- author = book.author
+ DescriptionText(
+ author = todo.description
)
}
Spacer(
modifier = Modifier.weight(1f)
)
ActionIconButton(
- onActionIconButtonClick = onEditBook,
- imageVector = Icons.Default.Edit,
- resourceId = R.string.edit_icon
+ onActionIconButtonClick = onEditTodo,
+ withCircle = false,
+ content = { mod ->
+ androidx.compose.material.Icon(
+ imageVector = Icons.Default.Edit,
+ contentDescription = stringResource(id = R.string.edit_icon),
+ modifier = mod
+ )
+ }
)
ActionIconButton(
- onActionIconButtonClick = onDeleteBook,
- imageVector = Icons.Default.Delete,
- resourceId = R.string.delete_icon
+ onActionIconButtonClick = onDeleteTodo,
+ withCircle = false,
+ content = { mod ->
+ androidx.compose.material.Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = stringResource(id = R.string.delete_icon),
+ modifier = mod
+ )
+ }
)
}
}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoListContent.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoListContent.kt
new file mode 100644
index 0000000..af1b443
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoListContent.kt
@@ -0,0 +1,72 @@
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import ro.alexmamo.roomjetpackcompose.domain.model.Todo
+
+const val NON_EXISTENT_TODO_ID = -1
+
+@Composable
+fun TodoListContent(
+ innerPadding: PaddingValues,
+ todoList: List,
+ onTodoCardClick: (Todo) -> Unit,
+ onUpdateTodo: (Todo) -> Unit,
+ onEmptyTodoField: (String) -> Unit,
+ onDeleteTodo: (Todo) -> Unit,
+ onNoTodoUpdates: () -> Unit
+) {
+ var editTodoId by remember { mutableIntStateOf(NON_EXISTENT_TODO_ID) }
+
+ LazyColumn(
+ modifier = Modifier.fillMaxSize().padding(innerPadding)
+ ) {
+ items(
+ items = todoList,
+ key = { todo ->
+ todo.id
+ }
+ ) { todo ->
+ if (editTodoId != todo.id) {
+ TodoCard(
+ todo = todo,
+ onTodoCardClick = {
+ onTodoCardClick(todo)
+ },
+ onEditTodo = {
+ editTodoId = todo.id
+ },
+ onDeleteTodo = {
+ onDeleteTodo(todo)
+ editTodoId = NON_EXISTENT_TODO_ID
+ }
+ )
+ } else {
+ EditableTodoCard(
+ todo = todo,
+ onUpdateTodo = { updatedTodo ->
+ onUpdateTodo(updatedTodo)
+ editTodoId = NON_EXISTENT_TODO_ID
+ },
+ onEmptyTodoField = onEmptyTodoField,
+ onNoTodoUpdates = {
+ onNoTodoUpdates()
+ editTodoId = NON_EXISTENT_TODO_ID
+ },
+ onCancel = {
+ editTodoId = NON_EXISTENT_TODO_ID
+ }
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListTopBar.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoListTopBar.kt
similarity index 72%
rename from app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListTopBar.kt
rename to app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoListTopBar.kt
index 852ccaa..1a7e6a0 100644
--- a/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/book_list/components/BookListTopBar.kt
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/presentation/todo_list/components/TodoListTopBar.kt
@@ -1,4 +1,4 @@
-package ro.alexmamo.roomjetpackcompose.presentation.book_list.components
+package ro.alexmamo.roomjetpackcompose.presentation.todo_list.components
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
@@ -7,12 +7,12 @@ import androidx.compose.ui.res.stringResource
import ro.alexmamo.roomjetpackcompose.R
@Composable
-fun BookListTopBar() {
+fun TodoListTopBar() {
TopAppBar (
title = {
Text(
text = stringResource(
- id = R.string.book_list_screen_title
+ id = R.string.todo_list_screen_name
)
)
}
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Color.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Color.kt
new file mode 100644
index 0000000..70345de
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Color.kt
@@ -0,0 +1,21 @@
+package ro.alexmamo.roomjetpackcompose.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Honeydew = Color(0xFFF1FFF3)
+
+val LightGreen = Color(0xFFDFF7E2)
+
+val CaribbeanGreen = Color(0xFF00D09E)
+
+val Cyprus = Color(0xFF0E3E3E)
+
+val FenceGreen = Color(0xFF052224)
+
+val Void = Color(0xFF031314)
+
+val LightBlue = Color(0xFF6DB6FE)
+
+val VividBlue = Color(0xFF3299FF)
+
+var OceanBlue = Color(0xFF0068FF)
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Dimens.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Dimens.kt
new file mode 100644
index 0000000..0160d9a
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Dimens.kt
@@ -0,0 +1,23 @@
+package ro.alexmamo.roomjetpackcompose.ui.theme
+
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+object Dimens {
+ // paddings
+ val paddingExtraSmall: Dp = 4.dp
+ val paddingSmall: Dp = 8.dp
+ val paddingMedium: Dp = 16.dp
+ val paddingLarge: Dp = 24.dp
+ val paddingExtraLarge: Dp = 32.dp
+
+ // corners
+ val cornerSmall: Dp = 8.dp
+ val cornerMedium: Dp = 16.dp
+ val cornerLarge: Dp = 32.dp
+
+ // sizes
+ val topBarHeight: Dp = 56.dp
+ val fabSize: Dp = 56.dp
+}
+
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Theme.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Theme.kt
new file mode 100644
index 0000000..79c64b8
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Theme.kt
@@ -0,0 +1,58 @@
+package ro.alexmamo.roomjetpackcompose.ui.theme
+
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+
+private val DarkColorScheme = darkColorScheme(
+ background = FenceGreen,
+ surface = Cyprus,
+ onPrimary = Void,
+ onSecondary = Honeydew
+)
+
+private val LightColorScheme = lightColorScheme(
+ background = CaribbeanGreen,
+ surface = LightGreen,
+ onPrimary = Void,
+ onSecondary = Void
+
+ /* Other default colors to override
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+)
+
+@Composable
+fun CustomTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = false,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Type.kt b/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Type.kt
new file mode 100644
index 0000000..fe32699
--- /dev/null
+++ b/app/src/main/java/ro/alexmamo/roomjetpackcompose/ui/theme/Type.kt
@@ -0,0 +1,27 @@
+package ro.alexmamo.roomjetpackcompose.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+import ro.alexmamo.roomjetpackcompose.R
+
+val Poppins = FontFamily(
+ Font(R.font.poppins_regular, FontWeight.Normal),
+ Font(R.font.poppins_medium, FontWeight.Medium),
+ Font(R.font.poppins_semi_bold, FontWeight.SemiBold),
+ Font(R.font.poppins_bold, FontWeight.Bold)
+)
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = Poppins,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp,
+ )
+)
\ No newline at end of file
diff --git a/app/src/main/res/drawable/analysis.xml b/app/src/main/res/drawable/analysis.xml
new file mode 100644
index 0000000..f0b155f
--- /dev/null
+++ b/app/src/main/res/drawable/analysis.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/arrow_down.xml b/app/src/main/res/drawable/arrow_down.xml
new file mode 100644
index 0000000..a96eb60
--- /dev/null
+++ b/app/src/main/res/drawable/arrow_down.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/arrow_left.xml b/app/src/main/res/drawable/arrow_left.xml
new file mode 100644
index 0000000..ec32681
--- /dev/null
+++ b/app/src/main/res/drawable/arrow_left.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/calendar.xml b/app/src/main/res/drawable/calendar.xml
new file mode 100644
index 0000000..8ce471a
--- /dev/null
+++ b/app/src/main/res/drawable/calendar.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/car.xml b/app/src/main/res/drawable/car.xml
new file mode 100644
index 0000000..576c6ec
--- /dev/null
+++ b/app/src/main/res/drawable/car.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/check.xml b/app/src/main/res/drawable/check.xml
new file mode 100644
index 0000000..e2278f8
--- /dev/null
+++ b/app/src/main/res/drawable/check.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/check_progress.xml b/app/src/main/res/drawable/check_progress.xml
new file mode 100644
index 0000000..f5d83bc
--- /dev/null
+++ b/app/src/main/res/drawable/check_progress.xml
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/dollar.xml b/app/src/main/res/drawable/dollar.xml
new file mode 100644
index 0000000..b2c1d81
--- /dev/null
+++ b/app/src/main/res/drawable/dollar.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/entertainment.xml b/app/src/main/res/drawable/entertainment.xml
new file mode 100644
index 0000000..61bdb72
--- /dev/null
+++ b/app/src/main/res/drawable/entertainment.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/expense.xml b/app/src/main/res/drawable/expense.xml
new file mode 100644
index 0000000..bcccdef
--- /dev/null
+++ b/app/src/main/res/drawable/expense.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/expenses.xml b/app/src/main/res/drawable/expenses.xml
new file mode 100644
index 0000000..f61a85c
--- /dev/null
+++ b/app/src/main/res/drawable/expenses.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/eye_off.xml b/app/src/main/res/drawable/eye_off.xml
new file mode 100644
index 0000000..8333687
--- /dev/null
+++ b/app/src/main/res/drawable/eye_off.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/eye_on.xml b/app/src/main/res/drawable/eye_on.xml
new file mode 100644
index 0000000..4a4b265
--- /dev/null
+++ b/app/src/main/res/drawable/eye_on.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/facebook.xml b/app/src/main/res/drawable/facebook.xml
new file mode 100644
index 0000000..61e1a70
--- /dev/null
+++ b/app/src/main/res/drawable/facebook.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/fingerprint.xml b/app/src/main/res/drawable/fingerprint.xml
new file mode 100644
index 0000000..89f26cb
--- /dev/null
+++ b/app/src/main/res/drawable/fingerprint.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/food.xml b/app/src/main/res/drawable/food.xml
new file mode 100644
index 0000000..b8ebd1f
--- /dev/null
+++ b/app/src/main/res/drawable/food.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/google.xml b/app/src/main/res/drawable/google.xml
new file mode 100644
index 0000000..d0b12dc
--- /dev/null
+++ b/app/src/main/res/drawable/google.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/groceries.xml b/app/src/main/res/drawable/groceries.xml
new file mode 100644
index 0000000..8009b31
--- /dev/null
+++ b/app/src/main/res/drawable/groceries.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/help.xml b/app/src/main/res/drawable/help.xml
new file mode 100644
index 0000000..c51147d
--- /dev/null
+++ b/app/src/main/res/drawable/help.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/home.xml b/app/src/main/res/drawable/home.xml
new file mode 100644
index 0000000..8e07216
--- /dev/null
+++ b/app/src/main/res/drawable/home.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/income.xml b/app/src/main/res/drawable/income.xml
new file mode 100644
index 0000000..6c316aa
--- /dev/null
+++ b/app/src/main/res/drawable/income.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/launch_1.xml b/app/src/main/res/drawable/launch_1.xml
new file mode 100644
index 0000000..12ea0f3
--- /dev/null
+++ b/app/src/main/res/drawable/launch_1.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/launch_2.xml b/app/src/main/res/drawable/launch_2.xml
new file mode 100644
index 0000000..e4e67b0
--- /dev/null
+++ b/app/src/main/res/drawable/launch_2.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/logout.xml b/app/src/main/res/drawable/logout.xml
new file mode 100644
index 0000000..8bff9db
--- /dev/null
+++ b/app/src/main/res/drawable/logout.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/medicine.xml b/app/src/main/res/drawable/medicine.xml
new file mode 100644
index 0000000..1af5317
--- /dev/null
+++ b/app/src/main/res/drawable/medicine.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/more.xml b/app/src/main/res/drawable/more.xml
new file mode 100644
index 0000000..90bb90d
--- /dev/null
+++ b/app/src/main/res/drawable/more.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/app/src/main/res/drawable/new_house.xml b/app/src/main/res/drawable/new_house.xml
new file mode 100644
index 0000000..889c376
--- /dev/null
+++ b/app/src/main/res/drawable/new_house.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/notification.xml b/app/src/main/res/drawable/notification.xml
new file mode 100644
index 0000000..f04faa6
--- /dev/null
+++ b/app/src/main/res/drawable/notification.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/profile.xml b/app/src/main/res/drawable/profile.xml
new file mode 100644
index 0000000..5ee0aa0
--- /dev/null
+++ b/app/src/main/res/drawable/profile.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/rent.xml b/app/src/main/res/drawable/rent.xml
new file mode 100644
index 0000000..8394517
--- /dev/null
+++ b/app/src/main/res/drawable/rent.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/salary.xml b/app/src/main/res/drawable/salary.xml
new file mode 100644
index 0000000..ec0af54
--- /dev/null
+++ b/app/src/main/res/drawable/salary.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/salary_white.xml b/app/src/main/res/drawable/salary_white.xml
new file mode 100644
index 0000000..f80c7e5
--- /dev/null
+++ b/app/src/main/res/drawable/salary_white.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/savings.xml b/app/src/main/res/drawable/savings.xml
new file mode 100644
index 0000000..eb23f02
--- /dev/null
+++ b/app/src/main/res/drawable/savings.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/security.xml b/app/src/main/res/drawable/security.xml
new file mode 100644
index 0000000..01b50fb
--- /dev/null
+++ b/app/src/main/res/drawable/security.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/setting.xml b/app/src/main/res/drawable/setting.xml
new file mode 100644
index 0000000..b232049
--- /dev/null
+++ b/app/src/main/res/drawable/setting.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/star.xml b/app/src/main/res/drawable/star.xml
new file mode 100644
index 0000000..8186c58
--- /dev/null
+++ b/app/src/main/res/drawable/star.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable/transactions.xml b/app/src/main/res/drawable/transactions.xml
new file mode 100644
index 0000000..4419f1d
--- /dev/null
+++ b/app/src/main/res/drawable/transactions.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/transport.xml b/app/src/main/res/drawable/transport.xml
new file mode 100644
index 0000000..ffe572d
--- /dev/null
+++ b/app/src/main/res/drawable/transport.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/travel.xml b/app/src/main/res/drawable/travel.xml
new file mode 100644
index 0000000..bc14e14
--- /dev/null
+++ b/app/src/main/res/drawable/travel.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/wedding.xml b/app/src/main/res/drawable/wedding.xml
new file mode 100644
index 0000000..b848d8b
--- /dev/null
+++ b/app/src/main/res/drawable/wedding.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/font/poppins_bold.otf b/app/src/main/res/font/poppins_bold.otf
new file mode 100644
index 0000000..3016454
Binary files /dev/null and b/app/src/main/res/font/poppins_bold.otf differ
diff --git a/app/src/main/res/font/poppins_medium.otf b/app/src/main/res/font/poppins_medium.otf
new file mode 100644
index 0000000..49e7b6b
Binary files /dev/null and b/app/src/main/res/font/poppins_medium.otf differ
diff --git a/app/src/main/res/font/poppins_regular.otf b/app/src/main/res/font/poppins_regular.otf
new file mode 100644
index 0000000..e5c4eee
Binary files /dev/null and b/app/src/main/res/font/poppins_regular.otf differ
diff --git a/app/src/main/res/font/poppins_semi_bold.otf b/app/src/main/res/font/poppins_semi_bold.otf
new file mode 100644
index 0000000..fcd0845
Binary files /dev/null and b/app/src/main/res/font/poppins_semi_bold.otf differ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6ac9b94..957b27d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,13 +1,13 @@
RoomJetpackCompose
- books_db
-
- "Book List"
- "Book Details"
+ todos_db
+
+ "Todo List"
+ "Todo Details"
- "The book list is empty."
- "Insert book"
+ "The todo list is empty."
+ "Insert todo"
"Insert"
"Update"
@@ -16,17 +16,31 @@
"Edit"
Delete
- "Open insert book dialog."
+ "Open insert todo dialog."
Navigate back
+
+ Notification
+ Today
+ Yesterday
+ This Weekend
+
+ Reminder!
+ Set up your automatic savings to meet your savings goal...
+ New Update
+ Transactions
+ A new transaction has been registered\nGroceries | Pantry | -$100,00
+ Expense record
+ We recommend that you be more attentive to your finances.
+ 17:00 - April 24
- "Type a book title…"
- "Type a book author…"
+ "Type a todo name…"
+ "Type a todo description…"
- "Book %1$s cannot be empty."
- "No book updates performed."
- "Book successfully %1$s."
+ "Todo %1$s cannot be empty."
+ "No todo updates performed."
+ "Todo successfully %1$s."
- "Title Test"
- "Author Test"
- "New Title Test"
+ "Name Test"
+ "Description Test"
+ "New Name Test"
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c575f53..fd9b33f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,4 +1,5 @@
[versions]
+coilCompose = "2.7.0"
gradle = "8.8.1"
kotlin = "2.1.10"
ksp = "2.1.10-1.0.30"
@@ -7,6 +8,8 @@ composeBom = "2025.02.00"
compose = "1.5.15"
navigationCompose = "2.8.7"
hiltNavigationCompose = "1.2.0"
+retrofit = "3.0.0"
+converterGson = "3.0.0"
room = "2.6.1"
serialization = "1.7.3"
#Tests
@@ -15,6 +18,11 @@ uiTestJunit4 = "1.7.8"
kotlinxCoroutinesTest = "1.9.0"
truth = "1.1.3"
uiTestManifest = "1.7.8"
+material3 = "1.4.0"
+foundation = "1.9.4"
+ui = "1.9.4"
+lifecycleViewmodelKtx = "2.9.4"
+runtime = "1.9.4"
[plugins]
android-application = { id = "com.android.application", version.ref = "gradle" }
@@ -25,6 +33,7 @@ hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
[libraries]
+coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
compose-material = { module = "androidx.compose.material:material" }
compose-material-icons = { module = "androidx.compose.material:material-icons-extended" }
@@ -32,6 +41,8 @@ navigation-compose = { module = "androidx.navigation:navigation-compose", versio
hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
hilt = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
+retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
+converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
@@ -43,4 +54,9 @@ runner = { module = "androidx.test:runner", version.ref = "runner" }
ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "uiTestJunit4" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" }
truth = { module = "com.google.truth:truth", version.ref = "truth" }
-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "uiTestManifest" }
\ No newline at end of file
+ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "uiTestManifest" }
+material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
+foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" }
+ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }
+lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
+runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "runtime" }