- Introduced a new feature for managing tasks and notes associated with lessons. - Added a `NotesStore` class to handle local storage of notes in JSON format. - Implemented UI components for displaying, adding, editing, and deleting notes. - Enhanced the `HomeScreen`, `ScheduleScreen`, and `SubjectsScreen` to integrate notes functionality. - Updated the API to handle versioning and added error handling for outdated endpoints. - Improved the overall user experience with visual indicators for tasks and notes in the schedule.
60 lines
2.0 KiB
Kotlin
60 lines
2.0 KiB
Kotlin
package ritm.schedule.data
|
|
|
|
import android.content.Context
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.flow.StateFlow
|
|
import kotlinx.coroutines.sync.Mutex
|
|
import kotlinx.coroutines.sync.withLock
|
|
import kotlinx.coroutines.withContext
|
|
import kotlinx.serialization.Serializable
|
|
import java.io.File
|
|
import java.util.UUID
|
|
|
|
/**
|
|
* A homework task or a plain note the student pinned to one lesson: the subject,
|
|
* the lesson's date and start time. Tasks can be ticked off; notes just remind.
|
|
*/
|
|
@Serializable
|
|
data class Note(
|
|
val id: String = UUID.randomUUID().toString(),
|
|
/** "docId|group": notes of one group never show up in another. */
|
|
val group: String,
|
|
val subject: String,
|
|
val date: String,
|
|
val start: String,
|
|
val text: String,
|
|
val task: Boolean = true,
|
|
val done: Boolean = false,
|
|
val created: Long = System.currentTimeMillis(),
|
|
)
|
|
|
|
fun groupKey(g: SavedGroup) = "${g.docId}|${g.group}"
|
|
|
|
/** Notes live only on the device, in one small JSON file. */
|
|
class NotesStore(context: Context) {
|
|
private val file = File(context.filesDir, "notes.json")
|
|
private val lock = Mutex()
|
|
private val _notes = MutableStateFlow(read())
|
|
val notes: StateFlow<List<Note>> = _notes
|
|
|
|
private fun read(): List<Note> =
|
|
runCatching { json.decodeFromString<List<Note>>(file.readText()) }.getOrDefault(emptyList())
|
|
|
|
private suspend fun change(block: (List<Note>) -> List<Note>) = withContext(Dispatchers.IO) {
|
|
lock.withLock {
|
|
val next = block(_notes.value)
|
|
file.writeText(json.encodeToString(next))
|
|
_notes.value = next
|
|
}
|
|
}
|
|
|
|
suspend fun save(note: Note) = change { list ->
|
|
if (list.any { it.id == note.id }) list.map { if (it.id == note.id) note else it } else list + note
|
|
}
|
|
|
|
suspend fun delete(id: String) = change { list -> list.filterNot { it.id == id } }
|
|
|
|
suspend fun toggle(id: String) = change { list -> list.map { if (it.id == id) it.copy(done = !it.done) else it } }
|
|
}
|