refactor: centralize domain models into core.model module
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidMultiplatformLibrary)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
iosArm64()
|
||||
iosSimulatorArm64()
|
||||
|
||||
jvm()
|
||||
|
||||
js {
|
||||
browser()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalWasmDsl::class)
|
||||
wasmJs {
|
||||
browser()
|
||||
}
|
||||
|
||||
androidLibrary {
|
||||
namespace = "fr.ajaury.gwenedeg.core.model"
|
||||
compileSdk =
|
||||
libs.versions.androidCompileSdk
|
||||
.get()
|
||||
.toInt()
|
||||
minSdk =
|
||||
libs.versions.androidMinSdk
|
||||
.get()
|
||||
.toInt()
|
||||
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonTest.dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package fr.ajaury.gwenedeg.core.model
|
||||
|
||||
/**
|
||||
* [translation] is the optional translation of [transcription] in the user's language
|
||||
* (e.g. French), shown alongside the original.
|
||||
*/
|
||||
data class Phrase(
|
||||
val transcription: String,
|
||||
val translation: String? = null,
|
||||
)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package fr.ajaury.gwenedeg.records.domain
|
||||
package fr.ajaury.gwenedeg.core.model
|
||||
|
||||
data class Record(
|
||||
val id: Int,
|
||||
@@ -42,6 +42,7 @@ kotlin {
|
||||
// Core modules
|
||||
implementation(projects.core.coroutines)
|
||||
implementation(projects.core.logging)
|
||||
implementation(projects.core.model)
|
||||
|
||||
// Coroutines
|
||||
implementation(libs.kotlinx.coroutinesCore)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.data
|
||||
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
/**
|
||||
@@ -14,23 +12,23 @@ internal class LrcParser {
|
||||
private val timeTag = Regex("""\[(\d{1,2}):(\d{2})[.:](\d{2,3})]""")
|
||||
private val leadingTimeTags = Regex("""^(?:\[\d{1,2}:\d{2}[.:]\d{2,3}])+""")
|
||||
|
||||
fun parse(content: String): Subtitle {
|
||||
val lines = content
|
||||
fun parse(content: String): List<TimeCodedText> =
|
||||
content
|
||||
.lineSequence()
|
||||
.flatMap { line -> parseLine(line) }
|
||||
.sortedBy { it.startTime }
|
||||
.toList()
|
||||
return Subtitle(lines)
|
||||
}
|
||||
|
||||
private fun parseLine(line: String): List<SubtitleLine> {
|
||||
private fun parseLine(line: String): List<TimeCodedText> {
|
||||
val tags = leadingTimeTags.find(line) ?: return emptyList()
|
||||
val text = line.substring(tags.range.last + 1).trim()
|
||||
return timeTag
|
||||
.findAll(tags.value)
|
||||
.mapNotNull { match ->
|
||||
SubtitleLine(startTime = match.toMs().milliseconds, text = text)
|
||||
.takeIf { it.text.isNotBlank() }
|
||||
TimeCodedText(
|
||||
startTime = match.toMs().milliseconds,
|
||||
text = text,
|
||||
).takeIf { text.isNotBlank() }
|
||||
}.toList()
|
||||
}
|
||||
|
||||
|
||||
+17
-10
@@ -2,9 +2,11 @@ package fr.ajaury.gwenedeg.subtitle.data
|
||||
|
||||
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
|
||||
import fr.ajaury.gwenedeg.core.logging.domain.Logger
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
|
||||
import fr.ajaury.gwenedeg.core.model.Phrase
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleRepository
|
||||
import fr.ajaury.gwenedeg.subtitle.model.CantGetSubtitleException
|
||||
import fr.ajaury.gwenedeg.subtitle.model.Subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class LrcSubtitleRepository(
|
||||
@@ -13,28 +15,33 @@ internal class LrcSubtitleRepository(
|
||||
private val logger: Logger,
|
||||
) : SubtitleRepository {
|
||||
override suspend fun getSubtitle(
|
||||
content: String,
|
||||
transcriptionContent: String,
|
||||
translationContent: String?,
|
||||
): Subtitle =
|
||||
withContext(dispatcherProvider.default) {
|
||||
try {
|
||||
val subtitle = parser.parse(content = content)
|
||||
val transcription = parser.parse(content = transcriptionContent)
|
||||
val translation = translationContent?.let { parser.parse(content = it) }
|
||||
subtitle.mergeTranslation(translation)
|
||||
transcription.mergeTranslation(translation)
|
||||
} catch (exception: Exception) {
|
||||
val message = "Failed to load subtitle: $content"
|
||||
val message = "Failed to load subtitle: $transcriptionContent"
|
||||
logger.error(message = message, throwable = exception)
|
||||
throw CantGetSubtitleException(message)
|
||||
}
|
||||
}
|
||||
|
||||
/** Attaches each [translation] line's text to the matching original line, paired by timestamp. */
|
||||
private fun Subtitle.mergeTranslation(translation: Subtitle?): Subtitle {
|
||||
if (translation == null) return this
|
||||
val translationByStart = translation.lines.associate { it.startTime to it.text }
|
||||
private fun List<TimeCodedText>.mergeTranslation(translation: List<TimeCodedText>?): Subtitle {
|
||||
val translationByStart = translation?.associate { it.startTime to it.text } ?: emptyMap()
|
||||
return Subtitle(
|
||||
lines = lines.map { line ->
|
||||
line.copy(translation = translationByStart[line.startTime])
|
||||
lines = this.map {
|
||||
SubtitleLine(
|
||||
startTime = it.startTime,
|
||||
phrase = Phrase(
|
||||
transcription = it.text,
|
||||
translation = translationByStart[it.startTime],
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.data
|
||||
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* A single timed text cue: [text] becomes active at [startTime].
|
||||
*/
|
||||
data class TimeCodedText(
|
||||
val startTime: Duration,
|
||||
val text: String,
|
||||
)
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.domain
|
||||
|
||||
import fr.ajaury.gwenedeg.subtitle.model.Subtitle
|
||||
import kotlin.time.Duration
|
||||
|
||||
class GetCurrentSubtitleIndexUseCase {
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.domain
|
||||
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* A single timed subtitle cue: [text] becomes active at [startTime]. [translation] is the optional
|
||||
* translation of [text] in the user's language (e.g. French), shown alongside the original.
|
||||
*/
|
||||
data class SubtitleLine(
|
||||
val startTime: Duration,
|
||||
val text: String,
|
||||
val translation: String? = null,
|
||||
)
|
||||
+4
-2
@@ -1,12 +1,14 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.domain
|
||||
|
||||
import fr.ajaury.gwenedeg.subtitle.model.Subtitle
|
||||
|
||||
interface SubtitleRepository {
|
||||
/**
|
||||
* Parses the subtitle [content]. When [translationContent] is provided, each line is matched to
|
||||
* Parses the subtitle [transcriptionContent]. When [translationContent] is provided, each line is matched to
|
||||
* its translation by timestamp.
|
||||
*/
|
||||
suspend fun getSubtitle(
|
||||
content: String,
|
||||
transcriptionContent: String,
|
||||
translationContent: String? = null,
|
||||
): Subtitle
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.domain
|
||||
package fr.ajaury.gwenedeg.subtitle.model
|
||||
|
||||
data class Subtitle(
|
||||
val lines: List<SubtitleLine>,
|
||||
@@ -0,0 +1,12 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.model
|
||||
|
||||
import fr.ajaury.gwenedeg.core.model.Phrase
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* A single timed subtitle cue: [phrase] becomes active at [startTime].
|
||||
*/
|
||||
data class SubtitleLine(
|
||||
val startTime: Duration,
|
||||
val phrase: Phrase,
|
||||
)
|
||||
+6
-5
@@ -1,8 +1,9 @@
|
||||
package fr.ajaury.gwenedeg.subtitle
|
||||
|
||||
import fr.ajaury.gwenedeg.core.model.Phrase
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.GetCurrentSubtitleIndexUseCase
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.subtitle.model.Subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
@@ -14,9 +15,9 @@ class GetCurrentSubtitleIndexUseCaseTest {
|
||||
Subtitle(
|
||||
lines =
|
||||
listOf(
|
||||
SubtitleLine(startTime = 0.milliseconds, text = "Demat deoc'h !"),
|
||||
SubtitleLine(startTime = 4310.milliseconds, text = "Kenavo !"),
|
||||
SubtitleLine(startTime = 7350.milliseconds, text = "Kenavo emberr !"),
|
||||
SubtitleLine(startTime = 0.milliseconds, phrase = Phrase(transcription = "Demat deoc'h !")),
|
||||
SubtitleLine(startTime = 4310.milliseconds, phrase = Phrase(transcription = "Kenavo !")),
|
||||
SubtitleLine(startTime = 7350.milliseconds, phrase = Phrase(transcription = "Kenavo emberr !")),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -23,31 +23,31 @@ class LrcParserTest {
|
||||
|
||||
@Test
|
||||
fun ignores_metadata_tags_and_keeps_only_timed_cues() {
|
||||
val subtitle = parser.parse(sample)
|
||||
val lines = parser.parse(sample)
|
||||
|
||||
assertEquals(3, subtitle.lines.size)
|
||||
assertEquals(3, lines.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun converts_timestamp_to_milliseconds() {
|
||||
val subtitle = parser.parse(sample)
|
||||
val lines = parser.parse(sample)
|
||||
|
||||
assertEquals(0.milliseconds, subtitle.lines[0].startTime)
|
||||
assertEquals(4310.milliseconds, subtitle.lines[1].startTime)
|
||||
assertEquals(7350.milliseconds, subtitle.lines[2].startTime)
|
||||
assertEquals(0.milliseconds, lines[0].startTime)
|
||||
assertEquals(4310.milliseconds, lines[1].startTime)
|
||||
assertEquals(7350.milliseconds, lines[2].startTime)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parses_three_digit_fraction_as_milliseconds() {
|
||||
val subtitle = parser.parse("[00:01.250]Hello")
|
||||
val lines = parser.parse("[00:01.250]Hello")
|
||||
|
||||
assertEquals(1250.milliseconds, subtitle.lines.single().startTime)
|
||||
assertEquals(1250.milliseconds, lines.single().startTime)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sorts_cues_ascending_by_start() {
|
||||
val subtitle = parser.parse("[00:05.00]Later\n[00:01.00]Earlier")
|
||||
val lines = parser.parse("[00:05.00]Later\n[00:01.00]Earlier")
|
||||
|
||||
assertEquals(listOf("Earlier", "Later"), subtitle.lines.map { it.text })
|
||||
assertEquals(listOf("Earlier", "Later"), lines.map { it.text })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ include(":shared")
|
||||
include(":core:audioplayer")
|
||||
include(":core:coroutines")
|
||||
include(":core:logging")
|
||||
include(":core:model")
|
||||
|
||||
// Data modules
|
||||
include(":data:subtitle")
|
||||
|
||||
@@ -62,6 +62,7 @@ kotlin {
|
||||
implementation(projects.core.audioplayer)
|
||||
implementation(projects.core.coroutines)
|
||||
implementation(projects.core.logging)
|
||||
implementation(projects.core.model)
|
||||
|
||||
// Data modules
|
||||
implementation(projects.data.subtitle)
|
||||
|
||||
+3
-3
@@ -1,15 +1,15 @@
|
||||
package fr.ajaury.gwenedeg.player.data
|
||||
|
||||
import fr.ajaury.gwenedeg.core.logging.domain.Logger
|
||||
import fr.ajaury.gwenedeg.core.model.Record
|
||||
import fr.ajaury.gwenedeg.player.domain.AudioPlayer
|
||||
import fr.ajaury.gwenedeg.player.domain.PlaybackRepository
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||
import fr.ajaury.gwenedeg.records.domain.Record
|
||||
import fr.ajaury.gwenedeg.resourcereader.data.domain.ResourceReader
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.GetCurrentSubtitleIndexUseCase
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleRepository
|
||||
import fr.ajaury.gwenedeg.subtitle.model.Subtitle
|
||||
import gwenedeg.shared.generated.resources.Res
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -91,7 +91,7 @@ internal class PlaybackRepositoryImpl(
|
||||
val translationContent = record.translationSubtitleResourcePath
|
||||
?.let { resourceReader.read(resourcePath = it) }
|
||||
subtitleRepository.getSubtitle(
|
||||
content = content,
|
||||
transcriptionContent = content,
|
||||
translationContent = translationContent,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package fr.ajaury.gwenedeg.player.domain
|
||||
|
||||
import fr.ajaury.gwenedeg.core.model.Record
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||
import fr.ajaury.gwenedeg.records.domain.Record
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.model.Subtitle
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlin.time.Duration
|
||||
|
||||
@@ -22,11 +22,12 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import fr.ajaury.gwenedeg.core.model.Phrase
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||
import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerUiState
|
||||
import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerViewModel
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
@@ -133,9 +134,27 @@ fun PlayerScreen(
|
||||
|
||||
private val previewSubtitleLines =
|
||||
listOf(
|
||||
SubtitleLine(startTime = 0.milliseconds, text = "Demat deoc'h !", translation = "Bonjour !"),
|
||||
SubtitleLine(startTime = 4310.milliseconds, text = "Kenavo !", translation = "Au revoir !"),
|
||||
SubtitleLine(startTime = 7350.milliseconds, text = "Kenavo emberr !", translation = "À ce soir !"),
|
||||
SubtitleLine(
|
||||
startTime = 0.milliseconds,
|
||||
phrase = Phrase(
|
||||
transcription = "Demat deoc'h !",
|
||||
translation = "Bonjour !",
|
||||
),
|
||||
),
|
||||
SubtitleLine(
|
||||
startTime = 4310.milliseconds,
|
||||
phrase = Phrase(
|
||||
transcription = "Kenavo !",
|
||||
translation = "Au revoir !",
|
||||
),
|
||||
),
|
||||
SubtitleLine(
|
||||
startTime = 7350.milliseconds,
|
||||
phrase = Phrase(
|
||||
transcription = "Kenavo emberr !",
|
||||
translation = "À ce soir !",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@Preview
|
||||
|
||||
@@ -27,7 +27,8 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.core.model.Phrase
|
||||
import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||
import kotlin.math.abs
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
@@ -132,12 +133,12 @@ private fun Subtitle(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
MainTextLine(
|
||||
text = line.text,
|
||||
TranscriptionLine(
|
||||
text = line.phrase.transcription,
|
||||
isCurrent = isCurrent,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
line.translation?.let { translation ->
|
||||
line.phrase.translation?.let { translation ->
|
||||
TranslationLine(
|
||||
text = translation,
|
||||
isCurrent = isCurrent,
|
||||
@@ -148,7 +149,7 @@ private fun Subtitle(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainTextLine(
|
||||
private fun TranscriptionLine(
|
||||
text: String,
|
||||
isCurrent: Boolean,
|
||||
contentColor: Color,
|
||||
@@ -191,9 +192,27 @@ private fun TranslationLine(
|
||||
|
||||
private val previewSubtitleLines =
|
||||
listOf(
|
||||
SubtitleLine(startTime = 0.milliseconds, text = "Demat deoc'h !", translation = "Bonjour !"),
|
||||
SubtitleLine(startTime = 4310.milliseconds, text = "Kenavo !", translation = "Au revoir !"),
|
||||
SubtitleLine(startTime = 7350.milliseconds, text = "Kenavo emberr !", translation = "À ce soir !"),
|
||||
SubtitleLine(
|
||||
startTime = 0.milliseconds,
|
||||
phrase = Phrase(
|
||||
transcription = "Demat deoc'h !",
|
||||
translation = "Bonjour !",
|
||||
),
|
||||
),
|
||||
SubtitleLine(
|
||||
startTime = 4310.milliseconds,
|
||||
phrase = Phrase(
|
||||
transcription = "Kenavo !",
|
||||
translation = "Au revoir !",
|
||||
),
|
||||
),
|
||||
SubtitleLine(
|
||||
startTime = 7350.milliseconds,
|
||||
phrase = Phrase(
|
||||
transcription = "Kenavo emberr !",
|
||||
translation = "À ce soir !",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@Preview
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package fr.ajaury.gwenedeg.player.ui.viewmodel
|
||||
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine
|
||||
|
||||
data class PlayerUiState(
|
||||
val recordTitle: String = "",
|
||||
|
||||
+1
-1
@@ -3,10 +3,10 @@ package fr.ajaury.gwenedeg.player.ui.viewmodel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import fr.ajaury.gwenedeg.core.logging.domain.Logger
|
||||
import fr.ajaury.gwenedeg.core.model.Record
|
||||
import fr.ajaury.gwenedeg.player.domain.AudioSessionManager
|
||||
import fr.ajaury.gwenedeg.player.domain.PlaybackRepository
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.records.domain.Record
|
||||
import fr.ajaury.gwenedeg.records.domain.RecordRepository
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
package fr.ajaury.gwenedeg.records.data
|
||||
|
||||
import fr.ajaury.gwenedeg.records.domain.Record
|
||||
import fr.ajaury.gwenedeg.records.domain.RecordRepository
|
||||
import fr.ajaury.gwenedeg.core.model.Record
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.ajaury.gwenedeg.records.domain
|
||||
|
||||
import fr.ajaury.gwenedeg.core.model.Record
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface RecordRepository {
|
||||
|
||||
@@ -14,7 +14,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fr.ajaury.gwenedeg.records.domain.Record
|
||||
import fr.ajaury.gwenedeg.core.model.Record
|
||||
|
||||
@Composable
|
||||
fun RecordView(
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package fr.ajaury.gwenedeg.records.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeContentPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
@@ -14,7 +12,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import fr.ajaury.gwenedeg.records.domain.Record
|
||||
import fr.ajaury.gwenedeg.core.model.Record
|
||||
import fr.ajaury.gwenedeg.records.ui.viewmodel.RecordsViewModel
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
|
||||
+1
-1
@@ -2,8 +2,8 @@ package fr.ajaury.gwenedeg.records.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import fr.ajaury.gwenedeg.records.domain.Record
|
||||
import fr.ajaury.gwenedeg.records.domain.RecordRepository
|
||||
import fr.ajaury.gwenedeg.core.model.Record
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
Reference in New Issue
Block a user