更新代码
This commit is contained in:
@@ -38,7 +38,7 @@ import kotlin.coroutines.suspendCoroutine
|
||||
object AppState {
|
||||
var UserId: Int? = null
|
||||
var profile: AccountProfileEntity? = null
|
||||
var darkMode = false
|
||||
var darkMode by mutableStateOf(false)
|
||||
var appTheme by mutableStateOf<AppThemeData>(LightThemeColors())
|
||||
var googleClientId: String? = null
|
||||
var enableGoogleLogin: Boolean = false
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.aiosman.ravenow.data.api.GoogleRegisterRequestBody
|
||||
import com.aiosman.ravenow.data.api.LoginUserRequestBody
|
||||
import com.aiosman.ravenow.data.api.RegisterMessageChannelRequestBody
|
||||
import com.aiosman.ravenow.data.api.RegisterRequestBody
|
||||
import com.aiosman.ravenow.data.api.RemoveAccountRequestBody
|
||||
import com.aiosman.ravenow.data.api.ResetPasswordRequestBody
|
||||
import com.aiosman.ravenow.data.api.TrtcSignResponseBody
|
||||
import com.aiosman.ravenow.data.api.UnRegisterMessageChannelRequestBody
|
||||
@@ -386,6 +387,8 @@ interface AccountService {
|
||||
suspend fun getMyTrtcSign(): TrtcSignResponseBody
|
||||
|
||||
suspend fun getAppConfig(): AppConfig
|
||||
|
||||
suspend fun removeAccount(password: String)
|
||||
}
|
||||
|
||||
class AccountServiceImpl : AccountService {
|
||||
@@ -554,4 +557,16 @@ class AccountServiceImpl : AccountService {
|
||||
val body = resp.body() ?: throw ServiceException("Failed to get app config")
|
||||
return body.data
|
||||
}
|
||||
|
||||
override suspend fun removeAccount(password: String) {
|
||||
val resp = ApiClient.api.deleteAccount(
|
||||
RemoveAccountRequestBody(password)
|
||||
)
|
||||
if (!resp.isSuccessful) {
|
||||
parseErrorResponse(resp.errorBody())?.let {
|
||||
throw it.toServiceException()
|
||||
}
|
||||
throw ServiceException("Failed to remove account")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,6 +200,10 @@ data class CreateReportRequestBody(
|
||||
val base64Images: List<String>,
|
||||
)
|
||||
|
||||
data class RemoveAccountRequestBody(
|
||||
@SerializedName("password")
|
||||
val password: String,
|
||||
)
|
||||
|
||||
interface RaveNowAPI {
|
||||
@POST("register")
|
||||
@@ -449,5 +453,11 @@ interface RaveNowAPI {
|
||||
suspend fun createReport(
|
||||
@Body body: CreateReportRequestBody
|
||||
): Response<Unit>
|
||||
|
||||
@POST("account/my/delete")
|
||||
suspend fun deleteAccount(
|
||||
@Body body: RemoveAccountRequestBody
|
||||
): Response<Unit>
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ import androidx.navigation.navArgument
|
||||
import com.aiosman.ravenow.LocalAnimatedContentScope
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.LocalSharedTransitionScope
|
||||
import com.aiosman.ravenow.ui.about.AboutScreen
|
||||
import com.aiosman.ravenow.ui.account.AccountEditScreen2
|
||||
import com.aiosman.ravenow.ui.account.AccountSetting
|
||||
import com.aiosman.ravenow.ui.account.ResetPasswordScreen
|
||||
import com.aiosman.ravenow.ui.chat.ChatScreen
|
||||
import com.aiosman.ravenow.ui.comment.CommentsScreen
|
||||
@@ -90,6 +92,8 @@ sealed class NavigationRoute(
|
||||
data object Chat : NavigationRoute("Chat/{id}")
|
||||
data object CommentNoticeScreen : NavigationRoute("CommentNoticeScreen")
|
||||
data object ImageCrop : NavigationRoute("ImageCrop")
|
||||
data object AccountSetting : NavigationRoute("AccountSetting")
|
||||
data object AboutScreen : NavigationRoute("AboutScreen")
|
||||
}
|
||||
|
||||
|
||||
@@ -377,6 +381,20 @@ fun NavigationController(
|
||||
ImageCropScreen()
|
||||
}
|
||||
}
|
||||
composable(route = NavigationRoute.AccountSetting.route) {
|
||||
CompositionLocalProvider(
|
||||
LocalAnimatedContentScope provides this,
|
||||
) {
|
||||
AccountSetting()
|
||||
}
|
||||
}
|
||||
composable(route = NavigationRoute.AboutScreen.route) {
|
||||
CompositionLocalProvider(
|
||||
LocalAnimatedContentScope provides this,
|
||||
) {
|
||||
AboutScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -424,4 +442,10 @@ fun NavHostController.navigateToChat(id: String) {
|
||||
route = NavigationRoute.Chat.route
|
||||
.replace("{id}", id)
|
||||
)
|
||||
}
|
||||
|
||||
fun NavHostController.goTo(
|
||||
route: NavigationRoute
|
||||
) {
|
||||
navigate(route.route)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.aiosman.ravenow.ui.about
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
||||
|
||||
@Composable
|
||||
fun AboutScreen() {
|
||||
val appColors = LocalAppTheme.current
|
||||
val context = LocalContext.current
|
||||
val versionText = context.packageManager.getPackageInfo(context.packageName, 0).versionName
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(appColors.background),
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
|
||||
) {
|
||||
NoticeScreenHeader(
|
||||
title = stringResource(R.string.about_rave_now),
|
||||
moreIcon = false
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.padding(start = 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
// app icon
|
||||
Box {
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.rider_pro_color_logo_next),
|
||||
contentDescription = "app icon",
|
||||
modifier = Modifier.size(80.dp)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
// app name
|
||||
Text(
|
||||
text = "Rave Now".uppercase(),
|
||||
fontSize = 24.sp,
|
||||
color = appColors.text,
|
||||
fontWeight = FontWeight.ExtraBold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
// app version
|
||||
Text(
|
||||
text = stringResource(R.string.version_text, versionText),
|
||||
fontSize = 16.sp,
|
||||
color = appColors.secondaryText,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package com.aiosman.ravenow.ui.account
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
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
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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 com.aiosman.ravenow.AppState
|
||||
import com.aiosman.ravenow.AppStore
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.Messaging
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.data.AccountServiceImpl
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.ravenow.ui.composables.ActionButton
|
||||
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
||||
import com.aiosman.ravenow.ui.index.NavItem
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AccountSetting() {
|
||||
val appColors = LocalAppTheme.current
|
||||
val navController = LocalNavController.current
|
||||
var removeAccountModal by remember { mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
fun removeAccount(password: String) {
|
||||
scope.launch {
|
||||
removeAccountModal = false
|
||||
try {
|
||||
val accountService = AccountServiceImpl()
|
||||
accountService.removeAccount(password)
|
||||
Messaging.unregisterDevice(context)
|
||||
AppStore.apply {
|
||||
token = null
|
||||
rememberMe = false
|
||||
saveData()
|
||||
}
|
||||
// 删除推送渠道
|
||||
AppState.ReloadAppState(context)
|
||||
navController.navigate(NavigationRoute.Login.route) {
|
||||
popUpTo(NavigationRoute.Login.route) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
// show toast
|
||||
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removeAccountModal) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
removeAccountModal = false
|
||||
},
|
||||
containerColor = appColors.background,
|
||||
sheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = true
|
||||
),
|
||||
dragHandle = {},
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
windowInsets = WindowInsets(0)
|
||||
) {
|
||||
RemoveAccountModal(
|
||||
onRemove = {
|
||||
removeAccount(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(appColors.background),
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
|
||||
) {
|
||||
NoticeScreenHeader(
|
||||
title = stringResource(R.string.account_and_security),
|
||||
moreIcon = false
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 24.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
NavItem(
|
||||
iconRes = R.mipmap.rider_pro_change_password,
|
||||
label = stringResource(R.string.change_password),
|
||||
modifier = Modifier.noRippleClickable {
|
||||
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// divider
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.background(appColors.divider)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
NavItem(
|
||||
iconRes = R.drawable.rider_pro_moment_delete,
|
||||
label = stringResource(R.string.remove_account),
|
||||
modifier = Modifier.noRippleClickable {
|
||||
removeAccountModal = true
|
||||
}
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.background(appColors.divider)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RemoveAccountModal(
|
||||
onRemove: (String) -> Unit
|
||||
) {
|
||||
val appColors = LocalAppTheme.current
|
||||
var inputPassword by remember { mutableStateOf("") }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 24.dp, vertical = 16.dp)
|
||||
.background(appColors.background),
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(stringResource(R.string.remove_account), fontWeight = FontWeight.Bold, fontSize = 20.sp)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 32.dp, top = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.remove_account_desc),
|
||||
fontSize = 16.sp,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
Text(
|
||||
stringResource(R.string.remove_account_password_hint),
|
||||
fontSize = 16.sp,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, bottom = 4.dp),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
appColors.inputBackground,
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
.padding(vertical = 16.dp, horizontal = 16.dp)
|
||||
){
|
||||
|
||||
BasicTextField(
|
||||
value = inputPassword,
|
||||
onValueChange = { inputPassword = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
ActionButton(
|
||||
text = stringResource(R.string.remove_account),
|
||||
fullWidth = true,
|
||||
enabled = inputPassword.isNotEmpty(),
|
||||
click = {
|
||||
onRemove(inputPassword)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ object FavouriteListViewModel:ViewModel() {
|
||||
}
|
||||
isLoading = false
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
config = PagingConfig(pageSize = 20, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
MomentPagingSource(
|
||||
MomentRemoteDataSource(momentService),
|
||||
|
||||
@@ -3,34 +3,58 @@ package com.aiosman.ravenow.ui.index
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.NavigationBarItemColors
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aiosman.ravenow.AppState
|
||||
import com.aiosman.ravenow.AppStore
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.Messaging
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.index.tabs.add.AddPage
|
||||
import com.aiosman.ravenow.ui.index.tabs.message.NotificationsScreen
|
||||
@@ -39,6 +63,7 @@ import com.aiosman.ravenow.ui.index.tabs.profile.ProfileWrap
|
||||
import com.aiosman.ravenow.ui.index.tabs.search.DiscoverScreen
|
||||
import com.aiosman.ravenow.ui.index.tabs.shorts.ShortVideo
|
||||
import com.aiosman.ravenow.ui.index.tabs.street.StreetPage
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import com.aiosman.ravenow.ui.post.NewPostViewModel
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -62,73 +87,217 @@ fun IndexScreen() {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val pagerState = rememberPagerState(pageCount = { item.size })
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(Unit) {
|
||||
systemUiController.setNavigationBarColor(Color.Transparent)
|
||||
}
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
NavigationBar(
|
||||
modifier = Modifier.height(56.dp + navigationBarHeight),
|
||||
containerColor = Color.Black
|
||||
) {
|
||||
item.forEachIndexed { idx, it ->
|
||||
val isSelected = model.tabIndex == idx
|
||||
val iconTint by animateColorAsState(
|
||||
targetValue = if (isSelected) Color.White else Color.White,
|
||||
animationSpec = tween(durationMillis = 250), label = ""
|
||||
)
|
||||
NavigationBarItem(
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
if (it.route === NavigationItem.Add.route) {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
return@NavigationBarItem
|
||||
LaunchedEffect(model.openDrawer) {
|
||||
if (model.openDrawer) {
|
||||
drawerState.open()
|
||||
model.openDrawer = false
|
||||
}
|
||||
}
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||
ModalNavigationDrawer(
|
||||
drawerState = drawerState,
|
||||
gesturesEnabled = drawerState.isOpen,
|
||||
drawerContent = {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.requiredWidth(250.dp)
|
||||
.fillMaxHeight()
|
||||
.background(
|
||||
AppColors.background
|
||||
)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(88.dp))
|
||||
NavItem(
|
||||
iconRes = R.drawable.rave_now_nav_account,
|
||||
label = stringResource(R.string.account_and_security),
|
||||
modifier = Modifier.noRippleClickable {
|
||||
coroutineScope.launch {
|
||||
drawerState.close()
|
||||
navController.navigate(NavigationRoute.AccountSetting.route)
|
||||
}
|
||||
|
||||
}
|
||||
coroutineScope.launch {
|
||||
pagerState.scrollToPage(idx)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
NavItem(
|
||||
iconRes = R.drawable.rider_pro_favourited,
|
||||
label = stringResource(R.string.favourites),
|
||||
modifier = Modifier.noRippleClickable {
|
||||
coroutineScope.launch {
|
||||
drawerState.close()
|
||||
navController.navigate(NavigationRoute.FavouriteList.route)
|
||||
}
|
||||
|
||||
}
|
||||
model.tabIndex = idx
|
||||
},
|
||||
colors = NavigationBarItemColors(
|
||||
selectedTextColor = Color.Red,
|
||||
selectedIndicatorColor = Color.Black,
|
||||
unselectedTextColor = Color.Red,
|
||||
disabledIconColor = Color.Red,
|
||||
disabledTextColor = Color.Red,
|
||||
selectedIconColor = iconTint,
|
||||
unselectedIconColor = iconTint,
|
||||
),
|
||||
icon = {
|
||||
Icon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = if (isSelected) it.selectedIcon() else it.icon(),
|
||||
contentDescription = null,
|
||||
tint = iconTint
|
||||
)
|
||||
|
||||
NavItem(
|
||||
iconRes = R.drawable.rave_now_nav_night,
|
||||
label = stringResource(R.string.dark_mode),
|
||||
rightContent = {
|
||||
Switch(
|
||||
checked = AppState.darkMode,
|
||||
onCheckedChange = {
|
||||
AppState.switchTheme()
|
||||
},
|
||||
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = Color.White,
|
||||
checkedTrackColor = AppColors.main,
|
||||
uncheckedThumbColor = Color.White,
|
||||
uncheckedTrackColor = AppColors.main.copy(alpha = 0.5f),
|
||||
uncheckedBorderColor = Color.Transparent
|
||||
),
|
||||
modifier = Modifier.scale(0.8f)
|
||||
)
|
||||
}
|
||||
)
|
||||
// divider
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, bottom = 16.dp, start = 16.dp, end = 16.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.background(AppColors.divider)
|
||||
)
|
||||
}
|
||||
)
|
||||
NavItem(
|
||||
iconRes = R.drawable.rave_now_nav_about,
|
||||
label = stringResource(R.string.about_rave_now),
|
||||
modifier = Modifier.noRippleClickable {
|
||||
coroutineScope.launch {
|
||||
drawerState.close()
|
||||
navController.navigate(NavigationRoute.AboutScreen.route)
|
||||
}
|
||||
}
|
||||
)
|
||||
// divider
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, bottom = 16.dp, start = 16.dp, end = 16.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.background(AppColors.divider)
|
||||
)
|
||||
}
|
||||
|
||||
// NavItem(
|
||||
// iconRes = R.drawable.rave_now_nav_switch,
|
||||
// label = "Switch Account"
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(16.dp))
|
||||
NavItem(
|
||||
iconRes = R.drawable.rave_now_nav_logout,
|
||||
label = stringResource(R.string.logout),
|
||||
modifier = Modifier.noRippleClickable {
|
||||
coroutineScope.launch {
|
||||
drawerState.close()
|
||||
Messaging.unregisterDevice(context)
|
||||
AppStore.apply {
|
||||
token = null
|
||||
rememberMe = false
|
||||
saveData()
|
||||
}
|
||||
// 删除推送渠道
|
||||
|
||||
navController.navigate(NavigationRoute.Login.route) {
|
||||
popUpTo(NavigationRoute.Login.route) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
AppState.ReloadAppState(context)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
NavigationBar(
|
||||
modifier = Modifier.height(56.dp + navigationBarHeight),
|
||||
containerColor = Color.Black
|
||||
) {
|
||||
item.forEachIndexed { idx, it ->
|
||||
val isSelected = model.tabIndex == idx
|
||||
val iconTint by animateColorAsState(
|
||||
targetValue = if (isSelected) Color.White else Color.White,
|
||||
animationSpec = tween(durationMillis = 250), label = ""
|
||||
)
|
||||
NavigationBarItem(
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
if (it.route === NavigationItem.Add.route) {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
return@NavigationBarItem
|
||||
}
|
||||
coroutineScope.launch {
|
||||
pagerState.scrollToPage(idx)
|
||||
}
|
||||
model.tabIndex = idx
|
||||
},
|
||||
colors = NavigationBarItemColors(
|
||||
selectedTextColor = Color.Red,
|
||||
selectedIndicatorColor = Color.Black,
|
||||
unselectedTextColor = Color.Red,
|
||||
disabledIconColor = Color.Red,
|
||||
disabledTextColor = Color.Red,
|
||||
selectedIconColor = iconTint,
|
||||
unselectedIconColor = iconTint,
|
||||
),
|
||||
icon = {
|
||||
Icon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = if (isSelected) it.selectedIcon() else it.icon(),
|
||||
contentDescription = null,
|
||||
tint = iconTint
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
innerPadding
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
.background(AppColors.background)
|
||||
.padding(0.dp),
|
||||
beyondBoundsPageCount = 5,
|
||||
userScrollEnabled = false
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> Home()
|
||||
1 -> DiscoverScreen()
|
||||
2 -> Add()
|
||||
3 -> Notifications()
|
||||
4 -> Profile()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
innerPadding
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier.background(AppColors.background).padding(0.dp),
|
||||
beyondBoundsPageCount = 5,
|
||||
userScrollEnabled = false
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> Home()
|
||||
1 -> DiscoverScreen()
|
||||
2 -> Add()
|
||||
3 -> Notifications()
|
||||
4 -> Profile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -228,4 +397,45 @@ fun Notifications() {
|
||||
) {
|
||||
NotificationsScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavItem(
|
||||
iconRes: Int,
|
||||
label: String,
|
||||
modifier: Modifier = Modifier,
|
||||
rightContent: @Composable (() -> Unit)? = null
|
||||
) {
|
||||
val appColors = LocalAppTheme.current
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp, start = 16.dp, end = 16.dp, bottom = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = iconRes),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
colorFilter = ColorFilter.tint(appColors.text)
|
||||
)
|
||||
Text(
|
||||
text = label,
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.weight(1f),
|
||||
color = appColors.text,
|
||||
)
|
||||
if (rightContent != null) {
|
||||
rightContent()
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rave_now_nav_right),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
colorFilter = ColorFilter.tint(appColors.secondaryText)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import androidx.lifecycle.ViewModel
|
||||
object IndexViewModel:ViewModel() {
|
||||
var tabIndex by mutableStateOf(0)
|
||||
|
||||
var openDrawer by mutableStateOf(false)
|
||||
|
||||
fun ResetModel(){
|
||||
tabIndex = 0
|
||||
}
|
||||
|
||||
@@ -26,10 +26,12 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.ExploreMomentsList
|
||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentsList
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
@@ -42,7 +44,6 @@ import kotlinx.coroutines.launch
|
||||
@Composable
|
||||
fun MomentsList() {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val model = MomentViewModel
|
||||
val navigationBarPaddings =
|
||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||
@@ -58,7 +59,9 @@ fun MomentsList() {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().height(44.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(44.dp),
|
||||
// center the tabs
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@@ -74,7 +77,7 @@ fun MomentsList() {
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
|
||||
) {
|
||||
Text(text = "Worldwide", fontSize = 16.sp, color = AppColors.text,fontWeight = FontWeight.W600)
|
||||
Text(text = stringResource(R.string.index_worldwide), fontSize = 16.sp, color = AppColors.text,fontWeight = FontWeight.W600)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Box(
|
||||
@@ -97,7 +100,7 @@ fun MomentsList() {
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(text = "Following", fontSize = 16.sp, color = AppColors.text, fontWeight = FontWeight.W600)
|
||||
Text(text = stringResource(R.string.index_following), fontSize = 16.sp, color = AppColors.text, fontWeight = FontWeight.W600)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Box(
|
||||
|
||||
@@ -80,6 +80,7 @@ import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher
|
||||
import com.aiosman.ravenow.ui.composables.toolbar.CollapsingToolbarScaffold
|
||||
import com.aiosman.ravenow.ui.composables.toolbar.ScrollStrategy
|
||||
import com.aiosman.ravenow.ui.composables.toolbar.rememberCollapsingToolbarScaffoldState
|
||||
import com.aiosman.ravenow.ui.index.IndexViewModel
|
||||
import com.aiosman.ravenow.ui.index.tabs.profile.composable.EmptyMomentPostUnit
|
||||
import com.aiosman.ravenow.ui.index.tabs.profile.composable.GalleryItem
|
||||
import com.aiosman.ravenow.ui.index.tabs.profile.composable.MomentPostUnit
|
||||
@@ -233,7 +234,9 @@ fun ProfileV3(
|
||||
it.noRippleClickable {
|
||||
Intent(Intent.ACTION_PICK).apply {
|
||||
type = "image/*"
|
||||
pickBannerImageLauncher.launch(this)
|
||||
pickBannerImageLauncher.launch(
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -278,70 +281,28 @@ fun ProfileV3(
|
||||
start = 8.dp,
|
||||
end = 8.dp
|
||||
)
|
||||
.noRippleClickable {
|
||||
IndexViewModel.openDrawer = true
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(AppColors.background.copy(alpha = 0.7f))
|
||||
.background(
|
||||
AppColors.background.copy(
|
||||
alpha = 0.7f
|
||||
)
|
||||
)
|
||||
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.noRippleClickable {
|
||||
expanded = true
|
||||
},
|
||||
|
||||
tint = AppColors.text
|
||||
)
|
||||
}
|
||||
val themeModeString =
|
||||
if (AppState.darkMode) R.string.light_mode else R.string.dark_mode
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
width = 250,
|
||||
menuItems = listOf(
|
||||
MenuItem(
|
||||
stringResource(R.string.logout),
|
||||
R.mipmap.rider_pro_logout
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
onLogout()
|
||||
navController.navigate(NavigationRoute.Login.route) {
|
||||
popUpTo(NavigationRoute.Index.route) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(R.string.change_password),
|
||||
R.mipmap.rider_pro_change_password
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(R.string.favourites),
|
||||
R.drawable.rider_pro_favourite
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
navController.navigate(NavigationRoute.FavouriteList.route)
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(themeModeString),
|
||||
R.drawable.rider_pro_theme_mode_light
|
||||
) {
|
||||
expanded = false
|
||||
switchTheme()
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +333,9 @@ fun ProfileV3(
|
||||
) {
|
||||
if (isSelf) {
|
||||
SelfProfileAction {
|
||||
navController.navigate(NavigationRoute.AccountEdit.route)
|
||||
navController.navigate(
|
||||
NavigationRoute.AccountEdit.route
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (it.id != AppState.UserId) {
|
||||
@@ -415,7 +378,10 @@ fun ProfileV3(
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 8.dp
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CustomAsyncImage(
|
||||
@@ -437,69 +403,22 @@ fun ProfileV3(
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (isSelf) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
modifier = Modifier.noRippleClickable {
|
||||
IndexViewModel.openDrawer = true
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.noRippleClickable {
|
||||
minibarExpanded = true
|
||||
},
|
||||
tint = AppColors.text
|
||||
)
|
||||
}
|
||||
val themeModeString =
|
||||
if (AppState.darkMode) R.string.light_mode else R.string.dark_mode
|
||||
DropdownMenu(
|
||||
expanded = minibarExpanded,
|
||||
onDismissRequest = { minibarExpanded = false },
|
||||
width = 250,
|
||||
menuItems = listOf(
|
||||
MenuItem(
|
||||
stringResource(R.string.logout),
|
||||
R.mipmap.rider_pro_logout
|
||||
) {
|
||||
minibarExpanded = false
|
||||
scope.launch {
|
||||
onLogout()
|
||||
navController.navigate(NavigationRoute.Login.route) {
|
||||
popUpTo(NavigationRoute.Index.route) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(R.string.change_password),
|
||||
R.mipmap.rider_pro_change_password
|
||||
) {
|
||||
minibarExpanded = false
|
||||
scope.launch {
|
||||
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(R.string.favourites),
|
||||
R.drawable.rider_pro_favourite
|
||||
) {
|
||||
minibarExpanded = false
|
||||
scope.launch {
|
||||
navController.navigate(NavigationRoute.FavouriteList.route)
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(themeModeString),
|
||||
R.drawable.rider_pro_theme_mode_light
|
||||
) {
|
||||
minibarExpanded = false
|
||||
switchTheme()
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +466,9 @@ fun ProfileV3(
|
||||
.padding(8.dp)
|
||||
.noRippleClickable {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
navController.navigate(
|
||||
NavigationRoute.NewPost.route
|
||||
)
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
@@ -616,6 +537,11 @@ fun ProfileV3(
|
||||
}
|
||||
|
||||
}
|
||||
PullRefreshIndicator(model.refreshing, refreshState, Modifier.align(Alignment.TopCenter))
|
||||
PullRefreshIndicator(
|
||||
model.refreshing,
|
||||
refreshState,
|
||||
Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user