finally got a fully working base

This commit is contained in:
Artemis 2025-03-07 16:16:21 +01:00
parent 975a8b9d8c
commit 3553659155
6 changed files with 199 additions and 46 deletions

View file

@ -6,18 +6,22 @@ import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import pet.catcomm.ui.compose.BottomBar
import pet.catcomm.ui.compose.Intro
import pet.catcomm.ui.compose.Peers
import pet.catcomm.ui.compose.SettingsPanel
import pet.catcomm.ui.compose.TopBar
import pet.catcomm.ui.theme.CatCommTheme
import pet.catcomm.ui.viewmodel.DataStoreViewModel
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -30,8 +34,11 @@ class MainActivity : ComponentActivity() {
}
@Composable
fun CatCommApp() {
fun CatCommApp(
dataStoreViewModel: DataStoreViewModel = viewModel(factory = DataStoreViewModel.Factory),
) {
val navController = rememberNavController()
val shouldShowIntro = dataStoreViewModel.settings.username.collectAsState().value == ""
val currentBackStack by navController.currentBackStackEntryAsState()
val currentCCBackStackDestination = currentBackStack?.destination
@ -39,24 +46,34 @@ fun CatCommApp() {
navBarTabs.find { tab -> tab.route == currentCCBackStackDestination?.route }
CatCommTheme {
Scaffold(
topBar = {
TopBar(tabTitle = currentCCDestination?.label)
},
bottomBar = {
BottomBar(
pages = navBarTabs,
currentPageRoute = currentCCDestination?.route,
onSelect = { selected ->
navController.navigateSingleTo(selected.route)
if (shouldShowIntro) {
Intro(
onConfirm = { name ->
if (name != "") {
dataStoreViewModel.changeUsername(name)
}
}
)
} else {
Scaffold(
topBar = {
TopBar(tabTitle = currentCCDestination?.label)
},
bottomBar = {
BottomBar(
pages = navBarTabs,
currentPageRoute = currentCCDestination?.route,
onSelect = { selected ->
navController.navigateSingleTo(selected.route)
}
)
}
) { innerPadding ->
CatCommNavHost(
navController = navController,
modifier = Modifier.padding(innerPadding),
)
}
) { innerPadding ->
CatCommNavHost(
navController = navController,
modifier = Modifier.padding(innerPadding),
)
}
}
}
@ -65,17 +82,23 @@ fun CatCommApp() {
fun CatCommNavHost(
navController: NavHostController,
modifier: Modifier = Modifier,
dataStoreViewModel: DataStoreViewModel = viewModel(factory = DataStoreViewModel.Factory),
) {
val settings = dataStoreViewModel.settings
NavHost(
navController = navController,
startDestination = Overview.route,
modifier = modifier,
) {
composable(route = Overview.route) {
Peers()
Peers(settings)
}
composable(route = Settings.route) {
SettingsPanel()
SettingsPanel(
settings = settings,
onSave = { dataStoreViewModel.changeUsername(it.username) }
)
}
}
}

View file

@ -0,0 +1,117 @@
package pet.catcomm.ui.compose
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
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.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import pet.catcomm.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Intro(
onConfirm: (name: String) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxSize()
.padding(8.dp),
) {
Column(
modifier = modifier
.weight(1f)
.fillMaxWidth(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(stringResource(R.string.intro_enter_a_username_label))
var username by rememberSaveable { mutableStateOf("") }
var error by rememberSaveable { mutableStateOf("") }
TextField(
value = username,
onValueChange = {
username = it
if (it != "" && error != "") {
error = ""
}
},
label = { Text(stringResource(R.string.intro_label_username_field)) },
placeholder = { Text(stringResource(R.string.intro_placeholder_username_field)) },
modifier = Modifier.padding(top = 16.dp),
supportingText = { Text(error) },
isError = error != "",
singleLine = true,
)
OutlinedButton(
onClick = {
if (username == "") {
error = "You need one."
} else {
onConfirm(username)
}
},
modifier = Modifier.padding(top = 32.dp),
) {
Text("Start CatComm")
}
}
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
var openDialog by remember { mutableStateOf(false) }
if (openDialog) {
BasicAlertDialog(
onDismissRequest = { openDialog = false },
) {
Card(
modifier = Modifier.padding(16.dp).fillMaxWidth().height(120.dp),
shape = RoundedCornerShape(16.dp),
) {
Text(
text = "Use your words, bottom",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.Center),
textAlign = TextAlign.Center,
)
}
}
}
TextButton(
onClick = { openDialog = true }
) {
Text("Need help choosing?")
}
}
}
}

View file

@ -10,15 +10,12 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.delay
import pet.catcomm.ui.theme.CatCommTheme
import pet.catcomm.ui.viewmodel.DataStoreViewModel
import pet.catcomm.ui.viewmodel.CatCommSettings
@Composable
fun Peer(name: String, modifier: Modifier = Modifier) {
@ -46,16 +43,10 @@ fun PeerPreview() {
@Composable
fun Peers(
settings: CatCommSettings,
modifier: Modifier = Modifier,
dataStoreViewModel: DataStoreViewModel = viewModel(factory = DataStoreViewModel.Factory),
) {
val username = dataStoreViewModel.username.collectAsStateWithLifecycle("")
LaunchedEffect(Unit) {
// Test: it'll change the username setting after 2s and this should refresh the UI as expected
delay(2000)
dataStoreViewModel.changeUsername("woof")
}
val username = settings.username.collectAsStateWithLifecycle("")
LazyColumn(modifier = modifier.padding(vertical = 4.dp)) {
items(items = listOf("nyx", "1686a", "8098", username.value)) { name ->
@ -63,11 +54,3 @@ fun Peers(
}
}
}
@Preview(showBackground = true)
@Composable
fun PeersPreview() {
CatCommTheme {
Peers()
}
}

View file

@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
@ -14,16 +15,21 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import pet.catcomm.ui.viewmodel.CatCommSettings
data class SettingsForm(val username: String)
@Composable
fun SettingsPanel(
modifier: Modifier = Modifier
settings: CatCommSettings,
onSave: (SettingsForm) -> Unit,
modifier: Modifier = Modifier,
) {
var usernameState by rememberSaveable { mutableStateOf("") }
var usernameState by rememberSaveable { mutableStateOf(settings.username.value) }
Column(
modifier = modifier
.fillMaxWidth()
.padding(8.dp)
.fillMaxWidth()
.padding(8.dp)
) {
TextField(
value = usernameState,
@ -31,7 +37,23 @@ fun SettingsPanel(
modifier = modifier.fillMaxWidth(),
label = { Text("Username") },
placeholder = { Text("i mean, you??") },
singleLine = true,
)
OutlinedButton(
onClick = {
onSave(
SettingsForm(
usernameState
)
)
},
modifier = modifier
.fillMaxWidth()
.padding(top = 8.dp)
) {
Text("Save settings")
}
}
}

View file

@ -16,13 +16,16 @@ private const val PREFERENCES = "settings"
const val SETTINGS_USERNAME_KEY = "cat_comm_username"
data class CatCommSettings(val username: StateFlow<String>)
class DataStoreViewModel(
private val prefs: SharedPreferences,
) : ViewModel(), OnSharedPreferenceChangeListener {
private var _username: MutableStateFlow<String> =
MutableStateFlow(prefs.getString(SETTINGS_USERNAME_KEY, "")!!)
val username: StateFlow<String>
get() = _username.asStateFlow()
val settings: CatCommSettings
get() = CatCommSettings(_username.asStateFlow())
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (sharedPreferences != prefs) {
@ -42,8 +45,10 @@ class DataStoreViewModel(
}
fun changeUsername(new: String) {
prefs.edit(true) {
putString(SETTINGS_USERNAME_KEY, new)
if (_username.value != new && new != "") {
prefs.edit(true) {
putString(SETTINGS_USERNAME_KEY, new)
}
}
}

View file

@ -2,4 +2,7 @@
<string name="app_name">CatComm</string>
<string name="comms_tab_label">Comms</string>
<string name="settings_tab_label">Settings</string>
<string name="intro_enter_a_username_label">To start, enter a username</string>
<string name="intro_placeholder_username_field">puppy :3</string>
<string name="intro_label_username_field">Your username?</string>
</resources>