WIP
This commit is contained in:
parent
f91fa2d27d
commit
633b366baf
@ -7,6 +7,7 @@ import com.isolaatti.auth.data.remote.AuthApi
|
||||
import com.isolaatti.auth.domain.AuthRepository
|
||||
import com.isolaatti.connectivity.RetrofitClient
|
||||
import com.isolaatti.database.AppDatabase
|
||||
import com.isolaatti.settings.domain.UserIdSetting
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
@ -21,7 +22,7 @@ class Module {
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideAuthRepository(tokenStorage: TokenStorage, authApi: AuthApi, keyValueDao: KeyValueDao): AuthRepository {
|
||||
return AuthRepositoryImpl(tokenStorage, authApi, keyValueDao)
|
||||
fun provideAuthRepository(tokenStorage: TokenStorage, authApi: AuthApi, userIdSetting: UserIdSetting): AuthRepository {
|
||||
return AuthRepositoryImpl(tokenStorage, authApi, userIdSetting)
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ import com.isolaatti.auth.data.local.TokenStorage
|
||||
import com.isolaatti.auth.data.remote.AuthApi
|
||||
import com.isolaatti.auth.data.remote.Credential
|
||||
import com.isolaatti.auth.domain.AuthRepository
|
||||
import com.isolaatti.settings.domain.UserIdSetting
|
||||
import com.isolaatti.utils.Resource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
@ -17,11 +18,8 @@ import javax.inject.Inject
|
||||
class AuthRepositoryImpl @Inject constructor(
|
||||
private val tokenStorage: TokenStorage,
|
||||
private val authApi: AuthApi,
|
||||
private val keyValueDao: KeyValueDao
|
||||
private val userIdSetting: UserIdSetting
|
||||
) : AuthRepository {
|
||||
companion object {
|
||||
val KEY_USERID = "user_id"
|
||||
}
|
||||
override fun authWithEmailAndPassword(
|
||||
email: String,
|
||||
password: String
|
||||
@ -37,7 +35,7 @@ class AuthRepositoryImpl @Inject constructor(
|
||||
return@flow
|
||||
}
|
||||
tokenStorage.storeToken(dto)
|
||||
keyValueDao.setValue(KeyValueEntity(KEY_USERID, dto.userId.toString()))
|
||||
userIdSetting.setUserId(dto.userId)
|
||||
emit(Resource.Success(true))
|
||||
return@flow
|
||||
}
|
||||
@ -66,7 +64,7 @@ class AuthRepositoryImpl @Inject constructor(
|
||||
}
|
||||
|
||||
override fun getUserId(): Flow<Int?> = flow {
|
||||
emit(keyValueDao.getValue(KEY_USERID).toIntOrNull())
|
||||
emit(userIdSetting.getUserId())
|
||||
}
|
||||
|
||||
}
|
||||
@ -25,6 +25,7 @@ import com.isolaatti.picture_viewer.ui.PictureViewerActivity
|
||||
import com.isolaatti.posting.PostViewerActivity
|
||||
import com.isolaatti.posting.comments.presentation.BottomSheetPostComments
|
||||
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
||||
import com.isolaatti.posting.common.domain.Ownable
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
|
||||
@ -160,7 +161,7 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
.usePlugin(PicassoImagesPluginDef.picassoImagePlugin)
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
adapter = PostsRecyclerViewAdapter(markwon, this, null)
|
||||
adapter = PostsRecyclerViewAdapter(markwon, this)
|
||||
viewBinding.feedRecyclerView.adapter = adapter
|
||||
viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
@ -227,8 +228,8 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||
|
||||
override fun onUnLiked(postId: Long) = viewModel.unLikePost(postId)
|
||||
|
||||
override fun onOptions(postId: Long) {
|
||||
optionsViewModel.setOptions(Options.myPostOptions, CALLER_ID, postId)
|
||||
override fun onOptions(post: Ownable) {
|
||||
optionsViewModel.setOptions(Options.POST_OPTIONS, CALLER_ID, post)
|
||||
val modalBottomSheet = BottomSheetPostOptionsFragment()
|
||||
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG)
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package com.isolaatti.home.presentation
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.auth.domain.AuthRepository
|
||||
import com.isolaatti.posting.posts.domain.PostsRepository
|
||||
@ -31,12 +30,16 @@ class FeedViewModel @Inject constructor(
|
||||
if (refresh) {
|
||||
posts.value = null
|
||||
}
|
||||
postsRepository.getFeed(getLastId()).onEach { feedDtoResource ->
|
||||
when (feedDtoResource) {
|
||||
postsRepository.getFeed(getLastId()).onEach { listResource ->
|
||||
when (listResource) {
|
||||
is Resource.Success -> {
|
||||
loadingPosts.postValue(false)
|
||||
posts.postValue(Pair(posts.value?.first?.concatFeed(feedDtoResource.data) ?: feedDtoResource.data, UpdateEvent(UpdateEvent.UpdateType.PAGE_ADDED, null)))
|
||||
noMoreContent.postValue(feedDtoResource.data?.moreContent == false)
|
||||
posts.postValue(Pair(postsList?.apply {
|
||||
addAll(listResource.data ?: listOf())
|
||||
} ?: listResource.data,
|
||||
UpdateEvent(UpdateEvent.UpdateType.PAGE_ADDED, null)))
|
||||
|
||||
noMoreContent.postValue(listResource.data?.size == 0)
|
||||
}
|
||||
|
||||
is Resource.Loading -> {
|
||||
@ -45,7 +48,7 @@ class FeedViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
is Resource.Error -> {
|
||||
errorLoading.postValue(feedDtoResource.errorType)
|
||||
//errorLoading.postValue(feedDtoResource.errorType)
|
||||
}
|
||||
}
|
||||
isLoadingFromScrolling = false
|
||||
|
||||
@ -3,8 +3,8 @@ package com.isolaatti.posting.comments.data.repository
|
||||
import com.isolaatti.posting.comments.data.remote.CommentDto
|
||||
import com.isolaatti.posting.comments.data.remote.CommentToPostDto
|
||||
import com.isolaatti.posting.comments.data.remote.CommentsApi
|
||||
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto
|
||||
import com.isolaatti.posting.comments.domain.CommentsRepository
|
||||
import com.isolaatti.posting.comments.domain.model.Comment
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import retrofit2.awaitResponse
|
||||
@ -12,10 +12,10 @@ import javax.inject.Inject
|
||||
|
||||
class CommentsRepositoryImpl @Inject constructor(private val commentsApi: CommentsApi) :
|
||||
CommentsRepository {
|
||||
override fun getComments(postId: Long, lastId: Long): Flow<FeedCommentsDto> = flow {
|
||||
override fun getComments(postId: Long, lastId: Long): Flow<MutableList<Comment>> = flow {
|
||||
val response = commentsApi.getCommentsOfPosts(postId, lastId, 15).awaitResponse()
|
||||
if(response.isSuccessful){
|
||||
response.body()?.let { emit(it) }
|
||||
response.body()?.let { emit(Comment.fromCommentsDto(it).toMutableList()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,11 +2,11 @@ package com.isolaatti.posting.comments.domain
|
||||
|
||||
import com.isolaatti.posting.comments.data.remote.CommentDto
|
||||
import com.isolaatti.posting.comments.data.remote.CommentToPostDto
|
||||
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto
|
||||
import com.isolaatti.posting.comments.domain.model.Comment
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface CommentsRepository {
|
||||
fun getComments(postId: Long, lastId: Long): Flow<FeedCommentsDto>
|
||||
fun getComments(postId: Long, lastId: Long): Flow<MutableList<Comment>>
|
||||
fun getComment(commentId: Long): Flow<CommentDto>
|
||||
fun postComment(commentToPostDto: CommentToPostDto, postId: Long): Flow<Boolean>
|
||||
}
|
||||
@ -1,12 +1,30 @@
|
||||
package com.isolaatti.posting.comments.domain.model
|
||||
|
||||
import com.isolaatti.posting.comments.data.remote.CommentDto
|
||||
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto
|
||||
import com.isolaatti.posting.common.domain.Ownable
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
|
||||
data class Comment(
|
||||
val id: Long,
|
||||
val textContent: String,
|
||||
val userId: Int,
|
||||
override val userId: Int,
|
||||
val postId: Long,
|
||||
val date: String,
|
||||
val responseForCommentId: Long?,
|
||||
val linkedDiscussionId: Long?,
|
||||
val linkedCommentId: Long?
|
||||
)
|
||||
val username: String
|
||||
) : Ownable {
|
||||
companion object {
|
||||
fun fromCommentsDto(dtoList: FeedCommentsDto): List<Comment> {
|
||||
return dtoList.data.map {
|
||||
Comment(
|
||||
id = it.comment.id,
|
||||
textContent = it.comment.textContent,
|
||||
userId = it.comment.userId,
|
||||
postId = it.comment.postId,
|
||||
date = it.comment.date,
|
||||
username = it.username
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
package com.isolaatti.posting.comments.domain.use_case
|
||||
|
||||
import com.isolaatti.posting.comments.data.remote.CommentDto
|
||||
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto
|
||||
import com.isolaatti.posting.comments.domain.CommentsRepository
|
||||
import com.isolaatti.posting.comments.domain.model.Comment
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetComments @Inject constructor(private val commentsRepository: CommentsRepository) {
|
||||
operator fun invoke(postId: Long, lastId: Long? = null): Flow<FeedCommentsDto> =
|
||||
operator fun invoke(postId: Long, lastId: Long? = null): Flow<MutableList<Comment>> =
|
||||
commentsRepository.getComments(postId, lastId ?: 0)
|
||||
}
|
||||
@ -13,6 +13,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.isolaatti.databinding.BottomSheetPostCommentsBinding
|
||||
import com.isolaatti.posting.common.domain.OnUserInteractedCallback
|
||||
import com.isolaatti.posting.common.domain.Ownable
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
|
||||
@ -115,8 +116,8 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
|
||||
}
|
||||
|
||||
|
||||
override fun onOptions(postId: Long) {
|
||||
optionsViewModel.setOptions(Options.myCommentOptions, CALLER_ID)
|
||||
override fun onOptions(comment: Ownable) {
|
||||
optionsViewModel.setOptions(Options.POST_OPTIONS, CALLER_ID, comment)
|
||||
val fragment = BottomSheetPostOptionsFragment()
|
||||
fragment.show(parentFragmentManager, BottomSheetPostOptionsFragment.TAG)
|
||||
}
|
||||
|
||||
@ -5,12 +5,13 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.isolaatti.databinding.CommentLayoutBinding
|
||||
import com.isolaatti.posting.comments.data.remote.CommentDto
|
||||
import com.isolaatti.posting.comments.domain.model.Comment
|
||||
import com.isolaatti.posting.common.domain.OnUserInteractedCallback
|
||||
import com.isolaatti.utils.UrlGen
|
||||
import com.squareup.picasso.Picasso
|
||||
import io.noties.markwon.Markwon
|
||||
|
||||
class CommentsRecyclerViewAdapter(private var list: List<CommentDto>, private val markwon: Markwon, private val callback: OnUserInteractedCallback) : RecyclerView.Adapter<CommentsRecyclerViewAdapter.CommentViewHolder>() {
|
||||
class CommentsRecyclerViewAdapter(private var list: List<Comment>, private val markwon: Markwon, private val callback: OnUserInteractedCallback) : RecyclerView.Adapter<CommentsRecyclerViewAdapter.CommentViewHolder>() {
|
||||
|
||||
inner class CommentViewHolder(val viewBinding: CommentLayoutBinding) : RecyclerView.ViewHolder(viewBinding.root)
|
||||
|
||||
@ -23,21 +24,21 @@ class CommentsRecyclerViewAdapter(private var list: List<CommentDto>, private va
|
||||
override fun onBindViewHolder(holder: CommentViewHolder, position: Int) {
|
||||
val comment = list[position]
|
||||
|
||||
holder.viewBinding.textViewDate.text = comment.comment.date
|
||||
markwon.setMarkdown(holder.viewBinding.postContent, comment.comment.textContent)
|
||||
holder.viewBinding.textViewDate.text = comment.date
|
||||
markwon.setMarkdown(holder.viewBinding.postContent, comment.textContent)
|
||||
holder.viewBinding.textViewUsername.text = comment.username
|
||||
holder.viewBinding.textViewUsername.setOnClickListener {
|
||||
callback.onProfileClick(comment.comment.userId)
|
||||
callback.onProfileClick(comment.userId)
|
||||
}
|
||||
holder.viewBinding.moreButton.setOnClickListener {
|
||||
callback.onOptions(comment.comment.id)
|
||||
callback.onOptions(comment)
|
||||
}
|
||||
Picasso.get()
|
||||
.load(UrlGen.userProfileImage(comment.comment.userId))
|
||||
.load(UrlGen.userProfileImage(comment.userId))
|
||||
.into(holder.viewBinding.avatarPicture)
|
||||
}
|
||||
|
||||
fun submitList(commentDtoList: List<CommentDto>) {
|
||||
fun submitList(commentDtoList: List<Comment>) {
|
||||
val lastIndex = if(list.count() - 1 < 1) 0 else list.count() - 1
|
||||
list = commentDtoList
|
||||
notifyItemRangeChanged(lastIndex, commentDtoList.count())
|
||||
|
||||
@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.posting.comments.data.remote.CommentDto
|
||||
import com.isolaatti.posting.comments.domain.model.Comment
|
||||
import com.isolaatti.posting.comments.domain.use_case.GetComments
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -16,9 +17,9 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class CommentsViewModel @Inject constructor(private val getComments: GetComments) : ViewModel() {
|
||||
private val _comments: MutableLiveData<List<CommentDto>> = MutableLiveData()
|
||||
private val _comments: MutableLiveData<List<Comment>> = MutableLiveData()
|
||||
|
||||
val comments: LiveData<List<CommentDto>> get() = _comments
|
||||
val comments: LiveData<List<Comment>> get() = _comments
|
||||
|
||||
/**
|
||||
* postId to query comments for. First page will be fetched when set.
|
||||
@ -36,10 +37,10 @@ class CommentsViewModel @Inject constructor(private val getComments: GetComments
|
||||
viewModelScope.launch {
|
||||
getComments(postId, lastId).onEach {
|
||||
val newList = _comments.value?.toMutableList() ?: mutableListOf()
|
||||
newList.addAll(it.data)
|
||||
newList.addAll(it)
|
||||
_comments.postValue(newList)
|
||||
if(it.data.isNotEmpty()){
|
||||
lastId = it.data.last().comment.id
|
||||
if(it.isNotEmpty()){
|
||||
lastId = it.last().id
|
||||
}
|
||||
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
@ -49,8 +50,8 @@ class CommentsViewModel @Inject constructor(private val getComments: GetComments
|
||||
/**
|
||||
* Use when new comment has been posted
|
||||
*/
|
||||
fun putCommentAtTheBeginning(commentDto: CommentDto) {
|
||||
val newList: MutableList<CommentDto> = mutableListOf(commentDto)
|
||||
fun putCommentAtTheBeginning(commentDto: Comment) {
|
||||
val newList: MutableList<Comment> = mutableListOf(commentDto)
|
||||
newList.addAll(_comments.value ?: mutableListOf())
|
||||
_comments.postValue(newList)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.isolaatti.posting.common.domain
|
||||
|
||||
interface OnUserInteractedCallback {
|
||||
fun onOptions(postId: Long)
|
||||
fun onOptions(postId: Ownable)
|
||||
fun onProfileClick(userId: Int)
|
||||
fun onLoadMore()
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package com.isolaatti.posting.common.domain
|
||||
|
||||
interface Ownable {
|
||||
val userId: Int
|
||||
}
|
||||
@ -21,6 +21,8 @@ data class Options(
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val POST_OPTIONS = 1
|
||||
|
||||
val noOptions = Options(0, listOf())
|
||||
val myPostOptions = Options(R.string.post_options_title, listOf(
|
||||
Option(R.string.delete, R.drawable.baseline_delete_24, Option.OPTION_DELETE),
|
||||
|
||||
@ -3,10 +3,21 @@ package com.isolaatti.posting.common.options_bottom_sheet.presentation
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.posting.common.domain.Ownable
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options.Companion.POST_OPTIONS
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.settings.domain.UserIdSetting
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class BottomSheetPostOptionsViewModel : ViewModel() {
|
||||
@HiltViewModel
|
||||
class BottomSheetPostOptionsViewModel @Inject constructor(private val userIdSetting: UserIdSetting) : ViewModel() {
|
||||
private val _options: MutableLiveData<Options> = MutableLiveData()
|
||||
val options: LiveData<Options> get() = _options
|
||||
|
||||
@ -21,10 +32,28 @@ class BottomSheetPostOptionsViewModel : ViewModel() {
|
||||
_optionClicked.postValue(null)
|
||||
}
|
||||
|
||||
fun setOptions(options: Options, callerId: Int, payload: Any? = null) {
|
||||
_options.postValue(options)
|
||||
fun setOptions(options: Int, callerId: Int, payload: Ownable? = null) {
|
||||
viewModelScope.launch {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
when(options) {
|
||||
POST_OPTIONS -> {
|
||||
userIdSetting.getUserId()?.let { userId ->
|
||||
if(userId == payload?.userId) {
|
||||
_options.postValue(Options.myPostOptions)
|
||||
} else {
|
||||
_options.postValue(Options.postOptions)
|
||||
}
|
||||
_callerId = callerId
|
||||
_payload = payload
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -10,18 +10,19 @@ import com.isolaatti.posting.posts.data.remote.FeedsApi
|
||||
import com.isolaatti.posting.posts.data.remote.PostApi
|
||||
import com.isolaatti.posting.posts.data.remote.PostDeletedDto
|
||||
import com.isolaatti.posting.posts.domain.PostsRepository
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.utils.Resource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, private val postApi: PostApi) : PostsRepository {
|
||||
override fun getFeed(lastId: Long): Flow<Resource<FeedDto>> = flow {
|
||||
override fun getFeed(lastId: Long): Flow<Resource<MutableList<Post>>> = flow {
|
||||
emit(Resource.Loading())
|
||||
try {
|
||||
val result = feedsApi.getChronology(lastId, 20).execute()
|
||||
if(result.isSuccessful) {
|
||||
emit(Resource.Success(result.body()))
|
||||
emit(Resource.Success(result.body()?.let { Post.fromFeedDto(it) }))
|
||||
return@flow
|
||||
}
|
||||
when(result.code()) {
|
||||
@ -35,13 +36,13 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
|
||||
}
|
||||
}
|
||||
|
||||
override fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>> = flow {
|
||||
override fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<MutableList<Post>>> = flow {
|
||||
emit(Resource.Loading())
|
||||
try {
|
||||
val gson = Gson()
|
||||
val result = feedsApi.postsOfUser(userId, 20, lastId, olderFirst, gson.toJson(filter)).execute()
|
||||
if(result.isSuccessful) {
|
||||
emit(Resource.Success(result.body()))
|
||||
emit(Resource.Success(result.body()?.let { Post.fromFeedDto(it) }))
|
||||
return@flow
|
||||
}
|
||||
when(result.code()) {
|
||||
|
||||
@ -5,14 +5,15 @@ import com.isolaatti.posting.posts.data.remote.EditPostDto
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.posting.posts.data.remote.FeedFilterDto
|
||||
import com.isolaatti.posting.posts.data.remote.PostDeletedDto
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.utils.Resource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface PostsRepository {
|
||||
|
||||
fun getFeed(lastId: Long): Flow<Resource<FeedDto>>
|
||||
fun getFeed(lastId: Long): Flow<Resource<MutableList<Post>>>
|
||||
|
||||
fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>>
|
||||
fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<MutableList<Post>>>
|
||||
|
||||
fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>>
|
||||
fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>>
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
package com.isolaatti.posting.posts.domain.entity
|
||||
|
||||
import com.isolaatti.posting.common.domain.Ownable
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
|
||||
data class Post(
|
||||
val id: Long,
|
||||
var textContent: String,
|
||||
override val userId: Int,
|
||||
val privacy: Int,
|
||||
val date: String,
|
||||
var audioId: String?,
|
||||
val squadId: String?,
|
||||
var numberOfLikes: Int,
|
||||
var numberOfComments: Int,
|
||||
val userName: String,
|
||||
val squadName: String?,
|
||||
var liked: Boolean
|
||||
) : Ownable {
|
||||
companion object {
|
||||
fun fromFeedDto(feedDto: FeedDto): MutableList<Post> {
|
||||
return feedDto.data.map {
|
||||
Post(
|
||||
id = it.post.id,
|
||||
userId = it.post.userId,
|
||||
textContent = it.post.textContent,
|
||||
privacy = it.post.privacy,
|
||||
date = it.post.date,
|
||||
audioId = it.post.audioId,
|
||||
squadId = it.post.squadId,
|
||||
numberOfComments = it.numberOfComments,
|
||||
numberOfLikes = it.numberOfLikes,
|
||||
userName = it.userName,
|
||||
squadName = it.squadName,
|
||||
liked = it.liked
|
||||
)
|
||||
}.toMutableList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.isolaatti.posting.posts.presentation
|
||||
|
||||
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
||||
import com.isolaatti.posting.common.domain.Ownable
|
||||
|
||||
abstract class PostListingRecyclerViewAdapterWiring(private val postsViewModelBase: PostListingViewModelBase) : OnUserInteractedWithPostCallback {
|
||||
|
||||
@ -16,7 +17,7 @@ abstract class PostListingRecyclerViewAdapterWiring(private val postsViewModelBa
|
||||
|
||||
abstract override fun onOpenPost(postId: Long)
|
||||
|
||||
abstract override fun onOptions(postId: Long)
|
||||
abstract override fun onOptions(post: Ownable)
|
||||
|
||||
abstract override fun onProfileClick(userId: Int)
|
||||
}
|
||||
@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.isolaatti.posting.likes.domain.repository.LikesRepository
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.posting.posts.domain.use_case.DeletePost
|
||||
import com.isolaatti.profile.domain.use_case.GetProfilePosts
|
||||
import com.isolaatti.utils.Resource
|
||||
@ -24,8 +25,9 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
@Inject
|
||||
lateinit var deletePostUseCase: DeletePost
|
||||
|
||||
val posts: MutableLiveData<Pair<FeedDto?, UpdateEvent>?> = MutableLiveData()
|
||||
val posts: MutableLiveData<Pair<MutableList<Post>?, UpdateEvent>?> = MutableLiveData()
|
||||
|
||||
val postsList get() = posts.value?.first
|
||||
|
||||
val loadingPosts = MutableLiveData(false)
|
||||
|
||||
@ -34,7 +36,7 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
val errorLoading: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||
var isLoadingFromScrolling = false
|
||||
|
||||
fun getLastId(): Long = try { posts.value?.first?.data?.last()?.post?.id ?: 0 } catch (e: NoSuchElementException) { 0 }
|
||||
fun getLastId(): Long = try { posts.value?.first?.last()?.id ?: 0 } catch (e: NoSuchElementException) { 0 }
|
||||
|
||||
|
||||
abstract fun getFeed(refresh: Boolean)
|
||||
@ -42,12 +44,12 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
fun likePost(postId: Long) {
|
||||
viewModelScope.launch {
|
||||
likesRepository.likePost(postId).onEach {likeDto ->
|
||||
val likedPost = posts.value?.first?.data?.find { post -> post.post.id == likeDto.postId }
|
||||
val index = posts.value?.first?.data?.indexOf(likedPost)
|
||||
val likedPost = posts.value?.first?.find { post -> post.id == likeDto.postId }
|
||||
val index = posts.value?.first?.indexOf(likedPost)
|
||||
if(index != null){
|
||||
val temp = posts.value?.first
|
||||
val temp = posts.value?.first?.toMutableList()
|
||||
Log.d("***", temp.toString())
|
||||
temp?.data?.set(index, likedPost!!.apply {
|
||||
temp?.set(index, likedPost!!.apply {
|
||||
liked = true
|
||||
numberOfLikes = likeDto.likesCount
|
||||
|
||||
@ -61,10 +63,11 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
fun unLikePost(postId: Long) {
|
||||
viewModelScope.launch {
|
||||
likesRepository.unLikePost(postId).onEach {likeDto ->
|
||||
val likedPost = posts.value?.first?.data?.find { post -> post.post.id == likeDto.postId }
|
||||
val index = posts.value?.first?.data?.indexOf(likedPost)
|
||||
val likedPost = posts.value?.first?.find { post -> post.id == likeDto.postId }
|
||||
val index = posts.value?.first?.indexOf(likedPost)
|
||||
if(index != null){
|
||||
posts.value?.first?.data?.set(index, likedPost!!.apply {
|
||||
val temp = posts.value?.first?.toMutableList()
|
||||
temp?.set(index, likedPost!!.apply {
|
||||
liked = false
|
||||
numberOfLikes = likeDto.likesCount
|
||||
})
|
||||
@ -79,11 +82,11 @@ abstract class PostListingViewModelBase : ViewModel() {
|
||||
deletePostUseCase(postId).onEach { res ->
|
||||
when(res) {
|
||||
is Resource.Success -> {
|
||||
val postDeleted = posts.value?.first?.data?.find { postDto -> postDto.post.id == postId }
|
||||
val postDeleted = posts.value?.first?.find { post -> post.id == postId }
|
||||
?: return@onEach
|
||||
val index = posts.value?.first?.data?.indexOf(postDeleted)
|
||||
val index = posts.value?.first?.indexOf(postDeleted)
|
||||
|
||||
posts.value?.first?.data?.removeAt(index!!)
|
||||
posts.value?.first?.removeAt(index!!)
|
||||
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_REMOVED, index)))
|
||||
}
|
||||
is Resource.Loading -> {}
|
||||
|
||||
@ -12,15 +12,16 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.utils.UrlGen.userProfileImage
|
||||
import com.squareup.picasso.Picasso
|
||||
import io.noties.markwon.Markwon
|
||||
|
||||
class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callback: OnUserInteractedWithPostCallback, private var feedDto: FeedDto?) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){
|
||||
class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callback: OnUserInteractedWithPostCallback) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){
|
||||
private var postList: List<Post>? = null
|
||||
inner class FeedViewHolder(itemView: View) : ViewHolder(itemView) {
|
||||
fun bindView(postDto: FeedDto.PostDto, payloads: List<Any>) {
|
||||
fun bindView(postDto: Post, payloads: List<Any>) {
|
||||
|
||||
Log.d("payloads", payloads.count().toString())
|
||||
val likeButton: MaterialButton = itemView.findViewById(R.id.like_button)
|
||||
@ -59,17 +60,17 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
|
||||
val username: TextView = itemView.findViewById(R.id.text_view_username)
|
||||
username.text = postDto.userName
|
||||
username.setOnClickListener {
|
||||
callback.onProfileClick(postDto.post.userId)
|
||||
callback.onProfileClick(postDto.userId)
|
||||
}
|
||||
|
||||
val profileImageView: ImageView = itemView.findViewById(R.id.avatar_picture)
|
||||
Picasso.get().load(userProfileImage(postDto.post.userId)).into(profileImageView)
|
||||
Picasso.get().load(userProfileImage(postDto.userId)).into(profileImageView)
|
||||
|
||||
val dateTextView: TextView = itemView.findViewById(R.id.text_view_date)
|
||||
dateTextView.text = postDto.post.date
|
||||
dateTextView.text = postDto.date
|
||||
|
||||
val content: TextView = itemView.findViewById(R.id.post_content)
|
||||
markwon.setMarkdown(content, postDto.post.textContent)
|
||||
markwon.setMarkdown(content, postDto.textContent)
|
||||
|
||||
likeButton.isEnabled = true
|
||||
|
||||
@ -87,23 +88,23 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
|
||||
|
||||
val moreButton: MaterialButton = itemView.findViewById(R.id.more_button)
|
||||
moreButton.setOnClickListener {
|
||||
callback.onOptions(postDto.post.id)
|
||||
callback.onOptions(postDto)
|
||||
}
|
||||
|
||||
likeButton.setOnClickListener {
|
||||
likeButton.isEnabled = false
|
||||
if(postDto.liked){
|
||||
callback.onUnLiked(postDto.post.id)
|
||||
callback.onUnLiked(postDto.id)
|
||||
} else {
|
||||
callback.onLiked(postDto.post.id)
|
||||
callback.onLiked(postDto.id)
|
||||
}
|
||||
}
|
||||
commentsButton.setOnClickListener {
|
||||
callback.onComment(postDto.post.id)
|
||||
callback.onComment(postDto.id)
|
||||
}
|
||||
|
||||
itemView.findViewById<MaterialCardView>(R.id.card).setOnClickListener {
|
||||
callback.onOpenPost(postDto.post.id)
|
||||
callback.onOpenPost(postDto.id)
|
||||
}
|
||||
|
||||
}
|
||||
@ -122,25 +123,25 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
|
||||
}
|
||||
|
||||
var previousSize = 0
|
||||
override fun getItemCount(): Int = feedDto?.data?.size ?: 0
|
||||
override fun getItemCount(): Int = postList?.size ?: 0
|
||||
|
||||
|
||||
override fun setHasStableIds(hasStableIds: Boolean) {
|
||||
super.setHasStableIds(true)
|
||||
}
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateList(updatedFeed: FeedDto, updateEvent: UpdateEvent? = null) {
|
||||
fun updateList(updatedFeed: List<Post>, updateEvent: UpdateEvent? = null) {
|
||||
if(updateEvent == null) {
|
||||
feedDto = updatedFeed
|
||||
postList = updatedFeed
|
||||
|
||||
notifyDataSetChanged()
|
||||
return
|
||||
}
|
||||
val postUpdated = updateEvent.affectedPosition?.let { feedDto?.data?.get(it) }
|
||||
val postUpdated = updateEvent.affectedPosition?.let { postList?.get(it) }
|
||||
val position = updateEvent.affectedPosition
|
||||
|
||||
previousSize = itemCount
|
||||
feedDto = updatedFeed
|
||||
postList = updatedFeed
|
||||
|
||||
when(updateEvent.updateType) {
|
||||
UpdateEvent.UpdateType.POST_LIKED -> {
|
||||
@ -179,8 +180,8 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
|
||||
requestedNewContent = false
|
||||
}
|
||||
override fun onBindViewHolder(holder: FeedViewHolder, position: Int, payloads: List<Any>) {
|
||||
holder.bindView(feedDto?.data?.get(position) ?: return, payloads)
|
||||
val totalItems = feedDto?.data?.size
|
||||
holder.bindView(postList?.get(position) ?: return, payloads)
|
||||
val totalItems = postList?.size
|
||||
if(totalItems != null && totalItems > 0 && !requestedNewContent) {
|
||||
if(position == totalItems - 1) {
|
||||
requestedNewContent = true
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
package com.isolaatti.profile.domain.use_case
|
||||
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.posting.posts.data.remote.FeedFilterDto
|
||||
import com.isolaatti.posting.posts.domain.PostsRepository
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.utils.Resource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetProfilePosts @Inject constructor(private val postsRepository: PostsRepository) {
|
||||
operator fun invoke(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>> =
|
||||
operator fun invoke(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<MutableList<Post>>> =
|
||||
postsRepository.getProfilePosts(userId, lastId, olderFirst, filter)
|
||||
}
|
||||
@ -65,8 +65,8 @@ class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetPro
|
||||
when (feedDtoResource) {
|
||||
is Resource.Success -> {
|
||||
loadingPosts.postValue(false)
|
||||
posts.postValue(Pair(posts.value?.first?.concatFeed(feedDtoResource.data) ?: feedDtoResource.data, UpdateEvent(if(refresh) UpdateEvent.UpdateType.REFRESH else UpdateEvent.UpdateType.PAGE_ADDED, null)))
|
||||
noMoreContent.postValue(feedDtoResource.data?.moreContent == false)
|
||||
posts.postValue(Pair(posts.value?.first?.apply { addAll(feedDtoResource.data ?: listOf()) } ?: feedDtoResource.data, UpdateEvent(if(refresh) UpdateEvent.UpdateType.REFRESH else UpdateEvent.UpdateType.PAGE_ADDED, null)))
|
||||
noMoreContent.postValue(feedDtoResource.data?.size == 0)
|
||||
}
|
||||
|
||||
is Resource.Loading -> {
|
||||
|
||||
@ -18,10 +18,12 @@ import com.isolaatti.followers.domain.FollowingState
|
||||
import com.isolaatti.home.FeedFragment
|
||||
import com.isolaatti.posting.PostViewerActivity
|
||||
import com.isolaatti.posting.comments.presentation.BottomSheetPostComments
|
||||
import com.isolaatti.posting.common.domain.Ownable
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
|
||||
import com.isolaatti.posting.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
|
||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
||||
import com.isolaatti.posting.posts.domain.entity.Post
|
||||
import com.isolaatti.posting.posts.presentation.PostListingRecyclerViewAdapterWiring
|
||||
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
|
||||
import com.isolaatti.posting.posts.presentation.UpdateEvent
|
||||
@ -67,7 +69,7 @@ class ProfileMainFragment : Fragment() {
|
||||
)
|
||||
}
|
||||
|
||||
private val postsObserver: Observer<Pair<FeedDto?, UpdateEvent>?> = Observer {
|
||||
private val postsObserver: Observer<Pair<List<Post>?, UpdateEvent>?> = Observer {
|
||||
if(it?.first != null) {
|
||||
postsAdapter.updateList(it.first!!, it.second)
|
||||
postsAdapter.newContentRequestFinished()
|
||||
@ -188,7 +190,7 @@ class ProfileMainFragment : Fragment() {
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.build()
|
||||
|
||||
postsAdapter = PostsRecyclerViewAdapter(markwon,postListingRecyclerViewAdapterWiring, null )
|
||||
postsAdapter = PostsRecyclerViewAdapter(markwon,postListingRecyclerViewAdapterWiring )
|
||||
|
||||
}
|
||||
|
||||
@ -210,8 +212,8 @@ class ProfileMainFragment : Fragment() {
|
||||
PostViewerActivity.startActivity(requireContext(), postId)
|
||||
}
|
||||
|
||||
override fun onOptions(postId: Long) {
|
||||
optionsViewModel.setOptions(Options.myPostOptions, FeedFragment.CALLER_ID, postId)
|
||||
override fun onOptions(post: Ownable) {
|
||||
optionsViewModel.setOptions(Options.POST_OPTIONS, FeedFragment.CALLER_ID, post)
|
||||
val modalBottomSheet = BottomSheetPostOptionsFragment()
|
||||
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG)
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ package com.isolaatti.settings
|
||||
|
||||
import com.isolaatti.database.AppDatabase
|
||||
import com.isolaatti.settings.data.KeyValueDao
|
||||
import com.isolaatti.settings.data.SettingsRepositoryImpl
|
||||
import com.isolaatti.settings.domain.SettingsRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
@ -14,4 +16,9 @@ class Module {
|
||||
fun provideKeyValueDao(database: AppDatabase): KeyValueDao {
|
||||
return database.keyValueDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideSettingsRepository(keyValueDao: KeyValueDao): SettingsRepository {
|
||||
return SettingsRepositoryImpl(keyValueDao)
|
||||
}
|
||||
}
|
||||
@ -8,8 +8,8 @@ import androidx.room.Query
|
||||
@Dao
|
||||
interface KeyValueDao {
|
||||
@Query("SELECT value FROM key_values WHERE id = :key")
|
||||
fun getValue(key: String): String
|
||||
suspend fun getValue(key: String): String
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun setValue(entity: KeyValueEntity)
|
||||
suspend fun setValue(entity: KeyValueEntity)
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.isolaatti.settings.data
|
||||
|
||||
import com.isolaatti.settings.domain.SettingsRepository
|
||||
|
||||
class SettingsRepositoryImpl(private val keyValueDao: KeyValueDao) : SettingsRepository {
|
||||
override suspend fun setKeyValue(key: String, value: String) {
|
||||
keyValueDao.setValue(KeyValueEntity(key, value))
|
||||
}
|
||||
|
||||
override suspend fun getKeyValue(key: String): String? {
|
||||
return keyValueDao.getValue(key)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.isolaatti.settings.domain
|
||||
|
||||
interface SettingsRepository {
|
||||
suspend fun setKeyValue(key: String, value: String)
|
||||
suspend fun getKeyValue(key: String): String?
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.isolaatti.settings.domain
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class UserIdSetting @Inject constructor(private val settingsRepository: SettingsRepository) {
|
||||
suspend fun getUserId(): Int? {
|
||||
return settingsRepository.getKeyValue(KEY_USER_ID)?.toIntOrNull()
|
||||
}
|
||||
|
||||
suspend fun setUserId(userId: Int) {
|
||||
settingsRepository.setKeyValue(KEY_USER_ID, userId.toString())
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_USER_ID = "userId"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user