更新
This commit is contained in:
@@ -1,10 +1,13 @@
|
|||||||
package com.aiosman.riderpro.data
|
package com.aiosman.riderpro.data
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
import com.aiosman.riderpro.AppStore
|
import com.aiosman.riderpro.AppStore
|
||||||
import com.aiosman.riderpro.data.api.ApiClient
|
import com.aiosman.riderpro.data.api.ApiClient
|
||||||
import com.aiosman.riderpro.data.api.ChangePasswordRequestBody
|
import com.aiosman.riderpro.data.api.ChangePasswordRequestBody
|
||||||
import com.aiosman.riderpro.data.api.LoginUserRequestBody
|
import com.aiosman.riderpro.data.api.LoginUserRequestBody
|
||||||
import com.aiosman.riderpro.data.api.RegisterRequestBody
|
import com.aiosman.riderpro.data.api.RegisterRequestBody
|
||||||
|
import com.aiosman.riderpro.model.MomentEntity
|
||||||
import com.aiosman.riderpro.test.TestDatabase
|
import com.aiosman.riderpro.test.TestDatabase
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
@@ -12,6 +15,7 @@ import okhttp3.RequestBody
|
|||||||
import okhttp3.RequestBody.Companion.asRequestBody
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
data class AccountProfileEntity(
|
data class AccountProfileEntity(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.aiosman.riderpro.AppStore
|
|||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.data.api.ApiClient
|
import com.aiosman.riderpro.data.api.ApiClient
|
||||||
import com.aiosman.riderpro.model.MomentEntity
|
import com.aiosman.riderpro.model.MomentEntity
|
||||||
|
import com.aiosman.riderpro.model.MomentImageEntity
|
||||||
import com.aiosman.riderpro.test.TestDatabase
|
import com.aiosman.riderpro.test.TestDatabase
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
@@ -57,7 +58,13 @@ data class Moment(
|
|||||||
commentCount = commentCount.toInt(),
|
commentCount = commentCount.toInt(),
|
||||||
shareCount = 0,
|
shareCount = 0,
|
||||||
favoriteCount = favoriteCount.toInt(),
|
favoriteCount = favoriteCount.toInt(),
|
||||||
images = images.map { ApiClient.BASE_SERVER + it.url + "?token=${AppStore.token}" },
|
images = images.map {
|
||||||
|
MomentImageEntity(
|
||||||
|
url = ApiClient.BASE_SERVER + it.url + "?token=${AppStore.token}",
|
||||||
|
thumbnail = ApiClient.BASE_SERVER + it.thumbnail + "?token=${AppStore.token}",
|
||||||
|
id = it.id
|
||||||
|
)
|
||||||
|
},
|
||||||
authorId = user.id.toInt(),
|
authorId = user.id.toInt(),
|
||||||
liked = isLiked,
|
liked = isLiked,
|
||||||
isFavorite = isFavorite
|
isFavorite = isFavorite
|
||||||
@@ -82,12 +89,14 @@ data class User(
|
|||||||
@SerializedName("avatar")
|
@SerializedName("avatar")
|
||||||
val avatar: String
|
val avatar: String
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UploadImage(
|
data class UploadImage(
|
||||||
val file: File,
|
val file: File,
|
||||||
val filename: String,
|
val filename: String,
|
||||||
val url: String,
|
val url: String,
|
||||||
val ext: String
|
val ext: String
|
||||||
)
|
)
|
||||||
|
|
||||||
interface MomentService {
|
interface MomentService {
|
||||||
suspend fun getMomentById(id: Int): MomentEntity
|
suspend fun getMomentById(id: Int): MomentEntity
|
||||||
suspend fun likeMoment(id: Int)
|
suspend fun likeMoment(id: Int)
|
||||||
@@ -95,7 +104,8 @@ interface MomentService {
|
|||||||
suspend fun getMoments(
|
suspend fun getMoments(
|
||||||
pageNumber: Int,
|
pageNumber: Int,
|
||||||
author: Int? = null,
|
author: Int? = null,
|
||||||
timelineId: Int? = null
|
timelineId: Int? = null,
|
||||||
|
contentSearch: String? = null
|
||||||
): ListContainer<MomentEntity>
|
): ListContainer<MomentEntity>
|
||||||
|
|
||||||
suspend fun createMoment(
|
suspend fun createMoment(
|
||||||
@@ -104,6 +114,7 @@ interface MomentService {
|
|||||||
images: List<UploadImage>,
|
images: List<UploadImage>,
|
||||||
relPostId: Int? = null
|
relPostId: Int? = null
|
||||||
): MomentEntity
|
): MomentEntity
|
||||||
|
|
||||||
suspend fun favoriteMoment(id: Int)
|
suspend fun favoriteMoment(id: Int)
|
||||||
suspend fun unfavoriteMoment(id: Int)
|
suspend fun unfavoriteMoment(id: Int)
|
||||||
}
|
}
|
||||||
@@ -112,7 +123,8 @@ interface MomentService {
|
|||||||
class MomentPagingSource(
|
class MomentPagingSource(
|
||||||
private val remoteDataSource: MomentRemoteDataSource,
|
private val remoteDataSource: MomentRemoteDataSource,
|
||||||
private val author: Int? = null,
|
private val author: Int? = null,
|
||||||
private val timelineId: Int? = null
|
private val timelineId: Int? = null,
|
||||||
|
private val contentSearch: String? = null
|
||||||
) : PagingSource<Int, MomentEntity>() {
|
) : PagingSource<Int, MomentEntity>() {
|
||||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentEntity> {
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentEntity> {
|
||||||
return try {
|
return try {
|
||||||
@@ -120,7 +132,8 @@ class MomentPagingSource(
|
|||||||
val moments = remoteDataSource.getMoments(
|
val moments = remoteDataSource.getMoments(
|
||||||
pageNumber = currentPage,
|
pageNumber = currentPage,
|
||||||
author = author,
|
author = author,
|
||||||
timelineId = timelineId
|
timelineId = timelineId,
|
||||||
|
contentSearch = contentSearch
|
||||||
)
|
)
|
||||||
|
|
||||||
LoadResult.Page(
|
LoadResult.Page(
|
||||||
@@ -145,9 +158,10 @@ class MomentRemoteDataSource(
|
|||||||
suspend fun getMoments(
|
suspend fun getMoments(
|
||||||
pageNumber: Int,
|
pageNumber: Int,
|
||||||
author: Int?,
|
author: Int?,
|
||||||
timelineId: Int?
|
timelineId: Int?,
|
||||||
|
contentSearch: String?
|
||||||
): ListContainer<MomentEntity> {
|
): ListContainer<MomentEntity> {
|
||||||
return momentService.getMoments(pageNumber, author, timelineId)
|
return momentService.getMoments(pageNumber, author, timelineId, contentSearch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,9 +172,10 @@ class TestMomentServiceImpl() : MomentService {
|
|||||||
override suspend fun getMoments(
|
override suspend fun getMoments(
|
||||||
pageNumber: Int,
|
pageNumber: Int,
|
||||||
author: Int?,
|
author: Int?,
|
||||||
timelineId: Int?
|
timelineId: Int?,
|
||||||
|
contentSearch: String?
|
||||||
): ListContainer<MomentEntity> {
|
): ListContainer<MomentEntity> {
|
||||||
return testMomentBackend.fetchMomentItems(pageNumber, author, timelineId)
|
return testMomentBackend.fetchMomentItems(pageNumber, author, timelineId, contentSearch)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getMomentById(id: Int): MomentEntity {
|
override suspend fun getMomentById(id: Int): MomentEntity {
|
||||||
@@ -202,13 +217,15 @@ class TestMomentBackend(
|
|||||||
suspend fun fetchMomentItems(
|
suspend fun fetchMomentItems(
|
||||||
pageNumber: Int,
|
pageNumber: Int,
|
||||||
author: Int? = null,
|
author: Int? = null,
|
||||||
timelineId: Int?
|
timelineId: Int?,
|
||||||
|
contentSearch: String?
|
||||||
): ListContainer<MomentEntity> {
|
): ListContainer<MomentEntity> {
|
||||||
val resp = ApiClient.api.getPosts(
|
val resp = ApiClient.api.getPosts(
|
||||||
pageSize = DataBatchSize,
|
pageSize = DataBatchSize,
|
||||||
page = pageNumber,
|
page = pageNumber,
|
||||||
timelineId = timelineId,
|
timelineId = timelineId,
|
||||||
authorId = author
|
authorId = author,
|
||||||
|
contentSearch = contentSearch
|
||||||
)
|
)
|
||||||
val body = resp.body() ?: throw ServiceException("Failed to get moments")
|
val body = resp.body() ?: throw ServiceException("Failed to get moments")
|
||||||
return ListContainer(
|
return ListContainer(
|
||||||
@@ -258,6 +275,7 @@ class TestMomentBackend(
|
|||||||
suspend fun favoriteMoment(id: Int) {
|
suspend fun favoriteMoment(id: Int) {
|
||||||
ApiClient.api.favoritePost(id)
|
ApiClient.api.favoritePost(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun unfavoriteMoment(id: Int) {
|
suspend fun unfavoriteMoment(id: Int) {
|
||||||
ApiClient.api.unfavoritePost(id)
|
ApiClient.api.unfavoritePost(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,53 @@
|
|||||||
package com.aiosman.riderpro.data
|
package com.aiosman.riderpro.data
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
import com.aiosman.riderpro.data.api.ApiClient
|
import com.aiosman.riderpro.data.api.ApiClient
|
||||||
|
import com.aiosman.riderpro.model.MomentEntity
|
||||||
import com.aiosman.riderpro.test.TestDatabase
|
import com.aiosman.riderpro.test.TestDatabase
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
data class UserAuth(
|
data class UserAuth(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val token: String? = null
|
val token: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class AccountPagingSource(
|
||||||
|
private val userService: UserService,
|
||||||
|
private val nickname: String? = null
|
||||||
|
) : PagingSource<Int, AccountProfileEntity>() {
|
||||||
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountProfileEntity> {
|
||||||
|
return try {
|
||||||
|
val currentPage = params.key ?: 1
|
||||||
|
val users = userService.getUsers(
|
||||||
|
page = currentPage,
|
||||||
|
nickname = nickname
|
||||||
|
)
|
||||||
|
LoadResult.Page(
|
||||||
|
data = users.list,
|
||||||
|
prevKey = if (currentPage == 1) null else currentPage - 1,
|
||||||
|
nextKey = if (users.list.isEmpty()) null else users.page + 1
|
||||||
|
)
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
return LoadResult.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRefreshKey(state: PagingState<Int, AccountProfileEntity>): Int? {
|
||||||
|
return state.anchorPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
interface UserService {
|
interface UserService {
|
||||||
suspend fun getUserProfile(id: String): AccountProfileEntity
|
suspend fun getUserProfile(id: String): AccountProfileEntity
|
||||||
suspend fun followUser(id: String)
|
suspend fun followUser(id: String)
|
||||||
suspend fun unFollowUser(id: String)
|
suspend fun unFollowUser(id: String)
|
||||||
|
suspend fun getUsers(
|
||||||
|
pageSize: Int = 20,
|
||||||
|
page: Int = 1,
|
||||||
|
nickname: String? = null
|
||||||
|
): ListContainer<AccountProfileEntity>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,4 +67,19 @@ class TestUserServiceImpl : UserService {
|
|||||||
val resp = ApiClient.api.unfollowUser(id.toInt())
|
val resp = ApiClient.api.unfollowUser(id.toInt())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getUsers(
|
||||||
|
pageSize: Int,
|
||||||
|
page: Int,
|
||||||
|
nickname: String?
|
||||||
|
): ListContainer<AccountProfileEntity> {
|
||||||
|
val resp = ApiClient.api.getUsers(page, pageSize, nickname)
|
||||||
|
val body = resp.body() ?: throw ServiceException("Failed to get account")
|
||||||
|
return ListContainer<AccountProfileEntity>(
|
||||||
|
list = body.list.map { it.toAccountProfileEntity() },
|
||||||
|
page = body.page,
|
||||||
|
total = body.total,
|
||||||
|
pageSize = body.pageSize
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,7 @@ interface RiderProAPI {
|
|||||||
@Query("pageSize") pageSize: Int = 20,
|
@Query("pageSize") pageSize: Int = 20,
|
||||||
@Query("timelineId") timelineId: Int? = null,
|
@Query("timelineId") timelineId: Int? = null,
|
||||||
@Query("authorId") authorId: Int? = null,
|
@Query("authorId") authorId: Int? = null,
|
||||||
|
@Query("contentSearch") contentSearch: String? = null,
|
||||||
): Response<ListContainer<Moment>>
|
): Response<ListContainer<Moment>>
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@@ -163,4 +164,11 @@ interface RiderProAPI {
|
|||||||
@Path("id") id: Int
|
@Path("id") id: Int
|
||||||
): Response<Unit>
|
): Response<Unit>
|
||||||
|
|
||||||
|
@GET("users")
|
||||||
|
suspend fun getUsers(
|
||||||
|
@Query("page") page: Int = 1,
|
||||||
|
@Query("pageSize") pageSize: Int = 20,
|
||||||
|
@Query("nickname") search: String? = null,
|
||||||
|
): Response<ListContainer<AccountProfile>>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.aiosman.riderpro.model
|
package com.aiosman.riderpro.model
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
data class MomentImageEntity(
|
||||||
|
val id: Long,
|
||||||
|
val url: String,
|
||||||
|
val thumbnail: String
|
||||||
|
)
|
||||||
data class MomentEntity(
|
data class MomentEntity(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val avatar: String,
|
val avatar: String,
|
||||||
@@ -15,7 +19,7 @@ data class MomentEntity(
|
|||||||
val commentCount: Int,
|
val commentCount: Int,
|
||||||
val shareCount: Int,
|
val shareCount: Int,
|
||||||
val favoriteCount: Int,
|
val favoriteCount: Int,
|
||||||
val images: List<String> = emptyList(),
|
val images: List<MomentImageEntity> = emptyList(),
|
||||||
val authorId: Int = 0,
|
val authorId: Int = 0,
|
||||||
var liked: Boolean = false,
|
var liked: Boolean = false,
|
||||||
var relPostId: Int? = null,
|
var relPostId: Int? = null,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.aiosman.riderpro.R
|
|||||||
import com.aiosman.riderpro.data.AccountProfileEntity
|
import com.aiosman.riderpro.data.AccountProfileEntity
|
||||||
import com.aiosman.riderpro.data.CommentEntity
|
import com.aiosman.riderpro.data.CommentEntity
|
||||||
import com.aiosman.riderpro.model.MomentEntity
|
import com.aiosman.riderpro.model.MomentEntity
|
||||||
|
import com.aiosman.riderpro.model.MomentImageEntity
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import io.github.serpro69.kfaker.faker
|
import io.github.serpro69.kfaker.faker
|
||||||
@@ -124,7 +125,13 @@ object TestDatabase {
|
|||||||
commentCount = commentCount + 1,
|
commentCount = commentCount + 1,
|
||||||
shareCount = faker.random.nextInt(0, 100),
|
shareCount = faker.random.nextInt(0, 100),
|
||||||
favoriteCount = faker.random.nextInt(0, 100),
|
favoriteCount = faker.random.nextInt(0, 100),
|
||||||
images = imageList.shuffled().take(3),
|
images = imageList.shuffled().take(3).map {
|
||||||
|
MomentImageEntity(
|
||||||
|
id = faker.random.nextLong(),
|
||||||
|
url = it,
|
||||||
|
thumbnail = it
|
||||||
|
)
|
||||||
|
},
|
||||||
authorId = person.id
|
authorId = person.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ fun RelPostCard(
|
|||||||
image?.let {
|
image?.let {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
context,
|
context,
|
||||||
image,
|
image.thumbnail,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(100.dp),
|
modifier = Modifier.size(100.dp),
|
||||||
contentScale = ContentScale.Crop
|
contentScale = ContentScale.Crop
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.aiosman.riderpro.ui.imageviewer
|
package com.aiosman.riderpro.ui.imageviewer
|
||||||
|
|
||||||
|
import com.aiosman.riderpro.model.MomentImageEntity
|
||||||
|
|
||||||
object ImageViewerViewModel {
|
object ImageViewerViewModel {
|
||||||
var imageList = mutableListOf<String>()
|
var imageList = mutableListOf<MomentImageEntity>()
|
||||||
var initialIndex = 0
|
var initialIndex = 0
|
||||||
fun asNew(images: List<String>, index: Int = 0) {
|
fun asNew(images: List<MomentImageEntity>, index: Int = 0) {
|
||||||
imageList.clear()
|
imageList.clear()
|
||||||
imageList.addAll(images)
|
imageList.addAll(images)
|
||||||
initialIndex = index
|
initialIndex = index
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ fun ImageViewer() {
|
|||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
context,
|
context,
|
||||||
images[page],
|
images[page].url,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.sharedElement(
|
modifier = Modifier.sharedElement(
|
||||||
rememberSharedContentState(key = images[page]),
|
rememberSharedContentState(key = images[page]),
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import com.aiosman.riderpro.ui.index.tabs.add.AddPage
|
import com.aiosman.riderpro.ui.index.tabs.add.AddPage
|
||||||
import com.aiosman.riderpro.ui.index.tabs.moment.MomentsList
|
import com.aiosman.riderpro.ui.index.tabs.moment.MomentsList
|
||||||
import com.aiosman.riderpro.ui.index.tabs.profile.ProfilePage
|
import com.aiosman.riderpro.ui.index.tabs.profile.ProfilePage
|
||||||
|
import com.aiosman.riderpro.ui.index.tabs.search.SearchScreen
|
||||||
import com.aiosman.riderpro.ui.index.tabs.shorts.ShortVideo
|
import com.aiosman.riderpro.ui.index.tabs.shorts.ShortVideo
|
||||||
import com.aiosman.riderpro.ui.index.tabs.street.StreetPage
|
import com.aiosman.riderpro.ui.index.tabs.street.StreetPage
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
@@ -40,9 +41,10 @@ fun IndexScreen() {
|
|||||||
}
|
}
|
||||||
val item = listOf(
|
val item = listOf(
|
||||||
NavigationItem.Home,
|
NavigationItem.Home,
|
||||||
NavigationItem.Street,
|
NavigationItem.Search,
|
||||||
|
// NavigationItem.Street,
|
||||||
NavigationItem.Add,
|
NavigationItem.Add,
|
||||||
NavigationItem.Message,
|
// NavigationItem.Message,
|
||||||
NavigationItem.Profile
|
NavigationItem.Profile
|
||||||
)
|
)
|
||||||
val systemUiController = rememberSystemUiController()
|
val systemUiController = rememberSystemUiController()
|
||||||
@@ -99,17 +101,22 @@ fun IndexScreen() {
|
|||||||
) {
|
) {
|
||||||
Home()
|
Home()
|
||||||
}
|
}
|
||||||
|
1 -> Box(
|
||||||
|
modifier = Modifier.padding(innerPadding)
|
||||||
|
) {
|
||||||
|
SearchScreen()
|
||||||
|
}
|
||||||
|
|
||||||
1 -> Street()
|
// 1 -> Street()
|
||||||
2 -> Box(
|
2 -> Box(
|
||||||
modifier = Modifier.padding(innerPadding)
|
modifier = Modifier.padding(innerPadding)
|
||||||
) { Add() }
|
) { Add() }
|
||||||
|
|
||||||
3 -> Box(
|
// 3 -> Box(
|
||||||
modifier = Modifier.padding(innerPadding)
|
// modifier = Modifier.padding(innerPadding)
|
||||||
) { Video() }
|
// ) { Video() }
|
||||||
|
|
||||||
4 -> Box(
|
3 -> Box(
|
||||||
modifier = Modifier.padding(innerPadding)
|
modifier = Modifier.padding(innerPadding)
|
||||||
) { Profile() }
|
) { Profile() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.aiosman.riderpro.ui.index
|
package com.aiosman.riderpro.ui.index
|
||||||
|
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
@@ -34,4 +37,9 @@ sealed class NavigationItem(
|
|||||||
icon = { ImageVector.vectorResource(R.drawable.rider_pro_profile) },
|
icon = { ImageVector.vectorResource(R.drawable.rider_pro_profile) },
|
||||||
selectedIcon = { ImageVector.vectorResource(R.drawable.rider_pro_profile_filed) }
|
selectedIcon = { ImageVector.vectorResource(R.drawable.rider_pro_profile_filed) }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data object Search : NavigationItem("Search",
|
||||||
|
icon = { Icons.Default.Search },
|
||||||
|
selectedIcon = { Icons.Default.Search }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ fun AddPage(){
|
|||||||
NewPostViewModel.asNewPost()
|
NewPostViewModel.asNewPost()
|
||||||
navController.navigate("NewPost")
|
navController.navigate("NewPost")
|
||||||
}
|
}
|
||||||
AddBtn(icon = R.drawable.rider_pro_location_create, text = "Location Create")
|
// AddBtn(icon = R.drawable.rider_pro_location_create, text = "Location Create")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,9 +131,10 @@ fun MomentsList() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun MomentCard(
|
fun MomentCard(
|
||||||
momentEntity: MomentEntity,
|
momentEntity: MomentEntity,
|
||||||
onLikeClick: () -> Unit,
|
onLikeClick: () -> Unit = {},
|
||||||
onFavoriteClick: () -> Unit = {},
|
onFavoriteClick: () -> Unit = {},
|
||||||
onAddComment: () -> Unit = {}
|
onAddComment: () -> Unit = {},
|
||||||
|
hideAction: Boolean = false
|
||||||
) {
|
) {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
Column(
|
Column(
|
||||||
@@ -153,18 +154,21 @@ fun MomentCard(
|
|||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
// ModificationListHeader()
|
// ModificationListHeader()
|
||||||
MomentBottomOperateRowGroup(
|
if (!hideAction){
|
||||||
momentOperateBtnBoxModifier,
|
MomentBottomOperateRowGroup(
|
||||||
momentEntity = momentEntity,
|
momentOperateBtnBoxModifier,
|
||||||
onLikeClick = onLikeClick,
|
momentEntity = momentEntity,
|
||||||
onAddComment = onAddComment,
|
onLikeClick = onLikeClick,
|
||||||
onShareClick = {
|
onAddComment = onAddComment,
|
||||||
NewPostViewModel.asNewPost()
|
onShareClick = {
|
||||||
NewPostViewModel.relPostId = momentEntity.id
|
NewPostViewModel.asNewPost()
|
||||||
navController.navigate(NavigationRoute.NewPost.route)
|
NewPostViewModel.relPostId = momentEntity.id
|
||||||
},
|
navController.navigate(NavigationRoute.NewPost.route)
|
||||||
onFavoriteClick = onFavoriteClick
|
},
|
||||||
)
|
onFavoriteClick = onFavoriteClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +346,7 @@ fun MomentContentGroup(
|
|||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
context,
|
context,
|
||||||
it,
|
it.thumbnail,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.sharedElement(
|
.sharedElement(
|
||||||
rememberSharedContentState(key = it),
|
rememberSharedContentState(key = it),
|
||||||
|
|||||||
@@ -396,26 +396,26 @@ fun CommunicationOperatorGroup(
|
|||||||
style = TextStyle(fontWeight = FontWeight.Bold)
|
style = TextStyle(fontWeight = FontWeight.Bold)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Box(
|
// Box(
|
||||||
modifier = Modifier
|
// modifier = Modifier
|
||||||
.size(width = 142.dp, height = 40.dp)
|
// .size(width = 142.dp, height = 40.dp)
|
||||||
.clickable {
|
// .clickable {
|
||||||
navController.navigate("ProfileTimeline")
|
// navController.navigate("ProfileTimeline")
|
||||||
},
|
// },
|
||||||
contentAlignment = Alignment.Center
|
// contentAlignment = Alignment.Center
|
||||||
) {
|
// ) {
|
||||||
Image(
|
// Image(
|
||||||
modifier = Modifier.fillMaxSize(),
|
// modifier = Modifier.fillMaxSize(),
|
||||||
painter = painterResource(id = R.drawable.rider_pro_profile_follow),
|
// painter = painterResource(id = R.drawable.rider_pro_profile_follow),
|
||||||
contentDescription = ""
|
// contentDescription = ""
|
||||||
)
|
// )
|
||||||
Text(
|
// Text(
|
||||||
text = "GALLERY",
|
// text = "GALLERY",
|
||||||
fontSize = 16.sp,
|
// fontSize = 16.sp,
|
||||||
color = Color.White,
|
// color = Color.White,
|
||||||
style = TextStyle(fontWeight = FontWeight.Bold)
|
// style = TextStyle(fontWeight = FontWeight.Bold)
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -504,7 +504,7 @@ fun MomentPostUnit(momentEntity: MomentEntity) {
|
|||||||
TimeGroup(momentEntity.time)
|
TimeGroup(momentEntity.time)
|
||||||
ProfileMomentCard(
|
ProfileMomentCard(
|
||||||
momentEntity.momentTextContent,
|
momentEntity.momentTextContent,
|
||||||
momentEntity.images[0],
|
momentEntity.images[0].thumbnail,
|
||||||
momentEntity.likeCount.toString(),
|
momentEntity.likeCount.toString(),
|
||||||
momentEntity.commentCount.toString(),
|
momentEntity.commentCount.toString(),
|
||||||
momentId = momentEntity.id
|
momentId = momentEntity.id
|
||||||
|
|||||||
@@ -0,0 +1,250 @@
|
|||||||
|
package com.aiosman.riderpro.ui.index.tabs.search
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
|
import androidx.compose.foundation.pager.PagerState
|
||||||
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.Tab
|
||||||
|
import androidx.compose.material.TabRow
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import com.aiosman.riderpro.LocalNavController
|
||||||
|
import com.aiosman.riderpro.data.AccountProfileEntity
|
||||||
|
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||||
|
import com.aiosman.riderpro.ui.index.tabs.moment.MomentCard
|
||||||
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun SearchScreen() {
|
||||||
|
val model = SearchViewModel
|
||||||
|
val categories = listOf("Moment", "User")
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val pagerState = rememberPagerState(pageCount = { categories.size })
|
||||||
|
val selectedTabIndex = remember { derivedStateOf { pagerState.currentPage } }
|
||||||
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
val systemUiController = rememberSystemUiController()
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(Color.White)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(start = 16.dp, end = 16.dp, top = 24.dp)
|
||||||
|
) {
|
||||||
|
SearchInput(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
text = model.searchText,
|
||||||
|
onTextChange = {
|
||||||
|
model.searchText = it
|
||||||
|
},
|
||||||
|
onSearch = {
|
||||||
|
model.search()
|
||||||
|
// hide ime
|
||||||
|
keyboardController?.hide() // Hide the keyboard
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (model.showResult) {
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = selectedTabIndex.value,
|
||||||
|
backgroundColor = Color.White,
|
||||||
|
) {
|
||||||
|
categories.forEachIndexed { index, category ->
|
||||||
|
Tab(
|
||||||
|
selected = selectedTabIndex.value == index,
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
pagerState.animateScrollToPage(index)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text = { Text(category) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchPager(
|
||||||
|
pagerState = pagerState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SearchInput(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
text: String = "",
|
||||||
|
onTextChange: (String) -> Unit = {},
|
||||||
|
onSearch: () -> Unit = {}
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.clip(shape = RoundedCornerShape(8.dp))
|
||||||
|
|
||||||
|
.background(Color(0xFFEEEEEE))
|
||||||
|
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Search,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Box {
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = "Search",
|
||||||
|
modifier = Modifier.padding(start = 8.dp),
|
||||||
|
color = Color(0xFF9E9E9E),
|
||||||
|
fontSize = 18.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BasicTextField(
|
||||||
|
value = text,
|
||||||
|
onValueChange = {
|
||||||
|
onTextChange(it)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 8.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
singleLine = true,
|
||||||
|
textStyle = TextStyle(
|
||||||
|
fontSize = 18.sp
|
||||||
|
),
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
imeAction = ImeAction.Search
|
||||||
|
),
|
||||||
|
keyboardActions = KeyboardActions(
|
||||||
|
onSearch = {
|
||||||
|
onSearch()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun SearchPager(
|
||||||
|
pagerState: PagerState,
|
||||||
|
) {
|
||||||
|
HorizontalPager(
|
||||||
|
state = pagerState,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
|
||||||
|
) { page ->
|
||||||
|
when (page) {
|
||||||
|
0 -> MomentResultTab()
|
||||||
|
1 -> UserResultTab()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MomentResultTab() {
|
||||||
|
val model = SearchViewModel
|
||||||
|
var dataFlow = model.momentsFlow
|
||||||
|
var moments = dataFlow.collectAsLazyPagingItems()
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
items(moments.itemCount) { idx ->
|
||||||
|
val momentItem = moments[idx] ?: return@items
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
MomentCard(momentEntity = momentItem, hideAction = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UserResultTab() {
|
||||||
|
val model = SearchViewModel
|
||||||
|
val users = model.usersFlow.collectAsLazyPagingItems()
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
items(users.itemCount) { idx ->
|
||||||
|
val userItem = users[idx] ?: return@items
|
||||||
|
UserItem(userItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UserItem(accountProfile: AccountProfileEntity) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val navController = LocalNavController.current
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(16.dp).noRippleClickable {
|
||||||
|
navController.navigate("AccountProfile/${accountProfile.id}")
|
||||||
|
},
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
CustomAsyncImage(
|
||||||
|
context,
|
||||||
|
imageUrl = accountProfile.avatar,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(64.dp)
|
||||||
|
.clip(CircleShape),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(16.dp))
|
||||||
|
Text(text = accountProfile.nickName, fontSize = 18.sp, fontWeight = FontWeight.Bold)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.aiosman.riderpro.ui.index.tabs.search
|
||||||
|
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.cachedIn
|
||||||
|
import com.aiosman.riderpro.data.AccountPagingSource
|
||||||
|
import com.aiosman.riderpro.data.AccountProfileEntity
|
||||||
|
import com.aiosman.riderpro.data.MomentPagingSource
|
||||||
|
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
||||||
|
import com.aiosman.riderpro.data.MomentService
|
||||||
|
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||||
|
import com.aiosman.riderpro.data.TestUserServiceImpl
|
||||||
|
import com.aiosman.riderpro.model.MomentEntity
|
||||||
|
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
|
||||||
|
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel.accountService
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
object SearchViewModel : ViewModel() {
|
||||||
|
var searchText by mutableStateOf("")
|
||||||
|
private val momentService: MomentService = TestMomentServiceImpl()
|
||||||
|
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||||
|
val momentsFlow = _momentsFlow.asStateFlow()
|
||||||
|
|
||||||
|
private val userService = TestUserServiceImpl()
|
||||||
|
private val _usersFlow = MutableStateFlow<PagingData<AccountProfileEntity>>(PagingData.empty())
|
||||||
|
val usersFlow = _usersFlow.asStateFlow()
|
||||||
|
var showResult by mutableStateOf(false)
|
||||||
|
fun search() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
Pager(
|
||||||
|
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||||
|
pagingSourceFactory = {
|
||||||
|
MomentPagingSource(
|
||||||
|
MomentRemoteDataSource(momentService),
|
||||||
|
contentSearch = searchText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
).flow.cachedIn(viewModelScope).collectLatest {
|
||||||
|
_momentsFlow.value = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewModelScope.launch {
|
||||||
|
Pager(
|
||||||
|
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||||
|
pagingSourceFactory = {
|
||||||
|
AccountPagingSource(
|
||||||
|
userService,
|
||||||
|
nickname = searchText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
).flow.cachedIn(viewModelScope).collectLatest {
|
||||||
|
_usersFlow.value = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showResult = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,6 +88,7 @@ import com.aiosman.riderpro.data.TestMomentServiceImpl
|
|||||||
import com.aiosman.riderpro.data.TestUserServiceImpl
|
import com.aiosman.riderpro.data.TestUserServiceImpl
|
||||||
import com.aiosman.riderpro.data.UserService
|
import com.aiosman.riderpro.data.UserService
|
||||||
import com.aiosman.riderpro.model.MomentEntity
|
import com.aiosman.riderpro.model.MomentEntity
|
||||||
|
import com.aiosman.riderpro.model.MomentImageEntity
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||||
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
||||||
@@ -426,7 +427,7 @@ fun Header(
|
|||||||
@Composable
|
@Composable
|
||||||
fun PostImageView(
|
fun PostImageView(
|
||||||
postId: String,
|
postId: String,
|
||||||
images: List<String>,
|
images: List<MomentImageEntity>,
|
||||||
) {
|
) {
|
||||||
val pagerState = rememberPagerState(pageCount = { images.size })
|
val pagerState = rememberPagerState(pageCount = { images.size })
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
@@ -445,7 +446,7 @@ fun PostImageView(
|
|||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
context,
|
context,
|
||||||
image,
|
image.thumbnail,
|
||||||
contentDescription = "Image",
|
contentDescription = "Image",
|
||||||
contentScale = ContentScale.Fit,
|
contentScale = ContentScale.Fit,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ object Utils {
|
|||||||
.okHttpClient(okHttpClient)
|
.okHttpClient(okHttpClient)
|
||||||
.diskCachePolicy(CachePolicy.ENABLED)
|
.diskCachePolicy(CachePolicy.ENABLED)
|
||||||
.memoryCachePolicy(CachePolicy.ENABLED)
|
.memoryCachePolicy(CachePolicy.ENABLED)
|
||||||
|
|
||||||
.components {
|
.components {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user