新增登录

This commit is contained in:
2024-07-30 16:57:25 +08:00
parent f28236b365
commit 2c79195f44
8 changed files with 237 additions and 63 deletions

View File

@@ -31,22 +31,45 @@ import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import com.aiosman.riderpro.data.TestUserServiceImpl
import com.aiosman.riderpro.data.UserService
import com.aiosman.riderpro.ui.Navigation import com.aiosman.riderpro.ui.Navigation
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.index.NavigationItem import com.aiosman.riderpro.ui.index.NavigationItem
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.android.libraries.places.api.Places import com.google.android.libraries.places.api.Places
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private val scope = CoroutineScope(Dispatchers.Main)
suspend fun getAccount() {
//TODO apply token to client
if (!AppStore.rememberMe) {
return
}
val userService: UserService = TestUserServiceImpl()
userService.getMyAccount()
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
if (!Places.isInitialized()) { if (!Places.isInitialized()) {
Places.initialize(applicationContext, "AIzaSyDpgLDH1-SECw_pdjJq_msynq1XrxwgKVI") Places.initialize(applicationContext, "AIzaSyDpgLDH1-SECw_pdjJq_msynq1XrxwgKVI")
} }
AppStore.init(this)
enableEdgeToEdge() enableEdgeToEdge()
scope.launch {
getAccount()
var startDestination = NavigationRoute.Login.route
if (AppStore.token != null && AppStore.rememberMe) {
startDestination = NavigationRoute.Index.route
}
setContent { setContent {
Navigation() Navigation(startDestination)
}
} }
} }
} }

View File

@@ -2,8 +2,16 @@ package com.aiosman.riderpro.data
import com.aiosman.riderpro.test.TestDatabase import com.aiosman.riderpro.test.TestDatabase
data class UserAuth(
val id: Int,
val token: String? = null
)
interface UserService { interface UserService {
suspend fun getUserProfile(id: String): AccountProfile suspend fun getUserProfile(id: String): AccountProfile
suspend fun getMyAccount(): UserAuth
suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth
suspend fun logout()
} }
class TestUserServiceImpl : UserService { class TestUserServiceImpl : UserService {
@@ -15,4 +23,17 @@ class TestUserServiceImpl : UserService {
} }
return AccountProfile(0, 0, 0, "", "", "", "") return AccountProfile(0, 0, 0, "", "", "", "")
} }
override suspend fun getMyAccount(): UserAuth {
return UserAuth(1)
}
override suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth {
return UserAuth(1, "token")
}
override suspend fun logout() {
// do nothing
}
} }

View File

@@ -0,0 +1,34 @@
package com.aiosman.riderpro
import android.content.Context
import android.content.SharedPreferences
/**
* 持久化本地数据
*/
object AppStore {
private const val STORE_VERSION = 1
private const val PREFS_NAME = "app_prefs_$STORE_VERSION"
var token: String? = null
var rememberMe: Boolean = false
private lateinit var sharedPreferences: SharedPreferences
fun init(context: Context) {
sharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
this.loadData()
}
suspend fun saveData() {
// shared preferences
sharedPreferences.edit().apply {
putString("token", token)
putBoolean("rememberMe", rememberMe)
}.apply()
}
fun loadData() {
// shared preferences
token = sharedPreferences.getString("token", null)
rememberMe = sharedPreferences.getBoolean("rememberMe", false)
}
}

View File

