Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,6 @@ android {
buildFeatures {
compose = true
}
testOptions {
managedDevices {
localDevices {
create("pixel7api34X86") {
device = "Pixel 7"
apiLevel = 34
systemImageSource = "aosp-atd"
require64Bit = true
testedAbi = "x86_64"
}
create("pixel7api34Arm64") {
device = "Pixel 7"
apiLevel = 34
systemImageSource = "aosp-atd"
require64Bit = true
testedAbi = "arm64-v8a"
}
}
}
}
}

kotlin {
Expand All @@ -77,6 +57,10 @@ dependencies {
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.materialicons.extended)
implementation(libs.koin.android)
implementation(libs.koin.androidx.compose)

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">

<application
android:name=".NotesApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/com/itlab/notes/NotesApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.itlab.notes

import android.app.Application
import com.itlab.notes.di.appModule
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin

class NotesApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@NotesApplication)
modules(appModule)
}
}
}
120 changes: 120 additions & 0 deletions app/src/main/java/com/itlab/notes/di/AppModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.itlab.notes.di

import com.itlab.domain.model.Note
import com.itlab.domain.model.NoteFolder
import com.itlab.domain.repository.NoteFolderRepository
import com.itlab.domain.repository.NotesRepository
import com.itlab.domain.usecase.CreateFolderUseCase
import com.itlab.domain.usecase.CreateNoteUseCase
import com.itlab.domain.usecase.DeleteFolderUseCase
import com.itlab.domain.usecase.DeleteNoteUseCase
import com.itlab.domain.usecase.ObserveFoldersUseCase
import com.itlab.domain.usecase.ObserveNotesByFolderUseCase
import com.itlab.domain.usecase.UpdateNoteUseCase
import com.itlab.notes.ui.NotesUseCases
import com.itlab.notes.ui.NotesViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module

val appModule =
module {
single<NotesRepository> { InMemoryNotesRepository() }
single<NoteFolderRepository> { InMemoryFolderRepository() }

factory { CreateNoteUseCase(get()) }
factory { CreateFolderUseCase(get()) }
factory { DeleteFolderUseCase(get()) }
factory { DeleteNoteUseCase(get()) }
factory { UpdateNoteUseCase(get()) }
factory { ObserveNotesByFolderUseCase(get()) }
factory { ObserveFoldersUseCase(get()) }
factory {
NotesUseCases(
createFolderUseCase = get(),
deleteFolderUseCase = get(),
createNoteUseCase = get(),
deleteNoteUseCase = get(),
updateNoteUseCase = get(),
observeNotesByFolderUseCase = get(),
observeFoldersUseCase = get(),
)
}

viewModel {
NotesViewModel(
useCases = get(),
)
}
}

private class InMemoryNotesRepository : NotesRepository {
private val notesFlow = MutableStateFlow<List<Note>>(emptyList())

override fun observeNotes(): Flow<List<Note>> = notesFlow

override fun observeNotesByFolder(folderId: String): Flow<List<Note>> =
notesFlow.map { notes -> notes.filter { it.folderId == folderId } }

override suspend fun getNoteById(id: String): Note? = notesFlow.value.firstOrNull { it.id == id }

override suspend fun createNote(note: Note): String {
notesFlow.value = notesFlow.value + note
return note.id
}

override suspend fun updateNote(note: Note) {
notesFlow.value =
notesFlow.value.map { existing ->
if (existing.id == note.id) note else existing
}
}

override suspend fun deleteNote(id: String) {
notesFlow.value = notesFlow.value.filterNot { it.id == id }
}
}

