Enhance AI Agent Profile Interaction
This commit introduces several enhancements to how AI agent profiles are displayed and interacted with:
**Profile Display:**
- **AI Account Distinction:** Profile pages now differentiate between regular user accounts and AI agent accounts.
- AI agent profiles will not display the "Agents" tab in their profile.
- The profile header height is adjusted for AI accounts.
- **Navigation Parameter:** An `isAiAccount` boolean parameter is added to the `AccountProfile` navigation route to indicate if the profile being viewed belongs to an AI.
**Interaction & Navigation:**
- **Avatar Click Navigation:**
- Clicking an AI agent's avatar in various lists (Hot Agents, My Agents, User Agents Row, User Agents List) now navigates to the agent's dedicated profile page.
- When navigating to an agent's profile from an agent list, `isAiAccount` is set to `true`.
- **Chat Initiation:** Clicking the chat button on AI agent cards in the "Agent" tab (both Hot and My Agents) now correctly initiates a chat with the respective AI.
- **ViewModel Updates:**
- `AgentViewModel`, `MineAgentViewModel`, and `HotAgentViewModel` now include a `goToProfile` function to handle navigation to agent profiles, correctly passing the `isAiAccount` flag.
**Code Refinements:**
- Click handlers for agent avatars and chat buttons are now wrapped with `DebounceUtils.simpleDebounceClick` to prevent multiple rapid clicks.
- The `UserContentPageIndicator` now conditionally hides the "Agent" tab based on the `isAiAccount` status.
- `UserAgentsRow` and `UserAgentsList` now accept an `onAvatarClick` callback for navigating to agent profiles.
- `AgentItem` (used in `UserAgentsRow`) and `UserAgentCard` (used in `UserAgentsList`) now handle avatar clicks.
- The general `Agent` composable (used in `AiPostComposable`) now also supports an `onAvatarClick` callback.
This commit is contained in:
@@ -45,6 +45,12 @@ object AppState {
|
|||||||
var enableGoogleLogin: Boolean = false
|
var enableGoogleLogin: Boolean = false
|
||||||
var enableChat = false
|
var enableChat = false
|
||||||
suspend fun initWithAccount(scope: CoroutineScope, context: Context) {
|
suspend fun initWithAccount(scope: CoroutineScope, context: Context) {
|
||||||
|
// 如果是游客模式,使用简化的初始化流程
|
||||||
|
if (AppStore.isGuest) {
|
||||||
|
initWithGuestAccount()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val accountService: AccountService = AccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
// 获取用户认证信息
|
// 获取用户认证信息
|
||||||
val resp = accountService.getMyAccount()
|
val resp = accountService.getMyAccount()
|
||||||
@@ -69,6 +75,18 @@ object AppState {
|
|||||||
initChat(context)
|
initChat(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 游客模式的简化初始化
|
||||||
|
*/
|
||||||
|
private fun initWithGuestAccount() {
|
||||||
|
// 游客模式下,不初始化推送和TRTC
|
||||||
|
// 设置默认的用户信息
|
||||||
|
UserId = 0
|
||||||
|
profile = null
|
||||||
|
enableChat = false
|
||||||
|
Log.d("AppState", "Guest mode initialized without push notifications and TRTC")
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun initChat(context: Context){
|
private suspend fun initChat(context: Context){
|
||||||
val dictService :DictService = DictServiceImpl()
|
val dictService :DictService = DictServiceImpl()
|
||||||
val enableItem = dictService.getDictByKey(ConstVars.DICT_KEY_ENABLE_TRTC)
|
val enableItem = dictService.getDictByKey(ConstVars.DICT_KEY_ENABLE_TRTC)
|
||||||
@@ -149,6 +167,27 @@ object AppState {
|
|||||||
AppStore.saveDarkMode(darkMode)
|
AppStore.saveDarkMode(darkMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否是游客模式,并且是否需要登录
|
||||||
|
* @return true 如果是游客模式
|
||||||
|
*/
|
||||||
|
fun isGuestMode(): Boolean {
|
||||||
|
return AppStore.isGuest
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查游客模式并提示登录
|
||||||
|
* @param onGuestMode 当是游客模式时的回调
|
||||||
|
* @return true 如果是游客模式
|
||||||
|
*/
|
||||||
|
fun checkGuestModeAndPromptLogin(onGuestMode: (() -> Unit)? = null): Boolean {
|
||||||
|
if (AppStore.isGuest) {
|
||||||
|
onGuestMode?.invoke()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fun ReloadAppState(context: Context) {
|
fun ReloadAppState(context: Context) {
|
||||||
// 重置动态列表页面
|
// 重置动态列表页面
|
||||||
TimelineMomentViewModel.ResetModel()
|
TimelineMomentViewModel.ResetModel()
|
||||||
@@ -175,6 +214,9 @@ object AppState {
|
|||||||
IndexViewModel.ResetModel()
|
IndexViewModel.ResetModel()
|
||||||
UserId = null
|
UserId = null
|
||||||
|
|
||||||
|
// 清除游客状态
|
||||||
|
AppStore.isGuest = false
|
||||||
|
|
||||||
// 关闭 TrtcService
|
// 关闭 TrtcService
|
||||||
val trtcService = Intent(
|
val trtcService = Intent(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -41,3 +41,33 @@ object ConstVars {
|
|||||||
const val DICT_KEY_REPORT_OPTIONS = "report_reasons"
|
const val DICT_KEY_REPORT_OPTIONS = "report_reasons"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class GuestLoginCheckOutScene {
|
||||||
|
CREATE_POST,
|
||||||
|
CREATE_AGENT,
|
||||||
|
VIEW_MESSAGES,
|
||||||
|
VIEW_PROFILE,
|
||||||
|
JOIN_GROUP_CHAT,
|
||||||
|
CHAT_WITH_AGENT,
|
||||||
|
LIKE_MOMENT,
|
||||||
|
COMMENT_MOMENT,
|
||||||
|
FOLLOW_USER,
|
||||||
|
REPORT_CONTENT
|
||||||
|
}
|
||||||
|
|
||||||
|
object GuestLoginCheckOut {
|
||||||
|
var NeedLoginScene = listOf<GuestLoginCheckOutScene>(
|
||||||
|
GuestLoginCheckOutScene.CREATE_POST,
|
||||||
|
GuestLoginCheckOutScene.CREATE_AGENT,
|
||||||
|
GuestLoginCheckOutScene.VIEW_MESSAGES,
|
||||||
|
GuestLoginCheckOutScene.VIEW_PROFILE,
|
||||||
|
GuestLoginCheckOutScene.JOIN_GROUP_CHAT,
|
||||||
|
GuestLoginCheckOutScene.CHAT_WITH_AGENT,
|
||||||
|
GuestLoginCheckOutScene.LIKE_MOMENT,
|
||||||
|
GuestLoginCheckOutScene.COMMENT_MOMENT,
|
||||||
|
GuestLoginCheckOutScene.FOLLOW_USER,
|
||||||
|
GuestLoginCheckOutScene.REPORT_CONTENT
|
||||||
|
)
|
||||||
|
fun needLogin(scene: GuestLoginCheckOutScene): Boolean {
|
||||||
|
return AppStore.isGuest && NeedLoginScene.contains(scene)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -114,8 +114,9 @@ class MainActivity : ComponentActivity() {
|
|||||||
// 检查是否有登录态
|
// 检查是否有登录态
|
||||||
val isAccountValidate = getAccount()
|
val isAccountValidate = getAccount()
|
||||||
var startDestination = NavigationRoute.Login.route
|
var startDestination = NavigationRoute.Login.route
|
||||||
// 如果有登录态,且记住登录状态,且账号有效,则初始化 FCM,下一步进入首页
|
// 如果有登录态,且记住登录状态,且账号有效,则初始化应用状态,下一步进入首页
|
||||||
if (AppStore.token != null && AppStore.rememberMe && isAccountValidate) {
|
if (AppStore.token != null && AppStore.rememberMe && (isAccountValidate || AppStore.isGuest)) {
|
||||||
|
// 根据用户类型进行相应的初始化(游客模式会跳过推送和TRTC初始化)
|
||||||
AppState.initWithAccount(scope, this@MainActivity)
|
AppState.initWithAccount(scope, this@MainActivity)
|
||||||
startDestination = NavigationRoute.Index.route
|
startDestination = NavigationRoute.Index.route
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.aiosman.ravenow.data.api.AppConfig
|
|||||||
import com.aiosman.ravenow.data.api.CaptchaInfo
|
import com.aiosman.ravenow.data.api.CaptchaInfo
|
||||||
import com.aiosman.ravenow.data.api.ChangePasswordRequestBody
|
import com.aiosman.ravenow.data.api.ChangePasswordRequestBody
|
||||||
import com.aiosman.ravenow.data.api.GoogleRegisterRequestBody
|
import com.aiosman.ravenow.data.api.GoogleRegisterRequestBody
|
||||||
|
import com.aiosman.ravenow.data.api.GuestLoginRequestBody
|
||||||
import com.aiosman.ravenow.data.api.LoginUserRequestBody
|
import com.aiosman.ravenow.data.api.LoginUserRequestBody
|
||||||
import com.aiosman.ravenow.data.api.RegisterMessageChannelRequestBody
|
import com.aiosman.ravenow.data.api.RegisterMessageChannelRequestBody
|
||||||
import com.aiosman.ravenow.data.api.RegisterRequestBody
|
import com.aiosman.ravenow.data.api.RegisterRequestBody
|
||||||
@@ -300,6 +301,13 @@ interface AccountService {
|
|||||||
*/
|
*/
|
||||||
suspend fun loginUserWithGoogle(googleId: String): UserAuth
|
suspend fun loginUserWithGoogle(googleId: String): UserAuth
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 游客登录
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
* @param deviceInfo 设备信息
|
||||||
|
*/
|
||||||
|
suspend fun guestLogin(deviceId: String, deviceInfo: String? = null): UserAuth
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 退出登录
|
* 退出登录
|
||||||
*/
|
*/
|
||||||
@@ -456,6 +464,21 @@ class AccountServiceImpl : AccountService {
|
|||||||
return UserAuth(0, body.token)
|
return UserAuth(0, body.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun guestLogin(deviceId: String, deviceInfo: String?): UserAuth {
|
||||||
|
val resp = ApiClient.api.guestLogin(GuestLoginRequestBody(
|
||||||
|
deviceId = deviceId,
|
||||||
|
deviceInfo = deviceInfo
|
||||||
|
))
|
||||||
|
if (!resp.isSuccessful) {
|
||||||
|
parseErrorResponse(resp.errorBody())?.let {
|
||||||
|
throw it.toServiceException()
|
||||||
|
}
|
||||||
|
throw ServiceException("Failed to guest login")
|
||||||
|
}
|
||||||
|
val body = resp.body() ?: throw ServiceException("Failed to guest login")
|
||||||
|
return UserAuth(0, body.token, isGuest = true)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun regiterUserWithGoogleAccount(idToken: String) {
|
override suspend fun regiterUserWithGoogleAccount(idToken: String) {
|
||||||
val resp = ApiClient.api.registerWithGoogle(GoogleRegisterRequestBody(idToken))
|
val resp = ApiClient.api.registerWithGoogle(GoogleRegisterRequestBody(idToken))
|
||||||
if (!resp.isSuccessful) {
|
if (!resp.isSuccessful) {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import com.aiosman.ravenow.entity.AccountProfileEntity
|
|||||||
|
|
||||||
data class UserAuth(
|
data class UserAuth(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val token: String? = null
|
val token: String? = null,
|
||||||
|
val isGuest: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,7 +23,29 @@ fun getUnsafeOkHttpClient(
|
|||||||
): OkHttpClient {
|
): OkHttpClient {
|
||||||
return try {
|
return try {
|
||||||
// Create a trust manager that does not validate certificate chains
|
// Create a trust manager that does not validate certificate chains
|
||||||
|
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
|
||||||
|
@Throws(CertificateException::class)
|
||||||
|
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(CertificateException::class)
|
||||||
|
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
|
||||||
|
return arrayOf()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Install the all-trusting trust manager
|
||||||
|
val sslContext = SSLContext.getInstance("SSL")
|
||||||
|
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
|
||||||
|
|
||||||
|
// Create an ssl socket factory with our all-trusting manager
|
||||||
|
val sslSocketFactory = sslContext.socketFactory
|
||||||
|
|
||||||
OkHttpClient.Builder()
|
OkHttpClient.Builder()
|
||||||
|
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
|
||||||
.hostnameVerifier { _, _ -> true }
|
.hostnameVerifier { _, _ -> true }
|
||||||
.apply {
|
.apply {
|
||||||
authInterceptor?.let {
|
authInterceptor?.let {
|
||||||
|
|||||||
@@ -95,6 +95,19 @@ data class LoginUserRequestBody(
|
|||||||
val captcha: CaptchaInfo? = null,
|
val captcha: CaptchaInfo? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class GuestLoginRequestBody(
|
||||||
|
@SerializedName("deviceID")
|
||||||
|
val deviceId: String,
|
||||||
|
@SerializedName("platform")
|
||||||
|
val platform: String = "android",
|
||||||
|
@SerializedName("deviceInfo")
|
||||||
|
val deviceInfo: String? = null,
|
||||||
|
@SerializedName("userAgent")
|
||||||
|
val userAgent: String? = null,
|
||||||
|
@SerializedName("ipAddress")
|
||||||
|
val ipAddress: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
data class GoogleRegisterRequestBody(
|
data class GoogleRegisterRequestBody(
|
||||||
@SerializedName("idToken")
|
@SerializedName("idToken")
|
||||||
val idToken: String
|
val idToken: String
|
||||||
@@ -274,6 +287,9 @@ interface RaveNowAPI {
|
|||||||
@POST("login")
|
@POST("login")
|
||||||
suspend fun login(@Body body: LoginUserRequestBody): Response<AuthResult>
|
suspend fun login(@Body body: LoginUserRequestBody): Response<AuthResult>
|
||||||
|
|
||||||
|
@POST("guest/login")
|
||||||
|
suspend fun guestLogin(@Body body: GuestLoginRequestBody): Response<AuthResult>
|
||||||
|
|
||||||
@GET("auth/token")
|
@GET("auth/token")
|
||||||
suspend fun checkToken(): Response<ValidateTokenResult>
|
suspend fun checkToken(): Response<ValidateTokenResult>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.aiosman.ravenow.entity
|
|||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
|
import com.aiosman.ravenow.AppStore
|
||||||
import com.aiosman.ravenow.data.Agent
|
import com.aiosman.ravenow.data.Agent
|
||||||
import com.aiosman.ravenow.data.ListContainer
|
import com.aiosman.ravenow.data.ListContainer
|
||||||
import com.aiosman.ravenow.data.AgentService
|
import com.aiosman.ravenow.data.AgentService
|
||||||
@@ -28,24 +29,37 @@ suspend fun createAgent(
|
|||||||
title: String,
|
title: String,
|
||||||
desc: String,
|
desc: String,
|
||||||
avatar: UploadImage? = null,
|
avatar: UploadImage? = null,
|
||||||
workflowId:Int = 1,
|
workflowId: Int = 1,
|
||||||
isPublic:Boolean = true,
|
isPublic: Boolean = true,
|
||||||
breakMode:Boolean = false,
|
breakMode: Boolean = false,
|
||||||
useWorkflow:Boolean = true,
|
useWorkflow: Boolean = true,
|
||||||
): AgentEntity {
|
): AgentEntity {
|
||||||
val textTitle = title.toRequestBody("text/plain".toMediaTypeOrNull())
|
val textTitle = title.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
val textDesc = desc.toRequestBody("text/plain".toMediaTypeOrNull())
|
val textDesc = desc.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
val workflowIdRequestBody = workflowId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
val workflowIdRequestBody =
|
||||||
|
workflowId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
val isPublicRequestBody = isPublic.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
val isPublicRequestBody = isPublic.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
val breakModeRequestBody = breakMode.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
val breakModeRequestBody = breakMode.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
val useWorkflowRequestBody = useWorkflow.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
val useWorkflowRequestBody =
|
||||||
|
useWorkflow.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
val workflowInputsValue = "{\"si\":\"$desc\"}"
|
val workflowInputsValue = "{\"si\":\"$desc\"}"
|
||||||
val workflowInputsRequestBody = workflowInputsValue.toRequestBody("text/plain".toMediaTypeOrNull())
|
val workflowInputsRequestBody =
|
||||||
|
workflowInputsValue.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
val avatarField: MultipartBody.Part? = avatar?.let {
|
val avatarField: MultipartBody.Part? = avatar?.let {
|
||||||
createMultipartBody(it.file, it.filename, "avatar")
|
createMultipartBody(it.file, it.filename, "avatar")
|
||||||
}
|
}
|
||||||
val response = ApiClient.api.createAgent(avatarField, textTitle ,textDesc,textDesc,workflowIdRequestBody,isPublicRequestBody,breakModeRequestBody,useWorkflowRequestBody,workflowInputsRequestBody)
|
val response = ApiClient.api.createAgent(
|
||||||
|
avatarField,
|
||||||
|
textTitle,
|
||||||
|
textDesc,
|
||||||
|
textDesc,
|
||||||
|
workflowIdRequestBody,
|
||||||
|
isPublicRequestBody,
|
||||||
|
breakModeRequestBody,
|
||||||
|
useWorkflowRequestBody,
|
||||||
|
workflowInputsRequestBody
|
||||||
|
)
|
||||||
val body = response.body()?.data ?: throw ServiceException("Failed to create agent")
|
val body = response.body()?.data ?: throw ServiceException("Failed to create agent")
|
||||||
return body.toAgentEntity()
|
return body.toAgentEntity()
|
||||||
|
|
||||||
@@ -99,7 +113,11 @@ class AgentRemoteDataSource(
|
|||||||
class AgentServiceImpl() : AgentService {
|
class AgentServiceImpl() : AgentService {
|
||||||
val agentBackend = AgentBackend()
|
val agentBackend = AgentBackend()
|
||||||
|
|
||||||
override suspend fun getAgent(pageNumber: Int, pageSize: Int, authorId: Int?): ListContainer<AgentEntity> {
|
override suspend fun getAgent(
|
||||||
|
pageNumber: Int,
|
||||||
|
pageSize: Int,
|
||||||
|
authorId: Int?
|
||||||
|
): ListContainer<AgentEntity> {
|
||||||
return agentBackend.getAgent(
|
return agentBackend.getAgent(
|
||||||
pageNumber = pageNumber,
|
pageNumber = pageNumber,
|
||||||
authorId = authorId
|
authorId = authorId
|
||||||
@@ -107,50 +125,62 @@ class AgentServiceImpl() : AgentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AgentBackend {
|
class AgentBackend {
|
||||||
val DataBatchSize = 20
|
val DataBatchSize = 20
|
||||||
suspend fun getAgent(
|
suspend fun getAgent(
|
||||||
pageNumber: Int,
|
pageNumber: Int,
|
||||||
authorId: Int? = null
|
authorId: Int? = null
|
||||||
): ListContainer<AgentEntity> {
|
): ListContainer<AgentEntity> {
|
||||||
val resp = if (authorId != null) {
|
// 如果是游客模式且获取我的Agent(authorId为null),返回空列表
|
||||||
ApiClient.api.getAgent(
|
if (authorId == null && AppStore.isGuest) {
|
||||||
page = pageNumber,
|
return ListContainer(
|
||||||
pageSize = DataBatchSize,
|
total = 0,
|
||||||
authorId = authorId
|
page = pageNumber,
|
||||||
)
|
pageSize = DataBatchSize,
|
||||||
} else {
|
list = emptyList()
|
||||||
ApiClient.api.getMyAgent(
|
)
|
||||||
page = pageNumber,
|
}
|
||||||
pageSize = DataBatchSize
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val body = resp.body() ?: throw ServiceException("Failed to get agents")
|
val resp = if (authorId != null) {
|
||||||
|
ApiClient.api.getAgent(
|
||||||
|
page = pageNumber,
|
||||||
|
pageSize = DataBatchSize,
|
||||||
|
authorId = authorId
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ApiClient.api.getMyAgent(
|
||||||
|
page = pageNumber,
|
||||||
|
pageSize = DataBatchSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 处理不同的返回类型
|
val body = resp.body() ?: throw ServiceException("Failed to get agents")
|
||||||
return if (authorId != null) {
|
|
||||||
// getAgent 返回 DataContainer<ListContainer<Agent>>
|
// 处理不同的返回类型
|
||||||
val dataContainer = body as com.aiosman.ravenow.data.DataContainer<com.aiosman.ravenow.data.ListContainer<com.aiosman.ravenow.data.Agent>>
|
return if (authorId != null) {
|
||||||
val listContainer = dataContainer.data
|
// getAgent 返回 DataContainer<ListContainer<Agent>>
|
||||||
ListContainer(
|
val dataContainer =
|
||||||
total = listContainer.total,
|
body as com.aiosman.ravenow.data.DataContainer<com.aiosman.ravenow.data.ListContainer<com.aiosman.ravenow.data.Agent>>
|
||||||
page = pageNumber,
|
val listContainer = dataContainer.data
|
||||||
pageSize = DataBatchSize,
|
ListContainer(
|
||||||
list = listContainer.list.map { it.toAgentEntity() }
|
total = listContainer.total,
|
||||||
)
|
page = pageNumber,
|
||||||
} else {
|
pageSize = DataBatchSize,
|
||||||
// getMyAgent 返回 ListContainer<Agent>
|
list = listContainer.list.map { it.toAgentEntity() }
|
||||||
val listContainer = body as com.aiosman.ravenow.data.ListContainer<com.aiosman.ravenow.data.Agent>
|
)
|
||||||
ListContainer(
|
} else {
|
||||||
total = listContainer.total,
|
// getMyAgent 返回 ListContainer<Agent>
|
||||||
page = pageNumber,
|
val listContainer =
|
||||||
pageSize = DataBatchSize,
|
body as com.aiosman.ravenow.data.ListContainer<com.aiosman.ravenow.data.Agent>
|
||||||
list = listContainer.list.map { it.toAgentEntity() }
|
ListContainer(
|
||||||
)
|
total = listContainer.total,
|
||||||
}
|
page = pageNumber,
|
||||||
|
pageSize = DataBatchSize,
|
||||||
|
list = listContainer.list.map { it.toAgentEntity() }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class AgentEntity(
|
data class AgentEntity(
|
||||||
//val author: String,
|
//val author: String,
|
||||||
@@ -172,15 +202,27 @@ fun createMultipartBody(file: File, filename: String, name: String): MultipartBo
|
|||||||
val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull())
|
val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull())
|
||||||
return MultipartBody.Part.createFormData(name, filename, requestFile)
|
return MultipartBody.Part.createFormData(name, filename, requestFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AgentLoaderExtraArgs(
|
class AgentLoaderExtraArgs(
|
||||||
val authorId: Int? = null
|
val authorId: Int? = null
|
||||||
)
|
)
|
||||||
class AgentLoader : DataLoader<AgentEntity,AgentLoaderExtraArgs>() {
|
|
||||||
|
class AgentLoader : DataLoader<AgentEntity, AgentLoaderExtraArgs>() {
|
||||||
override suspend fun fetchData(
|
override suspend fun fetchData(
|
||||||
page: Int,
|
page: Int,
|
||||||
pageSize: Int,
|
pageSize: Int,
|
||||||
extra: AgentLoaderExtraArgs
|
extra: AgentLoaderExtraArgs
|
||||||
): ListContainer<AgentEntity> {
|
): ListContainer<AgentEntity> {
|
||||||
|
// 如果是游客模式且获取我的Agent(authorId为null),返回空列表
|
||||||
|
if (extra.authorId == null && AppStore.isGuest) {
|
||||||
|
return ListContainer(
|
||||||
|
total = 0,
|
||||||
|
page = page,
|
||||||
|
pageSize = pageSize,
|
||||||
|
list = emptyList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val result = if (extra.authorId != null) {
|
val result = if (extra.authorId != null) {
|
||||||
ApiClient.api.getAgent(
|
ApiClient.api.getAgent(
|
||||||
page = page,
|
page = page,
|
||||||
@@ -201,16 +243,17 @@ class AgentLoader : DataLoader<AgentEntity,AgentLoaderExtraArgs>() {
|
|||||||
val dataContainer = body as DataContainer<ListContainer<Agent>>
|
val dataContainer = body as DataContainer<ListContainer<Agent>>
|
||||||
val listContainer = dataContainer.data
|
val listContainer = dataContainer.data
|
||||||
ListContainer(
|
ListContainer(
|
||||||
list = listContainer.list.map { it.toAgentEntity()},
|
list = listContainer.list.map { it.toAgentEntity() },
|
||||||
total = listContainer.total,
|
total = listContainer.total,
|
||||||
page = page,
|
page = page,
|
||||||
pageSize = pageSize
|
pageSize = pageSize
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// getMyAgent 返回 ListContainer<Agent>
|
// getMyAgent 返回 ListContainer<Agent>
|
||||||
val listContainer = body as com.aiosman.ravenow.data.ListContainer<com.aiosman.ravenow.data.Agent>
|
val listContainer =
|
||||||
|
body as com.aiosman.ravenow.data.ListContainer<com.aiosman.ravenow.data.Agent>
|
||||||
ListContainer(
|
ListContainer(
|
||||||
list = listContainer.list.map { it.toAgentEntity()},
|
list = listContainer.list.map { it.toAgentEntity() },
|
||||||
total = listContainer.total,
|
total = listContainer.total,
|
||||||
page = page,
|
page = page,
|
||||||
pageSize = pageSize
|
pageSize = pageSize
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ object AppStore {
|
|||||||
private const val PREFS_NAME = "app_prefs_$STORE_VERSION"
|
private const val PREFS_NAME = "app_prefs_$STORE_VERSION"
|
||||||
var token: String? = null
|
var token: String? = null
|
||||||
var rememberMe: Boolean = false
|
var rememberMe: Boolean = false
|
||||||
|
var isGuest: Boolean = false
|
||||||
private lateinit var sharedPreferences: SharedPreferences
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
lateinit var googleSignInOptions: GoogleSignInOptions
|
lateinit var googleSignInOptions: GoogleSignInOptions
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
@@ -36,6 +37,7 @@ object AppStore {
|
|||||||
sharedPreferences.edit().apply {
|
sharedPreferences.edit().apply {
|
||||||
putString("token", token)
|
putString("token", token)
|
||||||
putBoolean("rememberMe", rememberMe)
|
putBoolean("rememberMe", rememberMe)
|
||||||
|
putBoolean("isGuest", isGuest)
|
||||||
}.apply()
|
}.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ object AppStore {
|
|||||||
// shared preferences
|
// shared preferences
|
||||||
token = sharedPreferences.getString("token", null)
|
token = sharedPreferences.getString("token", null)
|
||||||
rememberMe = sharedPreferences.getBoolean("rememberMe", false)
|
rememberMe = sharedPreferences.getBoolean("rememberMe", false)
|
||||||
|
isGuest = sharedPreferences.getBoolean("isGuest", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveDarkMode(darkMode: Boolean) {
|
fun saveDarkMode(darkMode: Boolean) {
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.aiosman.ravenow.AppState
|
import com.aiosman.ravenow.AppState
|
||||||
import com.aiosman.ravenow.AppStore
|
import com.aiosman.ravenow.AppStore
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.Messaging
|
import com.aiosman.ravenow.Messaging
|
||||||
@@ -238,10 +240,14 @@ fun IndexScreen() {
|
|||||||
modifier = Modifier.noRippleClickable {
|
modifier = Modifier.noRippleClickable {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
drawerState.close()
|
drawerState.close()
|
||||||
Messaging.unregisterDevice(context)
|
// 只有非游客用户才需要取消注册推送设备
|
||||||
|
if (!AppStore.isGuest) {
|
||||||
|
Messaging.unregisterDevice(context)
|
||||||
|
}
|
||||||
AppStore.apply {
|
AppStore.apply {
|
||||||
token = null
|
token = null
|
||||||
rememberMe = false
|
rememberMe = false
|
||||||
|
isGuest = false // 清除游客状态
|
||||||
saveData()
|
saveData()
|
||||||
}
|
}
|
||||||
// 删除推送渠道
|
// 删除推送渠道
|
||||||
@@ -280,10 +286,32 @@ fun IndexScreen() {
|
|||||||
.padding(top = 2.dp)
|
.padding(top = 2.dp)
|
||||||
.noRippleClickable {
|
.noRippleClickable {
|
||||||
if (it.route === NavigationItem.Add.route) {
|
if (it.route === NavigationItem.Add.route) {
|
||||||
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CREATE_POST)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
return@noRippleClickable
|
||||||
|
}
|
||||||
NewPostViewModel.asNewPost()
|
NewPostViewModel.asNewPost()
|
||||||
navController.navigate(NavigationRoute.NewPost.route)
|
navController.navigate(NavigationRoute.NewPost.route)
|
||||||
return@noRippleClickable
|
return@noRippleClickable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查消息tab的游客模式
|
||||||
|
if (it.route === NavigationItem.Notification.route) {
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.VIEW_MESSAGES)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
return@noRippleClickable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查我的tab的游客模式
|
||||||
|
if (it.route === NavigationItem.Profile.route) {
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.VIEW_PROFILE)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
return@noRippleClickable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
pagerState.scrollToPage(idx)
|
pagerState.scrollToPage(idx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ 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.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
import com.aiosman.ravenow.AppStore
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.R
|
import com.aiosman.ravenow.R
|
||||||
@@ -70,7 +73,9 @@ fun Agent() {
|
|||||||
val navigationBarPaddings =
|
val navigationBarPaddings =
|
||||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
||||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||||
var pagerState = rememberPagerState { 2 }
|
// 游客模式下只显示热门Agent,正常用户显示我的Agent和热门Agent
|
||||||
|
val tabCount = if (AppStore.isGuest) 1 else 2
|
||||||
|
var pagerState = rememberPagerState { tabCount }
|
||||||
var scope = rememberCoroutineScope()
|
var scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val viewModel: AgentViewModel = viewModel()
|
val viewModel: AgentViewModel = viewModel()
|
||||||
@@ -137,10 +142,15 @@ fun Agent() {
|
|||||||
.size(36.dp)
|
.size(36.dp)
|
||||||
.noRippleClickable {
|
.noRippleClickable {
|
||||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||||
// 导航到添加智能体页面
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
navController.navigate(
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CREATE_AGENT)) {
|
||||||
NavigationRoute.AddAgent.route
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
)
|
} else {
|
||||||
|
// 导航到添加智能体页面
|
||||||
|
navController.navigate(
|
||||||
|
NavigationRoute.AddAgent.route
|
||||||
|
)
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
lastClickTime = System.currentTimeMillis()
|
lastClickTime = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
@@ -205,27 +215,33 @@ fun Agent() {
|
|||||||
color = AppColors.text
|
color = AppColors.text
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
TabItem(
|
|
||||||
text = stringResource(R.string.agent_mine),
|
// 只有非游客用户才显示"我的Agent"tab
|
||||||
isSelected = pagerState.currentPage == 0,
|
if (!AppStore.isGuest) {
|
||||||
onClick = {
|
TabItem(
|
||||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 300L) {
|
text = stringResource(R.string.agent_mine),
|
||||||
scope.launch {
|
isSelected = pagerState.currentPage == 0,
|
||||||
pagerState.animateScrollToPage(0)
|
onClick = {
|
||||||
|
if (DebounceUtils.simpleDebounceClick(lastClickTime, 300L) {
|
||||||
|
scope.launch {
|
||||||
|
pagerState.animateScrollToPage(0)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
lastClickTime = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
}) {
|
|
||||||
lastClickTime = System.currentTimeMillis()
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
TabSpacer()
|
||||||
TabSpacer()
|
}
|
||||||
|
|
||||||
TabItem(
|
TabItem(
|
||||||
text = stringResource(R.string.agent_hot),
|
text = stringResource(R.string.agent_hot),
|
||||||
isSelected = pagerState.currentPage == 1,
|
isSelected = if (AppStore.isGuest) pagerState.currentPage == 0 else pagerState.currentPage == 1,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 300L) {
|
if (DebounceUtils.simpleDebounceClick(lastClickTime, 300L) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
pagerState.animateScrollToPage(1)
|
val targetPage = if (AppStore.isGuest) 0 else 1
|
||||||
|
pagerState.animateScrollToPage(targetPage)
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
lastClickTime = System.currentTimeMillis()
|
lastClickTime = System.currentTimeMillis()
|
||||||
@@ -261,23 +277,24 @@ fun Agent() {
|
|||||||
.weight(1f),
|
.weight(1f),
|
||||||
beyondBoundsPageCount = 1 // 预加载相邻页面,避免切换时重新加载
|
beyondBoundsPageCount = 1 // 预加载相邻页面,避免切换时重新加载
|
||||||
) {
|
) {
|
||||||
when (it) {
|
if (AppStore.isGuest) {
|
||||||
0 -> {
|
// 游客模式下只显示热门Agent
|
||||||
MineAgent()
|
when (it) {
|
||||||
|
0 -> {
|
||||||
|
HotAgent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 正常用户显示我的Agent和热门Agent
|
||||||
|
when (it) {
|
||||||
|
0 -> {
|
||||||
|
MineAgent()
|
||||||
|
}
|
||||||
|
|
||||||
1 -> {
|
1 -> {
|
||||||
HotAgent()
|
HotAgent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
2 -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
3 -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import androidx.compose.ui.res.vectorResource
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.aiosman.ravenow.AppStore
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.R
|
import com.aiosman.ravenow.R
|
||||||
@@ -62,7 +63,9 @@ fun MomentsList() {
|
|||||||
val navigationBarPaddings =
|
val navigationBarPaddings =
|
||||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
||||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||||
var pagerState = rememberPagerState { 4 }
|
// 游客模式下不显示timeline,只显示3个tab:Explore、Dynamic、Hot
|
||||||
|
val tabCount = if (AppStore.isGuest) 3 else 4
|
||||||
|
var pagerState = rememberPagerState { tabCount }
|
||||||
var scope = rememberCoroutineScope()
|
var scope = rememberCoroutineScope()
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -141,36 +144,40 @@ fun MomentsList() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
//关注tab
|
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.noRippleClickable {
|
|
||||||
scope.launch {
|
|
||||||
pagerState.animateScrollToPage(2)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.index_following),
|
|
||||||
fontSize = if (pagerState.currentPage == 2)18.sp else 16.sp,
|
|
||||||
color = if (pagerState.currentPage == 2) AppColors.text else Color(0X993c3c43),
|
|
||||||
fontWeight = FontWeight.W600)
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
|
||||||
|
|
||||||
Image(
|
// 只有非游客用户才显示"关注"tab
|
||||||
painter = painterResource(
|
if (!AppStore.isGuest) {
|
||||||
if (pagerState.currentPage == 2) R.mipmap.tab_indicator_selected
|
//关注tab
|
||||||
else R.drawable.tab_indicator_unselected
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
),
|
Column(
|
||||||
contentDescription = "tab indicator",
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(34.dp)
|
.noRippleClickable {
|
||||||
.height(4.dp)
|
scope.launch {
|
||||||
)
|
pagerState.animateScrollToPage(2)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.index_following),
|
||||||
|
fontSize = if (pagerState.currentPage == 2)18.sp else 16.sp,
|
||||||
|
color = if (pagerState.currentPage == 2) AppColors.text else Color(0X993c3c43),
|
||||||
|
fontWeight = FontWeight.W600)
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
|
Image(
|
||||||
|
painter = painterResource(
|
||||||
|
if (pagerState.currentPage == 2) R.mipmap.tab_indicator_selected
|
||||||
|
else R.drawable.tab_indicator_unselected
|
||||||
|
),
|
||||||
|
contentDescription = "tab indicator",
|
||||||
|
modifier = Modifier
|
||||||
|
.width(34.dp)
|
||||||
|
.height(4.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//热门tab
|
//热门tab
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
@@ -178,7 +185,8 @@ fun MomentsList() {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.noRippleClickable {
|
.noRippleClickable {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
pagerState.animateScrollToPage(3)
|
val targetPage = if (AppStore.isGuest) 2 else 3
|
||||||
|
pagerState.animateScrollToPage(targetPage)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
@@ -186,14 +194,14 @@ fun MomentsList() {
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.index_hot),
|
text = stringResource(R.string.index_hot),
|
||||||
fontSize = if (pagerState.currentPage == 3)18.sp else 16.sp,
|
fontSize = if ((AppStore.isGuest && pagerState.currentPage == 2) || (!AppStore.isGuest && pagerState.currentPage == 3)) 18.sp else 16.sp,
|
||||||
color = if (pagerState.currentPage == 3) AppColors.text else Color(0X993c3c43),
|
color = if ((AppStore.isGuest && pagerState.currentPage == 2) || (!AppStore.isGuest && pagerState.currentPage == 3)) AppColors.text else Color(0X993c3c43),
|
||||||
fontWeight = FontWeight.W600)
|
fontWeight = FontWeight.W600)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(
|
painter = painterResource(
|
||||||
if (pagerState.currentPage == 3) R.mipmap.tab_indicator_selected
|
if ((AppStore.isGuest && pagerState.currentPage == 2) || (!AppStore.isGuest && pagerState.currentPage == 3)) R.mipmap.tab_indicator_selected
|
||||||
else R.drawable.tab_indicator_unselected
|
else R.drawable.tab_indicator_unselected
|
||||||
),
|
),
|
||||||
contentDescription = "tab indicator",
|
contentDescription = "tab indicator",
|
||||||
@@ -228,22 +236,35 @@ fun MomentsList() {
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
) {
|
) {
|
||||||
when (it) {
|
if (AppStore.isGuest) {
|
||||||
0 -> {
|
// 游客模式:Explore(0), Dynamic(1), Hot(2)
|
||||||
Explore()
|
when (it) {
|
||||||
|
0 -> {
|
||||||
|
Explore()
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
Dynamic()
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
HotMomentsList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
1 -> {
|
} else {
|
||||||
Dynamic()
|
// 正常用户:Explore(0), Dynamic(1), Timeline(2), Hot(3)
|
||||||
|
when (it) {
|
||||||
|
0 -> {
|
||||||
|
Explore()
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
Dynamic()
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
TimelineMomentsList()
|
||||||
|
}
|
||||||
|
3 -> {
|
||||||
|
HotMomentsList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
2 -> {
|
|
||||||
TimelineMomentsList()
|
|
||||||
}
|
|
||||||
|
|
||||||
3 -> {
|
|
||||||
HotMomentsList()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||||
|
import com.aiosman.ravenow.LocalNavController
|
||||||
|
import com.aiosman.ravenow.ui.NavigationRoute
|
||||||
import com.aiosman.ravenow.ui.composables.MomentCard
|
import com.aiosman.ravenow.ui.composables.MomentCard
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -28,6 +32,7 @@ import kotlinx.coroutines.launch
|
|||||||
fun Dynamic() {
|
fun Dynamic() {
|
||||||
val model = DynamicViewModel
|
val model = DynamicViewModel
|
||||||
var moments = model.moments
|
var moments = model.moments
|
||||||
|
val navController = LocalNavController.current
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
|
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
|
||||||
@@ -73,30 +78,50 @@ fun Dynamic() {
|
|||||||
val momentItem = moments[idx] ?: return@items
|
val momentItem = moments[idx] ?: return@items
|
||||||
MomentCard(momentEntity = momentItem,
|
MomentCard(momentEntity = momentItem,
|
||||||
onAddComment = {
|
onAddComment = {
|
||||||
scope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
model.onAddComment(momentItem.id)
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.COMMENT_MOMENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
scope.launch {
|
||||||
|
model.onAddComment(momentItem.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLikeClick = {
|
onLikeClick = {
|
||||||
scope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
if (momentItem.liked) {
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||||
model.dislikeMoment(momentItem.id)
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
} else {
|
} else {
|
||||||
model.likeMoment(momentItem.id)
|
scope.launch {
|
||||||
|
if (momentItem.liked) {
|
||||||
|
model.dislikeMoment(momentItem.id)
|
||||||
|
} else {
|
||||||
|
model.likeMoment(momentItem.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFavoriteClick = {
|
onFavoriteClick = {
|
||||||
scope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
if (momentItem.isFavorite) {
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||||
model.unfavoriteMoment(momentItem.id)
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
} else {
|
} else {
|
||||||
model.favoriteMoment(momentItem.id)
|
scope.launch {
|
||||||
|
if (momentItem.isFavorite) {
|
||||||
|
model.unfavoriteMoment(momentItem.id)
|
||||||
|
} else {
|
||||||
|
model.favoriteMoment(momentItem.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFollowClick = {
|
onFollowClick = {
|
||||||
model.followAction(momentItem)
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.FOLLOW_USER)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
model.followAction(momentItem)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
showFollowButton = true
|
showFollowButton = true
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.aiosman.ravenow.AppStore
|
import com.aiosman.ravenow.AppStore
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.R
|
import com.aiosman.ravenow.R
|
||||||
@@ -236,8 +238,13 @@ fun Explore() {
|
|||||||
shape = RoundedCornerShape(8.dp)
|
shape = RoundedCornerShape(8.dp)
|
||||||
)
|
)
|
||||||
.clickable {
|
.clickable {
|
||||||
viewModel.createSingleChat(agentItem.openId)
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
viewModel.goToChatAi(agentItem.openId, navController = navController)
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CHAT_WITH_AGENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
viewModel.createSingleChat(agentItem.openId)
|
||||||
|
viewModel.goToChatAi(agentItem.openId, navController = navController)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
@@ -353,19 +360,24 @@ fun Explore() {
|
|||||||
},
|
},
|
||||||
shape = RoundedCornerShape(12.dp))
|
shape = RoundedCornerShape(12.dp))
|
||||||
.clickable {
|
.clickable {
|
||||||
// 调用加入房间接口
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
viewModel.joinRoom(
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.JOIN_GROUP_CHAT)) {
|
||||||
trtcId = roomItem.trtcId.toString(),
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
name = roomItem.title,
|
} else {
|
||||||
avatar = roomItem.avatar,
|
// 调用加入房间接口
|
||||||
navController = navController,
|
viewModel.joinRoom(
|
||||||
onSuccess = {
|
trtcId = roomItem.trtcId.toString(),
|
||||||
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
name = roomItem.title,
|
||||||
},
|
avatar = roomItem.avatar,
|
||||||
onError = { errorMessage ->
|
navController = navController,
|
||||||
Toast.makeText(context, enterFailText, Toast.LENGTH_SHORT).show()
|
onSuccess = {
|
||||||
}
|
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
||||||
)
|
},
|
||||||
|
onError = { errorMessage ->
|
||||||
|
Toast.makeText(context, enterFailText, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
@@ -615,19 +627,24 @@ fun Explore() {
|
|||||||
shape = RoundedCornerShape(8.dp)
|
shape = RoundedCornerShape(8.dp)
|
||||||
)
|
)
|
||||||
.clickable {
|
.clickable {
|
||||||
// 调用加入房间接口
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
viewModel.joinRoom(
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.JOIN_GROUP_CHAT)) {
|
||||||
trtcId = bannerItem.trtcId.toString(),
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
name = bannerItem.title,
|
} else {
|
||||||
avatar = bannerItem.avatar,
|
// 调用加入房间接口
|
||||||
navController = navController,
|
viewModel.joinRoom(
|
||||||
onSuccess = {
|
trtcId = bannerItem.trtcId.toString(),
|
||||||
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
name = bannerItem.title,
|
||||||
},
|
avatar = bannerItem.avatar,
|
||||||
onError = { errorMessage ->
|
navController = navController,
|
||||||
Toast.makeText(context, enterFailText, Toast.LENGTH_SHORT).show()
|
onSuccess = {
|
||||||
}
|
Toast.makeText(context, enterSuccessText, Toast.LENGTH_SHORT).show()
|
||||||
)
|
},
|
||||||
|
onError = { errorMessage ->
|
||||||
|
Toast.makeText(context, enterFailText, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
@@ -671,7 +688,12 @@ fun Explore() {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
navController.navigate(NavigationRoute.CreateGroupChat.route)
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.JOIN_GROUP_CHAT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
navController.navigate(NavigationRoute.CreateGroupChat.route)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
@@ -706,8 +728,12 @@ fun Explore() {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
navController.navigate(
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
NavigationRoute.AddAgent.route)
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CREATE_AGENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
navController.navigate(NavigationRoute.AddAgent.route)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
@@ -737,8 +763,13 @@ fun Explore() {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
NewPostViewModel.asNewPost()
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
navController.navigate("NewPost")
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CREATE_POST)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
NewPostViewModel.asNewPost()
|
||||||
|
navController.navigate("NewPost")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ object MyProfileViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadUserProfile() {
|
suspend fun loadUserProfile() {
|
||||||
|
// 游客模式下不获取用户资料
|
||||||
|
if (AppStore.isGuest) {
|
||||||
|
MyProfileViewModel.profile = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val profile = accountService.getMyAccountProfile()
|
val profile = accountService.getMyAccountProfile()
|
||||||
MyProfileViewModel.profile = profile
|
MyProfileViewModel.profile = profile
|
||||||
}
|
}
|
||||||
@@ -71,6 +77,12 @@ object MyProfileViewModel : ViewModel() {
|
|||||||
firstLoad = false
|
firstLoad = false
|
||||||
loadUserProfile()
|
loadUserProfile()
|
||||||
refreshing = false
|
refreshing = false
|
||||||
|
|
||||||
|
// 游客模式下不加载个人动态和智能体
|
||||||
|
if (AppStore.isGuest) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
profile?.let {
|
profile?.let {
|
||||||
try {
|
try {
|
||||||
momentLoader.loadData(extra = MomentLoaderExtraArgs(authorId = it.id))
|
momentLoader.loadData(extra = MomentLoaderExtraArgs(authorId = it.id))
|
||||||
@@ -85,6 +97,12 @@ object MyProfileViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun loadMoreMoment() {
|
fun loadMoreMoment() {
|
||||||
|
// 游客模式下不加载更多动态
|
||||||
|
if (AppStore.isGuest) {
|
||||||
|
Log.d("MyProfileViewModel", "loadMoreMoment: 游客模式下跳过加载更多动态")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
profile?.let { profileData ->
|
profile?.let { profileData ->
|
||||||
try {
|
try {
|
||||||
@@ -100,19 +118,30 @@ object MyProfileViewModel : ViewModel() {
|
|||||||
|
|
||||||
fun logout(context: Context) {
|
fun logout(context: Context) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
Messaging.unregisterDevice(context)
|
// 只有非游客用户才需要取消注册推送设备
|
||||||
|
if (!AppStore.isGuest) {
|
||||||
|
Messaging.unregisterDevice(context)
|
||||||
|
}
|
||||||
|
|
||||||
AppStore.apply {
|
AppStore.apply {
|
||||||
token = null
|
token = null
|
||||||
rememberMe = false
|
rememberMe = false
|
||||||
|
isGuest = false // 清除游客状态
|
||||||
saveData()
|
saveData()
|
||||||
}
|
}
|
||||||
// 删除推送渠道
|
// 删除推送渠道和重置应用状态
|
||||||
AppState.ReloadAppState(context)
|
AppState.ReloadAppState(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateUserProfileBanner(bannerImageUrl: Uri?, file: File, context: Context) {
|
fun updateUserProfileBanner(bannerImageUrl: Uri?, file: File, context: Context) {
|
||||||
|
// 游客模式下不允许更新用户资料
|
||||||
|
if (AppStore.isGuest) {
|
||||||
|
Log.d("MyProfileViewModel", "updateUserProfileBanner: 游客模式下无法更新用户资料")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val newBanner = bannerImageUrl?.let {
|
val newBanner = bannerImageUrl?.let {
|
||||||
val cursor = context.contentResolver.query(it, null, null, null, null)
|
val cursor = context.contentResolver.query(it, null, null, null, null)
|
||||||
@@ -141,6 +170,12 @@ object MyProfileViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun likeMoment(momentLMomentEntity: MomentEntity) {
|
fun likeMoment(momentLMomentEntity: MomentEntity) {
|
||||||
|
// 游客模式下不允许点赞
|
||||||
|
if (AppStore.isGuest) {
|
||||||
|
Log.d("MyProfileViewModel", "likeMoment: 游客模式下无法点赞")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (momentLMomentEntity.liked) {
|
if (momentLMomentEntity.liked) {
|
||||||
momentService.dislikeMoment(momentLMomentEntity.id)
|
momentService.dislikeMoment(momentLMomentEntity.id)
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.aiosman.ravenow.AppState
|
import com.aiosman.ravenow.AppState
|
||||||
import com.aiosman.ravenow.ConstVars
|
import com.aiosman.ravenow.ConstVars
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.MainActivity
|
import com.aiosman.ravenow.MainActivity
|
||||||
|
|||||||
@@ -29,8 +29,12 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.aiosman.ravenow.AppState
|
import com.aiosman.ravenow.AppState
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||||
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.ui.NavigationRoute
|
||||||
import com.aiosman.ravenow.entity.AccountProfileEntity
|
import com.aiosman.ravenow.entity.AccountProfileEntity
|
||||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||||
|
|
||||||
@@ -41,6 +45,7 @@ fun OtherProfileAction(
|
|||||||
onChat: (() -> Unit)? = null
|
onChat: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
|
val navController = LocalNavController.current
|
||||||
|
|
||||||
// 定义渐变色
|
// 定义渐变色
|
||||||
val followGradient = Brush.horizontalGradient(
|
val followGradient = Brush.horizontalGradient(
|
||||||
@@ -84,7 +89,12 @@ fun OtherProfileAction(
|
|||||||
}
|
}
|
||||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||||
.noRippleClickable {
|
.noRippleClickable {
|
||||||
onFollow?.invoke()
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.FOLLOW_USER)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
onFollow?.invoke()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
@@ -112,7 +122,12 @@ fun OtherProfileAction(
|
|||||||
.background(AppColors.nonActive) // 使用主题灰色背景
|
.background(AppColors.nonActive) // 使用主题灰色背景
|
||||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||||
.noRippleClickable {
|
.noRippleClickable {
|
||||||
onChat?.invoke()
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CHAT_WITH_AGENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
onChat?.invoke()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
@@ -35,8 +35,12 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
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.GuestLoginCheckOut
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||||
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.ui.NavigationRoute
|
||||||
import com.aiosman.ravenow.entity.AgentEntity
|
import com.aiosman.ravenow.entity.AgentEntity
|
||||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||||
import com.aiosman.ravenow.utils.DebounceUtils
|
import com.aiosman.ravenow.utils.DebounceUtils
|
||||||
@@ -82,6 +86,7 @@ fun UserAgentCard(
|
|||||||
onAvatarClick: (AgentEntity) -> Unit = {}
|
onAvatarClick: (AgentEntity) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
|
val navController = LocalNavController.current
|
||||||
|
|
||||||
// 防抖状态
|
// 防抖状态
|
||||||
var lastClickTime by remember { mutableStateOf(0L) }
|
var lastClickTime by remember { mutableStateOf(0L) }
|
||||||
@@ -165,7 +170,12 @@ fun UserAgentCard(
|
|||||||
)
|
)
|
||||||
.clickable {
|
.clickable {
|
||||||
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
|
||||||
onAgentClick(agent)
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CHAT_WITH_AGENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
onAgentClick(agent)
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
lastClickTime = System.currentTimeMillis()
|
lastClickTime = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ fun EmailSignupScreen() {
|
|||||||
AppStore.apply {
|
AppStore.apply {
|
||||||
token = authResp.token
|
token = authResp.token
|
||||||
this.rememberMe = rememberMe
|
this.rememberMe = rememberMe
|
||||||
|
isGuest = false // 清除游客状态
|
||||||
saveData()
|
saveData()
|
||||||
}
|
}
|
||||||
// 获取token 信息
|
// 获取token 信息
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ fun LoginPage() {
|
|||||||
AppStore.apply {
|
AppStore.apply {
|
||||||
token = authResp.token
|
token = authResp.token
|
||||||
this.rememberMe = true
|
this.rememberMe = true
|
||||||
|
isGuest = false // 清除游客状态
|
||||||
saveData()
|
saveData()
|
||||||
}
|
}
|
||||||
// 获取token 信息
|
// 获取token 信息
|
||||||
@@ -180,6 +181,76 @@ fun LoginPage() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun guestLogin() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
// 生成设备ID
|
||||||
|
val deviceId = android.provider.Settings.Secure.getString(
|
||||||
|
context.contentResolver,
|
||||||
|
android.provider.Settings.Secure.ANDROID_ID
|
||||||
|
) ?: "unknown_device"
|
||||||
|
|
||||||
|
// 获取设备信息
|
||||||
|
val deviceInfo = "${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL}"
|
||||||
|
|
||||||
|
// 调用游客登录API
|
||||||
|
val authResp = accountService.guestLogin(deviceId, deviceInfo)
|
||||||
|
|
||||||
|
// 保存token和游客状态
|
||||||
|
AppStore.apply {
|
||||||
|
token = authResp.token
|
||||||
|
isGuest = true
|
||||||
|
rememberMe = true
|
||||||
|
saveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示成功提示
|
||||||
|
coroutineScope.launch(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
"游客登录成功",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化应用状态(游客模式会自动跳过推送和TRTC初始化)
|
||||||
|
try {
|
||||||
|
AppState.initWithAccount(coroutineScope, context)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to init with guest account", e)
|
||||||
|
// 游客模式下初始化失败不是致命错误,可以继续
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导航到主页
|
||||||
|
coroutineScope.launch(Dispatchers.Main) {
|
||||||
|
navController.navigate(NavigationRoute.Index.route) {
|
||||||
|
popUpTo(NavigationRoute.Login.route) { inclusive = true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: ServiceException) {
|
||||||
|
coroutineScope.launch(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
"游客登录失败: ${e.message}",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
Log.e(TAG, "Guest login failed", e)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
coroutineScope.launch(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
"游客登录失败",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
Log.e(TAG, "Guest login failed", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -260,6 +331,16 @@ fun LoginPage() {
|
|||||||
NavigationRoute.UserAuth.route,
|
NavigationRoute.UserAuth.route,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 游客登录按钮
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
ActionButton(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
text = "游客模式",
|
||||||
|
color = AppColors.text.copy(alpha = 0.7f),
|
||||||
|
) {
|
||||||
|
guestLogin()
|
||||||
|
}
|
||||||
Spacer(modifier = Modifier.height(70.dp))
|
Spacer(modifier = Modifier.height(70.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ fun SignupScreen() {
|
|||||||
AppStore.apply {
|
AppStore.apply {
|
||||||
token = authResp.token
|
token = authResp.token
|
||||||
this.rememberMe = true
|
this.rememberMe = true
|
||||||
|
isGuest = false // 清除游客状态
|
||||||
saveData()
|
saveData()
|
||||||
}
|
}
|
||||||
// 获取token 信息
|
// 获取token 信息
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ fun UserAuthScreen() {
|
|||||||
AppStore.apply {
|
AppStore.apply {
|
||||||
token = authResp.token
|
token = authResp.token
|
||||||
this.rememberMe = rememberMe
|
this.rememberMe = rememberMe
|
||||||
|
isGuest = false // 清除游客状态
|
||||||
saveData()
|
saveData()
|
||||||
}
|
}
|
||||||
AppState.initWithAccount(scope, context)
|
AppState.initWithAccount(scope, context)
|
||||||
@@ -163,6 +164,7 @@ fun UserAuthScreen() {
|
|||||||
AppStore.apply {
|
AppStore.apply {
|
||||||
token = authResp.token
|
token = authResp.token
|
||||||
this.rememberMe = rememberMe
|
this.rememberMe = rememberMe
|
||||||
|
isGuest = false // 清除游客状态
|
||||||
saveData()
|
saveData()
|
||||||
}
|
}
|
||||||
navController.navigate(NavigationRoute.Index.route) {
|
navController.navigate(NavigationRoute.Index.route) {
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ import androidx.paging.LoadState
|
|||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import com.aiosman.ravenow.AppState
|
import com.aiosman.ravenow.AppState
|
||||||
import com.aiosman.ravenow.ConstVars
|
import com.aiosman.ravenow.ConstVars
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||||
|
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||||
import com.aiosman.ravenow.LocalAppTheme
|
import com.aiosman.ravenow.LocalAppTheme
|
||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.R
|
import com.aiosman.ravenow.R
|
||||||
@@ -194,27 +196,45 @@ fun PostScreen(
|
|||||||
},
|
},
|
||||||
isSelf = AppState.UserId?.toLong() == contextComment?.author,
|
isSelf = AppState.UserId?.toLong() == contextComment?.author,
|
||||||
onLikeClick = {
|
onLikeClick = {
|
||||||
scope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
commentModalState.hide()
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||||
showCommentMenu = false
|
scope.launch {
|
||||||
}
|
commentModalState.hide()
|
||||||
contextComment?.let {
|
showCommentMenu = false
|
||||||
viewModel.viewModelScope.launch {
|
}
|
||||||
if (it.liked) {
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
viewModel.unlikeComment(it.id)
|
} else {
|
||||||
} else {
|
scope.launch {
|
||||||
viewModel.likeComment(it.id)
|
commentModalState.hide()
|
||||||
|
showCommentMenu = false
|
||||||
|
}
|
||||||
|
contextComment?.let {
|
||||||
|
viewModel.viewModelScope.launch {
|
||||||
|
if (it.liked) {
|
||||||
|
viewModel.unlikeComment(it.id)
|
||||||
|
} else {
|
||||||
|
viewModel.likeComment(it.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
onReplyClick = {
|
onReplyClick = {
|
||||||
scope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
commentModalState.hide()
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.COMMENT_MOMENT)) {
|
||||||
showCommentMenu = false
|
scope.launch {
|
||||||
replyComment = contextComment
|
commentModalState.hide()
|
||||||
showCommentModal = true
|
showCommentMenu = false
|
||||||
|
}
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
scope.launch {
|
||||||
|
commentModalState.hide()
|
||||||
|
showCommentMenu = false
|
||||||
|
replyComment = contextComment
|
||||||
|
showCommentModal = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -293,24 +313,39 @@ fun PostScreen(
|
|||||||
if (!viewModel.isError) {
|
if (!viewModel.isError) {
|
||||||
PostBottomBar(
|
PostBottomBar(
|
||||||
onLikeClick = {
|
onLikeClick = {
|
||||||
scope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
if (viewModel.moment?.liked == true) {
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||||
viewModel.dislikeMoment()
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
} else {
|
} else {
|
||||||
viewModel.likeMoment()
|
scope.launch {
|
||||||
|
if (viewModel.moment?.liked == true) {
|
||||||
|
viewModel.dislikeMoment()
|
||||||
|
} else {
|
||||||
|
viewModel.likeMoment()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCreateCommentClick = {
|
onCreateCommentClick = {
|
||||||
replyComment = null
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
showCommentModal = true
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.COMMENT_MOMENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
replyComment = null
|
||||||
|
showCommentModal = true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onFavoriteClick = {
|
onFavoriteClick = {
|
||||||
scope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
if (viewModel.moment?.isFavorite == true) {
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||||
viewModel.unfavoriteMoment()
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
} else {
|
} else {
|
||||||
viewModel.favoriteMoment()
|
scope.launch {
|
||||||
|
if (viewModel.moment?.isFavorite == true) {
|
||||||
|
viewModel.unfavoriteMoment()
|
||||||
|
} else {
|
||||||
|
viewModel.favoriteMoment()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -356,11 +391,16 @@ fun PostScreen(
|
|||||||
userId = viewModel.moment?.authorId,
|
userId = viewModel.moment?.authorId,
|
||||||
isFollowing = viewModel.moment?.followStatus == true,
|
isFollowing = viewModel.moment?.followStatus == true,
|
||||||
onFollowClick = {
|
onFollowClick = {
|
||||||
scope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
if (viewModel.moment?.followStatus == true) {
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.FOLLOW_USER)) {
|
||||||
viewModel.unfollowUser()
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
} else {
|
} else {
|
||||||
viewModel.followUser()
|
scope.launch {
|
||||||
|
if (viewModel.moment?.followStatus == true) {
|
||||||
|
viewModel.unfollowUser()
|
||||||
|
} else {
|
||||||
|
viewModel.followUser()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -370,7 +410,12 @@ fun PostScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onReportClick = {
|
onReportClick = {
|
||||||
showReportDialog = true
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.REPORT_CONTENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
showReportDialog = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@@ -430,8 +475,13 @@ fun PostScreen(
|
|||||||
contextComment = comment
|
contextComment = comment
|
||||||
},
|
},
|
||||||
onReply = { parentComment, _, _, _ ->
|
onReply = { parentComment, _, _, _ ->
|
||||||
replyComment = parentComment
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
showCommentModal = true
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.COMMENT_MOMENT)) {
|
||||||
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
|
} else {
|
||||||
|
replyComment = parentComment
|
||||||
|
showCommentModal = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -454,6 +504,7 @@ fun CommentContent(
|
|||||||
onReply: (CommentEntity, Long?, String?, String?) -> Unit
|
onReply: (CommentEntity, Long?, String?, String?) -> Unit
|
||||||
) {
|
) {
|
||||||
val AppColors = LocalAppTheme.current
|
val AppColors = LocalAppTheme.current
|
||||||
|
val navController = LocalNavController.current
|
||||||
|
|
||||||
val commentsPagging = viewModel.commentsFlow.collectAsLazyPagingItems()
|
val commentsPagging = viewModel.commentsFlow.collectAsLazyPagingItems()
|
||||||
val addedTopLevelComment = viewModel.addedCommentList.filter {
|
val addedTopLevelComment = viewModel.addedCommentList.filter {
|
||||||
@@ -468,11 +519,16 @@ fun CommentContent(
|
|||||||
CommentItem(
|
CommentItem(
|
||||||
it,
|
it,
|
||||||
onLike = { comment ->
|
onLike = { comment ->
|
||||||
viewModel.viewModelScope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
if (comment.liked) {
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||||
viewModel.unlikeComment(comment.id)
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
} else {
|
} else {
|
||||||
viewModel.likeComment(comment.id)
|
viewModel.viewModelScope.launch {
|
||||||
|
if (comment.liked) {
|
||||||
|
viewModel.unlikeComment(comment.id)
|
||||||
|
} else {
|
||||||
|
viewModel.likeComment(comment.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -480,12 +536,17 @@ fun CommentContent(
|
|||||||
onLongClick(comment)
|
onLongClick(comment)
|
||||||
},
|
},
|
||||||
onReply = { parentComment, _, _, _ ->
|
onReply = { parentComment, _, _, _ ->
|
||||||
onReply(
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
parentComment,
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.COMMENT_MOMENT)) {
|
||||||
parentComment.author,
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
parentComment.name,
|
} else {
|
||||||
parentComment.avatar
|
onReply(
|
||||||
)
|
parentComment,
|
||||||
|
parentComment.author,
|
||||||
|
parentComment.name,
|
||||||
|
parentComment.avatar
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onLoadMoreSubComments = {
|
onLoadMoreSubComments = {
|
||||||
viewModel.viewModelScope.launch {
|
viewModel.viewModelScope.launch {
|
||||||
@@ -512,11 +573,16 @@ fun CommentContent(
|
|||||||
CommentItem(
|
CommentItem(
|
||||||
item,
|
item,
|
||||||
onLike = { comment ->
|
onLike = { comment ->
|
||||||
viewModel.viewModelScope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
if (comment.liked) {
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||||
viewModel.unlikeComment(comment.id)
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
} else {
|
} else {
|
||||||
viewModel.likeComment(comment.id)
|
viewModel.viewModelScope.launch {
|
||||||
|
if (comment.liked) {
|
||||||
|
viewModel.unlikeComment(comment.id)
|
||||||
|
} else {
|
||||||
|
viewModel.likeComment(comment.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -560,11 +626,16 @@ fun CommentContent(
|
|||||||
CommentItem(
|
CommentItem(
|
||||||
item,
|
item,
|
||||||
onLike = { comment ->
|
onLike = { comment ->
|
||||||
viewModel.viewModelScope.launch {
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
if (comment.liked) {
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.LIKE_MOMENT)) {
|
||||||
viewModel.unlikeComment(comment.id)
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
} else {
|
} else {
|
||||||
viewModel.likeComment(comment.id)
|
viewModel.viewModelScope.launch {
|
||||||
|
if (comment.liked) {
|
||||||
|
viewModel.unlikeComment(comment.id)
|
||||||
|
} else {
|
||||||
|
viewModel.likeComment(comment.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -572,12 +643,17 @@ fun CommentContent(
|
|||||||
onLongClick(comment)
|
onLongClick(comment)
|
||||||
},
|
},
|
||||||
onReply = { parentComment, _, _, _ ->
|
onReply = { parentComment, _, _, _ ->
|
||||||
onReply(
|
// 检查游客模式,如果是游客则跳转登录
|
||||||
parentComment,
|
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.COMMENT_MOMENT)) {
|
||||||
parentComment.author,
|
navController.navigate(NavigationRoute.Login.route)
|
||||||
parentComment.name,
|
} else {
|
||||||
parentComment.avatar
|
onReply(
|
||||||
)
|
parentComment,
|
||||||
|
parentComment.author,
|
||||||
|
parentComment.name,
|
||||||
|
parentComment.avatar
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onLoadMoreSubComments = {
|
onLoadMoreSubComments = {
|
||||||
viewModel.viewModelScope.launch {
|
viewModel.viewModelScope.launch {
|
||||||
|
|||||||
Reference in New Issue
Block a user