diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/CreateBottomSheet.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/CreateBottomSheet.kt new file mode 100644 index 0000000..08e640a --- /dev/null +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/CreateBottomSheet.kt @@ -0,0 +1,150 @@ +package com.aiosman.ravenow.ui.index + +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.BottomSheetDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.SheetState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.res.stringResource +import com.aiosman.ravenow.LocalAppTheme +import com.aiosman.ravenow.R +import com.aiosman.ravenow.ui.modifiers.noRippleClickable + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CreateBottomSheet( + sheetState: SheetState, + onDismiss: () -> Unit, + onAiClick: () -> Unit, + onGroupChatClick: () -> Unit, + onMomentClick: () -> Unit +) { + val appColors = LocalAppTheme.current + + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + windowInsets = BottomSheetDefaults.windowInsets, + containerColor = appColors.background, + dragHandle = null, + shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp, bottom = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // 标题 + Text( + text = stringResource(R.string.create_title), + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = appColors.text, + modifier = Modifier.padding(bottom = 32.dp) + ) + + // 三个创建选项 + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + // 群聊选项 + CreateOption( + icon = R.drawable.ic_create_group_chat, + label = stringResource(R.string.create_group_chat_option), + onClick = onGroupChatClick + ) + // 动态选项 + CreateOption( + icon = R.drawable.ic_create_monent, + label = stringResource(R.string.create_moment), + onClick = onMomentClick + ) + // AI选项 + CreateOption( + icon = R.drawable.ic_create_ai, + label = stringResource(R.string.create_ai), + onClick = onAiClick + ) + + + + + } + + Spacer(modifier = Modifier.height(40.dp)) + + // 关闭按钮 + Box( + modifier = Modifier + .size(32.dp) + .clip(CircleShape) + .noRippleClickable { onDismiss() }, + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(R.drawable.ic_create_close), + contentDescription = stringResource(R.string.create_close), + modifier = Modifier.size(32.dp) + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + } + } +} + +@Composable +private fun CreateOption( + icon: Int, + label: String, + onClick: () -> Unit +) { + val appColors = LocalAppTheme.current + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.noRippleClickable { onClick() } + ) { + // 直接使用图标,不要背景 + Image( + painter = painterResource(icon), + contentDescription = label, + modifier = Modifier.size(72.dp) + ) + + Spacer(modifier = Modifier.height(12.dp)) + + // 文字标签 + Text( + text = label, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = appColors.text + ) + } +} diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt index 492ca51..aa67042 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/Index.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.DrawerValue +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.NavigationBar @@ -34,6 +35,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -78,9 +80,10 @@ import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.post.NewPostViewModel import com.aiosman.ravenow.utils.ResourceCleanupManager import com.google.accompanist.systemuicontroller.rememberSystemUiController +import kotlinx.coroutines.delay import kotlinx.coroutines.launch -@OptIn(ExperimentalFoundationApi::class) +@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable fun IndexScreen() { val AppColors = LocalAppTheme.current @@ -101,6 +104,7 @@ fun IndexScreen() { val pagerState = rememberPagerState(pageCount = { item.size }) val coroutineScope = rememberCoroutineScope() val drawerState = rememberDrawerState(DrawerValue.Closed) + val bottomSheetState = rememberModalBottomSheetState() val context = LocalContext.current // 注意:不要在离开 Index 路由时全量清理资源,以免返回后列表被重置 @@ -292,8 +296,8 @@ fun IndexScreen() { navController.navigate(NavigationRoute.Login.route) return@noRippleClickable } - NewPostViewModel.asNewPost() - navController.navigate(NavigationRoute.NewPost.route) + // 显示创建底部弹窗 + model.showCreateBottomSheet = true return@noRippleClickable } @@ -389,6 +393,56 @@ fun IndexScreen() { } } } + + // 创建底部弹窗 + if (model.showCreateBottomSheet) { + CreateBottomSheet( + sheetState = bottomSheetState, + onDismiss = { + // 使用协程来优雅地关闭弹窗 + coroutineScope.launch { + bottomSheetState.hide() + model.showCreateBottomSheet = false + } + }, + onAiClick = { + // 使用协程来优雅地关闭弹窗并导航 + coroutineScope.launch { + bottomSheetState.hide() // 触发关闭动画 + model.showCreateBottomSheet = false + // 检查游客模式,如果是游客则跳转登录 + if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CREATE_AGENT)) { + navController.navigate(NavigationRoute.Login.route) + } else { + navController.navigate(NavigationRoute.AddAgent.route) + } + } + }, + onGroupChatClick = { + // 使用协程来优雅地关闭弹窗并导航 + coroutineScope.launch { + bottomSheetState.hide() // 触发关闭动画 + model.showCreateBottomSheet = false + // 检查游客模式,如果是游客则跳转登录 + if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.JOIN_GROUP_CHAT)) { + navController.navigate(NavigationRoute.Login.route) + } else { + navController.navigate(NavigationRoute.CreateGroupChat.route) + } + } + }, + onMomentClick = { + // 使用协程来优雅地关闭弹窗并导航 + coroutineScope.launch { + bottomSheetState.hide() // 触发关闭动画 + model.showCreateBottomSheet = false + // 导航到动态创建页面 + NewPostViewModel.asNewPost() + navController.navigate(NavigationRoute.NewPost.route) + } + } + ) + } } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/IndexViewModel.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/IndexViewModel.kt index 78cf73e..cc8e5fc 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/IndexViewModel.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/IndexViewModel.kt @@ -9,9 +9,12 @@ object IndexViewModel:ViewModel() { var tabIndex by mutableStateOf(0) var openDrawer by mutableStateOf(false) + + var showCreateBottomSheet by mutableStateOf(false) fun ResetModel(){ tabIndex = 0 + showCreateBottomSheet = false } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_create_ai.xml b/app/src/main/res/drawable/ic_create_ai.xml new file mode 100644 index 0000000..2b53d35 --- /dev/null +++ b/app/src/main/res/drawable/ic_create_ai.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_create_close.xml b/app/src/main/res/drawable/ic_create_close.xml new file mode 100644 index 0000000..ba97b4d --- /dev/null +++ b/app/src/main/res/drawable/ic_create_close.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/drawable/ic_create_group_chat.xml b/app/src/main/res/drawable/ic_create_group_chat.xml new file mode 100644 index 0000000..d872100 --- /dev/null +++ b/app/src/main/res/drawable/ic_create_group_chat.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_create_monent.xml b/app/src/main/res/drawable/ic_create_monent.xml new file mode 100644 index 0000000..7c4fea7 --- /dev/null +++ b/app/src/main/res/drawable/ic_create_monent.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000..4bb424f --- /dev/null +++ b/app/src/main/res/values-ja/strings.xml @@ -0,0 +1,9 @@ + + + + 作成 + AI + グループチャット + モーメント + 閉じる + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 4a5ab7e..169da43 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -190,5 +190,12 @@ 创建中... 发现 密码不能超过 %1$d 个字符 + + + 创建 + AI + 群聊 + 动态 + 关闭 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4938d21..dae2fb1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -186,5 +186,12 @@ 创建中... 发现 Password cannot exceed %1$d characters + + + Create + AI + Group Chat + Moment + Close \ No newline at end of file