private class InMemoryFolderRepository : NoteFolderRepository {
private val foldersFlow =
MutableStateFlow(
listOf(
NoteFolder(id = "all", name = "All Notes"),
NoteFolder(id = "study", name = "My Study"),
NoteFolder(id = "cook", name = "How to Cook"),
NoteFolder(id = "poems", name = "My poems"),
),
)

override fun observeFolders(): Flow<List<NoteFolder>> = foldersFlow

override suspend fun createFolder(folder: NoteFolder): String {
foldersFlow.value = foldersFlow.value + folder
return folder.id
}

override suspend fun renameFolder(
id: String,
name: String,
) {
foldersFlow.value =
foldersFlow.value.map { folder ->
if (folder.id == id) folder.copy(name = name) else folder
}
}

override suspend fun deleteFolder(id: String) {
foldersFlow.value = foldersFlow.value.filterNot { it.id == id }
}

override suspend fun getFolderById(id: String): NoteFolder? = foldersFlow.value.firstOrNull { it.id == id }

override suspend fun updateFolder(folder: NoteFolder) {
foldersFlow.value =
foldersFlow.value.map { existing ->
if (existing.id == folder.id) folder else existing
}
}
}
25 changes: 18 additions & 7 deletions app/src/main/java/com/itlab/notes/ui/NotesApp.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package com.itlab.notes.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.itlab.notes.ui.editor.editorScreen
import com.itlab.notes.ui.notes.DirectoryItemUi
import com.itlab.notes.ui.notes.NoteItemUi
import com.itlab.notes.ui.notes.NotesListActions
import com.itlab.notes.ui.notes.directoriesScreen
import com.itlab.notes.ui.notes.notesListScreen
import org.koin.androidx.compose.koinViewModel

@Composable
fun notesApp() {
val viewModel = remember { NotesViewModel() }
val viewModel: NotesViewModel = koinViewModel()
val state = viewModel.uiState

when (val screen = state.screen) {
NotesUiScreen.Directories -> {
directoriesScreen(
directories = state.directories,
onCreateDirectory = { name ->
viewModel.onEvent(NotesUiEvent.CreateDirectory(name))
},
onDeleteDirectory = { directory ->
viewModel.onEvent(NotesUiEvent.DeleteDirectory(directory.id))
},
onDirectoryClick = { directory ->
viewModel.onEvent(NotesUiEvent.OpenDirectory(directory))
},
Expand All @@ -28,11 +35,15 @@ fun notesApp() {
notesListScreen(
directoryName = directory.name,
notes = state.notes,
onBack = { viewModel.onEvent(NotesUiEvent.BackToDirectories) },
onAddNoteClick = { viewModel.onEvent(NotesUiEvent.CreateNote) },
onNoteClick = { note: NoteItemUi ->
viewModel.onEvent(NotesUiEvent.OpenNote(note))
},
actions =
NotesListActions(
onBack = { viewModel.onEvent(NotesUiEvent.BackToDirectories) },
onAddNoteClick = { viewModel.onEvent(NotesUiEvent.CreateNote) },
onNoteDelete = { note -> viewModel.onEvent(NotesUiEvent.DeleteNote(note.id)) },
onNoteClick = { note: NoteItemUi ->
viewModel.onEvent(NotesUiEvent.OpenNote(note))
},
),
)
}

Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/com/itlab/notes/ui/NotesUiContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,23 @@ sealed interface NotesUiEvent {

data object CreateNote : NotesUiEvent

data class CreateDirectory(
val name: String,
) : NotesUiEvent

data class DeleteDirectory(
val directoryId: String,
) : NotesUiEvent

data object BackToDirectoryNotes : NotesUiEvent

data class SaveNote(
val note: NoteItemUi,
) : NotesUiEvent

data class DeleteNote(
val noteId: String,
) : NotesUiEvent
}

interface NotesViewModelContract {
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/java/com/itlab/notes/ui/NotesUseCases.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.itlab.notes.ui

import com.itlab.domain.usecase.CreateFolderUseCase
import com.itlab.domain.usecase.CreateNoteUseCase
import com.itlab.domain.usecase.DeleteFolderUseCase
import com.itlab.domain.usecase.DeleteNoteUseCase
import com.itlab.domain.usecase.ObserveFoldersUseCase
import com.itlab.domain.usecase.ObserveNotesByFolderUseCase
import com.itlab.domain.usecase.UpdateNoteUseCase

data class NotesUseCases(
val createFolderUseCase: CreateFolderUseCase,
val deleteFolderUseCase: DeleteFolderUseCase,
val createNoteUseCase: CreateNoteUseCase,
val deleteNoteUseCase: DeleteNoteUseCase,
val updateNoteUseCase: UpdateNoteUseCase,
val observeNotesByFolderUseCase: ObserveNotesByFolderUseCase,
val observeFoldersUseCase: ObserveFoldersUseCase,
)
Loading
Loading