@@ -1,21 +1,22 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.jetbrains.kotlin.android)
|
alias(libs.plugins.jetbrains.kotlin.android)
|
||||||
|
alias(libs.plugins.compose.compiler)
|
||||||
id("com.google.gms.google-services")
|
id("com.google.gms.google-services")
|
||||||
id("com.google.firebase.crashlytics")
|
id("com.google.firebase.crashlytics")
|
||||||
id("com.google.firebase.firebase-perf")
|
id("com.google.firebase.firebase-perf")
|
||||||
id("org.jetbrains.kotlin.kapt")
|
id("org.jetbrains.kotlin.kapt")
|
||||||
id("com.google.devtools.ksp") version "1.9.10-1.0.13"
|
alias(libs.plugins.ksp)
|
||||||
|
|
||||||
}
|
}
|
||||||
android {
|
android {
|
||||||
namespace = "com.aiosman.ravenow"
|
namespace = "com.aiosman.ravenow"
|
||||||
compileSdk = 34
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.aiosman.ravenow"
|
applicationId = "com.aiosman.ravenow"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 34
|
targetSdk = 35
|
||||||
versionCode = 1000019
|
versionCode = 1000019
|
||||||
versionName = "1.0.000.19"
|
versionName = "1.0.000.19"
|
||||||
|
|
||||||
@@ -46,19 +47,16 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "1.8"
|
jvmTarget = "17"
|
||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
}
|
}
|
||||||
composeOptions {
|
|
||||||
kotlinCompilerExtensionVersion = "1.5.3"
|
|
||||||
}
|
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||||
@@ -99,11 +97,13 @@ dependencies {
|
|||||||
debugImplementation(libs.androidx.ui.tooling)
|
debugImplementation(libs.androidx.ui.tooling)
|
||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
implementation(libs.androidx.animation)
|
implementation(libs.androidx.animation)
|
||||||
implementation(libs.coil.compose)
|
|
||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
|
implementation(libs.coil.compose)
|
||||||
|
implementation(libs.coil.network.okhttp)
|
||||||
implementation(libs.play.services.auth)
|
implementation(libs.play.services.auth)
|
||||||
implementation(libs.kotlin.faker)
|
implementation(libs.kotlin.faker)
|
||||||
implementation(libs.androidx.material)
|
implementation(libs.androidx.material)
|
||||||
|
implementation(libs.androidx.material.icons.extended)
|
||||||
implementation(libs.zoomable)
|
implementation(libs.zoomable)
|
||||||
implementation(libs.retrofit)
|
implementation(libs.retrofit)
|
||||||
implementation(libs.converter.gson)
|
implementation(libs.converter.gson)
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.rememberAsyncImagePainter
|
import coil3.ImageLoader
|
||||||
import coil.request.ImageRequest
|
import coil3.compose.rememberAsyncImagePainter
|
||||||
import coil.ImageLoader
|
import coil3.disk.DiskCache
|
||||||
import coil.disk.DiskCache
|
import coil3.memory.MemoryCache
|
||||||
import coil.memory.MemoryCache
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.crossfade
|
||||||
|
import okio.Path.Companion.toPath
|
||||||
|
|
||||||
data class ImageItem(val url: String)
|
data class ImageItem(val url: String)
|
||||||
|
|
||||||
@@ -53,14 +55,15 @@ fun ImageItem(item: ImageItem, imageLoader: ImageLoader, context: Context) { //
|
|||||||
fun getImageLoader(context: Context): ImageLoader {
|
fun getImageLoader(context: Context): ImageLoader {
|
||||||
return ImageLoader.Builder(context)
|
return ImageLoader.Builder(context)
|
||||||
.memoryCache {
|
.memoryCache {
|
||||||
MemoryCache.Builder(context)
|
MemoryCache.Builder()
|
||||||
.maxSizePercent(0.25) // 设置内存缓存大小为可用内存的 25%
|
.maxSizePercent(context,0.25) // 设置内存缓存大小为可用内存的 25%
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
.diskCache {
|
.diskCache {
|
||||||
|
val cacheDir = context.cacheDir.resolve("image_cache")
|
||||||
DiskCache.Builder()
|
DiskCache.Builder()
|
||||||
.directory(context.cacheDir.resolve("image_cache"))
|
.directory(cacheDir.absolutePath.toPath())
|
||||||
.maxSizePercent(0.02) // 设置磁盘缓存大小为可用存储空间的 2%
|
.maxSizeBytes(250L * 1024 * 1024) // 250MB
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ data class Profile(
|
|||||||
@SerializedName("nickname")
|
@SerializedName("nickname")
|
||||||
val nickname: String,
|
val nickname: String,
|
||||||
@SerializedName("trtcUserId")
|
@SerializedName("trtcUserId")
|
||||||
val trtcUserId: String,
|
val trtcUserId: String? = null,
|
||||||
@SerializedName("username")
|
@SerializedName("username")
|
||||||
val username: String
|
val username: String
|
||||||
){
|
){
|
||||||
@@ -85,7 +85,7 @@ data class Profile(
|
|||||||
avatar = "${ApiClient.BASE_SERVER}$avatar",
|
avatar = "${ApiClient.BASE_SERVER}$avatar",
|
||||||
bio = bio,
|
bio = bio,
|
||||||
banner = "${ApiClient.BASE_SERVER}$banner",
|
banner = "${ApiClient.BASE_SERVER}$banner",
|
||||||
trtcUserId = trtcUserId,
|
trtcUserId = trtcUserId ?: "",
|
||||||
chatAIId = chatAIId,
|
chatAIId = chatAIId,
|
||||||
aiAccount = aiAccount
|
aiAccount = aiAccount
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,6 +26,22 @@ import com.aiosman.ravenow.entity.RoomRuleQuotaEntity
|
|||||||
import com.aiosman.ravenow.entity.UsersEntity
|
import com.aiosman.ravenow.entity.UsersEntity
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房间内的智能体信息(PromptTemplate)
|
||||||
|
*/
|
||||||
|
data class PromptTemplate(
|
||||||
|
@SerializedName("id")
|
||||||
|
val id: Int,
|
||||||
|
@SerializedName("openId")
|
||||||
|
val openId: String,
|
||||||
|
@SerializedName("title")
|
||||||
|
val title: String,
|
||||||
|
@SerializedName("desc")
|
||||||
|
val desc: String,
|
||||||
|
@SerializedName("avatar")
|
||||||
|
val avatar: String
|
||||||
|
)
|
||||||
|
|
||||||
data class Room(
|
data class Room(
|
||||||
@SerializedName("id")
|
@SerializedName("id")
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@@ -51,12 +67,26 @@ data class Room(
|
|||||||
val creator: Creator,
|
val creator: Creator,
|
||||||
@SerializedName("userCount")
|
@SerializedName("userCount")
|
||||||
val userCount: Int,
|
val userCount: Int,
|
||||||
|
@SerializedName("totalMemberCount")
|
||||||
|
val totalMemberCount: Int? = null,
|
||||||
@SerializedName("maxMemberLimit")
|
@SerializedName("maxMemberLimit")
|
||||||
val maxMemberLimit: Int,
|
val maxMemberLimit: Int,
|
||||||
|
@SerializedName("maxTotal")
|
||||||
|
val maxTotal: Int? = null,
|
||||||
|
@SerializedName("systemMaxTotal")
|
||||||
|
val systemMaxTotal: Int? = null,
|
||||||
@SerializedName("canJoin")
|
@SerializedName("canJoin")
|
||||||
val canJoin: Boolean,
|
val canJoin: Boolean,
|
||||||
@SerializedName("canJoinCode")
|
@SerializedName("canJoinCode")
|
||||||
val canJoinCode: Int,
|
val canJoinCode: Int,
|
||||||
|
@SerializedName("privateFeePaid")
|
||||||
|
val privateFeePaid: Boolean? = null,
|
||||||
|
@SerializedName("prompts")
|
||||||
|
val prompts: List<PromptTemplate>? = null,
|
||||||
|
@SerializedName("createdAt")
|
||||||
|
val createdAt: String? = null,
|
||||||
|
@SerializedName("updatedAt")
|
||||||
|
val updatedAt: String? = null,
|
||||||
@SerializedName("users")
|
@SerializedName("users")
|
||||||
val users: List<Users>
|
val users: List<Users>
|
||||||
|
|
||||||
@@ -75,9 +105,24 @@ data class Room(
|
|||||||
allowInHot = allowInHot,
|
allowInHot = allowInHot,
|
||||||
creator = creator.toCreatorEntity(),
|
creator = creator.toCreatorEntity(),
|
||||||
userCount = userCount,
|
userCount = userCount,
|
||||||
|
totalMemberCount = totalMemberCount,
|
||||||
maxMemberLimit = maxMemberLimit,
|
maxMemberLimit = maxMemberLimit,
|
||||||
|
maxTotal = maxTotal,
|
||||||
|
systemMaxTotal = systemMaxTotal,
|
||||||
canJoin = canJoin,
|
canJoin = canJoin,
|
||||||
canJoinCode = canJoinCode,
|
canJoinCode = canJoinCode,
|
||||||
|
privateFeePaid = privateFeePaid ?: false,
|
||||||
|
prompts = prompts?.map {
|
||||||
|
com.aiosman.ravenow.entity.PromptTemplateEntity(
|
||||||
|
id = it.id,
|
||||||
|
openId = it.openId,
|
||||||
|
title = it.title,
|
||||||
|
desc = it.desc,
|
||||||
|
avatar = it.avatar
|
||||||
|
)
|
||||||
|
} ?: emptyList(),
|
||||||
|
createdAt = createdAt,
|
||||||
|
updatedAt = updatedAt,
|
||||||
users = users.map { it.toUsersEntity() }
|
users = users.map { it.toUsersEntity() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -90,7 +135,7 @@ data class Creator(
|
|||||||
@SerializedName("userId")
|
@SerializedName("userId")
|
||||||
val userId: String,
|
val userId: String,
|
||||||
@SerializedName("trtcUserId")
|
@SerializedName("trtcUserId")
|
||||||
val trtcUserId: String,
|
val trtcUserId: String? = null,
|
||||||
@SerializedName("profile")
|
@SerializedName("profile")
|
||||||
val profile: Profile
|
val profile: Profile
|
||||||
){
|
){
|
||||||
@@ -98,7 +143,7 @@ data class Creator(
|
|||||||
return CreatorEntity(
|
return CreatorEntity(
|
||||||
id = id,
|
id = id,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
trtcUserId = trtcUserId,
|
trtcUserId = trtcUserId ?: "",
|
||||||
profile = profile.toProfileEntity()
|
profile = profile.toProfileEntity()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -110,7 +155,7 @@ data class Users(
|
|||||||
@SerializedName("userId")
|
@SerializedName("userId")
|
||||||
val userId: String,
|
val userId: String,
|
||||||
@SerializedName("trtcUserId")
|
@SerializedName("trtcUserId")
|
||||||
val trtcUserId: String,
|
val trtcUserId: String? = null,
|
||||||
@SerializedName("profile")
|
@SerializedName("profile")
|
||||||
val profile: Profile
|
val profile: Profile
|
||||||
){
|
){
|
||||||
|
|||||||
@@ -1423,11 +1423,39 @@ interface RaveNowAPI {
|
|||||||
@POST("outside/rooms")
|
@POST("outside/rooms")
|
||||||
suspend fun createGroupChat(@Body body: CreateGroupChatRequestBody): Response<DataContainer<Unit>>
|
suspend fun createGroupChat(@Body body: CreateGroupChatRequestBody): Response<DataContainer<Unit>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取房间列表
|
||||||
|
*
|
||||||
|
* 支持游客和认证用户访问,根据用户类型返回不同的房间数据。
|
||||||
|
* 游客模式返回公开推荐房间列表,认证用户模式返回用户可访问的群聊列表。
|
||||||
|
*
|
||||||
|
* @param page 页码,默认 1
|
||||||
|
* @param pageSize 每页数量,默认 20(游客模式最大50)
|
||||||
|
* @param roomId 房间ID,用于精确查询特定房间(仅认证用户)
|
||||||
|
* @param includeUsers 是否包含用户列表,默认false(仅认证用户)
|
||||||
|
* @param isRecommended 是否推荐过滤器:1=推荐,0=非推荐,null=不过滤(仅认证用户)
|
||||||
|
* @param roomType 房间类型过滤:all=公有私有都显示, public=只显示公有, private=只显示私有, created=只显示自己创建的, joined=只显示自己加入的(仅认证用户)
|
||||||
|
* @param search 搜索关键字,支持房间名称、描述、智能体名称模糊匹配
|
||||||
|
* @param random 是否随机排序,字符串长度不为0则为true(传任意非空字符串即可)
|
||||||
|
* @param ownerSessionId 创建者用户ID(ChatAIID),用于过滤特定创建者的房间
|
||||||
|
* @param showPublic 是否显示公有房间,只有明确设置为true时才生效(优先级高于roomType,仅认证用户)
|
||||||
|
* @param showCreated 是否显示自己创建的房间,只有明确设置为true时才生效(优先级高于roomType,仅认证用户)
|
||||||
|
* @param showJoined 是否显示自己加入的房间,只有明确设置为true时才生效(优先级高于roomType,仅认证用户)
|
||||||
|
*/
|
||||||
@GET("outside/rooms")
|
@GET("outside/rooms")
|
||||||
suspend fun getRooms(@Query("page") page: Int = 1,
|
suspend fun getRooms(
|
||||||
|
@Query("page") page: Int = 1,
|
||||||
@Query("pageSize") pageSize: Int = 20,
|
@Query("pageSize") pageSize: Int = 20,
|
||||||
@Query("isRecommended") isRecommended: Int = 1,
|
@Query("roomId") roomId: Long? = null,
|
||||||
@Query("random") random: Int? = null,
|
@Query("includeUsers") includeUsers: Boolean? = null,
|
||||||
|
@Query("isRecommended") isRecommended: Int? = null,
|
||||||
|
@Query("roomType") roomType: String? = null,
|
||||||
|
@Query("search") search: String? = null,
|
||||||
|
@Query("random") random: String? = null,
|
||||||
|
@Query("ownerSessionId") ownerSessionId: String? = null,
|
||||||
|
@Query("showPublic") showPublic: Boolean? = null,
|
||||||
|
@Query("showCreated") showCreated: Boolean? = null,
|
||||||
|
@Query("showJoined") showJoined: Boolean? = null,
|
||||||
): Response<ListContainer<Room>>
|
): Response<ListContainer<Room>>
|
||||||
|
|
||||||
@GET("outside/rooms/detail")
|
@GET("outside/rooms/detail")
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ import com.aiosman.ravenow.data.api.ApiClient
|
|||||||
* 群聊房间
|
* 群聊房间
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房间内的智能体信息实体
|
||||||
|
*/
|
||||||
|
data class PromptTemplateEntity(
|
||||||
|
val id: Int,
|
||||||
|
val openId: String,
|
||||||
|
val title: String,
|
||||||
|
val desc: String,
|
||||||
|
val avatar: String
|
||||||
|
)
|
||||||
|
|
||||||
data class RoomEntity(
|
data class RoomEntity(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
@@ -21,9 +32,16 @@ data class RoomEntity(
|
|||||||
val allowInHot: Boolean,
|
val allowInHot: Boolean,
|
||||||
val creator: CreatorEntity,
|
val creator: CreatorEntity,
|
||||||
val userCount: Int,
|
val userCount: Int,
|
||||||
|
val totalMemberCount: Int? = null,
|
||||||
val maxMemberLimit: Int,
|
val maxMemberLimit: Int,
|
||||||
|
val maxTotal: Int? = null,
|
||||||
|
val systemMaxTotal: Int? = null,
|
||||||
val canJoin: Boolean,
|
val canJoin: Boolean,
|
||||||
val canJoinCode: Int,
|
val canJoinCode: Int,
|
||||||
|
val privateFeePaid: Boolean = false,
|
||||||
|
val prompts: List<PromptTemplateEntity> = emptyList(),
|
||||||
|
val createdAt: String? = null,
|
||||||
|
val updatedAt: String? = null,
|
||||||
val users: List<UsersEntity>,
|
val users: List<UsersEntity>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ fun AboutScreen() {
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
// app version
|
// app version
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.version_text, versionText),
|
text = stringResource(R.string.version_text, versionText ?: ""),
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
color = appColors.secondaryText,
|
color = appColors.secondaryText,
|
||||||
fontWeight = FontWeight.Normal
|
fontWeight = FontWeight.Normal
|
||||||
|
|||||||
@@ -118,8 +118,7 @@ fun CommentModalContent(
|
|||||||
skipPartiallyExpanded = true
|
skipPartiallyExpanded = true
|
||||||
),
|
),
|
||||||
dragHandle = {},
|
dragHandle = {},
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
||||||
windowInsets = WindowInsets(0)
|
|
||||||
) {
|
) {
|
||||||
CommentMenuModal(
|
CommentMenuModal(
|
||||||
onDeleteClick = {
|
onDeleteClick = {
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ import androidx.compose.ui.Modifier
|
|||||||
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.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import coil.annotation.ExperimentalCoilApi
|
import coil3.annotation.ExperimentalCoilApi
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.crossfade
|
||||||
|
import coil3.request.fallback
|
||||||
|
import coil3.request.placeholder
|
||||||
import com.aiosman.ravenow.utils.BlurHashDecoder
|
import com.aiosman.ravenow.utils.BlurHashDecoder
|
||||||
import com.aiosman.ravenow.utils.Utils.getImageLoader
|
import com.aiosman.ravenow.utils.Utils.getImageLoader
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.aiosman.ravenow.ui.composables
|
|||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.composed
|
import androidx.compose.ui.composed
|
||||||
@@ -68,7 +69,6 @@ fun Modifier.debouncedClickableWithRipple(
|
|||||||
clickable(
|
clickable(
|
||||||
enabled = enabled && isClickable,
|
enabled = enabled && isClickable,
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
indication = androidx.compose.material.ripple.rememberRipple()
|
|
||||||
) {
|
) {
|
||||||
if (isClickable) {
|
if (isClickable) {
|
||||||
isClickable = false
|
isClickable = false
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ fun LazyGridItemScope.DraggableItem(
|
|||||||
translationY = dragDropState.previousItemOffset.value.y
|
translationY = dragDropState.previousItemOffset.value.y
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Modifier.animateItemPlacement()
|
Modifier
|
||||||
}
|
}
|
||||||
Box(modifier = modifier.then(draggingModifier).clip(RoundedCornerShape(8.dp)), propagateMinConstraints = true) {
|
Box(modifier = modifier.then(draggingModifier).clip(RoundedCornerShape(8.dp)), propagateMinConstraints = true) {
|
||||||
content(dragging)
|
content(dragging)
|
||||||
|
|||||||
@@ -16,11 +16,16 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
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.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil.compose.AsyncImage
|
import coil3.asDrawable
|
||||||
import coil.request.ImageRequest
|
import coil3.asImage
|
||||||
import coil.request.SuccessResult
|
import coil3.compose.AsyncImage
|
||||||
|
import coil3.request.CachePolicy
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.SuccessResult
|
||||||
|
import coil3.request.crossfade
|
||||||
import com.aiosman.ravenow.utils.Utils.getImageLoader
|
import com.aiosman.ravenow.utils.Utils.getImageLoader
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -59,7 +64,11 @@ fun rememberImageBitmap(imageUrl: String, imageLoader: ImageLoader): Bitmap? {
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val result = withContext(Dispatchers.IO) {
|
val result = withContext(Dispatchers.IO) {
|
||||||
(imageLoader.execute(request) as? SuccessResult)?.drawable?.toBitmap()
|
val successResult = imageLoader.execute(request) as? SuccessResult
|
||||||
|
successResult?.let {
|
||||||
|
val drawable = it.image.asDrawable(context.resources)
|
||||||
|
drawable.toBitmap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap = result
|
bitmap = result
|
||||||
@@ -138,25 +147,33 @@ fun CustomAsyncImage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理字符串URL
|
// 处理字符串URL
|
||||||
|
val ctx = context ?: localContext
|
||||||
|
val placeholderImage = remember(placeholderRes, ctx) {
|
||||||
|
placeholderRes?.let { resId ->
|
||||||
|
ContextCompat.getDrawable(ctx, resId)?.asImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val errorImage = remember(errorRes, ctx) {
|
||||||
|
errorRes?.let { resId ->
|
||||||
|
ContextCompat.getDrawable(ctx, resId)?.asImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (showShimmer) {
|
if (showShimmer) {
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(context ?: localContext)
|
model = ImageRequest.Builder(ctx)
|
||||||
.data(imageUrl)
|
.data(imageUrl)
|
||||||
.crossfade(200)
|
.crossfade(200)
|
||||||
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
|
.memoryCachePolicy(coil3.request.CachePolicy.ENABLED)
|
||||||
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
|
.diskCachePolicy(coil3.request.CachePolicy.ENABLED)
|
||||||
.apply {
|
.apply {
|
||||||
// 设置占位符图片
|
// 设置占位符图片
|
||||||
if (placeholderRes != null) {
|
placeholderImage?.let { placeholder(it) }
|
||||||
placeholder(placeholderRes)
|
|
||||||
}
|
|
||||||
// 设置错误时显示的图片
|
// 设置错误时显示的图片
|
||||||
if (errorRes != null) {
|
errorImage?.let { error(it) }
|
||||||
error(errorRes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
@@ -177,20 +194,16 @@ fun CustomAsyncImage(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(context ?: localContext)
|
model = ImageRequest.Builder(ctx)
|
||||||
.data(imageUrl)
|
.data(imageUrl)
|
||||||
.crossfade(200)
|
.crossfade(200)
|
||||||
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
|
.memoryCachePolicy(CachePolicy.ENABLED)
|
||||||
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
|
.diskCachePolicy(CachePolicy.ENABLED)
|
||||||
.apply {
|
.apply {
|
||||||
// 设置占位符图片
|
// 设置占位符图片
|
||||||
if (placeholderRes != null) {
|
placeholderImage?.let { placeholder(it) }
|
||||||
placeholder(placeholderRes)
|
|
||||||
}
|
|
||||||
// 设置错误时显示的图片
|
// 设置错误时显示的图片
|
||||||
if (errorRes != null) {
|
errorImage?.let { error(it) }
|
||||||
error(errorRes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
|
|||||||
@@ -515,7 +515,6 @@ fun MomentBottomOperateRowGroup(
|
|||||||
sheetState = rememberModalBottomSheetState(
|
sheetState = rememberModalBottomSheetState(
|
||||||
skipPartiallyExpanded = true
|
skipPartiallyExpanded = true
|
||||||
),
|
),
|
||||||
windowInsets = WindowInsets(0),
|
|
||||||
dragHandle = {
|
dragHandle = {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ fun PolicyCheckbox(
|
|||||||
showModal = false
|
showModal = false
|
||||||
},
|
},
|
||||||
sheetState = modalSheetState,
|
sheetState = modalSheetState,
|
||||||
windowInsets = WindowInsets(0),
|
|
||||||
containerColor = Color.White,
|
containerColor = Color.White,
|
||||||
) {
|
) {
|
||||||
WebViewDisplay(
|
WebViewDisplay(
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ fun CreateBottomSheet(
|
|||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
windowInsets = BottomSheetDefaults.windowInsets,
|
|
||||||
containerColor = appColors.background,
|
containerColor = appColors.background,
|
||||||
dragHandle = null,
|
dragHandle = null,
|
||||||
shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
|
shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
|
||||||
|
|||||||
@@ -265,7 +265,6 @@ fun IndexScreen() {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(AppColors.background)
|
.background(AppColors.background)
|
||||||
.padding(0.dp),
|
.padding(0.dp),
|
||||||
beyondBoundsPageCount = 4,
|
|
||||||
userScrollEnabled = false
|
userScrollEnabled = false
|
||||||
) { page ->
|
) { page ->
|
||||||
when (page) {
|
when (page) {
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.aiosman.ravenow.AppStore
|
import com.aiosman.ravenow.AppStore
|
||||||
import com.aiosman.ravenow.GuestLoginCheckOut
|
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||||
@@ -115,7 +114,7 @@ fun Agent() {
|
|||||||
var pagerState = rememberPagerState { tabCount }
|
var pagerState = rememberPagerState { tabCount }
|
||||||
var scope = rememberCoroutineScope()
|
var scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val viewModel: AgentViewModel = viewModel()
|
val viewModel: AgentViewModel = AgentViewModel
|
||||||
|
|
||||||
// 确保推荐Agent数据已加载
|
// 确保推荐Agent数据已加载
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
@@ -183,7 +182,6 @@ fun Agent() {
|
|||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = AppColors.background
|
containerColor = AppColors.background
|
||||||
),
|
),
|
||||||
windowInsets = WindowInsets(0, 0, 0, 0),
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(44.dp + statusBarPaddingValues.calculateTopPadding())
|
.height(44.dp + statusBarPaddingValues.calculateTopPadding())
|
||||||
.padding(top = statusBarPaddingValues.calculateTopPadding())
|
.padding(top = statusBarPaddingValues.calculateTopPadding())
|
||||||
@@ -899,7 +897,7 @@ fun ChatRoomCard(
|
|||||||
) {
|
) {
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
val cardSize = 180.dp
|
val cardSize = 180.dp
|
||||||
val viewModel: AgentViewModel = viewModel()
|
val viewModel: AgentViewModel = AgentViewModel
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
// 防抖状态
|
// 防抖状态
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ object AgentViewModel: ViewModel() {
|
|||||||
page = 1,
|
page = 1,
|
||||||
pageSize = 20,
|
pageSize = 20,
|
||||||
isRecommended = 1,
|
isRecommended = 1,
|
||||||
random = 1
|
random = "1"
|
||||||
)
|
)
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
val allRooms = response.body()?.list ?: emptyList()
|
val allRooms = response.body()?.list ?: emptyList()
|
||||||
|
|||||||
@@ -156,10 +156,10 @@ object HotAgentViewModel : ViewModel() {
|
|||||||
try {
|
try {
|
||||||
// 预加载头像图片到缓存
|
// 预加载头像图片到缓存
|
||||||
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
|
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
|
||||||
coil.request.ImageRequest.Builder(context)
|
coil3.request.ImageRequest.Builder(context)
|
||||||
.data(agent.avatar)
|
.data(agent.avatar)
|
||||||
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
|
.memoryCachePolicy(coil3.request.CachePolicy.ENABLED)
|
||||||
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
|
.diskCachePolicy(coil3.request.CachePolicy.ENABLED)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
preloadedImageIds.add(agent.id)
|
preloadedImageIds.add(agent.id)
|
||||||
|
|||||||
@@ -841,20 +841,20 @@ fun Explore() {
|
|||||||
if (bannerItem.backgroundImageUrl.isNotEmpty()) {
|
if (bannerItem.backgroundImageUrl.isNotEmpty()) {
|
||||||
// 预加载背景图片
|
// 预加载背景图片
|
||||||
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
|
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
|
||||||
coil.request.ImageRequest.Builder(context)
|
coil3.request.ImageRequest.Builder(context)
|
||||||
.data(bannerItem.backgroundImageUrl)
|
.data(bannerItem.backgroundImageUrl)
|
||||||
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
|
.memoryCachePolicy(coil3.request.CachePolicy.ENABLED)
|
||||||
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
|
.diskCachePolicy(coil3.request.CachePolicy.ENABLED)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (bannerItem.imageUrl.isNotEmpty()) {
|
if (bannerItem.imageUrl.isNotEmpty()) {
|
||||||
// 预加载头像图片
|
// 预加载头像图片
|
||||||
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
|
com.aiosman.ravenow.utils.Utils.getImageLoader(context).enqueue(
|
||||||
coil.request.ImageRequest.Builder(context)
|
coil3.request.ImageRequest.Builder(context)
|
||||||
.data(bannerItem.imageUrl)
|
.data(bannerItem.imageUrl)
|
||||||
.memoryCachePolicy(coil.request.CachePolicy.ENABLED)
|
.memoryCachePolicy(coil3.request.CachePolicy.ENABLED)
|
||||||
.diskCachePolicy(coil.request.CachePolicy.ENABLED)
|
.diskCachePolicy(coil3.request.CachePolicy.ENABLED)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ fun FullArticleModal(
|
|||||||
.height(sheetHeight),
|
.height(sheetHeight),
|
||||||
containerColor = appColors.background,
|
containerColor = appColors.background,
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
windowInsets = androidx.compose.foundation.layout.WindowInsets(0)
|
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -154,7 +154,6 @@ fun NewsCommentModal(
|
|||||||
),
|
),
|
||||||
dragHandle = {},
|
dragHandle = {},
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
windowInsets = WindowInsets(0)
|
|
||||||
) {
|
) {
|
||||||
CommentMenuModal(
|
CommentMenuModal(
|
||||||
onDeleteClick = {
|
onDeleteClick = {
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ fun NewsScreen() {
|
|||||||
.height(sheetHeight),
|
.height(sheetHeight),
|
||||||
containerColor = AppColors.background,
|
containerColor = AppColors.background,
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
windowInsets = androidx.compose.foundation.layout.WindowInsets(0)
|
|
||||||
) {
|
) {
|
||||||
NewsCommentModal(
|
NewsCommentModal(
|
||||||
postId = selectedMoment?.id,
|
postId = selectedMoment?.id,
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ import com.aiosman.ravenow.entity.MomentEntity
|
|||||||
import com.aiosman.ravenow.entity.MomentLoader
|
import com.aiosman.ravenow.entity.MomentLoader
|
||||||
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
|
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
|
||||||
import com.aiosman.ravenow.entity.MomentServiceImpl
|
import com.aiosman.ravenow.entity.MomentServiceImpl
|
||||||
|
import com.aiosman.ravenow.entity.RoomEntity
|
||||||
|
import com.aiosman.ravenow.data.api.ApiClient
|
||||||
|
import com.aiosman.ravenow.data.api.RaveNowAPI
|
||||||
|
import com.aiosman.ravenow.data.Room
|
||||||
import com.aiosman.ravenow.event.FollowChangeEvent
|
import com.aiosman.ravenow.event.FollowChangeEvent
|
||||||
import com.aiosman.ravenow.event.MomentAddEvent
|
import com.aiosman.ravenow.event.MomentAddEvent
|
||||||
import com.aiosman.ravenow.event.MomentFavouriteChangeEvent
|
import com.aiosman.ravenow.event.MomentFavouriteChangeEvent
|
||||||
@@ -40,6 +44,14 @@ object MyProfileViewModel : ViewModel() {
|
|||||||
var profile by mutableStateOf<AccountProfileEntity?>(null)
|
var profile by mutableStateOf<AccountProfileEntity?>(null)
|
||||||
var moments by mutableStateOf<List<MomentEntity>>(emptyList())
|
var moments by mutableStateOf<List<MomentEntity>>(emptyList())
|
||||||
var agents by mutableStateOf<List<AgentEntity>>(emptyList())
|
var agents by mutableStateOf<List<AgentEntity>>(emptyList())
|
||||||
|
var rooms by mutableStateOf<List<RoomEntity>>(emptyList())
|
||||||
|
var roomsLoading by mutableStateOf(false)
|
||||||
|
var roomsRefreshing by mutableStateOf(false)
|
||||||
|
var roomsCurrentPage by mutableStateOf(1)
|
||||||
|
var roomsHasMore by mutableStateOf(true)
|
||||||
|
private val roomsPageSize = 20
|
||||||
|
private val apiClient: RaveNowAPI = ApiClient.api
|
||||||
|
|
||||||
val momentLoader: MomentLoader = MomentLoader().apply {
|
val momentLoader: MomentLoader = MomentLoader().apply {
|
||||||
pageSize = 20 // 设置与后端一致的页面大小
|
pageSize = 20 // 设置与后端一致的页面大小
|
||||||
onListChanged = {
|
onListChanged = {
|
||||||
@@ -254,4 +266,115 @@ object MyProfileViewModel : ViewModel() {
|
|||||||
fun onFollowChangeEvent(event: FollowChangeEvent) {
|
fun onFollowChangeEvent(event: FollowChangeEvent) {
|
||||||
momentLoader.updateFollowStatus(event.userId, event.isFollow)
|
momentLoader.updateFollowStatus(event.userId, event.isFollow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载房间列表
|
||||||
|
* @param filterType 筛选类型:0=全部,1=公开,2=私有
|
||||||
|
* @param pullRefresh 是否下拉刷新
|
||||||
|
*/
|
||||||
|
fun loadRooms(filterType: Int = 0, pullRefresh: Boolean = false) {
|
||||||
|
// 游客模式下不加载房间列表
|
||||||
|
if (AppStore.isGuest) {
|
||||||
|
Log.d("MyProfileViewModel", "loadRooms: 游客模式下跳过加载房间列表")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roomsLoading && !pullRefresh) return
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
roomsLoading = true
|
||||||
|
roomsRefreshing = pullRefresh
|
||||||
|
|
||||||
|
val currentPage = if (pullRefresh) {
|
||||||
|
roomsCurrentPage = 1
|
||||||
|
roomsHasMore = true
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
roomsCurrentPage
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = when (filterType) {
|
||||||
|
0 -> {
|
||||||
|
// 全部:显示自己创建或加入的所有房间
|
||||||
|
apiClient.getRooms(
|
||||||
|
page = currentPage,
|
||||||
|
pageSize = roomsPageSize,
|
||||||
|
showCreated = true,
|
||||||
|
showJoined = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
// 公开:显示公开房间中自己创建或加入的
|
||||||
|
apiClient.getRooms(
|
||||||
|
page = currentPage,
|
||||||
|
pageSize = roomsPageSize,
|
||||||
|
roomType = "public",
|
||||||
|
showCreated = true,
|
||||||
|
showJoined = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
// 私有:显示自己创建或加入的私有房间
|
||||||
|
apiClient.getRooms(
|
||||||
|
page = currentPage,
|
||||||
|
pageSize = roomsPageSize,
|
||||||
|
roomType = "private"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
apiClient.getRooms(
|
||||||
|
page = currentPage,
|
||||||
|
pageSize = roomsPageSize,
|
||||||
|
showCreated = true,
|
||||||
|
showJoined = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val roomList = response.body()?.list ?: emptyList()
|
||||||
|
val total = response.body()?.total ?: 0L
|
||||||
|
|
||||||
|
if (pullRefresh || currentPage == 1) {
|
||||||
|
rooms = roomList.map { it.toRoomtEntity() }
|
||||||
|
} else {
|
||||||
|
rooms = rooms + roomList.map { it.toRoomtEntity() }
|
||||||
|
}
|
||||||
|
|
||||||
|
roomsHasMore = rooms.size < total
|
||||||
|
if (roomsHasMore && !pullRefresh) {
|
||||||
|
roomsCurrentPage++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("MyProfileViewModel", "loadRooms failed: ${response.code()}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("MyProfileViewModel", "loadRooms error: ", e)
|
||||||
|
} finally {
|
||||||
|
roomsLoading = false
|
||||||
|
roomsRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载更多房间
|
||||||
|
* @param filterType 筛选类型:0=全部,1=公开,2=私有
|
||||||
|
*/
|
||||||
|
fun loadMoreRooms(filterType: Int = 0) {
|
||||||
|
if (roomsLoading || !roomsHasMore) return
|
||||||
|
loadRooms(filterType = filterType, pullRefresh = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新房间列表
|
||||||
|
* @param filterType 筛选类型:0=全部,1=公开,2=私有
|
||||||
|
*/
|
||||||
|
fun refreshRooms(filterType: Int = 0) {
|
||||||
|
rooms = emptyList()
|
||||||
|
roomsCurrentPage = 1
|
||||||
|
roomsHasMore = true
|
||||||
|
loadRooms(filterType = filterType, pullRefresh = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +258,6 @@ fun ProfileV3(
|
|||||||
sheetState = agentMenuModalState,
|
sheetState = agentMenuModalState,
|
||||||
dragHandle = {},
|
dragHandle = {},
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
windowInsets = WindowInsets(0)
|
|
||||||
) {
|
) {
|
||||||
AgentMenuModal(
|
AgentMenuModal(
|
||||||
agent = contextAgent,
|
agent = contextAgent,
|
||||||
@@ -535,7 +534,7 @@ fun ProfileV3(
|
|||||||
containerColor = Color.Transparent, // 设置容器背景透明
|
containerColor = Color.Transparent, // 设置容器背景透明
|
||||||
contentColor = Color.Transparent, // 设置内容背景透明
|
contentColor = Color.Transparent, // 设置内容背景透明
|
||||||
dragHandle = null, // 移除拖拽手柄
|
dragHandle = null, // 移除拖拽手柄
|
||||||
windowInsets = androidx.compose.foundation.layout.WindowInsets(0) // 移除窗口边距
|
contentWindowInsets = {androidx.compose.foundation.layout.WindowInsets(0)},
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -567,7 +566,7 @@ fun ProfileV3(
|
|||||||
containerColor = Color.Transparent,
|
containerColor = Color.Transparent,
|
||||||
contentColor = Color.Transparent,
|
contentColor = Color.Transparent,
|
||||||
dragHandle = null,
|
dragHandle = null,
|
||||||
windowInsets = androidx.compose.foundation.layout.WindowInsets(0)
|
contentWindowInsets = { androidx.compose.foundation.layout.WindowInsets(0) }
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -14,17 +14,28 @@ 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.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
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.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.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
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
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.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@@ -32,13 +43,46 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.aiosman.ravenow.ConstVars
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.R
|
import com.aiosman.ravenow.R
|
||||||
|
import com.aiosman.ravenow.entity.RoomEntity
|
||||||
|
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||||
|
import com.aiosman.ravenow.ui.composables.rememberDebouncer
|
||||||
|
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
|
||||||
|
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||||
|
import com.aiosman.ravenow.ui.navigateToGroupChat
|
||||||
|
import com.aiosman.ravenow.AppStore
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun GroupChatEmptyContent() {
|
fun GroupChatEmptyContent() {
|
||||||
var selectedSegment by remember { mutableStateOf(0) } // 0: 全部, 1: 公开, 2: 私有
|
var selectedSegment by remember { mutableStateOf(0) } // 0: 全部, 1: 公开, 2: 私有
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
val navController = LocalNavController.current
|
||||||
|
val viewModel = MyProfileViewModel
|
||||||
|
|
||||||
|
val state = rememberPullRefreshState(
|
||||||
|
refreshing = viewModel.roomsRefreshing,
|
||||||
|
onRefresh = {
|
||||||
|
viewModel.refreshRooms(filterType = selectedSegment)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 当分段改变时,重新加载数据
|
||||||
|
LaunchedEffect(selectedSegment) {
|
||||||
|
// 切换分段时重新加载
|
||||||
|
viewModel.refreshRooms(filterType = selectedSegment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始加载
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
if (viewModel.rooms.isEmpty() && !viewModel.roomsLoading) {
|
||||||
|
viewModel.loadRooms(filterType = selectedSegment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -50,12 +94,21 @@ fun GroupChatEmptyContent() {
|
|||||||
// 分段控制器
|
// 分段控制器
|
||||||
SegmentedControl(
|
SegmentedControl(
|
||||||
selectedIndex = selectedSegment,
|
selectedIndex = selectedSegment,
|
||||||
onSegmentSelected = { selectedSegment = it },
|
onSegmentSelected = {
|
||||||
|
selectedSegment = it
|
||||||
|
// LaunchedEffect 会监听 selectedSegment 的变化并自动刷新
|
||||||
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.pullRefresh(state)
|
||||||
|
) {
|
||||||
|
if (viewModel.rooms.isEmpty() && !viewModel.roomsLoading) {
|
||||||
// 空状态内容(居中)
|
// 空状态内容(居中)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
@@ -78,6 +131,152 @@ fun GroupChatEmptyContent() {
|
|||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
itemsIndexed(
|
||||||
|
items = viewModel.rooms,
|
||||||
|
key = { _, item -> item.id }
|
||||||
|
) { index, room ->
|
||||||
|
RoomItem(
|
||||||
|
room = room,
|
||||||
|
onRoomClick = { roomEntity ->
|
||||||
|
// 导航到群聊聊天界面
|
||||||
|
navController.navigateToGroupChat(
|
||||||
|
id = roomEntity.trtcRoomId,
|
||||||
|
name = roomEntity.name,
|
||||||
|
avatar = roomEntity.avatar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (index < viewModel.rooms.size - 1) {
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.padding(horizontal = 24.dp),
|
||||||
|
color = AppColors.divider
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多指示器
|
||||||
|
if (viewModel.roomsLoading && viewModel.rooms.isNotEmpty()) {
|
||||||
|
item {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
color = AppColors.main
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多触发
|
||||||
|
if (viewModel.roomsHasMore && !viewModel.roomsLoading) {
|
||||||
|
item {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.loadMoreRooms(filterType = selectedSegment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PullRefreshIndicator(
|
||||||
|
refreshing = viewModel.roomsRefreshing,
|
||||||
|
state = state,
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RoomItem(
|
||||||
|
room: RoomEntity,
|
||||||
|
onRoomClick: (RoomEntity) -> Unit = {}
|
||||||
|
) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
val roomDebouncer = rememberDebouncer()
|
||||||
|
|
||||||
|
// 构建头像URL
|
||||||
|
val avatarUrl = if (room.avatar.isNotEmpty()) {
|
||||||
|
"${ConstVars.BASE_SERVER}/api/v1/outside/${room.avatar}?token=${AppStore.token}"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 24.dp, vertical = 12.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
roomDebouncer {
|
||||||
|
onRoomClick(room)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Box {
|
||||||
|
CustomAsyncImage(
|
||||||
|
context = context,
|
||||||
|
imageUrl = avatarUrl,
|
||||||
|
contentDescription = room.name,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.clip(RoundedCornerShape(12.dp))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(start = 12.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = room.name,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = AppColors.text,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = room.description.ifEmpty { "暂无描述" },
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = AppColors.secondaryText,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "${room.userCount}人",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = AppColors.secondaryText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,11 +114,14 @@ fun getFeedItems(): List<FeedItem> {
|
|||||||
@Composable
|
@Composable
|
||||||
fun LocationDetailScreen(x: Float, y: Float) {
|
fun LocationDetailScreen(x: Float, y: Float) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
val density = LocalDensity.current
|
||||||
val scaffoldState = rememberBottomSheetScaffoldState(
|
val scaffoldState = rememberBottomSheetScaffoldState(
|
||||||
SheetState(
|
bottomSheetState = SheetState(
|
||||||
skipPartiallyExpanded = false,
|
skipPartiallyExpanded = false,
|
||||||
density = LocalDensity.current, initialValue = SheetValue.PartiallyExpanded,
|
initialValue = SheetValue.PartiallyExpanded,
|
||||||
skipHiddenState = true
|
skipHiddenState = true,
|
||||||
|
positionalThreshold = { 0.5f },
|
||||||
|
velocityThreshold = { with(density) { 125.dp.toPx() } }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.compose.material.Text
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -30,7 +31,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import coil.compose.rememberAsyncImagePainter
|
import coil3.compose.rememberAsyncImagePainter
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
@@ -67,7 +68,7 @@ fun NewPostImageGridScreen() {
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.AutoMirrored.Default.ArrowBack,
|
Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
contentDescription = "back",
|
contentDescription = "back",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(24.dp)
|
.size(24.dp)
|
||||||
@@ -84,7 +85,7 @@ fun NewPostImageGridScreen() {
|
|||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
)
|
)
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Delete,
|
Icons.Filled.Delete,
|
||||||
contentDescription = "delete",
|
contentDescription = "delete",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(24.dp)
|
.size(24.dp)
|
||||||
|
|||||||
@@ -173,7 +173,6 @@ fun PostScreen(
|
|||||||
sheetState = commentModalState,
|
sheetState = commentModalState,
|
||||||
dragHandle = {},
|
dragHandle = {},
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
windowInsets = WindowInsets(0)
|
|
||||||
) {
|
) {
|
||||||
CommentMenuModal(
|
CommentMenuModal(
|
||||||
onDeleteClick = {
|
onDeleteClick = {
|
||||||
@@ -262,7 +261,6 @@ fun PostScreen(
|
|||||||
),
|
),
|
||||||
dragHandle = {},
|
dragHandle = {},
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
windowInsets = WindowInsets(0)
|
|
||||||
) {
|
) {
|
||||||
EditCommentBottomModal(replyComment) {
|
EditCommentBottomModal(replyComment) {
|
||||||
viewModel.viewModelScope.launch {
|
viewModel.viewModelScope.launch {
|
||||||
@@ -849,7 +847,6 @@ fun Header(
|
|||||||
),
|
),
|
||||||
dragHandle = {},
|
dragHandle = {},
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
windowInsets = WindowInsets(0)
|
|
||||||
) {
|
) {
|
||||||
PostMenuModal(
|
PostMenuModal(
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ import android.content.ContentValues
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import coil.request.ImageRequest
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import coil.request.SuccessResult
|
import coil3.asDrawable
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.SuccessResult
|
||||||
|
import coil3.request.allowHardware
|
||||||
import com.aiosman.ravenow.utils.Utils.getImageLoader
|
import com.aiosman.ravenow.utils.Utils.getImageLoader
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -30,8 +32,9 @@ object FileUtil {
|
|||||||
.allowHardware(false) // Disable hardware bitmaps.
|
.allowHardware(false) // Disable hardware bitmaps.
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val result = (loader.execute(request) as SuccessResult).drawable
|
val result = loader.execute(request) as? SuccessResult ?: return
|
||||||
val bitmap = (result as BitmapDrawable).bitmap
|
val drawable = result.image.asDrawable(context.resources)
|
||||||
|
val bitmap = drawable.toBitmap()
|
||||||
|
|
||||||
val contentValues = ContentValues().apply {
|
val contentValues = ContentValues().apply {
|
||||||
put(MediaStore.Images.Media.DISPLAY_NAME, "image_${System.currentTimeMillis()}.jpg")
|
put(MediaStore.Images.Media.DISPLAY_NAME, "image_${System.currentTimeMillis()}.jpg")
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import android.content.Context
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil.request.CachePolicy
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
|
import coil3.request.CachePolicy
|
||||||
import com.aiosman.ravenow.data.api.AuthInterceptor
|
import com.aiosman.ravenow.data.api.AuthInterceptor
|
||||||
import com.aiosman.ravenow.data.api.getSafeOkHttpClient
|
import com.aiosman.ravenow.data.api.getSafeOkHttpClient
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -32,7 +33,15 @@ object Utils {
|
|||||||
|
|
||||||
val okHttpClient = getSafeOkHttpClient(authInterceptor = AuthInterceptor())
|
val okHttpClient = getSafeOkHttpClient(authInterceptor = AuthInterceptor())
|
||||||
val loader = ImageLoader.Builder(appContext)
|
val loader = ImageLoader.Builder(appContext)
|
||||||
.okHttpClient(okHttpClient)
|
.components {
|
||||||
|
add(
|
||||||
|
OkHttpNetworkFetcherFactory(
|
||||||
|
callFactory = {
|
||||||
|
okHttpClient
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
.memoryCachePolicy(CachePolicy.ENABLED)
|
.memoryCachePolicy(CachePolicy.ENABLED)
|
||||||
.diskCachePolicy(CachePolicy.ENABLED)
|
.diskCachePolicy(CachePolicy.ENABLED)
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||||
|
alias(libs.plugins.compose.compiler) apply false
|
||||||
id("com.google.gms.google-services") version "4.4.2" apply false
|
id("com.google.gms.google-services") version "4.4.2" apply false
|
||||||
id("com.google.firebase.crashlytics") version "3.0.2" apply false
|
id("com.google.firebase.crashlytics") version "3.0.2" apply false
|
||||||
id("com.google.firebase.firebase-perf") version "1.4.2" apply false
|
id("com.google.firebase.firebase-perf") version "1.4.2" apply false
|
||||||
|
|||||||
@@ -1,48 +1,51 @@
|
|||||||
[versions]
|
[versions]
|
||||||
accompanistSystemuicontroller = "0.27.0"
|
accompanistSystemuicontroller = "0.34.0"
|
||||||
agp = "8.4.0"
|
agp = "8.9.0"
|
||||||
animation = "1.7.0-beta05"
|
animation = "1.7.6"
|
||||||
coil = "2.7.0"
|
coil = "3.3.0"
|
||||||
composeImageBlurhash = "3.0.2"
|
composeImageBlurhash = "3.0.2"
|
||||||
converterGson = "2.11.0"
|
converterGson = "2.11.0"
|
||||||
imSdk = "3.8.3.2"
|
imSdk = "3.8.3.2"
|
||||||
imcoreSdk = "3.8.3-patch10"
|
imcoreSdk = "3.8.3-patch10"
|
||||||
coreSplashscreen = "1.0.1"
|
coreSplashscreen = "1.2.0"
|
||||||
credentialsPlayServicesAuth = "1.2.2"
|
credentialsPlayServicesAuth = "1.0.0-alpha05"
|
||||||
eventbus = "3.3.1"
|
eventbus = "3.3.1"
|
||||||
firebaseBom = "33.2.0"
|
firebaseBom = "33.7.0"
|
||||||
gson = "2.12.1"
|
gson = "2.12.1"
|
||||||
imagecropview = "3.0.1"
|
imagecropview = "3.0.1"
|
||||||
jpushGoogle = "5.4.0"
|
jpushGoogle = "5.4.0"
|
||||||
jwtdecode = "2.0.2"
|
jwtdecode = "2.0.2"
|
||||||
kotlin = "1.9.10"
|
kotlin = "2.2.21"
|
||||||
coreKtx = "1.10.1"
|
ksp = "2.2.21-2.0.4"
|
||||||
|
composeCompiler = "2.2.21"
|
||||||
|
coreKtx = "1.15.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.1.5"
|
junitVersion = "1.2.1"
|
||||||
espressoCore = "3.5.1"
|
espressoCore = "3.6.1"
|
||||||
kotlinFaker = "2.0.0-rc.5"
|
kotlinFaker = "2.0.0-rc.11"
|
||||||
lifecycleRuntimeKtx = "2.6.1"
|
lifecycleRuntimeKtx = "2.8.6"
|
||||||
activityCompose = "1.8.0"
|
activityCompose = "1.9.2"
|
||||||
composeBom = "2024.06.00"
|
composeBom = "2025.11.00"
|
||||||
lifecycleRuntimeKtxVersion = "2.6.2"
|
lifecycleRuntimeKtxVersion = "2.8.6"
|
||||||
mapsCompose = "4.3.3"
|
mapsCompose = "6.1.0"
|
||||||
material = "1.6.8"
|
material = "1.7.6"
|
||||||
material3Android = "1.2.1"
|
materialIconsExtended = "1.7.6"
|
||||||
media3Exoplayer = "1.3.1"
|
material3Android = "1.3.1"
|
||||||
navigationCompose = "2.7.7"
|
media3Exoplayer = "1.4.1"
|
||||||
pagingRuntime = "3.3.0"
|
navigationCompose = "2.8.6"
|
||||||
activityKtx = "1.9.0"
|
pagingRuntime = "3.3.6"
|
||||||
lifecycleCommonJvm = "2.8.2"
|
activityKtx = "1.9.2"
|
||||||
places = "3.3.0"
|
lifecycleCommonJvm = "2.8.6"
|
||||||
|
places = "3.4.0"
|
||||||
googleid = "1.1.1"
|
googleid = "1.1.1"
|
||||||
identityCredential = "20231002"
|
identityCredential = "20231002"
|
||||||
lifecycleProcess = "2.8.4"
|
lifecycleProcess = "2.8.6"
|
||||||
playServicesAuth = "21.4.0"
|
playServicesAuth = "21.4.0"
|
||||||
rendering = "1.17.1"
|
rendering = "1.17.1"
|
||||||
zoomable = "1.6.1"
|
zoomable = "1.6.1"
|
||||||
camerax = "1.3.4"
|
camerax = "1.4.0"
|
||||||
mlkitBarcode = "17.3.0"
|
mlkitBarcode = "17.3.0"
|
||||||
room = "2.6.1"
|
room = "2.8.3"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
||||||
@@ -53,6 +56,7 @@ androidx-credentials = { module = "androidx.credentials:credentials", version.re
|
|||||||
androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "credentialsPlayServicesAuth" }
|
androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "credentialsPlayServicesAuth" }
|
||||||
androidx-lifecycle-runtime-ktx-v262 = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtxVersion" }
|
androidx-lifecycle-runtime-ktx-v262 = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtxVersion" }
|
||||||
androidx-material = { module = "androidx.compose.material:material", version.ref = "material" }
|
androidx-material = { module = "androidx.compose.material:material", version.ref = "material" }
|
||||||
|
androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" }
|
||||||
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3Exoplayer" }
|
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3Exoplayer" }
|
||||||
androidx-media3-session = { module = "androidx.media3:media3-session", version.ref = "media3Exoplayer" }
|
androidx-media3-session = { module = "androidx.media3:media3-session", version.ref = "media3Exoplayer" }
|
||||||
androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3Exoplayer" }
|
androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3Exoplayer" }
|
||||||
@@ -62,8 +66,9 @@ androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.r
|
|||||||
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax" }
|
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax" }
|
||||||
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camerax" }
|
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camerax" }
|
||||||
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "camerax" }
|
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "camerax" }
|
||||||
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
|
coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
|
||||||
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
|
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||||
|
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||||
compose-image-blurhash = { module = "com.github.orlando-dev-code:compose-image-blurhash", version.ref = "composeImageBlurhash" }
|
compose-image-blurhash = { module = "com.github.orlando-dev-code:compose-image-blurhash", version.ref = "composeImageBlurhash" }
|
||||||
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
|
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
|
||||||
eventbus = { module = "org.greenrobot:eventbus", version.ref = "eventbus" }
|
eventbus = { module = "org.greenrobot:eventbus", version.ref = "eventbus" }
|
||||||
@@ -79,7 +84,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
|
|||||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version = "1.9.0" }
|
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||||
@@ -104,7 +109,7 @@ play-services-auth = { module = "com.google.android.gms:play-services-auth", ver
|
|||||||
rendering = { group = "com.google.ar.sceneform", name = "rendering", version.ref = "rendering" }
|
rendering = { group = "com.google.ar.sceneform", name = "rendering", version.ref = "rendering" }
|
||||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "converterGson" }
|
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "converterGson" }
|
||||||
zoomable = { module = "net.engawapg.lib:zoomable", version.ref = "zoomable" }
|
zoomable = { module = "net.engawapg.lib:zoomable", version.ref = "zoomable" }
|
||||||
lottie = { module="com.airbnb.android:lottie-compose", version="6.6.10"}
|
lottie = { module="com.airbnb.android:lottie-compose", version="6.7.0"}
|
||||||
mlkit-barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "mlkitBarcode" }
|
mlkit-barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "mlkitBarcode" }
|
||||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||||
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||||
@@ -112,3 +117,5 @@ androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref =
|
|||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||||
|
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "composeCompiler" }
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Fri Jun 14 03:23:01 CST 2024
|
#Fri Jun 14 03:23:01 CST 2024
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
Reference in New Issue
Block a user