新增登录
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 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
|
||||
}
|
||||
|
||||
}
|
||||
34
app/src/main/java/com/aiosman/riderpro/store.kt
Normal file
34
app/src/main/java/com/aiosman/riderpro/store.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ?: ""
|
||||
|
||||
@@ -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)
|
||||
@@ -73,6 +125,8 @@ fun ProfilePage() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -123,7 +177,7 @@ fun CarTopPicture() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformation(isSelf: Boolean = true,accountProfile: AccountProfile) {
|
||||
fun UserInformation(isSelf: Boolean = true, accountProfile: AccountProfile) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -132,9 +186,9 @@ fun UserInformation(isSelf: Boolean = true,accountProfile: AccountProfile) {
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
val userInfoModifier = Modifier.weight(1f)
|
||||
UserInformationFollowers(userInfoModifier,accountProfile)
|
||||
UserInformationBasic(userInfoModifier,accountProfile)
|
||||
UserInformationFollowing(userInfoModifier,accountProfile)
|
||||
UserInformationFollowers(userInfoModifier, accountProfile)
|
||||
UserInformationBasic(userInfoModifier, accountProfile)
|
||||
UserInformationFollowing(userInfoModifier, accountProfile)
|
||||
}
|
||||
UserInformationSlogan()
|
||||
CommunicationOperatorGroup(isSelf = isSelf)
|
||||
@@ -142,7 +196,7 @@ fun UserInformation(isSelf: Boolean = true,accountProfile: AccountProfile) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformationFollowers(modifier: Modifier,accountProfile: AccountProfile) {
|
||||
fun UserInformationFollowers(modifier: Modifier, accountProfile: AccountProfile) {
|
||||
Column(modifier = modifier.padding(top = 31.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 5.dp),
|
||||
@@ -168,7 +222,7 @@ fun UserInformationFollowers(modifier: Modifier,accountProfile: AccountProfile)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformationBasic(modifier: Modifier,accountProfile: AccountProfile) {
|
||||
fun UserInformationBasic(modifier: Modifier, accountProfile: AccountProfile) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
@@ -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,
|
||||
@@ -216,7 +272,7 @@ fun UserInformationBasic(modifier: Modifier,accountProfile: AccountProfile) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformationFollowing(modifier: Modifier,accountProfile: AccountProfile) {
|
||||
fun UserInformationFollowing(modifier: Modifier, accountProfile: AccountProfile) {
|
||||
Column(
|
||||
modifier = modifier.padding(top = 6.dp),
|
||||
horizontalAlignment = Alignment.End
|
||||
@@ -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
|
||||
@@ -469,7 +528,7 @@ fun MomentCardTopContent(content: String) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardPicture(imageUrl:String) {
|
||||
fun MomentCardPicture(imageUrl: String) {
|
||||
AsyncImage(
|
||||
imageUrl,
|
||||
modifier = Modifier
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user