Refactor code structure for improved readability and maintainability
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
This commit is contained in:
@@ -3245,7 +3245,7 @@ export default {
|
||||
const now = new Date();
|
||||
const currentTime = now.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||
const currentHHMM = currentTime.slice(0, 5);
|
||||
if (!this.trainingStart || !this.trainingEnd) {
|
||||
if (!this.trainingStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package de.tsschulz.tt_tagebuch.app.ui
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.MediaPlayer
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
@@ -96,6 +98,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlin.math.max
|
||||
import de.tsschulz.tt_tagebuch.app.AppDependencies
|
||||
import de.tsschulz.tt_tagebuch.R
|
||||
import de.tsschulz.tt_tagebuch.app.pdf.sharePdfFile
|
||||
import de.tsschulz.tt_tagebuch.app.pdf.writeMembersPhoneListPdf
|
||||
import de.tsschulz.tt_tagebuch.app.pdf.writeTrainingDaySummaryPdf
|
||||
@@ -156,6 +159,9 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/** Ab dieser Fensterbreite (dp): seitliche Navigation wie auf Tablet/Web. */
|
||||
private const val MAIN_NAV_RAIL_MIN_WIDTH_DP = 600
|
||||
@@ -4004,6 +4010,36 @@ private fun DiaryDetailScreen(
|
||||
val planStartTimes = remember(sortedPlan, entry.trainingStart) {
|
||||
calculatePlanStartLabels(sortedPlan, entry.trainingStart)
|
||||
}
|
||||
LaunchedEffect(entry.id, entry.trainingStart, entry.trainingEnd, sortedPlan, planStartTimes) {
|
||||
val playedMarks = mutableSetOf<String>()
|
||||
while (true) {
|
||||
val currentHHMM = currentMinuteLabel()
|
||||
val trainingStart = entry.trainingStart?.take(5)
|
||||
val trainingEnd = entry.trainingEnd?.take(5)
|
||||
|
||||
listOfNotNull(trainingStart, trainingEnd).forEach { bellTime ->
|
||||
val key = "bell:$bellTime"
|
||||
if (currentHHMM == bellTime && playedMarks.add(key)) {
|
||||
playRawSound(androidContext, R.raw.training_bell)
|
||||
}
|
||||
}
|
||||
|
||||
if (trainingStart != null) {
|
||||
sortedPlan
|
||||
.mapNotNull { item -> planStartTimes[item.id]?.take(5) }
|
||||
.filter { it != trainingStart }
|
||||
.distinct()
|
||||
.forEach { startTime ->
|
||||
val key = "item:$startTime"
|
||||
if (currentHHMM == startTime && playedMarks.add(key)) {
|
||||
playRawSound(androidContext, R.raw.plan_item_start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delay(1_000)
|
||||
}
|
||||
}
|
||||
if (expandedPlanActionsItemId != null && sortedPlan.none { it.id == expandedPlanActionsItemId }) {
|
||||
expandedPlanActionsItemId = null
|
||||
}
|
||||
@@ -6816,22 +6852,79 @@ private fun DiaryPlanQuickTextAction(
|
||||
}
|
||||
|
||||
private fun sameTrainingPlanScope(items: List<DiaryDateActivityItem>, item: DiaryDateActivityItem): List<DiaryDateActivityItem> {
|
||||
return items.filter { it.groupId == item.groupId }.sortedWith(::diaryPlanItemComparator)
|
||||
return items.sortedWith(::diaryPlanItemComparator)
|
||||
}
|
||||
|
||||
private fun calculatePlanStartLabels(items: List<DiaryDateActivityItem>, trainingStart: String?): Map<Int, String> {
|
||||
var current = normalizeTime(trainingStart)
|
||||
var globalCursor = normalizeTime(trainingStart)
|
||||
val groupCursors = mutableMapOf<Int, String>()
|
||||
val result = linkedMapOf<Int, String>()
|
||||
items.forEach { item ->
|
||||
current?.let { result[item.id] = it.take(5) }
|
||||
val delta = item.duration
|
||||
if (delta != null && delta > 0 && current != null) {
|
||||
current = addMinutesToTime(current, delta)
|
||||
val duration = item.duration?.takeIf { it > 0 } ?: 0
|
||||
val groupId = item.groupId
|
||||
if (groupId == null) {
|
||||
val start = latestTime(globalCursor, groupCursors.values)
|
||||
start?.let { result[item.id] = it.take(5) }
|
||||
val end = if (start != null && duration > 0) addMinutesToTime(start, duration) else start
|
||||
globalCursor = end
|
||||
if (end != null) {
|
||||
groupCursors.keys.toList().forEach { gid ->
|
||||
val cursor = groupCursors[gid]
|
||||
if (isTimeAfter(end, cursor)) {
|
||||
groupCursors[gid] = end
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val start = groupCursors[groupId] ?: globalCursor
|
||||
start?.let { result[item.id] = it.take(5) }
|
||||
val end = if (start != null && duration > 0) addMinutesToTime(start, duration) else start
|
||||
if (end != null) {
|
||||
groupCursors[groupId] = end
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun currentMinuteLabel(): String {
|
||||
return SimpleDateFormat("HH:mm", Locale.GERMANY).format(Date())
|
||||
}
|
||||
|
||||
private fun playRawSound(context: Context, resId: Int) {
|
||||
runCatching {
|
||||
MediaPlayer.create(context.applicationContext, resId)?.apply {
|
||||
setOnCompletionListener { player -> player.release() }
|
||||
setOnErrorListener { player, _, _ ->
|
||||
player.release()
|
||||
true
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun latestTime(base: String?, values: Collection<String>): String? {
|
||||
var latest = base
|
||||
values.forEach { candidate ->
|
||||
if (isTimeAfter(candidate, latest)) latest = candidate
|
||||
}
|
||||
return latest
|
||||
}
|
||||
|
||||
private fun isTimeAfter(candidate: String?, current: String?): Boolean {
|
||||
val c = timeToMinutes(candidate) ?: return false
|
||||
val v = timeToMinutes(current) ?: return true
|
||||
return c > v
|
||||
}
|
||||
|
||||
private fun timeToMinutes(value: String?): Int? {
|
||||
val v = normalizeTime(value) ?: return null
|
||||
val h = v.substring(0, 2).toIntOrNull() ?: return null
|
||||
val m = v.substring(3, 5).toIntOrNull() ?: return null
|
||||
return h * 60 + m
|
||||
}
|
||||
|
||||
private fun normalizeTime(value: String?): String? {
|
||||
val v = value?.trim().orEmpty()
|
||||
if (v.length >= 5 && v[2] == ':') return if (v.length >= 8) v.substring(0, 8) else "$v:00"
|
||||
@@ -6878,11 +6971,7 @@ private fun DiaryPlanEditableCard(
|
||||
val title = item.displayTitle(timeblockLabel).ifBlank {
|
||||
"${tr("mobile.activityFallback", "Aktivität")} ${item.id}"
|
||||
}
|
||||
val duration = when {
|
||||
!item.durationText.isNullOrBlank() -> item.durationText
|
||||
item.duration != null -> "${item.duration} min"
|
||||
else -> null
|
||||
}
|
||||
val duration = item.durationText?.trim()?.takeIf { it.isNotEmpty() }
|
||||
val groupLine = item.planGroup?.name?.takeIf { it.isNotBlank() }
|
||||
?: item.groupId?.let { gid -> planGroups.find { it.id == gid }?.name ?: "Gruppe $gid" }
|
||||
val showImageUrl = mainImageUrl ?: nestedImageUrl
|
||||
@@ -7000,6 +7089,7 @@ private fun DiaryPlanEditableCard(
|
||||
.forEach { ga ->
|
||||
val sub = ga.groupPredefinedActivity.displayLabel()
|
||||
val line = if (sub.isNotEmpty()) sub else "${tr("mobile.activityFallback", "Aktivität")} ${ga.id}"
|
||||
val nestedDuration = ga.durationText?.trim()?.takeIf { it.isNotEmpty() }
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(start = DiaryPlanColStart, top = 0.dp, bottom = 0.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@@ -7011,6 +7101,15 @@ private fun DiaryPlanEditableCard(
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
if (nestedDuration != null) {
|
||||
Text(
|
||||
nestedDuration,
|
||||
style = MaterialTheme.typography.caption,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.width(DiaryPlanColDuration),
|
||||
)
|
||||
}
|
||||
val nid = ga.id
|
||||
if (nid != null) {
|
||||
DiaryPlanQuickIconAction(
|
||||
|
||||
Binary file not shown.
BIN
mobile-app/composeApp/src/androidMain/res/raw/training_bell.mp3
Normal file
BIN
mobile-app/composeApp/src/androidMain/res/raw/training_bell.mp3
Normal file
Binary file not shown.
@@ -36,6 +36,8 @@ data class PredefinedActivitySummary(
|
||||
data class GroupActivitySummary(
|
||||
val id: Int? = null,
|
||||
val orderId: Int? = null,
|
||||
val duration: Int? = null,
|
||||
val durationText: String? = null,
|
||||
val groupPredefinedActivity: PredefinedActivitySummary? = null,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user