添加新的表单文本输入组件 FormTextInput2,包含错误提示和动态显示功能;新增图标和图片资源。

This commit is contained in:
weber
2025-08-05 13:56:03 +08:00
parent 29d2bb753f
commit 3e544844bb
22 changed files with 712 additions and 116 deletions

View File

@@ -20,6 +20,7 @@ open class AppThemeData(
var decentBackground: Color, var decentBackground: Color,
var divider: Color, var divider: Color,
var inputBackground: Color, var inputBackground: Color,
var inputBackground2: Color,
var inputHint: Color, var inputHint: Color,
var error: Color, var error: Color,
var checkedBackground: Color, var checkedBackground: Color,
@@ -30,7 +31,7 @@ open class AppThemeData(
) )
class LightThemeColors : AppThemeData( class LightThemeColors : AppThemeData(
main = Color(0xffda3832), main = Color(0xffD80264),
mainText = Color(0xffffffff), mainText = Color(0xffffffff),
basicMain = Color(0xfff0f0f0), basicMain = Color(0xfff0f0f0),
nonActive = Color(0xfff5f5f5), nonActive = Color(0xfff5f5f5),
@@ -43,6 +44,7 @@ class LightThemeColors : AppThemeData(
background = Color(0xFFFFFFFF), background = Color(0xFFFFFFFF),
divider = Color(0xFFEbEbEb), divider = Color(0xFFEbEbEb),
inputBackground = Color(0xFFF7f7f7), inputBackground = Color(0xFFF7f7f7),
inputBackground2 = Color(0xFFFFFFFF),
inputHint = Color(0xffdadada), inputHint = Color(0xffdadada),
error = Color(0xffFF0000), error = Color(0xffFF0000),
checkedBackground = Color(0xff000000), checkedBackground = Color(0xff000000),
@@ -68,6 +70,7 @@ class DarkThemeColors : AppThemeData(
background = Color(0xFF121212), background = Color(0xFF121212),
divider = Color(0xFF282828), divider = Color(0xFF282828),
inputBackground = Color(0xFF1C1C1C), inputBackground = Color(0xFF1C1C1C),
inputBackground2 = Color(0xFF1C1C1C),
inputHint = Color(0xff888888), inputHint = Color(0xff888888),
error = Color(0xffFF0000), error = Color(0xffFF0000),
checkedBackground = Color(0xffffffff), checkedBackground = Color(0xffffffff),

View File

@@ -148,6 +148,10 @@ interface MomentService {
relPostId: Int? = null relPostId: Int? = null
): MomentEntity ): MomentEntity
suspend fun agentMoment(
content: String,
): String
/** /**
* 收藏动态 * 收藏动态
* @param id 动态ID * @param id 动态ID

View File

@@ -31,6 +31,12 @@ data class RegisterRequestBody(
@SerializedName("password") @SerializedName("password")
val password: String val password: String
) )
data class AgentMomentRequestBody(
@SerializedName("generateText")
val generateText: String,
@SerializedName("sessionId")
val sessionId: String
)
data class LoginUserRequestBody( data class LoginUserRequestBody(
@SerializedName("username") @SerializedName("username")
@@ -472,6 +478,17 @@ interface RaveNowAPI {
@Query("pageSize") pageSize: Int = 20, @Query("pageSize") pageSize: Int = 20,
): Response<ListContainer<Agent>> ): Response<ListContainer<Agent>>
@Multipart
@POST("outside/prompts")
suspend fun createAgent(
@Part avatar: MultipartBody.Part?,
@Part("title") title: RequestBody?,
@Part("desc") desc: RequestBody?,
): Response<DataContainer<Agent>>
@POST("generate/postText")
suspend fun agentMoment(@Body body: AgentMomentRequestBody): Response<DataContainer<String>>
} }

View File

@@ -5,14 +5,35 @@ import androidx.paging.PagingState
import com.aiosman.ravenow.data.ListContainer import com.aiosman.ravenow.data.ListContainer
import com.aiosman.ravenow.data.AgentService import com.aiosman.ravenow.data.AgentService
import com.aiosman.ravenow.data.ServiceException import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.data.api.ApiClient import com.aiosman.ravenow.data.api.ApiClient
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.io.IOException import java.io.IOException
/** /**
* 智能体 * 智能体
*/ */
suspend fun createAgent(
title: String,
desc: String,
avatar: UploadImage,
): AgentEntity {
val textTitle = title.toRequestBody("text/plain".toMediaTypeOrNull())
val textDesc = desc.toRequestBody("text/plain".toMediaTypeOrNull())
val avatarField: MultipartBody.Part? = avatar?.let {
createMultipartBody(it.file, it.filename, "avatar")
}
val response = ApiClient.api.createAgent(avatarField, textTitle ,textDesc)
val body = response.body()?.data ?: throw ServiceException("Failed to create agent")
return body.toAgentEntity()
}
data class AgentEntity( data class AgentEntity(
val author: String, val author: String,
val avatar: String, val avatar: String,
@@ -39,7 +60,10 @@ data class ProfileEntity(
val trtcUserId: String, val trtcUserId: String,
val username: String val username: String
) )
fun createMultipartBody(file: File, filename: String, name: String): MultipartBody.Part {
val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull())
return MultipartBody.Part.createFormData(name, filename, requestFile)
}
class AgentLoaderExtraArgs( class AgentLoaderExtraArgs(
) )

View File

@@ -7,6 +7,7 @@ import com.aiosman.ravenow.data.ListContainer
import com.aiosman.ravenow.data.MomentService import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.ServiceException import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.UploadImage import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.data.api.AgentMomentRequestBody
import com.aiosman.ravenow.data.api.ApiClient import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.parseErrorResponse import com.aiosman.ravenow.data.parseErrorResponse
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -127,6 +128,10 @@ class MomentServiceImpl() : MomentService {
return momentBackend.createMoment(content, authorId, images, relPostId) return momentBackend.createMoment(content, authorId, images, relPostId)
} }
override suspend fun agentMoment(content: String): String {
return momentBackend.agentMoment(content)
}
override suspend fun favoriteMoment(id: Int) { override suspend fun favoriteMoment(id: Int) {
momentBackend.favoriteMoment(id) momentBackend.favoriteMoment(id)
} }
@@ -212,6 +217,17 @@ class MomentBackend {
} }
suspend fun agentMoment(
content: String,
): String {
val textContent = content.toRequestBody("text/plain".toMediaTypeOrNull())
val sessionId = ""
val response = ApiClient.api.agentMoment(AgentMomentRequestBody(generateText = content, sessionId =sessionId ))
val body = response.body()?.data ?: throw ServiceException("Failed to agent moment")
return body.toString()
}
suspend fun favoriteMoment(id: Int) { suspend fun favoriteMoment(id: Int) {
ApiClient.api.favoritePost(id) ApiClient.api.favoritePost(id)
} }

View File

@@ -25,6 +25,7 @@ import androidx.compose.runtime.setValue
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.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -42,6 +43,7 @@ import com.aiosman.ravenow.ui.composables.ActionButton
import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.composables.form.FormTextInput import com.aiosman.ravenow.ui.composables.form.FormTextInput
import com.aiosman.ravenow.ui.composables.form.FormTextInput2
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -84,12 +86,13 @@ fun AddAgentScreen() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(color = appColors.background), .background(color = appColors.decentBackground),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
StatusBarSpacer() StatusBarSpacer()
Box( Box(
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp) modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
.background(color = appColors.decentBackground)
) { ) {
ScreenHeader ( ScreenHeader (
title = stringResource(R.string.agent_add), title = stringResource(R.string.agent_add),
@@ -150,15 +153,17 @@ fun AddAgentScreen() {
value = model.name, value = model.name,
label = stringResource(R.string.agent_name), label = stringResource(R.string.agent_name),
hint = stringResource(R.string.agent_name_hint), hint = stringResource(R.string.agent_name_hint),
background = appColors.inputBackground2,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { value -> ) { value ->
onNicknameChange(value) onNicknameChange(value)
} }
// Spacer(modifier = Modifier.height(16.dp)) // Spacer(modifier = Modifier.height(16.dp))
FormTextInput( FormTextInput2(
value = model.desc, value = model.desc,
label = stringResource(R.string.agent_desc), label = stringResource(R.string.agent_desc),
hint = stringResource(R.string.agent_desc_hint), hint = stringResource(R.string.agent_desc_hint),
background = appColors.inputBackground2,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { value -> ) { value ->
onDescChange(value) onDescChange(value)
@@ -167,7 +172,20 @@ fun AddAgentScreen() {
Spacer(modifier = Modifier.height(58.dp)) Spacer(modifier = Modifier.height(58.dp))
ActionButton( ActionButton(
modifier = Modifier modifier = Modifier
.width(345.dp), .width(345.dp)
.padding(horizontal = 16.dp)
.background(
brush = Brush.linearGradient(
colors = listOf(
Color(0xFFEE2A33),
Color(0xFFD80264),
Color(0xFF8468BC)
)
),
shape = RoundedCornerShape(24.dp)
),
color = Color.White,
backgroundColor = Color.Transparent,
text = stringResource(R.string.agent_create), text = stringResource(R.string.agent_create),
) { ) {

View File

@@ -129,7 +129,7 @@ fun ScreenHeader(
) )
Spacer(modifier = Modifier.size(12.dp)) Spacer(modifier = Modifier.size(12.dp))
Text(title, Text(title,
fontWeight = FontWeight.W800, fontWeight = FontWeight.W600,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
fontSize = 17.sp, fontSize = 17.sp,

View File

@@ -11,6 +11,7 @@ import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
import androidx.compose.foundation.lazy.grid.LazyGridItemScope import androidx.compose.foundation.lazy.grid.LazyGridItemScope
@@ -18,6 +19,7 @@ import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -26,6 +28,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
@@ -58,12 +61,12 @@ fun <T : Any> DraggableGrid(
val dragDropState = val dragDropState =
rememberGridDragDropState(gridState, onMove, onDragModeStart, onDragModeEnd, lockedIndices) rememberGridDragDropState(gridState, onMove, onDragModeStart, onDragModeEnd, lockedIndices)
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(3), columns = GridCells.Fixed(5),
modifier = Modifier.dragContainer(dragDropState), modifier = Modifier.dragContainer(dragDropState).padding(horizontal = 8.dp),
state = gridState, state = gridState,
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(8.dp),
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
itemsIndexed(items, key = { _, item -> itemsIndexed(items, key = { _, item ->
@@ -122,7 +125,7 @@ fun LazyGridItemScope.DraggableItem(
} else { } else {
Modifier.animateItemPlacement() Modifier.animateItemPlacement()
} }
Box(modifier = modifier.then(draggingModifier), propagateMinConstraints = true) { Box(modifier = modifier.then(draggingModifier).clip(RoundedCornerShape(8.dp)), propagateMinConstraints = true) {
content(dragging) content(dragging)
} }
} }

View File

@@ -57,10 +57,29 @@ fun CustomAsyncImage(
val imageLoader = getImageLoader(context ?: localContext) val imageLoader = getImageLoader(context ?: localContext)
// 处理 imageUrl 为 null 的情况
if (imageUrl == null|| imageUrl == "") {
// 如果 imageUrl 为 null 且有占位符,则直接显示占位符
if (placeholderRes != null) {
androidx.compose.foundation.Image(
painter = androidx.compose.ui.res.painterResource(placeholderRes),
contentDescription = contentDescription,
modifier = modifier,
contentScale = contentScale
)
return
}
}
AsyncImage( AsyncImage(
model = ImageRequest.Builder(context ?: localContext) model = ImageRequest.Builder(context ?: localContext)
.data(imageUrl) .data(imageUrl)
.crossfade(200) .crossfade(200)
.apply {
// 设置占位符图片
if (placeholderRes != null) {
placeholder(placeholderRes)
}
}
.build(), .build(),
contentDescription = contentDescription, contentDescription = contentDescription,
modifier = modifier, modifier = modifier,

View File

@@ -23,6 +23,7 @@ import androidx.compose.runtime.Composable
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.graphics.Color
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
@@ -32,6 +33,9 @@ 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.R
/**
* 水平布局的输入框
*/
@Composable @Composable
fun FormTextInput( fun FormTextInput(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -39,6 +43,7 @@ fun FormTextInput(
label: String? = null, label: String? = null,
error: String? = null, error: String? = null,
hint: String? = null, hint: String? = null,
background: Color? = null,
onValueChange: (String) -> Unit onValueChange: (String) -> Unit
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
@@ -48,7 +53,7 @@ fun FormTextInput(
Row( Row(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
.clip(RoundedCornerShape(16.dp)) .clip(RoundedCornerShape(16.dp))
.background(AppColors.inputBackground) .background(background ?: AppColors.inputBackground)
.let { .let {
if (error != null) { if (error != null) {
it.border(1.dp, AppColors.error, RoundedCornerShape(24.dp)) it.border(1.dp, AppColors.error, RoundedCornerShape(24.dp))
@@ -66,7 +71,7 @@ fun FormTextInput(
.widthIn(100.dp), .widthIn(100.dp),
style = TextStyle( style = TextStyle(
fontSize = 16.sp, fontSize = 16.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.W600,
color = AppColors.text color = AppColors.text
) )
) )

View File

@@ -0,0 +1,142 @@
package com.aiosman.ravenow.ui.composables.form
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
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.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
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.graphics.SolidColor
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
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
/**
* 垂直布局的输入框
*/
@Composable
fun FormTextInput2(
modifier: Modifier = Modifier,
value: String,
label: String? = null,
error: String? = null,
hint: String? = null,
background: Color? = null,
onValueChange: (String) -> Unit
) {
val AppColors = LocalAppTheme.current
Column(
modifier = modifier.height(150.dp)
) {
Column(
modifier = Modifier.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(background ?: AppColors.inputBackground)
.let {
if (error != null) {
it.border(1.dp, AppColors.error, RoundedCornerShape(24.dp))
} else {
it
}
}
.padding(17.dp),
) {
label?.let {
Text(
text = it,
modifier = Modifier
.widthIn(100.dp),
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.W600,
color = AppColors.text
)
)
}
Box(
modifier = Modifier
.weight(1f)
.padding(top = 8.dp)
) {
if (value.isEmpty()) {
Text(
text = hint ?: "",
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = AppColors.inputHint
)
)
}
BasicTextField(
maxLines = 5,
value = value,
onValueChange = {
onValueChange(it)
},
textStyle = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = AppColors.text,
lineHeight = 20.sp
),
cursorBrush = SolidColor(AppColors.text),
)
}
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.height(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
AnimatedVisibility(
visible = error != null,
enter = fadeIn(),
exit = fadeOut()
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
painter = painterResource(id = R.mipmap.rider_pro_input_error),
contentDescription = "Error",
modifier = Modifier.size(8.dp)
)
Spacer(modifier = Modifier.size(4.dp))
AnimatedContent(targetState = error) { targetError ->
Text(targetError ?: "", color = AppColors.error, fontSize = 12.sp)
}
}
}
}
}
}

View File

@@ -62,16 +62,16 @@ fun Agent() {
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.height(36.dp) // 设置高度为36dp .height(36.dp)
.fillMaxWidth(), // 占据整行宽度 .fillMaxWidth(),
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom verticalAlignment = Alignment.Bottom
) { ) {
// 搜索框 - 占据剩余空间 // 搜索框
Row( Row(
modifier = Modifier modifier = Modifier
.height(36.dp) .height(36.dp)
.weight(1f) // 权重为1占据剩余空间 .weight(1f)
.clip(shape = RoundedCornerShape(18.dp)) .clip(shape = RoundedCornerShape(18.dp))
.background(AppColors.inputBackground) .background(AppColors.inputBackground)
.padding(horizontal = 8.dp, vertical = 0.dp) .padding(horizontal = 8.dp, vertical = 0.dp)
@@ -94,16 +94,13 @@ fun Agent() {
) )
} }
} }
// 间隔
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
// 新增 // 新增
Icon( Icon(
modifier = Modifier modifier = Modifier
.size(36.dp) .size(36.dp)
.noRippleClickable { .noRippleClickable {
// 图标点击事件 //
navController.navigate( navController.navigate(
NavigationRoute.AddAgent.route NavigationRoute.AddAgent.route
) )

View File

@@ -1,6 +1,7 @@
package com.aiosman.ravenow.ui.index.tabs.message package com.aiosman.ravenow.ui.index.tabs.message
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -14,7 +15,10 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
@@ -28,6 +32,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -60,13 +65,15 @@ import kotlinx.coroutines.launch
/** /**
* 消息列表界面 * 消息列表界面
*/ */
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable @Composable
fun NotificationsScreen() { fun NotificationsScreen() {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val navController = LocalNavController.current val navController = LocalNavController.current
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
val context = LocalContext.current val context = LocalContext.current
var pagerState = rememberPagerState (pageCount = { 4 })
var scope = rememberCoroutineScope()
val state = rememberPullRefreshState(MessageListViewModel.isLoading, onRefresh = { val state = rememberPullRefreshState(MessageListViewModel.isLoading, onRefresh = {
MessageListViewModel.viewModelScope.launch { MessageListViewModel.viewModelScope.launch {
MessageListViewModel.initData(context, force = true, loadChat = AppState.enableChat) MessageListViewModel.initData(context, force = true, loadChat = AppState.enableChat)
@@ -96,10 +103,9 @@ fun NotificationsScreen() {
.padding(horizontal = 15.dp, vertical = 8.dp), .padding(horizontal = 15.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
// 左侧占位元素
Box(modifier = Modifier.size(24.dp)) Box(modifier = Modifier.size(24.dp))
// 左侧 Columnlabel 居中显示
Column( Column(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
@@ -113,7 +119,7 @@ fun NotificationsScreen() {
color = AppColors.text color = AppColors.text
) )
} }
// 右侧图标
Image( Image(
painter = painterResource(id = R.drawable.rider_pro_group), painter = painterResource(id = R.drawable.rider_pro_group),
contentDescription = "add", contentDescription = "add",
@@ -169,8 +175,111 @@ fun NotificationsScreen() {
navController.navigate(NavigationRoute.CommentNoticeScreen.route) navController.navigate(NavigationRoute.CommentNoticeScreen.route)
} }
} }
HorizontalDivider(color = AppColors.divider, modifier = Modifier.padding(16.dp)) Row(
Box( modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(horizontal = 16.dp),
// center the tabs
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom
) {
Column(
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(0)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.chat_ai),
fontSize = 14.sp,
color = if (pagerState.currentPage == 0) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 0) AppColors.checkedBackground else AppColors.unCheckedBackground)
.padding(horizontal = 11.dp, vertical = 4.dp)
)
}
Spacer(modifier = Modifier.width(8.dp))
Column(
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(1)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
androidx.compose.material.Text(
text = stringResource(R.string.chat_group),
fontSize = 14.sp,
color = if (pagerState.currentPage == 1) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 1) AppColors.checkedBackground else AppColors.unCheckedBackground)
.padding(horizontal = 11.dp, vertical = 4.dp)
)
}
Spacer(modifier = Modifier.width(8.dp))
Column(
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(2)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
androidx.compose.material.Text(
text = stringResource(R.string.chat_friend),
fontSize = 14.sp,
color = if (pagerState.currentPage == 2) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 2) AppColors.checkedBackground else AppColors.unCheckedBackground)
.padding(horizontal = 11.dp, vertical = 4.dp)
)
}
}
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
when (it) {
0 -> {
}
1 -> {
}
2 -> {
}
}
}
/*Box(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.fillMaxWidth(), .fillMaxWidth(),
@@ -194,7 +303,7 @@ fun NotificationsScreen() {
) )
} }
} }*/
} }
PullRefreshIndicator( PullRefreshIndicator(
MessageListViewModel.isLoading, MessageListViewModel.isLoading,
@@ -226,28 +335,14 @@ fun NotificationIndicator(
onClick() onClick()
} }
) { ) {
if (notificationCount > 0) {
Box(
modifier = Modifier
.background(AppColors.main, RoundedCornerShape(16.dp))
.padding(4.dp)
.align(Alignment.TopEnd)
) {
Text(
text = notificationCount.toString(),
color = AppColors.mainText,
fontSize = 10.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.align(Alignment.Center)
)
}
}
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.size(64.dp) .size(69.dp)
.padding(5.dp)
.background(color = backgroundColor, .background(color = backgroundColor,
shape = RoundedCornerShape(16.dp)), shape = RoundedCornerShape(16.dp)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@@ -265,6 +360,22 @@ fun NotificationIndicator(
} }
} }
if (notificationCount > 0) {
Box(
modifier = Modifier
.background(AppColors.main, RoundedCornerShape(16.dp))
.padding(horizontal = 8.dp, vertical = 4.dp)
.align(Alignment.TopEnd)
) {
Text(
text = if (notificationCount > 99) "99+" else notificationCount.toString(),
color = AppColors.mainText,
fontSize = 10.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.align(Alignment.Center)
)
}
}
} }
} }

View File

@@ -4,6 +4,11 @@ import android.net.Uri
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -31,6 +36,8 @@ import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -42,6 +49,8 @@ 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.draw.drawBehind import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -49,20 +58,25 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.entity.createAgent
import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.DraggableGrid import com.aiosman.ravenow.ui.composables.DraggableGrid
@@ -78,6 +92,10 @@ import java.io.File
@Composable @Composable
fun NewPostScreen() { fun NewPostScreen() {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
var isAiEnabled by remember { mutableStateOf(false) }
var isRotating by remember { mutableStateOf(false) }
var isRequesting by remember { mutableStateOf(false) }
val keyboardController = LocalSoftwareKeyboardController.current // 添加这行
val model = NewPostViewModel val model = NewPostViewModel
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
@@ -102,9 +120,6 @@ fun NewPostScreen() {
) { ) {
NewPostTopBar { NewPostTopBar {
} }
NewPostTextField("Share your adventure…", NewPostViewModel.textContent) {
NewPostViewModel.textContent = it
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -128,7 +143,168 @@ fun NewPostScreen() {
} }
AddImageGrid() AddImageGrid()
// AdditionalPostItem() NewPostTextField(stringResource(R.string.moment_content_hint), NewPostViewModel.textContent) {
NewPostViewModel.textContent = it
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(AppColors.divider)
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp, start = 16.dp, end = 16.dp, bottom = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.mipmap.rider_pro_moment_ai),
contentDescription = null,
modifier = Modifier
.size(24.dp)
)
Text(
text = stringResource(R.string.moment_ai_co),
fontWeight = FontWeight.Bold,
fontSize = 15.sp,
modifier = Modifier
.padding(start = 8.dp)
.weight(1f),
color = AppColors.text,
)
Switch(
checked = isAiEnabled,
onCheckedChange = {
isChecked ->
isAiEnabled = isChecked
if (isChecked) {
// 收起键盘
keyboardController?.hide()
isRequesting = true
isRotating = true
model.viewModelScope.launch {
try {
model.agentMoment(model.textContent)
} catch (e: Exception) {
e.printStackTrace()
}finally {
isRequesting = false
isRotating = false
isAiEnabled = false
}
}
} else {
}
},
enabled = !isRequesting && model.textContent.isNotEmpty(),
colors = SwitchDefaults.colors(
checkedThumbColor = Color.White,
checkedTrackColor = AppColors.brandColorsColor,
uncheckedThumbColor = Color.White,
uncheckedTrackColor = Color(0xFFE9E9EA),
uncheckedBorderColor = Color.White,
disabledCheckedTrackColor = AppColors.brandColorsColor.copy(alpha = 0.8f),
disabledCheckedThumbColor= Color.White,
disabledUncheckedTrackColor = Color(0xFFE9E9EA),
disabledUncheckedThumbColor= Color.White
),
modifier = Modifier.scale(0.8f)
)
}
Column(
modifier = Modifier.fillMaxWidth()
) {
BasicTextField(
value = model.aiTextContent,
onValueChange = { newValue ->
model.aiTextContent = newValue
},
modifier = Modifier
.height(160.dp)
.heightIn(160.dp)
.padding(horizontal = 16.dp, vertical = 10.dp)
.fillMaxWidth(),
cursorBrush = SolidColor(AppColors.text),
textStyle = TextStyle(
lineHeight = 24.sp,
color = AppColors.text,
),
readOnly = true
)
if (model.aiTextContent.isNotEmpty()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
horizontalArrangement = Arrangement.End // 靠右对齐
) {
// 删除按钮
Row(
modifier = Modifier
.noRippleClickable {
model.aiTextContent = ""
}
.background(
color = AppColors.basicMain,
shape = RoundedCornerShape(16.dp)
)
.padding(horizontal = 8.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_moment_delete),
contentDescription = "delete",
modifier = Modifier.size(16.dp),
tint = AppColors.text
)
Text(
text = stringResource(R.string.moment_ai_delete),
fontSize = 12.sp,
color = AppColors.text,
modifier = Modifier.padding(start = 4.dp)
)
}
Spacer(modifier = Modifier.width(14.dp))
//应用生成文案
Row(
modifier = Modifier
.noRippleClickable {
if (model.aiTextContent.isNotEmpty()) {
model.textContent = model.aiTextContent
}
}
.background(
color = AppColors.basicMain,
shape = RoundedCornerShape(16.dp)
)
.padding(horizontal = 8.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_moment_apply),
contentDescription = "apply",
modifier = Modifier.size(16.dp),
tint = AppColors.text
)
Text(
text = stringResource(R.string.moment_ai_apply),
fontSize = 12.sp,
color = AppColors.text,
modifier = Modifier.padding(start = 4.dp)
)
}
}
}
}
} }
} }
} }
@@ -172,7 +348,7 @@ fun NewPostTopBar(onSendClick: () -> Unit = {}) {
modifier = Modifier.align(Alignment.CenterStart), modifier = Modifier.align(Alignment.CenterStart),
) { ) {
Image( Image(
painter = painterResource(id = R.drawable.rider_pro_back_icon), painter = painterResource(id = R.drawable.rider_pro_close),
contentDescription = "Back", contentDescription = "Back",
modifier = Modifier modifier = Modifier
.size(24.dp) .size(24.dp)
@@ -182,12 +358,11 @@ fun NewPostTopBar(onSendClick: () -> Unit = {}) {
colorFilter = ColorFilter.tint(AppColors.text) colorFilter = ColorFilter.tint(AppColors.text)
) )
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Icon( Image(
painter = painterResource(id = R.drawable.rider_pro_video_share), painter = painterResource(id = R.mipmap.rider_pro_moment_post),
tint = AppColors.text,
contentDescription = "Send", contentDescription = "Send",
modifier = Modifier modifier = Modifier
.size(32.dp) .size(24.dp)
.noRippleClickable { .noRippleClickable {
// 检查输入 // 检查输入
val errorMessage = model.validateMoment() val errorMessage = model.validateMoment()
@@ -208,7 +383,7 @@ fun NewPostTopBar(onSendClick: () -> Unit = {}) {
uploading = false uploading = false
} }
// 上传完成后隐藏进度条
} }
} }
@@ -227,13 +402,15 @@ fun NewPostTextField(hint: String, value: String, onValueChange: (String) -> Uni
value = value, value = value,
onValueChange = onValueChange, onValueChange = onValueChange,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .height(160.dp)
.heightIn(200.dp) .heightIn(160.dp)
.padding(horizontal = 18.dp, vertical = 10.dp), .padding(horizontal = 16.dp, vertical = 10.dp),
cursorBrush = SolidColor(AppColors.text), cursorBrush = SolidColor(AppColors.text),
textStyle = TextStyle( textStyle = TextStyle(
lineHeight = 24.sp,
color = AppColors.text, color = AppColors.text,
)
),
) )
if (value.isEmpty()) { if (value.isEmpty()) {
@@ -326,11 +503,11 @@ fun AddImageGrid() {
} }
} }
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(3), columns = GridCells.Fixed(5),
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(8.dp),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(18.dp), .padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
@@ -339,18 +516,8 @@ fun AddImageGrid() {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.aspectRatio(1f) .aspectRatio(1f)
.drawBehind { .clip(RoundedCornerShape(16.dp)) // 设置圆角
val strokeWidth = 1.dp.toPx() .background(Color(0xFFFAF9FB)) // 设置背景色
val dashLength = 10f
val dashGap = 10f
val pathEffect =
PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap))
drawRoundRect(
color = Color(0xFFD6D6D6),
style = Stroke(strokeWidth, pathEffect = pathEffect),
cornerRadius = CornerRadius(8.dp.toPx())
)
}
.noRippleClickable { .noRippleClickable {
pickImagesLauncher.launch("image/*") pickImagesLauncher.launch("image/*")
}, },
@@ -359,7 +526,7 @@ fun AddImageGrid() {
painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic), painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic),
contentDescription = "Add Image", contentDescription = "Add Image",
modifier = Modifier modifier = Modifier
.size(48.dp) .size(24.dp)
.align(Alignment.Center), .align(Alignment.Center),
tint = Color(0xFFD6D6D6) tint = Color(0xFFD6D6D6)
@@ -371,18 +538,8 @@ fun AddImageGrid() {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.aspectRatio(1f) .aspectRatio(1f)
.drawBehind { .clip(RoundedCornerShape(16.dp)) // 设置圆角
val strokeWidth = 1.dp.toPx() .background(Color(0xFFFAF9FB)) // 设置背景色
val dashLength = 10f
val dashGap = 10f
val pathEffect =
PathEffect.dashPathEffect(floatArrayOf(dashLength, dashGap))
drawRoundRect(
color = Color(0xFFD6D6D6),
style = Stroke(strokeWidth, pathEffect = pathEffect),
cornerRadius = CornerRadius(8.dp.toPx())
)
}
.noRippleClickable { .noRippleClickable {
val photoFile = File(context.cacheDir, "photo.jpg") val photoFile = File(context.cacheDir, "photo.jpg")
val photoUri: Uri = FileProvider.getUriForFile( val photoUri: Uri = FileProvider.getUriForFile(
@@ -398,7 +555,7 @@ fun AddImageGrid() {
painter = painterResource(id = R.drawable.rider_pro_camera), painter = painterResource(id = R.drawable.rider_pro_camera),
contentDescription = "Take Photo", contentDescription = "Take Photo",
modifier = Modifier modifier = Modifier
.size(48.dp) .size(24.dp)
.align(Alignment.Center), .align(Alignment.Center),
tint = Color(0xFFD6D6D6) tint = Color(0xFFD6D6D6)
) )

View File

@@ -8,12 +8,18 @@ import android.net.Uri
import android.util.Log import android.util.Log
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.aiosman.ravenow.data.MomentService import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.UploadImage import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.entity.MomentEntity import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.entity.MomentServiceImpl import com.aiosman.ravenow.entity.MomentServiceImpl
import com.aiosman.ravenow.entity.createMultipartBody
import com.aiosman.ravenow.event.MomentAddEvent import com.aiosman.ravenow.event.MomentAddEvent
import com.aiosman.ravenow.exp.rotate import com.aiosman.ravenow.exp.rotate
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
@@ -21,6 +27,9 @@ import com.aiosman.ravenow.ui.modification.Modification
import com.aiosman.ravenow.utils.FileUtil import com.aiosman.ravenow.utils.FileUtil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@@ -96,6 +105,7 @@ data class Draft(
object NewPostViewModel : ViewModel() { object NewPostViewModel : ViewModel() {
var momentService: MomentService = MomentServiceImpl() var momentService: MomentService = MomentServiceImpl()
var textContent by mutableStateOf("") var textContent by mutableStateOf("")
var aiTextContent by mutableStateOf("")
var searchPlaceAddressResult by mutableStateOf<SearchPlaceAddressResult?>(null) var searchPlaceAddressResult by mutableStateOf<SearchPlaceAddressResult?>(null)
var modificationList by mutableStateOf<List<Modification>>(listOf()) var modificationList by mutableStateOf<List<Modification>>(listOf())
var imageList by mutableStateOf(listOf<ImageItem>()) var imageList by mutableStateOf(listOf<ImageItem>())
@@ -111,6 +121,7 @@ object NewPostViewModel : ViewModel() {
// } // }
fun asNewPost() { fun asNewPost() {
textContent = "" textContent = ""
aiTextContent = ""
searchPlaceAddressResult = null searchPlaceAddressResult = null
modificationList = listOf() modificationList = listOf()
imageList = listOf() imageList = listOf()
@@ -163,12 +174,17 @@ object NewPostViewModel : ViewModel() {
onUploadProgress(((index / imageList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50% onUploadProgress(((index / imageList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50%
index += 1 index += 1
} }
aiTextContent = ""
val result = momentService.createMoment(textContent, 1, uploadImageList, relPostId) val result = momentService.createMoment(textContent, 1, uploadImageList, relPostId)
// 刷新个人动态 // 刷新个人动态
MyProfileViewModel.loadProfile(pullRefresh = true) MyProfileViewModel.loadProfile(pullRefresh = true)
EventBus.getDefault().post(MomentAddEvent(result)) EventBus.getDefault().post(MomentAddEvent(result))
} }
suspend fun agentMoment(textContent: String,) {
aiTextContent = momentService.agentMoment(textContent)
}
suspend fun init() { suspend fun init() {
relPostId?.let { relPostId?.let {
val moment = momentService.getMomentById(it) val moment = momentService.getMomentById(it)

View File

@@ -1,12 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path
android:pathData="M9.5,6L12.5,10L15.5,6L19,6L14.5,12L19,18L15.5,18L12.5,14L9.5,18L6,18L10.5,12L6,6L9.5,6Z" <group
android:strokeWidth="1" android:translateX="-16"
android:fillColor="#000000" android:translateY="-64">
android:fillType="evenOdd" <group
android:strokeColor="#00000000"/> android:translateX="16"
</vector> android:translateY="64">
<path
android:fillType="evenOdd"
android:strokeWidth="1"
android:pathData="M 0 0 L 24 0 L 24 24 L 0 24 Z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000000"
android:strokeWidth="3"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M 18 6 L 6 18" />
<path
android:fillType="evenOdd"
android:strokeColor="#000000"
android:strokeWidth="3"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M 6 6 L 18 18" />
</group>
</group>
</vector>

View File

@@ -7,32 +7,27 @@
<group <group
android:translateX="2" android:translateX="2"
android:translateY="4"> android:translateY="2.945">
<path <path
android:fillType="evenOdd" android:fillType="evenOdd"
android:strokeColor="#000" android:strokeColor="#000"
android:strokeWidth="1.778" android:strokeWidth="2"
android:pathData="M7.273 10.777c4.016 0 7.272 1.791 7.272 4 0 0.706-0.331 1.368-0.914 1.944-0.392 0.179 -0.828 0.278 -1.288 0.278 H2.202a3.1 3.1 0 0 1-1.288-0.278C0.332 16.145 0 15.483 0 14.777c0-2.209 3.256-4 7.273-4z" /> android:pathData="M13 14.055c3.314 0 6 1.44 6 3.215 0 0.564-0.272 1.095-0.749 1.556-0.324 0.147 -0.684 0.23 -1.063 0.23 H8.812c-0.379 0-0.739-0.083-1.062-0.23-0.478-0.461-0.75-0.992-0.75-1.556 0-1.775 2.686-3.215 6-3.215zm0-3a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" />
<path <path
android:fillType="evenOdd" android:fillType="evenOdd"
android:strokeColor="#000" android:strokeColor="#000"
android:strokeWidth="1.778" android:strokeWidth="2"
android:strokeLineJoin="round" android:pathData="M 5 2.056 C 6.38071187458 2.056 7.5 3.20707590819 7.5 4.627 C 7.5 6.04692409181 6.38071187458 7.198 5 7.198 C 3.61928812542 7.198 2.5 6.04692409181 2.5 4.627 C 2.5 3.20707590819 3.61928812542 2.056 5 2.056 Z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="2"
android:pathData="M5.677 14.055H1.51c-0.312 0-0.608-0.066-0.875-0.187C0.23 13.471 0 13.013 0 12.525 0 11.003 2.239 9.77 5 9.77c1.558 0 2.95 0.393 3.867 1.008" />
<path
android:fillType="evenOdd"
android:strokeColor="#110C13"
android:strokeWidth="1.5"
android:strokeLineCap="round" android:strokeLineCap="round"
android:pathData="M16.491 17h1.236c0.5 0 2.273-0.398 2.273-2.223 0-1.49-1.482-2.79-3.68-3.478" /> android:pathData="M16.364 2h4M18.364 0v4" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="1.778"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M 7.273 0 C 9.28110735038 0 10.909 1.59162771435 10.909 3.555 C 10.909 5.51837228565 9.28110735038 7.11 7.273 7.11 C 5.26489264962 7.11 3.637 5.51837228565 3.637 3.555 C 3.637 1.59162771435 5.26489264962 0 7.273 0 Z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000"
android:strokeWidth="1.778"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M13.636 0.116 c1.61 0.402 2.735 1.82 2.735 3.444 0 1.624-1.126 3.041-2.735 3.444" />
</group> </group>
</vector> </vector>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<group>
<group>
<group>
<group>
<group>
<path
android:fillType="evenOdd"
android:strokeWidth="1"
android:pathData="M0,0 L16,0 L16,16 L0,16 Z" />
<path
android:fillType="evenOdd"
android:strokeColor="#000000"
android:strokeWidth="1.33333333"
android:strokeLineJoin="round"
android:strokeLineCap="round"
android:pathData="M14,2 L9.66667,14 C9.60694,14.1303,9.47671,14.2139,9.33333,14.2139
C9.18996,14.2139,9.05973,14.1303,9,14 L6.66667,9.33333 L2,7
C1.86966,6.94027,1.78609,6.81004,1.78609,6.66667
C1.78609,6.52329,1.86966,6.39306,2,6.33333 L14,2" />
</group>
</group>
</group>
</group>
</group>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -125,5 +125,13 @@
<string name="agent_desc">设定描述</string> <string name="agent_desc">设定描述</string>
<string name="agent_desc_hint">示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题</string> <string name="agent_desc_hint">示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题</string>
<string name="agent_create">创建智能体</string> <string name="agent_create">创建智能体</string>
<string name="moment_content_hint">需要一些灵感来写文章吗?让人工智能来帮你!</string>
<string name="moment_ai_co">AI文案优化</string>
<string name="moment_ai_delete">删除</string>
<string name="moment_ai_apply">应用</string>
<string name="chat_ai">智能体</string>
<string name="chat_group">群聊</string>
<string name="chat_friend">朋友</string>
</resources> </resources>

View File

@@ -124,4 +124,11 @@
<string name="agent_desc">设定描述</string> <string name="agent_desc">设定描述</string>
<string name="agent_desc_hint">示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题</string> <string name="agent_desc_hint">示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题</string>
<string name="agent_create">创建智能体</string> <string name="agent_create">创建智能体</string>
<string name="moment_content_hint">Need some inspiration for your post? Let AI assist you!</string>
<string name="moment_ai_co">AI copywriting optimization</string>
<string name="moment_ai_delete">Delete</string>
<string name="moment_ai_apply">Apply</string>
<string name="chat_ai">Ai</string>
<string name="chat_group">Group</string>
<string name="chat_friend">Friends</string>
</resources> </resources>