Compare commits
2 Commits
37e125945b
...
b4229c29c4
| Author | SHA1 | Date | |
|---|---|---|---|
|
b4229c29c4
|
|||
|
d05d93a0d7
|
@@ -13,6 +13,8 @@ The app is fully open source, lightweight, and can run on small devices like Ras
|
|||||||
|
|
||||||
You can either build the app from source or download the apk _(will be available soon)_
|
You can either build the app from source or download the apk _(will be available soon)_
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This work is marked <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0 1.0</a>
|
This work is marked <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0 1.0</a>
|
||||||
@@ -4,66 +4,27 @@ import android.os.Bundle
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.layout.statusBarsPadding
|
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextField
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import de.miaurizius.shap_planner.TokenStorage
|
import de.miaurizius.shap_planner.TokenStorage
|
||||||
import de.miaurizius.shap_planner.UserPreferences
|
import de.miaurizius.shap_planner.UserPreferences
|
||||||
import de.miaurizius.shap_planner.entities.Account
|
|
||||||
import de.miaurizius.shap_planner.network.SessionState
|
|
||||||
import de.miaurizius.shap_planner.room.AppDatabase
|
import de.miaurizius.shap_planner.room.AppDatabase
|
||||||
|
import de.miaurizius.shap_planner.ui.AppContent
|
||||||
import de.miaurizius.shap_planner.ui.theme.ShapPlannerTheme
|
import de.miaurizius.shap_planner.ui.theme.ShapPlannerTheme
|
||||||
import de.miaurizius.shap_planner.viewmodels.LoginViewModel
|
import de.miaurizius.shap_planner.viewmodels.LoginViewModel
|
||||||
import de.miaurizius.shap_planner.viewmodels.MainViewModel
|
import de.miaurizius.shap_planner.viewmodels.MainViewModel
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
// enableEdgeToEdge()
|
|
||||||
|
|
||||||
val prefs = UserPreferences(this)
|
val prefs = UserPreferences(this)
|
||||||
val loginViewModel = LoginViewModel(prefs, applicationContext)
|
val loginViewModel = LoginViewModel(prefs, applicationContext)
|
||||||
|
|
||||||
val database = AppDatabase.getDatabase(applicationContext)
|
val database = AppDatabase.getDatabase(applicationContext)
|
||||||
val dao = database.accountDao()
|
val dao = database.accountDao()
|
||||||
|
|
||||||
val tokenStorage = TokenStorage(applicationContext)
|
val tokenStorage = TokenStorage(applicationContext)
|
||||||
|
|
||||||
val mainViewModel = MainViewModel(dao, tokenStorage)
|
val mainViewModel = MainViewModel(dao, tokenStorage)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
@@ -71,215 +32,30 @@ class MainActivity : ComponentActivity() {
|
|||||||
val isLoggedIn by loginViewModel.isLoggedIn.collectAsState()
|
val isLoggedIn by loginViewModel.isLoggedIn.collectAsState()
|
||||||
val accountList by mainViewModel.accounts.collectAsState()
|
val accountList by mainViewModel.accounts.collectAsState()
|
||||||
val selectedAccount = mainViewModel.selectedAccount
|
val selectedAccount = mainViewModel.selectedAccount
|
||||||
var showLoginForNewAccount by remember { mutableStateOf(false) }
|
val showLoginForNewAccount = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
when {
|
BackHandler(enabled = showLoginForNewAccount.value && accountList.isNotEmpty()) {
|
||||||
|
showLoginForNewAccount.value = false
|
||||||
showLoginForNewAccount -> {
|
|
||||||
LoginScreen(
|
|
||||||
onLogin = { serverUrl, username, password ->
|
|
||||||
loginViewModel.login(serverUrl, username, password, mainViewModel)
|
|
||||||
showLoginForNewAccount = false
|
|
||||||
},
|
|
||||||
onBack = {
|
|
||||||
showLoginForNewAccount = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
accountList.isEmpty() -> {
|
|
||||||
LoginScreen(
|
|
||||||
onLogin = { serverUrl, username, password ->
|
|
||||||
loginViewModel.login(serverUrl, username, password, mainViewModel)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedAccount != null -> {
|
|
||||||
DashboardScreen(
|
|
||||||
account = selectedAccount,
|
|
||||||
onBack = { mainViewModel.logoutFromAccount() },
|
|
||||||
onDelete = { mainViewModel.deleteAccount(selectedAccount) },
|
|
||||||
sessionState = mainViewModel.sessionState,
|
|
||||||
onValidate = { mainViewModel.validateSession(selectedAccount) },
|
|
||||||
onSessionInvalid = { mainViewModel.logoutFromAccount() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
AccountSelectionScreen(
|
|
||||||
accounts = accountList,
|
|
||||||
onAccountClick = { account ->
|
|
||||||
mainViewModel.selectAccount(account)
|
|
||||||
},
|
|
||||||
onAddAccountClick = {
|
|
||||||
showLoginForNewAccount = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppContent(
|
||||||
|
isLoggedIn = isLoggedIn,
|
||||||
|
accountList = accountList,
|
||||||
|
selectedAccount = selectedAccount,
|
||||||
|
showLoginForNewAccount = showLoginForNewAccount.value,
|
||||||
|
onLogin = { server, user, pass ->
|
||||||
|
loginViewModel.login(server, user, pass, mainViewModel)
|
||||||
|
showLoginForNewAccount.value = false
|
||||||
|
},
|
||||||
|
onSelectAccount = { mainViewModel.selectAccount(it) },
|
||||||
|
onLogoutAccount = { mainViewModel.logoutFromAccount() },
|
||||||
|
onAddAccountClick = { showLoginForNewAccount.value = true },
|
||||||
|
onDeleteAccount = { mainViewModel.deleteAccount(selectedAccount!!) },
|
||||||
|
sessionState = mainViewModel.sessionState,
|
||||||
|
onValidateSession = { mainViewModel.validateSession(selectedAccount!!) },
|
||||||
|
onSessionInvalid = { mainViewModel.logoutFromAccount() }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun AccountSelectionScreen(accounts: List<Account>, onAccountClick: (Account) -> Unit, onAddAccountClick: () -> Unit) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(16.dp)
|
|
||||||
.statusBarsPadding()
|
|
||||||
.navigationBarsPadding(),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
||||||
) {
|
|
||||||
item {
|
|
||||||
Text("Wähle einen Account", style = MaterialTheme.typography.headlineSmall)
|
|
||||||
}
|
|
||||||
|
|
||||||
items(accounts) { account ->
|
|
||||||
Card(modifier = Modifier.fillMaxWidth().clickable{ onAccountClick(account) }) {
|
|
||||||
Row(modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
Box(modifier = Modifier.size(40.dp).background(Color.Gray, shape = CircleShape))
|
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
|
||||||
Column {
|
|
||||||
Text(text = account.name, fontWeight = FontWeight.Bold)
|
|
||||||
Text(text = account.wgName, style = MaterialTheme.typography.bodyMedium)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Button(
|
|
||||||
onClick = onAddAccountClick,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Text("Anderen Account hinzufügen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun LoginScreen(onLogin: (String, String, String) -> Unit, onBack: (() -> Unit)? = null) {
|
|
||||||
|
|
||||||
if (onBack != null) {
|
|
||||||
BackHandler {
|
|
||||||
onBack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverUrl by remember { mutableStateOf("") }
|
|
||||||
var username by remember { mutableStateOf("") }
|
|
||||||
var password by remember { mutableStateOf("") }
|
|
||||||
|
|
||||||
Column(modifier = Modifier.padding(16.dp).statusBarsPadding().navigationBarsPadding()) {
|
|
||||||
Text("Bitte anmelden")
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
//Home-Server
|
|
||||||
TextField(
|
|
||||||
value = serverUrl,
|
|
||||||
onValueChange = { serverUrl = it },
|
|
||||||
label = { Text("Server-URL") }
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
//Username
|
|
||||||
TextField(
|
|
||||||
value = username,
|
|
||||||
onValueChange = { username = it },
|
|
||||||
label = { Text("Nutzername") }
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
//Password
|
|
||||||
TextField(
|
|
||||||
value = password,
|
|
||||||
onValueChange = { password = it },
|
|
||||||
label = { Text("Passwort") }
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
Button(onClick = { if(serverUrl.isNotEmpty() && username.isNotEmpty() && password.isNotEmpty()) onLogin(
|
|
||||||
serverUrl,
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
) }) {
|
|
||||||
Text("Login")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DashboardScreen(
|
|
||||||
account: Account,
|
|
||||||
onBack: () -> Unit,
|
|
||||||
onDelete: () -> Unit,
|
|
||||||
sessionState: SessionState,
|
|
||||||
onValidate: () -> Unit,
|
|
||||||
onSessionInvalid: () -> Unit) {
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) { onValidate() }
|
|
||||||
|
|
||||||
when (sessionState) {
|
|
||||||
SessionState.Loading -> {
|
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
|
||||||
CircularProgressIndicator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SessionState.Valid -> {
|
|
||||||
BackHandler {
|
|
||||||
onBack()
|
|
||||||
}
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(16.dp)
|
|
||||||
.statusBarsPadding()
|
|
||||||
.navigationBarsPadding()
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Column {
|
|
||||||
Text(text = "Hallo, ${account.name}!", style = MaterialTheme.typography.headlineMedium)
|
|
||||||
Text(text = "WG: ${account.wgName}", style = MaterialTheme.typography.bodyLarge, color = Color.Gray)
|
|
||||||
}
|
|
||||||
Button(onClick = onBack) {
|
|
||||||
Text("Wechseln")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(5.dp))
|
|
||||||
|
|
||||||
Button(onClick = onDelete) {
|
|
||||||
Text("Löschen")
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.background(MaterialTheme.colorScheme.surfaceVariant, shape = MaterialTheme.shapes.medium),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Text("Hier kommen bald deine WG-Kosten hin 🚀")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SessionState.Invalid -> {
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
onSessionInvalid()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is SessionState.Error -> {
|
|
||||||
Text("Server error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,14 +4,13 @@ import retrofit2.Response
|
|||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Header
|
import retrofit2.http.Header
|
||||||
import retrofit2.http.Headers
|
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
|
|
||||||
data class LoginRequest(val username: String, val password: String)
|
data class LoginRequest(val username: String, val password: String)
|
||||||
data class LoginUser(val id: String, val username: String, val role: String, val avatarUrl: String?)
|
data class LoginUser(val id: String, val username: String, val role: String, val avatarUrl: String?)
|
||||||
data class LoginResponse(val access_token: String, val refresh_token: String, val user: LoginUser, val wgName: String)
|
data class LoginResponse(val access_token: String, val refresh_token: String, val user: LoginUser, val wgName: String)
|
||||||
|
|
||||||
data class RefreshRequest(val refreshToken: String)
|
data class RefreshRequest(val refresh_token: String)
|
||||||
data class RefreshResponse(val accessToken: String, val refreshToken: String)
|
data class RefreshResponse(val accessToken: String, val refreshToken: String)
|
||||||
|
|
||||||
interface APIService {
|
interface APIService {
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package de.miaurizius.shap_planner.ui
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import de.miaurizius.shap_planner.entities.Account
|
||||||
|
import de.miaurizius.shap_planner.network.SessionState
|
||||||
|
import de.miaurizius.shap_planner.ui.screens.AccountSelectionScreen
|
||||||
|
import de.miaurizius.shap_planner.ui.screens.DashboardScreen
|
||||||
|
import de.miaurizius.shap_planner.ui.screens.LoginScreen
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AppContent(
|
||||||
|
isLoggedIn: Boolean,
|
||||||
|
accountList: List<Account>,
|
||||||
|
selectedAccount: Account?,
|
||||||
|
showLoginForNewAccount: Boolean,
|
||||||
|
onLogin: (String, String, String) -> Unit,
|
||||||
|
onSelectAccount: (Account) -> Unit,
|
||||||
|
onLogoutAccount: () -> Unit,
|
||||||
|
onAddAccountClick: () -> Unit,
|
||||||
|
onDeleteAccount: () -> Unit,
|
||||||
|
sessionState: SessionState,
|
||||||
|
onValidateSession: () -> Unit,
|
||||||
|
onSessionInvalid: () -> Unit
|
||||||
|
) {
|
||||||
|
when {
|
||||||
|
showLoginForNewAccount -> LoginScreen(onLogin)
|
||||||
|
accountList.isEmpty() -> LoginScreen(onLogin)
|
||||||
|
selectedAccount != null -> DashboardScreen(
|
||||||
|
account = selectedAccount,
|
||||||
|
onBack = onLogoutAccount,
|
||||||
|
onDelete = onDeleteAccount,
|
||||||
|
sessionState = sessionState,
|
||||||
|
onValidate = onValidateSession,
|
||||||
|
onSessionInvalid = onSessionInvalid
|
||||||
|
)
|
||||||
|
else -> AccountSelectionScreen(
|
||||||
|
accounts = accountList,
|
||||||
|
onAccountClick = onSelectAccount,
|
||||||
|
onAddAccountClick = onAddAccountClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package de.miaurizius.shap_planner.ui.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import de.miaurizius.shap_planner.entities.Account
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AccountSelectionScreen(accounts: List<Account>, onAccountClick: (Account) -> Unit, onAddAccountClick: () -> Unit) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp)
|
||||||
|
.statusBarsPadding()
|
||||||
|
.navigationBarsPadding(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
Text("Wähle einen Account", style = MaterialTheme.typography.headlineSmall)
|
||||||
|
}
|
||||||
|
|
||||||
|
items(accounts) { account ->
|
||||||
|
Card(modifier = Modifier.fillMaxWidth().clickable{ onAccountClick(account) }) {
|
||||||
|
Row(modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Box(modifier = Modifier.size(40.dp).background(Color.Gray, shape = CircleShape))
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
Column {
|
||||||
|
Text(text = account.name, fontWeight = FontWeight.Bold)
|
||||||
|
Text(text = account.wgName, style = MaterialTheme.typography.bodyMedium)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Button(
|
||||||
|
onClick = onAddAccountClick,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("Anderen Account hinzufügen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package de.miaurizius.shap_planner.ui.screens
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import de.miaurizius.shap_planner.entities.Account
|
||||||
|
import de.miaurizius.shap_planner.network.SessionState
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DashboardScreen(
|
||||||
|
account: Account,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
onDelete: () -> Unit,
|
||||||
|
sessionState: SessionState,
|
||||||
|
onValidate: () -> Unit,
|
||||||
|
onSessionInvalid: () -> Unit) {
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) { onValidate() }
|
||||||
|
|
||||||
|
when (sessionState) {
|
||||||
|
SessionState.Loading -> {
|
||||||
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionState.Valid -> {
|
||||||
|
BackHandler {
|
||||||
|
onBack()
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp)
|
||||||
|
.statusBarsPadding()
|
||||||
|
.navigationBarsPadding()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = "Hallo, ${account.name}!",
|
||||||
|
style = MaterialTheme.typography.headlineMedium
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "WG: ${account.wgName}",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = Color.Gray
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Button(onClick = onBack) {
|
||||||
|
Text("Wechseln")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(5.dp))
|
||||||
|
|
||||||
|
Button(onClick = onDelete) {
|
||||||
|
Text("Löschen")
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
shape = MaterialTheme.shapes.medium
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text("Hier kommen bald deine WG-Kosten hin 🚀")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionState.Invalid -> {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
onSessionInvalid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is SessionState.Error -> {
|
||||||
|
Text("Server error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package de.miaurizius.shap_planner.ui.screens
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoginScreen(onLogin: (String, String, String) -> Unit, onBack: (() -> Unit)? = null) {
|
||||||
|
|
||||||
|
if (onBack != null) {
|
||||||
|
BackHandler {
|
||||||
|
onBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverUrl by remember { mutableStateOf("") }
|
||||||
|
var username by remember { mutableStateOf("") }
|
||||||
|
var password by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
Column(modifier = Modifier.padding(16.dp).statusBarsPadding().navigationBarsPadding()) {
|
||||||
|
Text("Bitte anmelden")
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
//Home-Server
|
||||||
|
TextField(
|
||||||
|
value = serverUrl,
|
||||||
|
onValueChange = { serverUrl = it },
|
||||||
|
label = { Text("Server-URL") }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
//Username
|
||||||
|
TextField(
|
||||||
|
value = username,
|
||||||
|
onValueChange = { username = it },
|
||||||
|
label = { Text("Nutzername") }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
//Password
|
||||||
|
TextField(
|
||||||
|
value = password,
|
||||||
|
onValueChange = { password = it },
|
||||||
|
label = { Text("Passwort") }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Button(onClick = { if(serverUrl.isNotEmpty() && username.isNotEmpty() && password.isNotEmpty()) onLogin(
|
||||||
|
serverUrl,
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
) }) {
|
||||||
|
Text("Login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ import kotlinx.coroutines.flow.SharingStarted
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.Locale
|
|
||||||
import java.util.Locale.getDefault
|
import java.util.Locale.getDefault
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user