How do I retrieve the correct user ID when creating a login-enabled note-taking app using Jetpack Compose and Room Database in Android Studio?

huangapple go评论57阅读模式
英文:

How do I retrieve the correct user ID when creating a login-enabled note-taking app using Jetpack Compose and Room Database in Android Studio?

问题

抱歉,你的请求要求我只翻译文本,不回答问题。以下是你提供的文本的翻译:

无法检索正确的用户ID,这阻止我将备注分配给已登录的用户。

嘿,
在过去的两周里,我一直在使用Android Studio、Jetpack Compose和Room Database创建一个支持登录的记事应用程序时遇到了一个问题。我刚刚开始学习,这个问题让我很烦恼,因为我知道有经验的人可能会在5分钟内解决这个问题。

首先,我想提到我在一个论坛上找到了一个类似的问题链接,但提供的解决方案在我的情况下不起作用,我甚至尝试向ChatGPT寻求帮助,但仍然没有解决。

接下来,我将提供LoginRegisterViewModel、NotesViewModel和CreateNote Composable函数的代码,其中我调用了已登录的用户。对于代码的混乱,我表示抱歉,但那是我修复问题时的“最新版本”:

英文:

The inability to retrieve the correct user ID, which prevents me from assigning a note to the logged-in user.

Hey,
for the past two weeks, I've been struggling with an issue while creating a login-enabled note-taking application in Android Studio using Jetpack Compose and Room Database. I just started learning and this problem makes me crazy because i know someone with experience would fix this probably in 5 minutes.

First and foremost, I'd like to mention that I found a similar problem on a forum text, but the solution provided didn't work in my case, I even tried to ask ChatGPT for help and that's still not working.

Below, I'm providing the code for LoginRegisterViewModel, NotesViewModel, and the CreateNote Composable function where I invoke the logged-in user. Sorry for a mess in code but that's a "last version" of my code when i was trying to repair this :/

LoginRegisterViewModel:

class LoginRegisterViewModel(app: Application) : AndroidViewModel(app) {

    private val repo = UserRepository(app.applicationContext)
    private var userExist = false
    private val currentUser = MutableStateFlow<User?>(null)

    private var userId : Int? = null

    suspend fun checkPassword(username: String, password: String): Boolean {
        return withContext(Dispatchers.IO) {
            val user = getUserByLoginAndPassword(username, password)
            user != null
        }
    }

    fun getUserId(): Int? {
        println(userId)
        return userId
    }

    private fun setCurrentUser(user: User) {
        currentUser.value = user
        userId = user.id
    }

    suspend fun checkIfUserExists(loginValue: String): Boolean {
        return withContext(Dispatchers.IO) {
            val user = repo.getUserByLogin(loginValue)
            user != null
        }
    }
    private suspend fun getUserByLoginAndPassword(
        loginValue: String,
        passwordValue: String
    ): User? {
        return repo.getUserByLoginAndPassword(loginValue, passwordValue)
    }

    suspend fun login(loginValue: String, passwordValue: String): Boolean {
        return withContext(Dispatchers.IO) {
            val user = getUserByLoginAndPassword(loginValue, passwordValue)
            if (user != null) {
                setCurrentUser(user)
                userId = repo.getUserId(loginValue)
                println(currentUser.value)
                true
            } else {
                false
            }
        }
    }

    fun logout() {
        currentUser.value = null
    }


    fun registerUser(
        nameValue: String,
        loginValue: String,
        passwordValue: String,
        confirmPasswordValue: String
    ) {
        viewModelScope.launch(Dispatchers.IO) {
            if (passwordValue == confirmPasswordValue) {
                userExist = false
                val user = User(
                    nameValue = nameValue,
                    loginValue = loginValue,
                    passwordValue = passwordValue
                )
                repo.insert(user)
            } else {
                userExist = true
            }
        }
    }
}

NotesViewModel:

class NotesViewModel(
    private val repo: NotesRepository, private val userId: Int?
) : ViewModel() {

    val getNotesByUserId: List<Note> = runBlocking { repo.getNotesByUserId(userId) }

    fun deleteNotes(note: Note) {
        viewModelScope.launch(Dispatchers.IO) {
            repo.deleteNote(note)
        }
    }

    fun updateNote(note: Note) {
        viewModelScope.launch(Dispatchers.IO) {
            repo.updateNote(note)
        }
    }

    fun createNote(title: String, note: String, userId: Int?) {
        viewModelScope.launch(Dispatchers.IO) {
            repo.insertNote(Note(title = title, note = note, userId = userId))
            println(userId)
        }
    }

    suspend fun getNote(noteId: Int): Note? {
        return repo.getNoteById(noteId)
    }

}