@@ -64,14 +64,14 @@ sealed class NavigationRoute(
@Composable @Composable
fun NavigationController(navController: NavHostController) { fun NavigationController(navController: NavHostController,startDestination: String = NavigationRoute.Login.route) {
val navigationBarHeight = with(LocalDensity.current) { val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp() WindowInsets.navigationBars.getBottom(this).toDp()
} }
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = NavigationRoute.Index.route, startDestination = startDestination,
) { ) {
composable(route = NavigationRoute.Index.route) { composable(route = NavigationRoute.Index.route) {
CompositionLocalProvider( CompositionLocalProvider(
@@ -170,7 +170,7 @@ fun NavigationController(navController: NavHostController) {
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
fun Navigation() { fun Navigation(startDestination: String = NavigationRoute.Login.route) {
val navController = rememberNavController() val navController = rememberNavController()
SharedTransitionLayout { SharedTransitionLayout {
CompositionLocalProvider( CompositionLocalProvider(
@@ -178,7 +178,7 @@ fun Navigation() {
LocalSharedTransitionScope provides this@SharedTransitionLayout, LocalSharedTransitionScope provides this@SharedTransitionLayout,
) { ) {
Box { Box {
NavigationController(navController = navController) NavigationController(navController = navController,startDestination = startDestination)
} }
} }
} }

View File

@@ -66,14 +66,6 @@ fun IndexScreen() {
selected = isSelected, selected = isSelected,
onClick = { onClick = {
model.tabIndex = idx model.tabIndex = idx
// if (it.route == NavigationItem.Add.route || it.route == NavigationItem.Message.route) {
// systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
// } else {
// systemUiController.setStatusBarColor(
// Color.Transparent,
// darkIcons = true
// )
// }
}, },
colors = NavigationBarItemColors( colors = NavigationBarItemColors(
selectedTextColor = Color.Red, selectedTextColor = Color.Red,

View File

@@ -3,20 +3,25 @@ package com.aiosman.riderpro.ui.index.tabs.profile
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.navigation.NavController
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.PagingData import androidx.paging.PagingData
import com.aiosman.riderpro.AppStore
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.data.AccountProfile import com.aiosman.riderpro.data.AccountProfile
import com.aiosman.riderpro.data.AccountService import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.TestAccountServiceImpl import com.aiosman.riderpro.data.TestAccountServiceImpl
import com.aiosman.riderpro.data.MomentPagingSource import com.aiosman.riderpro.data.MomentPagingSource
import com.aiosman.riderpro.data.MomentRemoteDataSource import com.aiosman.riderpro.data.MomentRemoteDataSource
import com.aiosman.riderpro.data.TestMomentServiceImpl import com.aiosman.riderpro.data.TestMomentServiceImpl
import com.aiosman.riderpro.data.TestUserServiceImpl
import com.aiosman.riderpro.model.MomentItem import com.aiosman.riderpro.model.MomentItem
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
object MyProfileViewModel { object MyProfileViewModel {
val service: AccountService = TestAccountServiceImpl() val service: AccountService = TestAccountServiceImpl()
val userService = TestUserServiceImpl()
var profile by mutableStateOf<AccountProfile?>(null) var profile by mutableStateOf<AccountProfile?>(null)
var momentsFlow by mutableStateOf<Flow<PagingData<MomentItem>>?>(null) var momentsFlow by mutableStateOf<Flow<PagingData<MomentItem>>?>(null)
suspend fun loadProfile() { suspend fun loadProfile() {
@@ -32,6 +37,15 @@ object MyProfileViewModel {
} }
).flow ).flow
} }
suspend fun logout() {
service.getMyAccountProfile()
AppStore.apply {
token = null
rememberMe = false
saveData()
}
}
val followerCount get() = profile?.followerCount ?: 0 val followerCount get() = profile?.followerCount ?: 0
val followingCount get() = profile?.followingCount ?: 0 val followingCount get() = profile?.followingCount ?: 0
val bio get() = profile?.bio ?: "" val bio get() = profile?.bio ?: ""

View File

@@ -20,9 +20,17 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@@ -35,21 +43,29 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.AccountProfile import com.aiosman.riderpro.data.AccountProfile
import com.aiosman.riderpro.model.MomentItem import com.aiosman.riderpro.model.MomentItem
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@Composable @Composable
fun ProfilePage() { fun ProfilePage() {
val model = MyProfileViewModel val model = MyProfileViewModel
var expanded by remember { mutableStateOf(false) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
model.loadProfile() model.loadProfile()
} }
val moments = model.momentsFlow?.collectAsLazyPagingItems() val moments = model.momentsFlow?.collectAsLazyPagingItems()
val navController: NavController = LocalNavController.current
val scope = rememberCoroutineScope()
Box {
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -57,7 +73,43 @@ fun ProfilePage() {
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top, verticalArrangement = Arrangement.Top,
) { ) {
item { item {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
) {
Box(
modifier = Modifier.align(Alignment.TopEnd)
) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "",
modifier = Modifier.noRippleClickable {
expanded = true
}
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(onClick = {
scope.launch {
model.logout()
navController.navigate(NavigationRoute.Login.route) {
popUpTo(NavigationRoute.Index.route) {
inclusive = true
}
}
}
}, text = {
Text("Logout")
})
}
}
}
CarGroup() CarGroup()
model.profile?.let { model.profile?.let {
UserInformation(accountProfile = it) UserInformation(accountProfile = it)
@@ -75,6 +127,8 @@ fun ProfilePage() {
} }
} }
}
@Composable @Composable
fun CarGroup() { fun CarGroup() {
Column( Column(
@@ -193,7 +247,9 @@ fun UserInformationBasic(modifier: Modifier,accountProfile: AccountProfile) {
} }
Text( Text(
modifier = Modifier.widthIn(max =220.dp).padding(top = 8.dp), modifier = Modifier
.widthIn(max = 220.dp)
.padding(top = 8.dp),
text = accountProfile.nickName, text = accountProfile.nickName,
fontSize = 32.sp, fontSize = 32.sp,
color = Color.Black, color = Color.Black,
@@ -264,6 +320,7 @@ fun CommunicationOperatorGroup(isSelf: Boolean = true) {
.fillMaxWidth() .fillMaxWidth()
.padding(top = 16.dp), horizontalArrangement = Arrangement.Center .padding(top = 16.dp), horizontalArrangement = Arrangement.Center
) { ) {
if (!isSelf) {
Box( Box(
modifier = Modifier.size(width = 142.dp, height = 40.dp), modifier = Modifier.size(width = 142.dp, height = 40.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@@ -280,6 +337,8 @@ fun CommunicationOperatorGroup(isSelf: Boolean = true) {
style = TextStyle(fontWeight = FontWeight.Bold) style = TextStyle(fontWeight = FontWeight.Bold)
) )
} }
}
if (isSelf) { if (isSelf) {
Box( Box(
modifier = Modifier modifier = Modifier

View File

@@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
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.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -33,10 +34,17 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.AppStore
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.TestUserServiceImpl
import com.aiosman.riderpro.data.UserService
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -44,6 +52,25 @@ fun UserAuthScreen() {
var email by remember { mutableStateOf("") } var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") } var password by remember { mutableStateOf("") }
var rememberMe by remember { mutableStateOf(false) } var rememberMe by remember { mutableStateOf(false) }
var userService: UserService = TestUserServiceImpl()
val scope = rememberCoroutineScope()
val navController = LocalNavController.current
fun onLogin() {
scope.launch {
val authResp = userService.loginUserWithPassword(email, password)
if (authResp.token != null) {
AppStore.apply {
token = authResp.token
this.rememberMe = rememberMe
saveData()
}
navController.navigate(NavigationRoute.Index.route) {
popUpTo(NavigationRoute.Login.route) { inclusive = true }
}
}
}
}
StatusBarMaskLayout { StatusBarMaskLayout {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
@@ -106,7 +133,9 @@ fun UserAuthScreen() {
.height(48.dp), .height(48.dp),
text = "LET'S RIDE".uppercase(), text = "LET'S RIDE".uppercase(),
backgroundImage = R.mipmap.rider_pro_signup_red_bg backgroundImage = R.mipmap.rider_pro_signup_red_bg
) ) {
onLogin()
}
Spacer(modifier = Modifier.height(121.dp)) Spacer(modifier = Modifier.height(121.dp))
Text("or login with", color = Color(0xFF999999)) Text("or login with", color = Color(0xFF999999))
@@ -153,7 +182,9 @@ fun TextInputField(
Image( Image(
painter = painterResource(id = R.drawable.rider_pro_eye), painter = painterResource(id = R.drawable.rider_pro_eye),
contentDescription = "Password", contentDescription = "Password",
modifier = Modifier.size(24.dp).noRippleClickable { modifier = Modifier
.size(24.dp)
.noRippleClickable {
showPassword = !showPassword showPassword = !showPassword
}, },
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.Black) colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.Black)