新增登录

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.navigation.NavHostController
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.NavigationRoute
import com.aiosman.riderpro.ui.index.NavigationItem
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.android.libraries.places.api.Places
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
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?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
if (!Places.isInitialized()) {
Places.initialize(applicationContext, "AIzaSyDpgLDH1-SECw_pdjJq_msynq1XrxwgKVI")
}
AppStore.init(this)
enableEdgeToEdge()
scope.launch {
getAccount()
var startDestination = NavigationRoute.Login.route
if (AppStore.token != null && AppStore.rememberMe) {
startDestination = NavigationRoute.Index.route
}
setContent {
Navigation()
Navigation(startDestination)
}
}
}
}

View File

@@ -2,8 +2,16 @@ package com.aiosman.riderpro.data
import com.aiosman.riderpro.test.TestDatabase
data class UserAuth(
val id: Int,
val token: String? = null
)
interface UserService {
suspend fun getUserProfile(id: String): AccountProfile
suspend fun getMyAccount(): UserAuth
suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth
suspend fun logout()
}
class TestUserServiceImpl : UserService {
@@ -15,4 +23,17 @@ class TestUserServiceImpl : UserService {
}
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
fun NavigationController(navController: NavHostController) {
fun NavigationController(navController: NavHostController,startDestination: String = NavigationRoute.Login.route) {
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
}
NavHost(
navController = navController,
startDestination = NavigationRoute.Index.route,
startDestination = startDestination,
) {
composable(route = NavigationRoute.Index.route) {
CompositionLocalProvider(
@@ -170,7 +170,7 @@ fun NavigationController(navController: NavHostController) {
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun Navigation() {
fun Navigation(startDestination: String = NavigationRoute.Login.route) {
val navController = rememberNavController()
SharedTransitionLayout {
CompositionLocalProvider(
@@ -178,7 +178,7 @@ fun Navigation() {
LocalSharedTransitionScope provides this@SharedTransitionLayout,
) {
Box {
NavigationController(navController = navController)
NavigationController(navController = navController,startDestination = startDestination)
}
}
}

View File

@@ -66,14 +66,6 @@ fun IndexScreen() {
selected = isSelected,
onClick = {
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(
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.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.navigation.NavController
import androidx.paging.Pager
import androidx.paging.PagingConfig
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.AccountService
import com.aiosman.riderpro.data.TestAccountServiceImpl
import com.aiosman.riderpro.data.MomentPagingSource
import com.aiosman.riderpro.data.MomentRemoteDataSource
import com.aiosman.riderpro.data.TestMomentServiceImpl
import com.aiosman.riderpro.data.TestUserServiceImpl
import com.aiosman.riderpro.model.MomentItem
import kotlinx.coroutines.flow.Flow
object MyProfileViewModel {
val service: AccountService = TestAccountServiceImpl()
val userService = TestUserServiceImpl()
var profile by mutableStateOf<AccountProfile?>(null)
var momentsFlow by mutableStateOf<Flow<PagingData<MomentItem>>?>(null)
suspend fun loadProfile() {
@@ -32,6 +37,15 @@ object MyProfileViewModel {
}
).flow
}
suspend fun logout() {
service.getMyAccountProfile()
AppStore.apply {
token = null
rememberMe = false
saveData()
}
}
val followerCount get() = profile?.followerCount ?: 0
val followingCount get() = profile?.followingCount ?: 0
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.LazyListScope
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.runtime.Composable
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.Modifier
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.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.paging.compose.collectAsLazyPagingItems
import coil.compose.AsyncImage
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.AccountProfile
import com.aiosman.riderpro.model.MomentItem
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@Composable
fun ProfilePage() {
val model = MyProfileViewModel
var expanded by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
model.loadProfile()
}
val moments = model.momentsFlow?.collectAsLazyPagingItems()
val navController: NavController = LocalNavController.current
val scope = rememberCoroutineScope()
Box {
LazyColumn(
modifier = Modifier
.fillMaxSize()
@@ -57,7 +73,43 @@ fun ProfilePage() {
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
) {
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()
model.profile?.let {
UserInformation(accountProfile = it)
@@ -75,6 +127,8 @@ fun ProfilePage() {
}
}
}
@Composable
fun CarGroup() {
Column(
@@ -193,7 +247,9 @@ fun UserInformationBasic(modifier: Modifier,accountProfile: AccountProfile) {
}
Text(
modifier = Modifier.widthIn(max =220.dp).padding(top = 8.dp),
modifier = Modifier
.widthIn(max = 220.dp)
.padding(top = 8.dp),
text = accountProfile.nickName,
fontSize = 32.sp,
color = Color.Black,
@@ -264,6 +320,7 @@ fun CommunicationOperatorGroup(isSelf: Boolean = true) {
.fillMaxWidth()
.padding(top = 16.dp), horizontalArrangement = Arrangement.Center
) {
if (!isSelf) {
Box(
modifier = Modifier.size(width = 142.dp, height = 40.dp),
contentAlignment = Alignment.Center
@@ -280,6 +337,8 @@ fun CommunicationOperatorGroup(isSelf: Boolean = true) {
style = TextStyle(fontWeight = FontWeight.Bold)
)
}
}
if (isSelf) {
Box(
modifier = Modifier

View File

@@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
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.Modifier
@@ -33,10 +34,17 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
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.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.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -44,6 +52,25 @@ fun UserAuthScreen() {
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
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 {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@@ -106,7 +133,9 @@ fun UserAuthScreen() {
.height(48.dp),
text = "LET'S RIDE".uppercase(),
backgroundImage = R.mipmap.rider_pro_signup_red_bg
)
) {
onLogin()
}
Spacer(modifier = Modifier.height(121.dp))
Text("or login with", color = Color(0xFF999999))
@@ -153,7 +182,9 @@ fun TextInputField(
Image(
painter = painterResource(id = R.drawable.rider_pro_eye),
contentDescription = "Password",
modifier = Modifier.size(24.dp).noRippleClickable {
modifier = Modifier
.size(24.dp)
.noRippleClickable {
showPassword = !showPassword
},
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.Black)