添加新的表单文本输入组件 FormTextInput2,包含错误提示和动态显示功能;新增图标和图片资源。
This commit is contained in:
@@ -20,6 +20,7 @@ open class AppThemeData(
|
||||
var decentBackground: Color,
|
||||
var divider: Color,
|
||||
var inputBackground: Color,
|
||||
var inputBackground2: Color,
|
||||
var inputHint: Color,
|
||||
var error: Color,
|
||||
var checkedBackground: Color,
|
||||
@@ -30,7 +31,7 @@ open class AppThemeData(
|
||||
)
|
||||
|
||||
class LightThemeColors : AppThemeData(
|
||||
main = Color(0xffda3832),
|
||||
main = Color(0xffD80264),
|
||||
mainText = Color(0xffffffff),
|
||||
basicMain = Color(0xfff0f0f0),
|
||||
nonActive = Color(0xfff5f5f5),
|
||||
@@ -43,6 +44,7 @@ class LightThemeColors : AppThemeData(
|
||||
background = Color(0xFFFFFFFF),
|
||||
divider = Color(0xFFEbEbEb),
|
||||
inputBackground = Color(0xFFF7f7f7),
|
||||
inputBackground2 = Color(0xFFFFFFFF),
|
||||
inputHint = Color(0xffdadada),
|
||||
error = Color(0xffFF0000),
|
||||
checkedBackground = Color(0xff000000),
|
||||
@@ -68,6 +70,7 @@ class DarkThemeColors : AppThemeData(
|
||||
background = Color(0xFF121212),
|
||||
divider = Color(0xFF282828),
|
||||
inputBackground = Color(0xFF1C1C1C),
|
||||
inputBackground2 = Color(0xFF1C1C1C),
|
||||
inputHint = Color(0xff888888),
|
||||
error = Color(0xffFF0000),
|
||||
checkedBackground = Color(0xffffffff),
|
||||
|
||||
@@ -148,6 +148,10 @@ interface MomentService {
|
||||
relPostId: Int? = null
|
||||
): MomentEntity
|
||||
|
||||
suspend fun agentMoment(
|
||||
content: String,
|
||||
): String
|
||||
|
||||
/**
|
||||
* 收藏动态
|
||||
* @param id 动态ID
|
||||
|
||||
@@ -31,6 +31,12 @@ data class RegisterRequestBody(
|
||||
@SerializedName("password")
|
||||
val password: String
|
||||
)
|
||||
data class AgentMomentRequestBody(
|
||||
@SerializedName("generateText")
|
||||
val generateText: String,
|
||||
@SerializedName("sessionId")
|
||||
val sessionId: String
|
||||
)
|
||||
|
||||
data class LoginUserRequestBody(
|
||||
@SerializedName("username")
|
||||
@@ -472,6 +478,17 @@ interface RaveNowAPI {
|
||||
@Query("pageSize") pageSize: Int = 20,
|
||||
): 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>>
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,35 @@ import androidx.paging.PagingState
|
||||
import com.aiosman.ravenow.data.ListContainer
|
||||
import com.aiosman.ravenow.data.AgentService
|
||||
import com.aiosman.ravenow.data.ServiceException
|
||||
import com.aiosman.ravenow.data.UploadImage
|
||||
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
|
||||
|
||||
/**
|
||||
* 智能体
|
||||
*/
|
||||
|
||||
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(
|
||||
val author: String,
|
||||
val avatar: String,
|
||||
@@ -39,7 +60,10 @@ data class ProfileEntity(
|
||||
val trtcUserId: 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(
|
||||
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.aiosman.ravenow.data.ListContainer
|
||||
import com.aiosman.ravenow.data.MomentService
|
||||
import com.aiosman.ravenow.data.ServiceException
|
||||
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.parseErrorResponse
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
@@ -127,6 +128,10 @@ class MomentServiceImpl() : MomentService {
|
||||
return momentBackend.createMoment(content, authorId, images, relPostId)
|
||||
}
|
||||
|
||||
override suspend fun agentMoment(content: String): String {
|
||||
return momentBackend.agentMoment(content)
|
||||
}
|
||||
|
||||
override suspend fun favoriteMoment(id: Int) {
|
||||
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) {
|
||||
ApiClient.api.favoritePost(id)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
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.StatusBarSpacer
|
||||
import com.aiosman.ravenow.ui.composables.form.FormTextInput
|
||||
import com.aiosman.ravenow.ui.composables.form.FormTextInput2
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -84,12 +86,13 @@ fun AddAgentScreen() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = appColors.background),
|
||||
.background(color = appColors.decentBackground),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
|
||||
.background(color = appColors.decentBackground)
|
||||
) {
|
||||
ScreenHeader (
|
||||
title = stringResource(R.string.agent_add),
|
||||
@@ -150,15 +153,17 @@ fun AddAgentScreen() {
|
||||
value = model.name,
|
||||
label = stringResource(R.string.agent_name),
|
||||
hint = stringResource(R.string.agent_name_hint),
|
||||
background = appColors.inputBackground2,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) { value ->
|
||||
onNicknameChange(value)
|
||||
}
|
||||
// Spacer(modifier = Modifier.height(16.dp))
|
||||
FormTextInput(
|
||||
FormTextInput2(
|
||||
value = model.desc,
|
||||
label = stringResource(R.string.agent_desc),
|
||||
hint = stringResource(R.string.agent_desc_hint),
|
||||
background = appColors.inputBackground2,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) { value ->
|
||||
onDescChange(value)
|
||||
@@ -167,7 +172,20 @@ fun AddAgentScreen() {
|
||||
Spacer(modifier = Modifier.height(58.dp))
|
||||
ActionButton(
|
||||
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),
|
||||
) {
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ fun ScreenHeader(
|
||||
)
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
Text(title,
|
||||
fontWeight = FontWeight.W800,
|
||||
fontWeight = FontWeight.W600,
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 17.sp,
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.gestures.scrollBy
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.LazyGridItemInfo
|
||||
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.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -26,6 +28,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
@@ -58,12 +61,12 @@ fun <T : Any> DraggableGrid(
|
||||
val dragDropState =
|
||||
rememberGridDragDropState(gridState, onMove, onDragModeStart, onDragModeEnd, lockedIndices)
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3),
|
||||
modifier = Modifier.dragContainer(dragDropState),
|
||||
columns = GridCells.Fixed(5),
|
||||
modifier = Modifier.dragContainer(dragDropState).padding(horizontal = 8.dp),
|
||||
state = gridState,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
|
||||
) {
|
||||
itemsIndexed(items, key = { _, item ->
|
||||
@@ -122,7 +125,7 @@ fun LazyGridItemScope.DraggableItem(
|
||||
} else {
|
||||
Modifier.animateItemPlacement()
|
||||
}
|
||||
Box(modifier = modifier.then(draggingModifier), propagateMinConstraints = true) {
|
||||
Box(modifier = modifier.then(draggingModifier).clip(RoundedCornerShape(8.dp)), propagateMinConstraints = true) {
|
||||
content(dragging)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,10 +57,29 @@ fun CustomAsyncImage(
|
||||
|
||||
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(
|
||||
model = ImageRequest.Builder(context ?: localContext)
|
||||
.data(imageUrl)
|
||||
.crossfade(200)
|
||||
.apply {
|
||||
// 设置占位符图片
|
||||
if (placeholderRes != null) {
|
||||
placeholder(placeholderRes)
|
||||
}
|
||||
}
|
||||
.build(),
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
|
||||
@@ -23,6 +23,7 @@ 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
|
||||
@@ -32,6 +33,9 @@ import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.R
|
||||
|
||||
/**
|
||||
* 水平布局的输入框
|
||||
*/
|
||||
@Composable
|
||||
fun FormTextInput(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -39,6 +43,7 @@ fun FormTextInput(
|
||||
label: String? = null,
|
||||
error: String? = null,
|
||||
hint: String? = null,
|
||||
background: Color? = null,
|
||||
onValueChange: (String) -> Unit
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
@@ -48,7 +53,7 @@ fun FormTextInput(
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(AppColors.inputBackground)
|
||||
.background(background ?: AppColors.inputBackground)
|
||||
.let {
|
||||
if (error != null) {
|
||||
it.border(1.dp, AppColors.error, RoundedCornerShape(24.dp))
|
||||
@@ -66,7 +71,7 @@ fun FormTextInput(
|
||||
.widthIn(100.dp),
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontWeight = FontWeight.W600,
|
||||
color = AppColors.text
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,16 +62,16 @@ fun Agent() {
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(36.dp) // 设置高度为36dp
|
||||
.fillMaxWidth(), // 占据整行宽度
|
||||
.height(36.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
// 搜索框 - 占据剩余空间
|
||||
// 搜索框
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
.weight(1f) // 权重为1,占据剩余空间
|
||||
.weight(1f)
|
||||
.clip(shape = RoundedCornerShape(18.dp))
|
||||
.background(AppColors.inputBackground)
|
||||
.padding(horizontal = 8.dp, vertical = 0.dp)
|
||||
@@ -94,16 +94,13 @@ fun Agent() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 间隔
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
// 新增
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.noRippleClickable {
|
||||
// 图标点击事件
|
||||
//
|
||||
navController.navigate(
|
||||
NavigationRoute.AddAgent.route
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.message
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
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.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
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.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
@@ -28,6 +32,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
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
|
||||
@@ -60,13 +65,15 @@ import kotlinx.coroutines.launch
|
||||
/**
|
||||
* 消息列表界面
|
||||
*/
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun NotificationsScreen() {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val navController = LocalNavController.current
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val context = LocalContext.current
|
||||
var pagerState = rememberPagerState (pageCount = { 4 })
|
||||
var scope = rememberCoroutineScope()
|
||||
val state = rememberPullRefreshState(MessageListViewModel.isLoading, onRefresh = {
|
||||
MessageListViewModel.viewModelScope.launch {
|
||||
MessageListViewModel.initData(context, force = true, loadChat = AppState.enableChat)
|
||||
@@ -96,10 +103,9 @@ fun NotificationsScreen() {
|
||||
.padding(horizontal = 15.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 左侧占位元素
|
||||
|
||||
Box(modifier = Modifier.size(24.dp))
|
||||
|
||||
// 左侧 Column:label 居中显示
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
@@ -113,7 +119,7 @@ fun NotificationsScreen() {
|
||||
color = AppColors.text
|
||||
)
|
||||
}
|
||||
// 右侧图标
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_group),
|
||||
contentDescription = "add",
|
||||
@@ -169,8 +175,111 @@ fun NotificationsScreen() {
|
||||
navController.navigate(NavigationRoute.CommentNoticeScreen.route)
|
||||
}
|
||||
}
|
||||
HorizontalDivider(color = AppColors.divider, modifier = Modifier.padding(16.dp))
|
||||
Box(
|
||||
Row(
|
||||
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
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
@@ -194,7 +303,7 @@ fun NotificationsScreen() {
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}*/
|
||||
}
|
||||
PullRefreshIndicator(
|
||||
MessageListViewModel.isLoading,
|
||||
@@ -226,28 +335,14 @@ fun NotificationIndicator(
|
||||
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(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(64.dp)
|
||||
.size(69.dp)
|
||||
.padding(5.dp)
|
||||
.background(color = backgroundColor,
|
||||
shape = RoundedCornerShape(16.dp)),
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@ import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
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.background
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -31,6 +36,8 @@ import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -42,6 +49,8 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.geometry.CornerRadius
|
||||
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.SolidColor
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aiosman.ravenow.AppState
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.entity.createAgent
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.ravenow.ui.composables.DraggableGrid
|
||||
@@ -78,6 +92,10 @@ import java.io.File
|
||||
@Composable
|
||||
fun NewPostScreen() {
|
||||
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 systemUiController = rememberSystemUiController()
|
||||
@@ -102,9 +120,6 @@ fun NewPostScreen() {
|
||||
) {
|
||||
NewPostTopBar {
|
||||
}
|
||||
NewPostTextField("Share your adventure…", NewPostViewModel.textContent) {
|
||||
NewPostViewModel.textContent = it
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -128,7 +143,168 @@ fun NewPostScreen() {
|
||||
}
|
||||
|
||||
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),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_back_icon),
|
||||
painter = painterResource(id = R.drawable.rider_pro_close),
|
||||
contentDescription = "Back",
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
@@ -182,12 +358,11 @@ fun NewPostTopBar(onSendClick: () -> Unit = {}) {
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_video_share),
|
||||
tint = AppColors.text,
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.rider_pro_moment_post),
|
||||
contentDescription = "Send",
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.size(24.dp)
|
||||
.noRippleClickable {
|
||||
// 检查输入
|
||||
val errorMessage = model.validateMoment()
|
||||
@@ -208,7 +383,7 @@ fun NewPostTopBar(onSendClick: () -> Unit = {}) {
|
||||
uploading = false
|
||||
}
|
||||
|
||||
// 上传完成后隐藏进度条
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -227,13 +402,15 @@ fun NewPostTextField(hint: String, value: String, onValueChange: (String) -> Uni
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(200.dp)
|
||||
.padding(horizontal = 18.dp, vertical = 10.dp),
|
||||
.height(160.dp)
|
||||
.heightIn(160.dp)
|
||||
.padding(horizontal = 16.dp, vertical = 10.dp),
|
||||
cursorBrush = SolidColor(AppColors.text),
|
||||
textStyle = TextStyle(
|
||||
lineHeight = 24.sp,
|
||||
color = AppColors.text,
|
||||
)
|
||||
|
||||
),
|
||||
|
||||
)
|
||||
if (value.isEmpty()) {
|
||||
@@ -326,11 +503,11 @@ fun AddImageGrid() {
|
||||
}
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
columns = GridCells.Fixed(5),
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(18.dp),
|
||||
.padding(horizontal = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
@@ -339,18 +516,8 @@ fun AddImageGrid() {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
.drawBehind {
|
||||
val strokeWidth = 1.dp.toPx()
|
||||
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())
|
||||
)
|
||||
}
|
||||
.clip(RoundedCornerShape(16.dp)) // 设置圆角
|
||||
.background(Color(0xFFFAF9FB)) // 设置背景色
|
||||
.noRippleClickable {
|
||||
pickImagesLauncher.launch("image/*")
|
||||
},
|
||||
@@ -359,7 +526,7 @@ fun AddImageGrid() {
|
||||
painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic),
|
||||
contentDescription = "Add Image",
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.size(24.dp)
|
||||
.align(Alignment.Center),
|
||||
tint = Color(0xFFD6D6D6)
|
||||
|
||||
@@ -371,18 +538,8 @@ fun AddImageGrid() {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
.drawBehind {
|
||||
val strokeWidth = 1.dp.toPx()
|
||||
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())
|
||||
)
|
||||
}
|
||||
.clip(RoundedCornerShape(16.dp)) // 设置圆角
|
||||
.background(Color(0xFFFAF9FB)) // 设置背景色
|
||||
.noRippleClickable {
|
||||
val photoFile = File(context.cacheDir, "photo.jpg")
|
||||
val photoUri: Uri = FileProvider.getUriForFile(
|
||||
@@ -398,7 +555,7 @@ fun AddImageGrid() {
|
||||
painter = painterResource(id = R.drawable.rider_pro_camera),
|
||||
contentDescription = "Take Photo",
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.size(24.dp)
|
||||
.align(Alignment.Center),
|
||||
tint = Color(0xFFD6D6D6)
|
||||
)
|
||||
|
||||
@@ -8,12 +8,18 @@ import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.aiosman.ravenow.data.MomentService
|
||||
import com.aiosman.ravenow.data.ServiceException
|
||||
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.MomentServiceImpl
|
||||
import com.aiosman.ravenow.entity.createMultipartBody
|
||||
import com.aiosman.ravenow.event.MomentAddEvent
|
||||
import com.aiosman.ravenow.exp.rotate
|
||||
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 kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@@ -96,6 +105,7 @@ data class Draft(
|
||||
object NewPostViewModel : ViewModel() {
|
||||
var momentService: MomentService = MomentServiceImpl()
|
||||
var textContent by mutableStateOf("")
|
||||
var aiTextContent by mutableStateOf("")
|
||||
var searchPlaceAddressResult by mutableStateOf<SearchPlaceAddressResult?>(null)
|
||||
var modificationList by mutableStateOf<List<Modification>>(listOf())
|
||||
var imageList by mutableStateOf(listOf<ImageItem>())
|
||||
@@ -111,6 +121,7 @@ object NewPostViewModel : ViewModel() {
|
||||
// }
|
||||
fun asNewPost() {
|
||||
textContent = ""
|
||||
aiTextContent = ""
|
||||
searchPlaceAddressResult = null
|
||||
modificationList = listOf()
|
||||
imageList = listOf()
|
||||
@@ -163,12 +174,17 @@ object NewPostViewModel : ViewModel() {
|
||||
onUploadProgress(((index / imageList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50%
|
||||
index += 1
|
||||
}
|
||||
aiTextContent = ""
|
||||
val result = momentService.createMoment(textContent, 1, uploadImageList, relPostId)
|
||||
// 刷新个人动态
|
||||
MyProfileViewModel.loadProfile(pullRefresh = true)
|
||||
EventBus.getDefault().post(MomentAddEvent(result))
|
||||
}
|
||||
|
||||
suspend fun agentMoment(textContent: String,) {
|
||||
aiTextContent = momentService.agentMoment(textContent)
|
||||
}
|
||||
|
||||
suspend fun init() {
|
||||
relPostId?.let {
|
||||
val moment = momentService.getMomentById(it)
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="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"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
||||
|
||||
<group
|
||||
android:translateX="-16"
|
||||
android:translateY="-64">
|
||||
<group
|
||||
android:translateX="16"
|
||||
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>
|
||||
@@ -7,32 +7,27 @@
|
||||
|
||||
<group
|
||||
android:translateX="2"
|
||||
android:translateY="4">
|
||||
android:translateY="2.945">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#000"
|
||||
android:strokeWidth="1.778"
|
||||
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:strokeWidth="2"
|
||||
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
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#000"
|
||||
android:strokeWidth="1.778"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
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: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" />
|
||||
<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" />
|
||||
android:pathData="M16.364 2h4M18.364 0v4" />
|
||||
</group>
|
||||
</vector>
|
||||
32
app/src/main/res/drawable/rider_pro_moment_apply.xml
Normal file
32
app/src/main/res/drawable/rider_pro_moment_apply.xml
Normal 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>
|
||||
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_moment_ai.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_moment_ai.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_moment_post.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_moment_post.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -125,5 +125,13 @@
|
||||
<string name="agent_desc">设定描述</string>
|
||||
<string name="agent_desc_hint">示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题</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>
|
||||
@@ -124,4 +124,11 @@
|
||||
<string name="agent_desc">设定描述</string>
|
||||
<string name="agent_desc_hint">示例: 一位经验丰富的销售员,擅长通过幽默风趣的语言和生动的案例,将复杂的产品转化为客户易于理解并感兴趣的话题</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>
|
||||
Reference in New Issue
Block a user