feat(core, device-initialization): Netzwerk-Discovery verbessert, IP-Binding hinzugefügt und UI optimiert
Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
+2
-1
@@ -90,7 +90,8 @@ fun DesktopApp() {
|
||||
currentScreen is AppScreen.ConnectivityCheck ||
|
||||
currentScreen is AppScreen.Dashboard ||
|
||||
currentScreen is AppScreen.Profile ||
|
||||
currentScreen is AppScreen.ProfileOnboarding
|
||||
currentScreen is AppScreen.ProfileOnboarding ||
|
||||
currentScreen is AppScreen.Chat
|
||||
|
||||
if (!authState.isAuthenticated && !isAllowedScreen) {
|
||||
LaunchedEffect(currentScreen) {
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
package at.mocode.frontend.shell.desktop.screens.chat
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Send
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import at.mocode.frontend.core.designsystem.theme.AppColors
|
||||
import at.mocode.frontend.core.designsystem.theme.Dimens
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
data class ChatMessage(
|
||||
val id: String,
|
||||
val sender: String,
|
||||
val text: String,
|
||||
val time: String,
|
||||
val isFromMe: Boolean
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ChatScreen(
|
||||
onBack: () -> Unit
|
||||
) {
|
||||
var messageText by remember { mutableStateOf("") }
|
||||
val messages = remember { mutableStateListOf<ChatMessage>() }
|
||||
val timeFormatter = DateTimeFormatter.ofPattern("HH:mm")
|
||||
|
||||
// Mock initial messages
|
||||
LaunchedEffect(Unit) {
|
||||
if (messages.isEmpty()) {
|
||||
messages.add(ChatMessage("1", "Richter-Turm 1", "Startliste für Bewerb 5 ist fertig?", "10:45", false))
|
||||
messages.add(ChatMessage("2", "Meldestelle", "Ja, wird gerade gedruckt.", "10:46", true))
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surface)) {
|
||||
// Header
|
||||
Surface(
|
||||
tonalElevation = 2.dp,
|
||||
shadowElevation = 2.dp,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(Dimens.SpacingM),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
"Veranstaltungs-Chat",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Text(
|
||||
"LAN-Kanal: aktiv (3 Teilnehmer)",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = AppColors.Success
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chat Messages
|
||||
LazyColumn(
|
||||
modifier = Modifier.weight(1f).fillMaxWidth().padding(horizontal = Dimens.SpacingM),
|
||||
contentPadding = PaddingValues(vertical = Dimens.SpacingM),
|
||||
verticalArrangement = Arrangement.spacedBy(Dimens.SpacingS)
|
||||
) {
|
||||
items(messages) { msg ->
|
||||
ChatBubble(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Input Area
|
||||
Surface(
|
||||
tonalElevation = 4.dp,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(Dimens.SpacingM),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Dimens.SpacingS)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = messageText,
|
||||
onValueChange = { messageText = it },
|
||||
placeholder = { Text("Nachricht schreiben...") },
|
||||
modifier = Modifier.weight(1f),
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
maxLines = 3
|
||||
)
|
||||
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (messageText.isNotBlank()) {
|
||||
messages.add(
|
||||
ChatMessage(
|
||||
id = messages.size.toString(),
|
||||
sender = "Meldestelle",
|
||||
text = messageText,
|
||||
time = LocalTime.now().format(timeFormatter),
|
||||
isFromMe = true
|
||||
)
|
||||
)
|
||||
messageText = ""
|
||||
}
|
||||
},
|
||||
colors = IconButtonDefaults.iconButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||
),
|
||||
modifier = Modifier.size(48.dp)
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Filled.Send, contentDescription = "Senden")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChatBubble(msg: ChatMessage) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = if (msg.isFromMe) Alignment.End else Alignment.Start
|
||||
) {
|
||||
if (!msg.isFromMe) {
|
||||
Text(
|
||||
msg.sender,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
modifier = Modifier.padding(start = 4.dp, bottom = 2.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Surface(
|
||||
color = if (msg.isFromMe) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.secondaryContainer,
|
||||
shape = RoundedCornerShape(
|
||||
topStart = 12.dp,
|
||||
topEnd = 12.dp,
|
||||
bottomStart = if (msg.isFromMe) 12.dp else 0.dp,
|
||||
bottomEnd = if (msg.isFromMe) 0.dp else 12.dp
|
||||
),
|
||||
modifier = Modifier.widthIn(max = 400.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp)) {
|
||||
Text(msg.text, style = MaterialTheme.typography.bodyMedium)
|
||||
Text(
|
||||
msg.time,
|
||||
style = MaterialTheme.typography.labelSmall.copy(
|
||||
fontSize = 9.sp,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||
),
|
||||
modifier = Modifier.align(Alignment.End)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -86,7 +86,8 @@ fun DesktopMainLayout(
|
||||
HorizontalDivider(thickness = Dimens.BorderThin, color = MaterialTheme.colorScheme.outlineVariant)
|
||||
DesktopFooterBar(
|
||||
settings = onboardingSettings,
|
||||
onSetupClick = { onNavigate(AppScreen.DeviceInitialization) }
|
||||
onSetupClick = { onNavigate(AppScreen.DeviceInitialization) },
|
||||
onNavigate = onNavigate
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -42,6 +42,7 @@ import at.mocode.frontend.features.veranstalter.presentation.VeranstaltungKonfig
|
||||
import at.mocode.frontend.features.verein.presentation.VereinScreen
|
||||
import at.mocode.frontend.features.verein.presentation.VereinViewModel
|
||||
import at.mocode.frontend.features.zns.import.presentation.StammdatenImportScreen
|
||||
import at.mocode.frontend.shell.desktop.screens.chat.ChatScreen
|
||||
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterAuswahl
|
||||
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterDetail
|
||||
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterVerwaltungScreen
|
||||
@@ -341,6 +342,12 @@ fun DesktopContentArea(
|
||||
)
|
||||
}
|
||||
|
||||
is AppScreen.Chat -> {
|
||||
ChatScreen(
|
||||
onBack = onBack
|
||||
)
|
||||
}
|
||||
|
||||
is AppScreen.EntryManagement -> {
|
||||
val viewModel = koinViewModel<NennungViewModel>()
|
||||
NennungManagementScreen(viewModel = viewModel)
|
||||
|
||||
+24
-2
@@ -3,6 +3,7 @@ package at.mocode.frontend.shell.desktop.screens.layout.components
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Chat
|
||||
import androidx.compose.material.icons.filled.CloudDone
|
||||
import androidx.compose.material.icons.filled.CloudOff
|
||||
import androidx.compose.material.icons.filled.Dataset
|
||||
@@ -18,6 +19,7 @@ import androidx.compose.ui.unit.sp
|
||||
import at.mocode.frontend.core.designsystem.theme.AppColors
|
||||
import at.mocode.frontend.core.designsystem.theme.Dimens
|
||||
import at.mocode.frontend.core.domain.zns.ZnsImportProvider
|
||||
import at.mocode.frontend.core.navigation.AppScreen
|
||||
import at.mocode.frontend.core.network.ConnectivityTracker
|
||||
import at.mocode.frontend.core.network.discovery.NetworkDiscoveryService
|
||||
import at.mocode.frontend.features.device.initialization.domain.model.DeviceInitializationSettings
|
||||
@@ -28,7 +30,8 @@ import kotlin.time.Duration.Companion.milliseconds
|
||||
@Composable
|
||||
fun DesktopFooterBar(
|
||||
settings: DeviceInitializationSettings,
|
||||
onSetupClick: () -> Unit = {}
|
||||
onSetupClick: () -> Unit = {},
|
||||
onNavigate: (AppScreen) -> Unit = {}
|
||||
) {
|
||||
val connectivityTracker = koinInject<ConnectivityTracker>()
|
||||
val discoveryService = koinInject<NetworkDiscoveryService>()
|
||||
@@ -102,7 +105,26 @@ fun DesktopFooterBar(
|
||||
)
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Dimens.SpacingM)
|
||||
) {
|
||||
// Chat Trigger
|
||||
Button(
|
||||
onClick = { onNavigate(AppScreen.Chat) },
|
||||
contentPadding = PaddingValues(horizontal = Dimens.SpacingS, vertical = 0.dp),
|
||||
modifier = Modifier.height(22.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
),
|
||||
shape = MaterialTheme.shapes.small
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Filled.Chat, contentDescription = null, modifier = Modifier.size(12.dp))
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text("Chat", style = MaterialTheme.typography.labelSmall.copy(fontSize = 10.sp))
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "v2.4.0-rc1 | Desktop-Alpha",
|
||||
style = MaterialTheme.typography.labelSmall.copy(fontSize = 10.sp),
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
+44
@@ -180,6 +180,46 @@ fun Erfolgsscreen(email: String, onBack: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DownloadDesktopAppCard() {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(containerColor = AppColors.PrimaryContainer),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
"Meldestelle Desktop",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.OnPrimaryContainer
|
||||
)
|
||||
Text(
|
||||
"Laden Sie die professionelle Desktop-App für die Offline-Verwaltung Ihres Turniers herunter.",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = AppColors.OnPrimaryContainer.copy(alpha = 0.8f),
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { /* In POC: Zeigt Hinweis oder simuliert Download */ },
|
||||
colors = ButtonDefaults.buttonColors(containerColor = AppColors.Primary),
|
||||
modifier = Modifier.height(56.dp)
|
||||
) {
|
||||
Icon(Icons.Default.Description, contentDescription = null) // Verwende Description als Ersatz für Download
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Text("Desktop-App laden", style = MaterialTheme.typography.titleMedium)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LandingPage(
|
||||
onVeranstaltungClick: (Long) -> Unit,
|
||||
@@ -205,6 +245,10 @@ fun LandingPage(
|
||||
modifier = Modifier.fillMaxSize().padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
item {
|
||||
DownloadDesktopAppCard()
|
||||
}
|
||||
|
||||
item {
|
||||
Text(
|
||||
"Willkommen bei der Meldestelle Online",
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ fun main() {
|
||||
}
|
||||
|
||||
ComposeViewport("compose-target") {
|
||||
AppTheme {
|
||||
// Web-Shell wird hart auf Light-Mode gesetzt (Ablesbarkeit am Turnierplatz)
|
||||
AppTheme(darkTheme = false) {
|
||||
WebMainScreen()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user