更新代码

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 { object AppState {
var UserId: Int? = null var UserId: Int? = null
var profile: AccountProfileEntity? = null var profile: AccountProfileEntity? = null
var darkMode = false var darkMode by mutableStateOf(false)
var appTheme by mutableStateOf<AppThemeData>(LightThemeColors()) var appTheme by mutableStateOf<AppThemeData>(LightThemeColors())
var googleClientId: String? = null var googleClientId: String? = null
var enableGoogleLogin: Boolean = false 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.LoginUserRequestBody
import com.aiosman.ravenow.data.api.RegisterMessageChannelRequestBody import com.aiosman.ravenow.data.api.RegisterMessageChannelRequestBody
import com.aiosman.ravenow.data.api.RegisterRequestBody 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.ResetPasswordRequestBody
import com.aiosman.ravenow.data.api.TrtcSignResponseBody import com.aiosman.ravenow.data.api.TrtcSignResponseBody
import com.aiosman.ravenow.data.api.UnRegisterMessageChannelRequestBody import com.aiosman.ravenow.data.api.UnRegisterMessageChannelRequestBody
@@ -386,6 +387,8 @@ interface AccountService {
suspend fun getMyTrtcSign(): TrtcSignResponseBody suspend fun getMyTrtcSign(): TrtcSignResponseBody
suspend fun getAppConfig(): AppConfig suspend fun getAppConfig(): AppConfig
suspend fun removeAccount(password: String)
} }
class AccountServiceImpl : AccountService { class AccountServiceImpl : AccountService {
@@ -554,4 +557,16 @@ class AccountServiceImpl : AccountService {
val body = resp.body() ?: throw ServiceException("Failed to get app config") val body = resp.body() ?: throw ServiceException("Failed to get app config")
return body.data 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>, val base64Images: List<String>,
) )
data class RemoveAccountRequestBody(
@SerializedName("password")
val password: String,
)
interface RaveNowAPI { interface RaveNowAPI {
@POST("register") @POST("register")
@@ -449,5 +453,11 @@ interface RaveNowAPI {
suspend fun createReport( suspend fun createReport(
@Body body: CreateReportRequestBody @Body body: CreateReportRequestBody
): Response<Unit> ): 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.LocalAnimatedContentScope
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.LocalSharedTransitionScope 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.AccountEditScreen2
import com.aiosman.ravenow.ui.account.AccountSetting
import com.aiosman.ravenow.ui.account.ResetPasswordScreen import com.aiosman.ravenow.ui.account.ResetPasswordScreen
import com.aiosman.ravenow.ui.chat.ChatScreen import com.aiosman.ravenow.ui.chat.ChatScreen
import com.aiosman.ravenow.ui.comment.CommentsScreen import com.aiosman.ravenow.ui.comment.CommentsScreen
@@ -90,6 +92,8 @@ sealed class NavigationRoute(
data object Chat : NavigationRoute("Chat/{id}") data object Chat : NavigationRoute("Chat/{id}")
data object CommentNoticeScreen : NavigationRoute("CommentNoticeScreen") data object CommentNoticeScreen : NavigationRoute("CommentNoticeScreen")
data object ImageCrop : NavigationRoute("ImageCrop") data object ImageCrop : NavigationRoute("ImageCrop")
data object AccountSetting : NavigationRoute("AccountSetting")
data object AboutScreen : NavigationRoute("AboutScreen")
} }
@@ -377,6 +381,20 @@ fun NavigationController(
ImageCropScreen() ImageCropScreen()
} }
} }
composable(route = NavigationRoute.AccountSetting.route) {
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
AccountSetting()
}
}
composable(route = NavigationRoute.AboutScreen.route) {
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
AboutScreen()
}
}
} }
} }
@@ -425,3 +443,9 @@ fun NavHostController.navigateToChat(id: String) {
.replace("{id}", id) .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 isLoading = false
Pager( Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false), config = PagingConfig(pageSize = 20, enablePlaceholders = false),
pagingSourceFactory = { pagingSourceFactory = {
MomentPagingSource( MomentPagingSource(
MomentRemoteDataSource(momentService), MomentRemoteDataSource(momentService),

View File

@@ -3,34 +3,58 @@ package com.aiosman.ravenow.ui.index
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column 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.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemColors import androidx.compose.material3.NavigationBarItemColors
import androidx.compose.material3.Scaffold 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.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color 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.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 androidx.compose.ui.unit.dp
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController 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.NavigationRoute
import com.aiosman.ravenow.ui.index.tabs.add.AddPage import com.aiosman.ravenow.ui.index.tabs.add.AddPage
import com.aiosman.ravenow.ui.index.tabs.message.NotificationsScreen 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.search.DiscoverScreen
import com.aiosman.ravenow.ui.index.tabs.shorts.ShortVideo import com.aiosman.ravenow.ui.index.tabs.shorts.ShortVideo
import com.aiosman.ravenow.ui.index.tabs.street.StreetPage 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.aiosman.ravenow.ui.post.NewPostViewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -62,10 +87,148 @@ fun IndexScreen() {
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
val pagerState = rememberPagerState(pageCount = { item.size }) val pagerState = rememberPagerState(pageCount = { item.size })
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val drawerState = rememberDrawerState(DrawerValue.Closed)
val context = LocalContext.current
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
systemUiController.setNavigationBarColor(Color.Transparent) systemUiController.setNavigationBarColor(Color.Transparent)
} }
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)
}
}
)
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)
}
}
)
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( Scaffold(
bottomBar = { bottomBar = {
NavigationBar( NavigationBar(
@@ -116,7 +279,9 @@ fun IndexScreen() {
innerPadding innerPadding
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
modifier = Modifier.background(AppColors.background).padding(0.dp), modifier = Modifier
.background(AppColors.background)
.padding(0.dp),
beyondBoundsPageCount = 5, beyondBoundsPageCount = 5,
userScrollEnabled = false userScrollEnabled = false
) { page -> ) { page ->
@@ -130,6 +295,10 @@ fun IndexScreen() {
} }
} }
} }
}
}
}
@Composable @Composable
fun Home() { fun Home() {
@@ -229,3 +398,44 @@ fun Notifications() {
NotificationsScreen() 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() { object IndexViewModel:ViewModel() {
var tabIndex by mutableStateOf(0) var tabIndex by mutableStateOf(0)
var openDrawer by mutableStateOf(false)
fun ResetModel(){ fun ResetModel(){
tabIndex = 0 tabIndex = 0
} }

View File

@@ -26,10 +26,12 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment 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.res.stringResource
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.ravenow.LocalAppTheme 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.expolre.ExploreMomentsList
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentsList import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentsList
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
@@ -42,7 +44,6 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun MomentsList() { fun MomentsList() {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val model = MomentViewModel
val navigationBarPaddings = val navigationBarPaddings =
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues() val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
@@ -58,7 +59,9 @@ fun MomentsList() {
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Row( Row(
modifier = Modifier.fillMaxWidth().height(44.dp), modifier = Modifier
.fillMaxWidth()
.height(44.dp),
// center the tabs // center the tabs
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
@@ -74,7 +77,7 @@ fun MomentsList() {
horizontalAlignment = Alignment.CenterHorizontally 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)) Spacer(modifier = Modifier.height(4.dp))
Box( Box(
@@ -97,7 +100,7 @@ fun MomentsList() {
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally 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)) Spacer(modifier = Modifier.height(4.dp))
Box( 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.CollapsingToolbarScaffold
import com.aiosman.ravenow.ui.composables.toolbar.ScrollStrategy import com.aiosman.ravenow.ui.composables.toolbar.ScrollStrategy
import com.aiosman.ravenow.ui.composables.toolbar.rememberCollapsingToolbarScaffoldState 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.EmptyMomentPostUnit
import com.aiosman.ravenow.ui.index.tabs.profile.composable.GalleryItem import com.aiosman.ravenow.ui.index.tabs.profile.composable.GalleryItem
import com.aiosman.ravenow.ui.index.tabs.profile.composable.MomentPostUnit import com.aiosman.ravenow.ui.index.tabs.profile.composable.MomentPostUnit
@@ -233,7 +234,9 @@ fun ProfileV3(
it.noRippleClickable { it.noRippleClickable {
Intent(Intent.ACTION_PICK).apply { Intent(Intent.ACTION_PICK).apply {
type = "image/*" type = "image/*"
pickBannerImageLauncher.launch(this) pickBannerImageLauncher.launch(
this
)
} }
} }
} else { } else {
@@ -278,70 +281,28 @@ fun ProfileV3(
start = 8.dp, start = 8.dp,
end = 8.dp end = 8.dp
) )
.noRippleClickable {
IndexViewModel.openDrawer = true
}
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.padding(16.dp) .padding(16.dp)
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(AppColors.background.copy(alpha = 0.7f)) .background(
AppColors.background.copy(
alpha = 0.7f
)
)
) { ) {
Icon( Icon(
painter = painterResource(id = R.drawable.rider_pro_more_horizon), painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "", contentDescription = "",
modifier = Modifier.noRippleClickable {
expanded = true
},
tint = AppColors.text 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) { if (isSelf) {
SelfProfileAction { SelfProfileAction {
navController.navigate(NavigationRoute.AccountEdit.route) navController.navigate(
NavigationRoute.AccountEdit.route
)
} }
} else { } else {
if (it.id != AppState.UserId) { if (it.id != AppState.UserId) {
@@ -415,7 +378,10 @@ fun ProfileV3(
) { ) {
StatusBarSpacer() StatusBarSpacer()
Row( Row(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), modifier = Modifier.padding(
horizontal = 16.dp,
vertical = 8.dp
),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
CustomAsyncImage( CustomAsyncImage(
@@ -437,69 +403,22 @@ fun ProfileV3(
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
if (isSelf) { if (isSelf) {
Box( Box(
modifier = Modifier modifier = Modifier.noRippleClickable {
IndexViewModel.openDrawer = true
}
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.padding(16.dp) .padding(16.dp)
) { ) {
Icon( Icon(
painter = painterResource(id = R.drawable.rider_pro_more_horizon), painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "", contentDescription = "",
modifier = Modifier.noRippleClickable {
minibarExpanded = true
},
tint = AppColors.text 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) .padding(8.dp)
.noRippleClickable { .noRippleClickable {
NewPostViewModel.asNewPost() NewPostViewModel.asNewPost()
navController.navigate(NavigationRoute.NewPost.route) navController.navigate(
NavigationRoute.NewPost.route
)
} }
) { ) {
Box( 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_fail_desc">举报失败,可以尝试重试</string>
<string name="report_title">举报这篇帖子的原因是?</string> <string name="report_title">举报这篇帖子的原因是?</string>
<string name="close">关闭</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> </resources>

View File

@@ -99,4 +99,12 @@
<string name="report_fail_desc">Failed to report, please try again</string> <string name="report_fail_desc">Failed to report, please try again</string>
<string name="report_title">Reason for reporting this post?</string> <string name="report_title">Reason for reporting this post?</string>
<string name="close">Close</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> </resources>