@Suppress("UNCHECKED_CAST")
class NotesViewModelFactory(
    private val repo: NotesRepository, private val loginRegisterViewModel: LoginRegisterViewModel
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return NotesViewModel(repo, loginRegisterViewModel.getUserId()) as T
    }
}

CreateNote Composable function:

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter", "UnusedMaterialScaffoldPaddingParameter")
@Composable
fun CreateNote(
    navController: NavController,
    viewModel: NotesViewModel,
    userId: Int?
) {
    val currentNote = remember {
        mutableStateOf("")
    }

    val currentTitle = remember {
        mutableStateOf("")
    }

    val saveButtonState = remember {
        mutableStateOf(false)
    }

    val currentUser = remember {
        mutableStateOf(userId)
    }

    println(currentUser)

    NotepadTheme {
        Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
            Scaffold(
                topBar = {
                    AppBar(
                        title = "Create Note",
                        onIconClick = {

                            viewModel.createNote(
                                currentTitle.value,
                                currentNote.value,
                                currentUser.value
                            )
                            navController.popBackStack()
                        },
                        icon = {
                            Icon(
                                imageVector = ImageVector.vectorResource(R.drawable.save),
                                contentDescription = stringResource(R.string.save_note),
                                tint = Color.Black,
                            )
                        },
                        iconState = remember { mutableStateOf(true) }
                    )
                },
            ) {
                Column(
                    Modifier
                        .padding(12.dp)
                        .fillMaxSize()
                ) {

                    TextField(
                        value = currentTitle.value,
                        modifier = Modifier.fillMaxWidth(),
                        onValueChange = { value ->
                            currentTitle.value = value
                            saveButtonState.value =
                                currentTitle.value != "" && currentNote.value != ""
                        },
                        colors = TextFieldDefaults.textFieldColors(
                            cursorColor = Color.Black,
                            focusedLabelColor = Color.Black
                        ),
                        label = { Text(text = "Title") }
                    )
                    Spacer(modifier = Modifier.padding(12.dp))

                    TextField(
                        value = currentNote.value,
                        modifier = Modifier
                            .fillMaxHeight(0.5f)
                            .fillMaxWidth(),
                        onValueChange = { value ->
                            currentNote.value = value
                            saveButtonState.value =
                                currentTitle.value != "" && currentNote.value != ""
                        },
                        colors = TextFieldDefaults.textFieldColors(
                            cursorColor = Color.Black,
                            focusedLabelColor = Color.Black
                        ),
                        label = { Text(text = "Note") }
                    )
                }

            }
        }
    }
}

Thanks in advance for any kind of help!

答案1

得分: 1

在你的dao类中,你可以像这样做来通过用户名检索用户ID。

   @Query("SELECT userId FROM User WHERE username = :username")
   suspend fun getUserIdByUsername(username: String): Int?

然后将这个函数添加到你的repository中,接着在NotesViewModel中创建一个mutableStateOf,初始值为0,不要在notesViewModel的构造函数中传递userId,而是传递整个user对象。

class NotesViewModel(
    private val repo: NotesRepository, private val user: User?
) : ViewModel()

# 在NotesViewModel中创建一个mutableStateOf初始值为0

var userId = mutableStateOf(0)

# 在NotesViewModel中创建一个init块并从repository中调用该函数

init {
   userId = repo.getUserIdByUsername(user.username)
}

最后,在你的composable中,可以这样做,移除参数中的userId。

var userId by remember {
   mutableStateOf(viewModel.userId.value)
}

LaunchedEffect(viewModel.userId.value){
   userId = viewModel.userId.value
}

通过这种方式,你应该能够获取用户ID,并在后续的函数中使用它。

英文:

Okay so in your dao class you can do something like this to retrieve the user id by passing in the username .

   @Query("SELECT userId FROM User WHERE username = :username")
    suspend fun getUserIdByUsername(username: String): Int?

And then add this function to your repository
then in the NotesViewModel create a mutableStateOf Int as 0 and instead of passing the userId in the constructor of notesViewModel you must pass the whole user object

class NotesViewModel(
    private val repo: NotesRepository, private val user: User?
) : ViewModel()

#and then create a mutableStateOf Int and initialize it to zero in the NotesViewModel,

var userId = mutableStateOf(0)

#create a init block in the NotesViewModel and call the function from the #repository

init {
   userId = repo.getUserIdByUsername(user.username)
}


At last in your composable, you can do something like this
remove the userId from the parameters

var userId by remember{
   mutableStateOf(viewModel.userId.value)
}

LaunchedEffect(viewModel.userId.value){
userId = viewModel.userId.value
}

You should getTheUser id by this way and you can use it in further functions How do I retrieve the correct user ID when creating a login-enabled note-taking app using Jetpack Compose and Room Database in Android Studio?

答案2

得分: 1

I have found a way to pass the userId to the NotesList page.
首先删除我在第一个回答中提到的所有更改,还要从NotesViewModel类中删除user对象参数。

