This commit is contained in:
2024-08-03 15:58:19 +08:00
parent b17ac76005
commit 2dc0ee3307
9 changed files with 269 additions and 16 deletions

29
app/google-services.json Normal file
View File

@@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "150225239581",
"project_id": "riderp-30072",
"storage_bucket": "riderp-30072.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:150225239581:android:422283723db3908bdfd0f4",
"android_client_info": {
"package_name": "com.aiosman.riderpro"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyDNGmfzdCTJ0PxQ3yTwdWhRaDhqS1JDWaQ"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@@ -2,6 +2,7 @@ package com.aiosman.riderpro
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
/** /**
* 持久化本地数据 * 持久化本地数据
@@ -12,10 +13,16 @@ object AppStore {
var token: String? = null var token: String? = null
var rememberMe: Boolean = false var rememberMe: Boolean = false
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
lateinit var googleSignInOptions: GoogleSignInOptions
fun init(context: Context) { fun init(context: Context) {
sharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) sharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
this.loadData() this.loadData()
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("754277015802-uarf8br8k8gkpbj0t9g65bvkvit630q5.apps.googleusercontent.com") // Replace with your server's client ID
.requestEmail()
.build()
googleSignInOptions = gso
} }
suspend fun saveData() { suspend fun saveData() {

View File

@@ -1,5 +1,6 @@
package com.aiosman.riderpro.ui package com.aiosman.riderpro.ui
import ImageViewer
import ModificationListScreen import ModificationListScreen
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout import androidx.compose.animation.SharedTransitionLayout
@@ -62,6 +63,7 @@ sealed class NavigationRoute(
data object UserAuth : NavigationRoute("UserAuth") data object UserAuth : NavigationRoute("UserAuth")
data object EmailSignUp : NavigationRoute("EmailSignUp") data object EmailSignUp : NavigationRoute("EmailSignUp")
data object AccountEdit : NavigationRoute("AccountEditScreen") data object AccountEdit : NavigationRoute("AccountEditScreen")
data object ImageViewer : NavigationRoute("ImageViewer")
} }
@@ -171,6 +173,13 @@ fun NavigationController(
composable(route = NavigationRoute.AccountEdit.route) { composable(route = NavigationRoute.AccountEdit.route) {
AccountEditScreen() AccountEditScreen()
} }
composable(route = NavigationRoute.ImageViewer.route) {
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
ImageViewer()
}
}
} }

View File

@@ -4,8 +4,6 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -38,14 +36,12 @@ import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.TestAccountServiceImpl import com.aiosman.riderpro.data.TestAccountServiceImpl
import com.aiosman.riderpro.data.TestUserServiceImpl import com.aiosman.riderpro.data.TestUserServiceImpl
import com.aiosman.riderpro.data.UserService import com.aiosman.riderpro.data.UserService
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 import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun AccountEditScreen() { fun AccountEditScreen() {
val userService: UserService = TestUserServiceImpl() val userService: UserService = TestUserServiceImpl()
val accountService: AccountService = TestAccountServiceImpl() val accountService: AccountService = TestAccountServiceImpl()
var name by remember { mutableStateOf("") } var name by remember { mutableStateOf("") }

View File

@@ -0,0 +1,11 @@
package com.aiosman.riderpro.ui.imageviewer
object ImageViewerViewModel {
var imageList = mutableListOf<String>()
var initialIndex = 0
fun asNew(images: List<String>, index: Int = 0) {
imageList.clear()
imageList.addAll(images)
initialIndex = index
}
}

View File

@@ -0,0 +1,74 @@
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import coil.compose.AsyncImage
import com.aiosman.riderpro.LocalAnimatedContentScope
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.LocalSharedTransitionScope
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import net.engawapg.lib.zoomable.rememberZoomState
import net.engawapg.lib.zoomable.zoomable
@OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class)
@Composable
fun ImageViewer() {
val model = ImageViewerViewModel
val images = model.imageList
val pagerState = rememberPagerState(pageCount = { images.size }, initialPage = model.initialIndex)
val systemUiController = rememberSystemUiController()
val navController = LocalNavController.current
val sharedTransitionScope = LocalSharedTransitionScope.current
val animatedVisibilityScope = LocalAnimatedContentScope.current
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Black)
systemUiController.setNavigationBarColor(Color.Black)
}
StatusBarMaskLayout(
modifier = Modifier.background(Color.Black),
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize()
) { page ->
val zoomState = rememberZoomState()
with(sharedTransitionScope) {
AsyncImage(
images[page],
contentDescription = null,
modifier = Modifier.sharedElement(
rememberSharedContentState(key = images[page]),
animatedVisibilityScope = animatedVisibilityScope
)
.fillMaxSize()
.zoomable(
zoomState = zoomState,
onTap = {
navController.popBackStack()
}
),
contentScale = ContentScale.Fit,
)
}
}
}
}
}

View File

@@ -1,5 +1,9 @@
package com.aiosman.riderpro.ui.login package com.aiosman.riderpro.ui.login
import android.app.Activity
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -20,18 +24,49 @@ 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
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
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.LocalNavController
import com.aiosman.riderpro.R import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.tasks.Task
@Composable @Composable
fun SignupScreen() { fun SignupScreen() {
val navController = LocalNavController.current val navController = LocalNavController.current
val context = LocalContext.current
val googleSignInClient = GoogleSignIn.getClient(context, AppStore.googleSignInOptions)
fun handleSignInResult(
task: Task<GoogleSignInAccount>,
onSignInSuccess: (GoogleSignInAccount) -> Unit
) {
try {
val account = task.getResult(ApiException::class.java)
onSignInSuccess(account)
} catch (e: ApiException) {
// Handle sign-in failure
}
}
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = { result ->
if (result.resultCode == Activity.RESULT_OK) {
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
handleSignInResult(task) {account ->
// Handle sign-in success
Log.d("SignupScreen", "handleSignInResult: $account")
}
}
}
)
Scaffold( Scaffold(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -126,7 +161,10 @@ fun SignupScreen() {
}, },
text = "CONTINUE WITH GOOGLE".uppercase(), text = "CONTINUE WITH GOOGLE".uppercase(),
backgroundImage = R.mipmap.rider_pro_signup_white_bg backgroundImage = R.mipmap.rider_pro_signup_white_bg
) ) {
val signInIntent = googleSignInClient.signInIntent
launcher.launch(signInIntent)
}
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Box( Box(
modifier = Modifier modifier = Modifier

View File

@@ -70,7 +70,9 @@ import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.map import androidx.paging.map
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.aiosman.riderpro.LocalAnimatedContentScope
import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.LocalSharedTransitionScope
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.data.AccountService import com.aiosman.riderpro.data.AccountService
@@ -87,6 +89,7 @@ import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
import com.aiosman.riderpro.ui.composables.EditCommentBottomModal import com.aiosman.riderpro.ui.composables.EditCommentBottomModal
import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
@@ -332,28 +335,43 @@ fun Header(accountProfile: AccountProfile?) {
} }
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class)
@Composable @Composable
fun PostImageView( fun PostImageView(
postId: String, postId: String,
images: List<String>, images: List<String>,
) { ) {
val pagerState = rememberPagerState(pageCount = { images.size }) val pagerState = rememberPagerState(pageCount = { images.size })
val navController = LocalNavController.current
val sharedTransitionScope = LocalSharedTransitionScope.current
val animatedVisibilityScope = LocalAnimatedContentScope.current
Column { Column {
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.fillMaxWidth(), .fillMaxWidth().background(Color.Black),
) { page -> ) { page ->
val image = images[page] val image = images[page]
with(sharedTransitionScope) {
AsyncImage( AsyncImage(
image, image,
contentDescription = "Image", contentDescription = "Image",
contentScale = ContentScale.Crop, contentScale = ContentScale.Fit,
modifier = Modifier modifier = Modifier
.fillMaxSize() .sharedElement(
rememberSharedContentState(key = image),
animatedVisibilityScope = animatedVisibilityScope
) )
.fillMaxSize()
.noRippleClickable {
ImageViewerViewModel.asNew(images, page)
navController.navigate(
NavigationRoute.ImageViewer.route
)
}
)
}
} }
// Indicator container // Indicator container
@@ -547,7 +565,11 @@ fun BottomNavigationBar(
onClick = { onClick = {
onLikeClick() onLikeClick()
}) { }) {
Icon(Icons.Filled.Favorite, contentDescription = "like", tint = if (momentItem?.liked == true) Color.Red else Color.Gray) Icon(
Icons.Filled.Favorite,
contentDescription = "like",
tint = if (momentItem?.liked == true) Color.Red else Color.Gray
)
} }
Text(text = momentItem?.likeCount.toString()) Text(text = momentItem?.likeCount.toString())
IconButton( IconButton(

View File

@@ -0,0 +1,67 @@
package com.aiosman.riderpro.ui.search
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@Composable
fun SearchScreen() {
Scaffold { paddingValues ->
var tabIndex by remember { mutableStateOf(0) }
Column(
modifier = Modifier
.fillMaxWidth()
.padding(paddingValues)
) {
TextField(
value = "",
onValueChange = {},
label = { Text("Search") },
modifier = Modifier.fillMaxWidth(),
trailingIcon = {
Icon(
imageVector = Icons.Default.Search,
contentDescription = null
)
}
)
TabRow(selectedTabIndex = tabIndex) {
Tab(text = { Text("Post") },
selected = tabIndex == 0,
onClick = { tabIndex = 0 }
)
Tab(text = { Text("User") },
selected = tabIndex == 1,
onClick = { tabIndex = 1 }
)
}
when (tabIndex) {
0 -> SearchPostResults()
1 -> SearchUserResults()
}
}
}
}
@Composable
fun SearchPostResults() {
Text("Post Results")
}
@Composable
fun SearchUserResults(){
Text("User Results")
}