When understanding updating state, which of the fo...
# compose-desktop
v
When understanding updating state, which of the following two approaches is "correct"? My desktop app requires logging in, and I'm keeping the state of the app in an enum (UNAUTHENTICATED, AUTHENTICATED, etc). The user enters a username and password, and if it's correct, the app mode updates. Code in thread:
My underlying "ViewModel":
Copy code
class CorbelViewModel {
    var mode = mutableStateOf(Mode.UNAUTHENTICATED)

    private val _user = mutableStateOf(SubmitUser(username = null, password = null))
    val user: State<SubmitUser>
        get() = _user

    // these would actually call a Service to authenticate (via AWS Cognito)
    fun login(newUser: SubmitUser) {
        println("Updating user ${user.value} to $newUser")
        _user.value = newUser
    }

    fun logout() {
        _user.value = SubmitUser(null,null)
    }
}
And my composable LoginDialog function is called like this:
Copy code
val viewModel = CorbelViewModel()
 var mode by remember { viewModel.mode }
 val user = remember { viewModel.user }
 
LoginDialog(
                    onDismiss = { mode = Mode.UNAUTHENTICATED },
                    onSubmit = { viewModel.login(it); mode = Mode.BUSY; })
So which is correct:
var mode = mutableStateOf(Mode.UNAUTHENTICATED)
or the double fields of
private val _user
and
val user
? One requires
by remember
and the other requires
= remember
.
s
In general it's a decent idea to use double fields if you dont want outsides to be able to modify the attribute. But the
remember
parts are wrong: in generally you want to remember the ViewModel because otherwise it would get recreated every time your UI component is recomposed. On the other hand the individual states don't need to be remembered because they come from the already-remembered viewmodel.
Copy code
val vm = remember { CorbelViewModel() }
var mode by vm.mode // no need for remember
val user by vm.user  // both can use 'by'
val abc by remember { mutableStateOf("") } // this needs remember because this state doesn't originate from the viewModel.
Anyway, IMO the compose could should not touch
mode
. Instead, set it inside the login and logout functions. Besides, the two enum values UNAUTHENTICATED and BUSY sound like you're mixing two different kinds of information here. One is whether the user is authenticated or not, the other is whether there is a login currently in progress. If that is accurate then I suggest to split it in two.
v
Yes I worked all that out, eventually... Mostly because logout() didn't have any effect. I keep thinking in terms of classes, not functions. Now trying to find a way of handling authentication in a world where username + password isn't enough. I have AWS Cognito in the backend and think I might need a Web view in the front.
s
I don't know Cognito but Web-Login isn't that uncommon. If Cognito works like most others then the web login will redirect to a certain url like
../success?token=abc&scopes=abc...
which you need to parse and read the important information from. And then you likely need a mechanism to refresh the authentication token from time to time. It's a bit of work, but all possible 🙂
v
Work in progress! I've set the call back URL to point to localhost and I'm running an embedded ktor server in my Compose app.. Haven't tested it yet though, I forgot about https
I think I've bitten off more than I can chew. This is well beyond my skillset. I'll probably just retreat to my existing Web app (written in svelte) and stick to what I know.