更新代码

This commit is contained in:
2024-12-07 17:14:45 +08:00
parent 50fb1874e7
commit 9f93e6dc14
19 changed files with 744 additions and 171 deletions

View File

@@ -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

View File

@@ -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")
}
}
}

View File

@@ -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>
}

View File

@@ -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)
}

View File

@@ -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
)
}
}
}

View File

@@ -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)
}
)
}
}

View File

@@ -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),

View File

@@ -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)
)
}
}
}

View File

@@ -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
}

View File

@@ -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(

View File

@@ -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)
)
}
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M12,12m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M11.25,11.25l0.75,0l0,5.25l0.75,0" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#110C13" android:fillType="nonZero" android:pathData="M11.813,7.875m-1.125,0a1.125,1.125 0,1 1,2.25 0a1.125,1.125 0,1 1,-2.25 0" android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M12,12m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0" android:strokeColor="#000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M12,10m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" android:strokeColor="#000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M6.168,18.849C6.676,17.157 8.234,15.999 10,16L14,16C15.769,15.999 17.328,17.16 17.834,18.855" android:strokeColor="#000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
</vector>

View File

@@ -0,0 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M7,6C4.514,8.099 3.604,11.528 4.722,14.583C5.839,17.639 8.746,19.671 12,19.671C15.254,19.671 18.161,17.639 19.278,14.583C20.396,11.528 19.486,8.099 17,6" android:strokeColor="#000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M12,4L12,12" android:strokeColor="#000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
</vector>

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M20.253,10.5L20.253,6" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M22.5,8.253L18,8.253" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M15.753,2.25L15.753,5.25" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M17.25,3.753L14.25,3.753" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M20.316,14.306C17.317,15.147 14.099,14.305 11.897,12.103C9.695,9.901 8.853,6.683 9.694,3.684L9.694,3.684C5.682,4.801 3.04,8.625 3.417,12.773C3.793,16.921 7.079,20.207 11.227,20.583C15.375,20.96 19.199,18.318 20.316,14.306L20.316,14.306Z" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="22dp" android:viewportHeight="22" android:viewportWidth="13" android:width="13dp">
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M6.5,5.55L10.812,9.914C11.133,10.239 11.133,10.761 10.812,11.086L6.5,15.45L6.5,15.45" android:strokeColor="#B1AEB2" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="1.66862918"/>
</vector>

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M16.5,13.5l3,3l-3,3" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M4.5,16.503L19.5,16.503" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M7.5,10.5l-3,-3l3,-3" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="#00000000" android:fillType="evenOdd" android:pathData="M19.5,7.503L4.5,7.503" android:strokeColor="#110C13" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
</vector>

View File

@@ -100,4 +100,12 @@
<string name="report_fail_desc">举报失败,可以尝试重试</string>
<string name="report_title">举报这篇帖子的原因是?</string>
<string name="close">关闭</string>
<string name="about_rave_now">关于Rave Now</string>
<string name="account_and_security">账户与安全</string>
<string name="remove_account">删除账户</string>
<string name="remove_account_desc">注销账号为不可逆的操作,请确认</string>
<string name="remove_account_password_hint">输入密码以确认</string>
<string name="version_text">版本 %1$s</string>
<string name="index_worldwide">发现</string>
<string name="index_following">关注</string>
</resources>

View File

@@ -99,4 +99,12 @@
<string name="report_fail_desc">Failed to report, please try again</string>
<string name="report_title">Reason for reporting this post?</string>
<string name="close">Close</string>
<string name="about_rave_now">About Rave Now</string>
<string name="account_and_security">Account and security</string>
<string name="remove_account">Remove Account</string>
<string name="remove_account_desc">Are you sure you want to remove your account? This action cannot be undone.</string>
<string name="remove_account_password_hint">Please enter your password to confirm</string>
<string name="version_text">Version %1$s</string>
<string name="index_worldwide">Worldwide</string>
<string name="index_following">Following</string>
</resources>