I saw in your code that in the LoginRegisterViewModel you had flow object of currentUser that retrieves the current user object from the database and I tested it to know if it is getting the value correctly, so you won't need any extra methods just to get the userId.

我在你的代码中看到,在LoginRegisterViewModel中有一个名为currentUser的flow对象,它从数据库中检索当前用户对象,我已经测试过它,以确保它正确地获取了值,因此你不需要额外的方法来获取userId。

In the NotesList composable add a parameter of userId and change the route as I did, to send the userId from login page to NotesList page in the route parameter.

在NotesList组件中添加一个userId参数,并像我一样更改路由,以将userId从登录页面发送到NotesList页面的路由参数中。

In the LoginPage, this is how you would navigate.

在LoginPage中,这是你如何导航的。

And at last in the NotesListPage change the value of userId which is in the NotesViewModel.

最后,在NotesListPage中更改NotesViewModel中的userId值。

These lines of code in the NotesViewModel would retrieve the notes list using the userId.

NotesViewModel中的这些代码将使用userId检索笔记列表。

After doing all these changes, an error occurred regarding the userId generation, so what it did was remove the Unique key parameter from the Entity class and kept it simple, the autoGenerate = true attribute is enough to generate new id's.

在进行所有这些更改之后,出现了关于userId生成的错误,所以它删除了实体类中的Unique key参数,并使其保持简单,autoGenerate = true属性足以生成新的id。

After doing this change, you would also have to change the RoomDatabase version and set up the migration as well, and if you don't want to mention the migration, just add .fallbackToDestructiveMigration().

进行此更改后,您还需要更改RoomDatabase的版本并设置迁移,如果不想提及迁移,只需添加.fallbackToDestructiveMigration()。

英文:

I have found a way to pass the userId to NotesList page.
First of all remove all the changes said by me in the first answer also remove the user object parameter from the NotesViewModel class.

I saw in your code that in the LoginRegisterViewModel you had flow object of currentUser that retrieves the current user object from the database and I tested it to know if it is getting the value correctly , so you won't need any extra methods just to get the userId.

   composable("notelist_page/{userId}",
                    arguments = listOf(navArgument("userId"){
                        type = NavType.IntType
                    })
                ) {
                    val userId = remember {
                        it.arguments?.getInt("userId")
                    }
                    if(userId != null){
                        NoteList(navController = navController,notesViewModel,userId)
                    }

                }

In the NotesList composable add a paramter of userId and change the route as I did , to send the userId from login page to NotesList page in the route paramter.

I the LoginPage this is how you would navigate

      // Sprawdź, czy hasło zostało uzupełnione i czy zgadza się z użytkownikiem
            if (passwordState.value.isNotEmpty() && validPassword) {
                         // Wywołaj funkcję logowania z ViewModel
                             runBlocking {
                                loginRegisterViewModel.login(
                                        loginState.value,
                                        passwordState.value
                                    )
                                }
          navController.navigate(
"notelist_page/${loginRegisterViewModel.currentUser.value!!.id}"
) {
      launchSingleTop = true
      }
}

And at last in the NotesListPage
change the value of userId which is in the NotesViewModel

fun NoteList(
    navController: NavController,
    viewModel: NotesViewModel,
    userId : Int
) {

    viewModel.userId.value = userId
    val notes = viewModel.getNotesByUserId

}

these lines of code in the NotesViewModel would retrive the notes list using the userId.


class NotesViewModel(
    private val repo: NotesRepository, private val userRepo: UserRepository
) : ViewModel() {


    var userId = mutableStateOf(0)

    val getNotesByUserId: List<Note> = runBlocking { 
                 repo.getNotesByUserId(userId.value) 
}

After doing all these changes an error occurred regarding the userId generation, so what it did was remove the Unique key parameter from the Entity class and kept it simple , the autoGenerate = true attribute is enough to generate new id's.

@Entity(tableName = Constants.USERS_TABLE_NAME)
data class User(
    @PrimaryKey(autoGenerate = true) val id: Int? = 0,
    @ColumnInfo(name = "nameValue") val nameValue: String,
    @ColumnInfo(name = "loginValue") val loginValue: String,
    @ColumnInfo(name = "passwordValue") val passwordValue: String
)

After doing this change you would also have to change the RoomDatabase version and setup the migration as well and if you don't want to mention the migration just add .fallbackToDestructiveMigration().


   private var db: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase {
            if (db == null) {
                db = Room.databaseBuilder(context, AppDatabase::class.java, Constants.DATABASE_NAME)
                //    .addMigrations(MIGRATION_2_3)
                    .fallbackToDestructiveMigration()
                    .build()
            }
            return db!!
        }

huangapple
  • 本文由 发表于 2023年5月21日 21:32:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76300167.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定