mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-03 11:47:31 +01:00
feat(posts): improve post view
This commit is contained in:
parent
51a3d2b054
commit
4b80c78e30
@ -12,4 +12,8 @@ val coreApiModule = module {
|
||||
val provider: ServiceProvider = get()
|
||||
provider.postService
|
||||
}
|
||||
single {
|
||||
val provider: ServiceProvider = get()
|
||||
provider.communityService
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BlockCommunityForm(
|
||||
@SerialName("community_id") val communityId: CommunityId,
|
||||
@SerialName("block") val block: Boolean,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BlockCommunityResponse(
|
||||
@SerialName("community_view") val communityView: CommunityView,
|
||||
@SerialName("blocked") val blocked: Boolean,
|
||||
)
|
@ -0,0 +1,21 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Comment(
|
||||
@SerialName("id") val id: CommentId,
|
||||
@SerialName("creator_id") val creatorId: PersonId,
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("content") val content: String,
|
||||
@SerialName("removed") val removed: Boolean,
|
||||
@SerialName("published") val published: String,
|
||||
@SerialName("updated") val updated: String? = null,
|
||||
@SerialName("deleted") val deleted: Boolean,
|
||||
@SerialName("ap_id") val apId: String,
|
||||
@SerialName("local") val local: Boolean,
|
||||
@SerialName("path") val path: String,
|
||||
@SerialName("distinguished") val distinguished: Boolean,
|
||||
@SerialName("language_id") val languageId: LanguageId,
|
||||
)
|
@ -0,0 +1,16 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CommentAggregates(
|
||||
@SerialName("id") val id: Int,
|
||||
@SerialName("comment_id") val commentId: CommentId,
|
||||
@SerialName("score") val score: Int,
|
||||
@SerialName("upvotes") val upvotes: Int,
|
||||
@SerialName("downvotes") val downvotes: Int,
|
||||
@SerialName("published") val published: String,
|
||||
@SerialName("child_count") val childCount: Int,
|
||||
@SerialName("hot_rank") val hotRank: Int,
|
||||
)
|
@ -0,0 +1,18 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CommentView(
|
||||
@SerialName("comment") val comment: Comment,
|
||||
@SerialName("creator") val creator: Person,
|
||||
@SerialName("post") val post: Post,
|
||||
@SerialName("community") val community: Community,
|
||||
@SerialName("counts") val counts: CommentAggregates,
|
||||
@SerialName("creator_banned_from_community") val creatorBannedFromCommunity: Boolean,
|
||||
@SerialName("subscribed") val subscribed: SubscribedType,
|
||||
@SerialName("saved") val saved: Boolean,
|
||||
@SerialName("creator_blocked") val creatorBlocked: Boolean,
|
||||
@SerialName("my_vote") val myVote: Int? = null,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CommunityResponse(
|
||||
@SerialName("community_view") val communityView: CommunityView,
|
||||
@SerialName("discussion_languages") val discussionLanguages: List<LanguageId>,
|
||||
)
|
@ -6,3 +6,4 @@ typealias CommunityId = Int
|
||||
typealias LanguageId = Int
|
||||
typealias InstanceId = Int
|
||||
typealias CommentId = Int
|
||||
typealias SiteId = Int
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CreatePostForm(
|
||||
@SerialName("name") val name: String,
|
||||
@SerialName("community_id") val communityId: CommunityId,
|
||||
@SerialName("url") val url: String? = null,
|
||||
@SerialName("body") val body: String? = null,
|
||||
@SerialName("honeypot") val honeypot: String? = null,
|
||||
@SerialName("nsfw") val nsfw: Boolean? = null,
|
||||
@SerialName("language_id") val languageId: LanguageId? = null,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class DeletePostForm(
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("deleted") val deleted: Boolean,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,15 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EditPostForm(
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("name") val name: String? = null,
|
||||
@SerialName("url") val url: String? = null,
|
||||
@SerialName("body") val body: String? = null,
|
||||
@SerialName("nsfw") val nsfw: Boolean? = null,
|
||||
@SerialName("language_id") val languageId: LanguageId? = null,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class FollowCommunityForm(
|
||||
@SerialName("community_id") val communityId: CommunityId,
|
||||
@SerialName("follow") val follow: Boolean,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,13 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GetCommunityResponse(
|
||||
@SerialName("community_view") val communityView: CommunityView,
|
||||
@SerialName("site") val site: Site? = null,
|
||||
@SerialName("moderators") val moderators: List<CommunityModeratorView>,
|
||||
@SerialName("discussion_languages") val discussionLanguages: List<LanguageId>,
|
||||
)
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GetPersonDetailsResponse(
|
||||
@SerialName("person_view") val personView: PersonView,
|
||||
@SerialName("comments") val comments: List<CommentView>,
|
||||
@SerialName("posts") val posts: List<PostView>,
|
||||
@SerialName("moderates") val moderates: List<CommunityModeratorView>,
|
||||
)
|
@ -0,0 +1,14 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PersonAggregates(
|
||||
@SerialName("id") val id: Int,
|
||||
@SerialName("person_id") val personId: PersonId,
|
||||
@SerialName("post_count") val postCount: Int,
|
||||
@SerialName("post_score") val postScore: Int,
|
||||
@SerialName("comment_count") val commentCount: Int,
|
||||
@SerialName("comment_score") val commentScore: Int,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PersonView(
|
||||
@SerialName("person") val person: Person,
|
||||
@SerialName("counts") val counts: PersonAggregates,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PostResponse(
|
||||
@SerialName("post_view")
|
||||
val postView: PostView,
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SavePostForm(
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("save") val save: Boolean,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,22 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Site(
|
||||
@SerialName("id") val id: SiteId,
|
||||
@SerialName("name") val name: String,
|
||||
@SerialName("sidebar") val sidebar: String? = null,
|
||||
@SerialName("published") val published: String,
|
||||
@SerialName("updated") val updated: String? = null,
|
||||
@SerialName("icon") val icon: String? = null,
|
||||
@SerialName("banner") val banner: String? = null,
|
||||
@SerialName("description") val description: String? = null,
|
||||
@SerialName("actor_id") val actorId: String,
|
||||
@SerialName("last_refreshed_at") val lastRefreshedAt: String,
|
||||
@SerialName("inbox_url") val inboxUrl: String,
|
||||
@SerialName("private_key") val privateKey: String? = null,
|
||||
@SerialName("public_key") val publicKey: String,
|
||||
@SerialName("instance_id") val instanceId: InstanceId,
|
||||
)
|
@ -1,5 +1,6 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.provider
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.service.CommunityService
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.service.PostService
|
||||
import de.jensklingenberg.ktorfit.Ktorfit
|
||||
import io.ktor.client.HttpClient
|
||||
@ -12,7 +13,7 @@ import kotlinx.serialization.json.Json
|
||||
internal class DefaultServiceProvider : ServiceProvider {
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_INSTANCE = "enterprise.lemmy.ml"
|
||||
private const val DEFAULT_INSTANCE = "lemmy.world"
|
||||
private const val VERSION = "v3"
|
||||
}
|
||||
|
||||
@ -22,6 +23,9 @@ internal class DefaultServiceProvider : ServiceProvider {
|
||||
override lateinit var postService: PostService
|
||||
private set
|
||||
|
||||
override lateinit var communityService: CommunityService
|
||||
private set
|
||||
|
||||
private val baseUrl: String get() = "https://$currentInstance/api/$VERSION/"
|
||||
private val client = HttpClient {
|
||||
install(Logging) {
|
||||
@ -49,5 +53,6 @@ internal class DefaultServiceProvider : ServiceProvider {
|
||||
.httpClient(client)
|
||||
.build()
|
||||
postService = ktorfit.create()
|
||||
communityService = ktorfit.create()
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.provider
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.service.CommunityService
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.service.PostService
|
||||
|
||||
interface ServiceProvider {
|
||||
|
||||
val currentInstance: String
|
||||
val postService: PostService
|
||||
val communityService: CommunityService
|
||||
|
||||
fun changeInstance(value: String)
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.service
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.BlockCommunityForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.BlockCommunityResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.CommunityResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.FollowCommunityForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.GetCommunityResponse
|
||||
import de.jensklingenberg.ktorfit.Response
|
||||
import de.jensklingenberg.ktorfit.http.Body
|
||||
import de.jensklingenberg.ktorfit.http.GET
|
||||
import de.jensklingenberg.ktorfit.http.POST
|
||||
import de.jensklingenberg.ktorfit.http.Query
|
||||
|
||||
interface CommunityService {
|
||||
|
||||
@GET("community")
|
||||
suspend fun getCommunity(
|
||||
@Query("auth") auth: String? = null,
|
||||
@Query("id") id: Int? = null,
|
||||
@Query("name") name: String? = null,
|
||||
): Response<GetCommunityResponse>
|
||||
|
||||
@POST("community/follow")
|
||||
suspend fun followCommunity(@Body form: FollowCommunityForm): Response<CommunityResponse>
|
||||
|
||||
@POST("community/block")
|
||||
suspend fun blockCommunity(@Body form: BlockCommunityForm): Response<BlockCommunityResponse>
|
||||
}
|
||||
|
@ -1,11 +1,19 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.service
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.CreatePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.DeletePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.EditPostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.GetPostResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.GetPostsResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.PostResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.SavePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.SortType
|
||||
import de.jensklingenberg.ktorfit.Response
|
||||
import de.jensklingenberg.ktorfit.http.Body
|
||||
import de.jensklingenberg.ktorfit.http.GET
|
||||
import de.jensklingenberg.ktorfit.http.POST
|
||||
import de.jensklingenberg.ktorfit.http.PUT
|
||||
import de.jensklingenberg.ktorfit.http.Query
|
||||
|
||||
interface PostService {
|
||||
@ -28,4 +36,17 @@ interface PostService {
|
||||
@Query("id") id: Int? = null,
|
||||
@Query("comment_id") commentId: Int? = null,
|
||||
): Response<GetPostResponse>
|
||||
|
||||
@PUT("post/save")
|
||||
suspend fun savePost(@Body form: SavePostForm): Response<PostResponse>
|
||||
|
||||
@POST("post")
|
||||
suspend fun createPost(@Body form: CreatePostForm): Response<PostResponse>
|
||||
|
||||
@PUT("post")
|
||||
suspend fun editPost(@Body form: EditPostForm): Response<PostResponse>
|
||||
|
||||
@POST("post/delete")
|
||||
suspend fun deletePost(@Body form: DeletePostForm): Response<PostResponse>
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core_api.service
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.GetPersonDetailsResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.SortType
|
||||
import de.jensklingenberg.ktorfit.Response
|
||||
import de.jensklingenberg.ktorfit.http.GET
|
||||
import de.jensklingenberg.ktorfit.http.Query
|
||||
|
||||
interface UserService {
|
||||
|
||||
@GET("user")
|
||||
suspend fun getPersonDetails(
|
||||
@Query("auth") auth: String? = null,
|
||||
@Query("community_id") communityId: Int? = null,
|
||||
@Query("person_id") personId: Int? = null,
|
||||
@Query("page") page: Int? = null,
|
||||
@Query("limit") limit: Int? = null,
|
||||
@Query("sort") sort: SortType,
|
||||
@Query("username") username: String? = null,
|
||||
@Query("saved_only") savedOnly: Boolean? = null,
|
||||
): Response<GetPersonDetailsResponse>
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.data
|
||||
|
||||
data class CommunityModel(
|
||||
val id: Int = 0,
|
||||
val name: String = "",
|
||||
val instance: String = "",
|
||||
val icon: String? = null,
|
||||
)
|
@ -1,6 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.data
|
||||
|
||||
data class PostModel(
|
||||
val id: Int = 0,
|
||||
val title: String = "",
|
||||
val text: String = "",
|
||||
val score: Int = 0,
|
||||
val comments: Int = 0,
|
||||
val thumbnailUrl: String? = null,
|
||||
val community: CommunityModel? = null,
|
||||
)
|
@ -0,0 +1,5 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.data
|
||||
|
||||
data class UserModel(
|
||||
val name: String = "",
|
||||
)
|
@ -0,0 +1,20 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.domain_post.repository
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.CommunityView
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.service.CommunityService
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.CommunityModel
|
||||
|
||||
class CommunityRepository(
|
||||
private val communityService: CommunityService,
|
||||
) {
|
||||
|
||||
suspend fun getCommunity(id: Int): CommunityModel? {
|
||||
val response = communityService.getCommunity(id = id).body()
|
||||
return response?.communityView?.toModel()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CommunityView.toModel() = CommunityModel(
|
||||
name = community.name,
|
||||
icon = community.icon,
|
||||
)
|
@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.domain_post.repository
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.dto.PostView
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_api.service.PostService
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.CommunityModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.PostModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.SortType
|
||||
@ -34,8 +35,13 @@ class PostsRepository(
|
||||
}
|
||||
|
||||
private fun PostView.toModel() = PostModel(
|
||||
title = this.post.name,
|
||||
text = this.post.body.orEmpty(),
|
||||
id = post.id,
|
||||
title = post.name,
|
||||
text = post.body.orEmpty(),
|
||||
score = counts.score,
|
||||
comments = counts.comments,
|
||||
thumbnailUrl = post.thumbnailUrl.orEmpty(),
|
||||
community = CommunityModel(id = post.communityId)
|
||||
)
|
||||
|
||||
private fun ListingType.toDto() = when (this) {
|
||||
|
@ -4,6 +4,7 @@ import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.module
|
||||
|
||||
val postsRepositoryModule = module {
|
||||
singleOf(::PostsRepository)
|
||||
singleOf(::ApiConfigurationRepository)
|
||||
singleOf(::PostsRepository)
|
||||
singleOf(::CommunityRepository)
|
||||
}
|
@ -45,6 +45,7 @@ kotlin {
|
||||
|
||||
implementation(libs.voyager.navigator)
|
||||
implementation(libs.voyager.tab)
|
||||
implementation(libs.compose.imageloader)
|
||||
|
||||
implementation(projects.resources)
|
||||
implementation(projects.coreAppearance)
|
||||
|
@ -11,6 +11,7 @@ actual val homeTabModule = module {
|
||||
HomeScreenModel(
|
||||
mvi = DefaultMviModel(HomeScreenMviModel.UiState()),
|
||||
postsRepository = get(),
|
||||
communityRepository = get(),
|
||||
apiConfigRepository = get(),
|
||||
)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.SortType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain_post.repository.ApiConfigurationRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain_post.repository.CommunityRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain_post.repository.PostsRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
@ -14,6 +15,7 @@ import kotlinx.coroutines.launch
|
||||
class HomeScreenModel(
|
||||
private val mvi: DefaultMviModel<HomeScreenMviModel.Intent, HomeScreenMviModel.UiState, HomeScreenMviModel.Effect>,
|
||||
private val postsRepository: PostsRepository,
|
||||
private val communityRepository: CommunityRepository,
|
||||
private val apiConfigRepository: ApiConfigurationRepository,
|
||||
) : ScreenModel,
|
||||
MviModel<HomeScreenMviModel.Intent, HomeScreenMviModel.UiState, HomeScreenMviModel.Effect> by mvi {
|
||||
@ -56,12 +58,30 @@ class HomeScreenModel(
|
||||
page = currentPage,
|
||||
type = type,
|
||||
sort = sort,
|
||||
)
|
||||
).map {
|
||||
val community = it.community
|
||||
if (community?.id != null) {
|
||||
val remoteCommunity = communityRepository.getCommunity(community.id)
|
||||
it.copy(
|
||||
community = it.community?.copy(
|
||||
name = remoteCommunity?.name.orEmpty(),
|
||||
icon = remoteCommunity?.icon,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
currentPage++
|
||||
val canFetchMore = postList.size >= PostsRepository.DEFAULT_PAGE_SIZE
|
||||
mvi.updateState {
|
||||
val newPosts = if (refreshing) {
|
||||
postList
|
||||
} else {
|
||||
it.posts + postList
|
||||
}
|
||||
it.copy(
|
||||
posts = if (refreshing) postList else it.posts + postList,
|
||||
posts = newPosts,
|
||||
loading = false,
|
||||
canFetchMore = canFetchMore,
|
||||
refreshing = false,
|
||||
|
@ -1,49 +1,36 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.feature_home
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.SpaceDashboard
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.tab.Tab
|
||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
import com.github.diegoberaldin.racconforlemmy.core_utils.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_appearance.theme.CornerSize
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_architecture.bindToLifecycle
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_md.compose.Markdown
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature_home.modals.ListingTypeBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature_home.modals.SortBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
@ -81,7 +68,27 @@ object HomeTab : Tab {
|
||||
Scaffold(
|
||||
modifier = Modifier.padding(Spacing.xxs),
|
||||
topBar = {
|
||||
PostsTopBar(model, uiState)
|
||||
PostsTopBar(
|
||||
currentInstance = uiState.instance,
|
||||
listingType = uiState.listingType,
|
||||
sortType = uiState.sortType,
|
||||
onSelectListingType = {
|
||||
bottomSheetChannel.trySend @Composable {
|
||||
ListingTypeBottomSheet { type ->
|
||||
model.reduce(HomeScreenMviModel.Intent.ChangeListing(type))
|
||||
bottomSheetChannel.trySend(null)
|
||||
}
|
||||
}
|
||||
},
|
||||
onSelectSortType = {
|
||||
bottomSheetChannel.trySend @Composable {
|
||||
SortBottomSheet { type ->
|
||||
model.reduce(HomeScreenMviModel.Intent.ChangeSort(type))
|
||||
bottomSheetChannel.trySend(null)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
) {
|
||||
val pullRefreshState = rememberPullRefreshState(uiState.refreshing, {
|
||||
@ -95,25 +102,7 @@ object HomeTab : Tab {
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs)
|
||||
) {
|
||||
items(uiState.posts) { post ->
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
shape = RoundedCornerShape(CornerSize.m)
|
||||
).padding(Spacing.s)
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = post.title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
val body = post.text
|
||||
if (body.isNotEmpty()) {
|
||||
Markdown(content = body)
|
||||
}
|
||||
}
|
||||
}
|
||||
PostCard(post)
|
||||
}
|
||||
item {
|
||||
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
||||
@ -143,65 +132,4 @@ object HomeTab : Tab {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PostsTopBar(
|
||||
model: HomeScreenModel,
|
||||
uiState: HomeScreenMviModel.UiState,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.height(64.dp).padding(Spacing.s),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.onClick {
|
||||
bottomSheetChannel.trySend @Composable {
|
||||
ListingTypeBottomSheet { type ->
|
||||
model.reduce(HomeScreenMviModel.Intent.ChangeListing(type))
|
||||
bottomSheetChannel.trySend(null)
|
||||
}
|
||||
}
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.m),
|
||||
) {
|
||||
Image(
|
||||
imageVector = uiState.listingType.toIcon(),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxxs)
|
||||
) {
|
||||
Text(
|
||||
text = uiState.listingType.toReadableName(),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
MR.strings.home_instance_via,
|
||||
uiState.instance
|
||||
),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Image(
|
||||
modifier = Modifier.onClick {
|
||||
bottomSheetChannel.trySend @Composable {
|
||||
SortBottomSheet { type ->
|
||||
model.reduce(HomeScreenMviModel.Intent.ChangeSort(type))
|
||||
bottomSheetChannel.trySend(null)
|
||||
}
|
||||
}
|
||||
},
|
||||
imageVector = uiState.sortType.toIcon(),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,128 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.feature_home
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.filled.ArrowDropUp
|
||||
import androidx.compose.material.icons.filled.Chat
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_appearance.theme.CornerSize
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_md.compose.Markdown
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.PostModel
|
||||
import com.seiko.imageloader.rememberImagePainter
|
||||
|
||||
@Composable
|
||||
fun PostCard(
|
||||
post: PostModel,
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
shape = RoundedCornerShape(CornerSize.m)
|
||||
).padding(
|
||||
vertical = Spacing.lHalf,
|
||||
horizontal = Spacing.s,
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s)
|
||||
) {
|
||||
Text(
|
||||
text = post.title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
|
||||
val communityName = post.community?.name.orEmpty()
|
||||
val communityIcon = post.community?.icon.orEmpty()
|
||||
val iconSize = 21.dp
|
||||
if (communityName.isNotEmpty()) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
if (communityIcon.isNotEmpty()) {
|
||||
val painter = rememberImagePainter(communityIcon)
|
||||
Image(
|
||||
modifier = Modifier.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
painter = painter,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = communityName,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val imageUrl = post.thumbnailUrl
|
||||
if (!imageUrl.isNullOrEmpty()) {
|
||||
val painter = rememberImagePainter(imageUrl)
|
||||
Image(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
painter = painter,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
}
|
||||
val body = post.text
|
||||
if (body.isNotEmpty()) {
|
||||
Markdown(content = body)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
val buttonModifier = Modifier.size(42.dp)
|
||||
Image(
|
||||
modifier = buttonModifier,
|
||||
imageVector = Icons.Default.ArrowDropUp,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface)
|
||||
)
|
||||
Text(
|
||||
text = "${post.score}"
|
||||
)
|
||||
Image(
|
||||
modifier = buttonModifier,
|
||||
imageVector = Icons.Default.ArrowDropDown,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface)
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Image(
|
||||
modifier = buttonModifier.padding(10.dp),
|
||||
imageVector = Icons.Default.Chat,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface)
|
||||
)
|
||||
Text(
|
||||
text = "${post.comments}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.feature_home
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.diegoberaldin.racconforlemmy.core_utils.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core_appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.data.SortType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@Composable
|
||||
internal fun PostsTopBar(
|
||||
currentInstance: String,
|
||||
listingType: ListingType,
|
||||
sortType: SortType,
|
||||
onSelectListingType: () -> Unit,
|
||||
onSelectSortType: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.height(64.dp).padding(Spacing.s),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.onClick {
|
||||
onSelectListingType()
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.m),
|
||||
) {
|
||||
Image(
|
||||
imageVector = listingType.toIcon(),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxxs)
|
||||
) {
|
||||
Text(
|
||||
text = listingType.toReadableName(),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
MR.strings.home_instance_via,
|
||||
currentInstance,
|
||||
),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Image(
|
||||
modifier = Modifier.onClick {
|
||||
onSelectSortType()
|
||||
},
|
||||
imageVector = sortType.toIcon(),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
|
||||
)
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ kotlin {
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
implementation(compose.materialIconsExtended)
|
||||
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
|
||||
implementation(compose.components.resources)
|
||||
|
||||
|
@ -3,7 +3,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_search
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.Explore
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
@ -23,7 +23,7 @@ object SearchTab : Tab {
|
||||
@Composable
|
||||
get() {
|
||||
val title = stringResource(MR.strings.navigation_search)
|
||||
val icon = rememberVectorPainter(Icons.Default.Search)
|
||||
val icon = rememberVectorPainter(Icons.Default.Explore)
|
||||
|
||||
return remember {
|
||||
TabOptions(
|
||||
|
Loading…
x
Reference in New Issue
Block a user