WIP visor de discusiones
This commit is contained in:
parent
003ab3ea5d
commit
5cf9fcb426
@ -31,7 +31,7 @@
|
|||||||
android:parentActivityName=".MainActivity"/>
|
android:parentActivityName=".MainActivity"/>
|
||||||
<activity android:name=".settings.ui.SettingsActivity" android:theme="@style/Theme.Isolaatti"/>
|
<activity android:name=".settings.ui.SettingsActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||||
<activity android:name=".posting.posts.ui.CreatePostActivity" android:theme="@style/Theme.Isolaatti" android:windowSoftInputMode="adjustResize"/>
|
<activity android:name=".posting.posts.ui.CreatePostActivity" android:theme="@style/Theme.Isolaatti" android:windowSoftInputMode="adjustResize"/>
|
||||||
<activity android:name=".posting.PostViewerActivity" android:theme="@style/Theme.Isolaatti"/>
|
<activity android:name=".posting.posts.viewer.ui.PostViewerActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||||
<activity android:name=".drafts.ui.DraftsActivity" android:theme="@style/Theme.Isolaatti"/>
|
<activity android:name=".drafts.ui.DraftsActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||||
<activity android:name=".about.AboutActivity" android:theme="@style/Theme.Isolaatti"/>
|
<activity android:name=".about.AboutActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||||
<activity android:name=".picture_viewer.ui.PictureViewerActivity" android:theme="@style/Theme.Isolaatti"/>
|
<activity android:name=".picture_viewer.ui.PictureViewerActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import com.isolaatti.databinding.FragmentFeedBinding
|
|||||||
import com.isolaatti.drafts.ui.DraftsActivity
|
import com.isolaatti.drafts.ui.DraftsActivity
|
||||||
import com.isolaatti.home.presentation.FeedViewModel
|
import com.isolaatti.home.presentation.FeedViewModel
|
||||||
import com.isolaatti.picture_viewer.ui.PictureViewerActivity
|
import com.isolaatti.picture_viewer.ui.PictureViewerActivity
|
||||||
import com.isolaatti.posting.PostViewerActivity
|
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
|
||||||
import com.isolaatti.posting.comments.ui.BottomSheetPostComments
|
import com.isolaatti.posting.comments.ui.BottomSheetPostComments
|
||||||
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
||||||
import com.isolaatti.posting.common.domain.Ownable
|
import com.isolaatti.posting.common.domain.Ownable
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
package com.isolaatti.posting
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import com.isolaatti.common.IsolaattiBaseActivity
|
|
||||||
import com.isolaatti.databinding.ActivityCreatePostBinding
|
|
||||||
import com.isolaatti.databinding.ActivityPostViewerBinding
|
|
||||||
|
|
||||||
class PostViewerActivity : IsolaattiBaseActivity() {
|
|
||||||
companion object {
|
|
||||||
const val POST_ID = "postId"
|
|
||||||
fun startActivity(context: Context, postId: Long) {
|
|
||||||
context.startActivity(Intent(context, PostViewerActivity::class.java).apply {
|
|
||||||
putExtra(POST_ID, postId)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityPostViewerBinding
|
|
||||||
|
|
||||||
private var postId: Long? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
binding = ActivityPostViewerBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
setContentView(binding.root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -5,24 +5,36 @@ import com.isolaatti.posting.likes.data.remote.LikeDto
|
|||||||
import com.isolaatti.posting.likes.data.remote.LikesApi
|
import com.isolaatti.posting.likes.data.remote.LikesApi
|
||||||
import com.isolaatti.posting.likes.domain.repository.LikesRepository
|
import com.isolaatti.posting.likes.domain.repository.LikesRepository
|
||||||
import com.isolaatti.utils.LongIdentificationWrapper
|
import com.isolaatti.utils.LongIdentificationWrapper
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import retrofit2.Response
|
||||||
import retrofit2.awaitResponse
|
import retrofit2.awaitResponse
|
||||||
|
|
||||||
class LikesRepositoryImpl(private val likesApi: LikesApi) : LikesRepository {
|
class LikesRepositoryImpl(private val likesApi: LikesApi) : LikesRepository {
|
||||||
override fun likePost(postId: Long): Flow<LikeDto> = flow {
|
override fun likePost(postId: Long): Flow<Resource<LikeDto>> = flow {
|
||||||
val response = likesApi.likePost(LongIdentificationWrapper(postId)).awaitResponse()
|
try {
|
||||||
Log.d("likes_repo", response.toString())
|
val response = likesApi.likePost(LongIdentificationWrapper(postId)).awaitResponse()
|
||||||
if(response.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
response.body()?.let { emit(it) }
|
response.body()?.let { emit(Resource.Success(it)) }
|
||||||
|
} else {
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unLikePost(postId: Long): Flow<LikeDto> = flow {
|
override fun unLikePost(postId: Long): Flow<Resource<LikeDto>> = flow {
|
||||||
val response = likesApi.unLikePost(LongIdentificationWrapper(postId)).awaitResponse()
|
try {
|
||||||
Log.d("likes_repo", response.toString())
|
val response = likesApi.unLikePost(LongIdentificationWrapper(postId)).awaitResponse()
|
||||||
if(response.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
response.body()?.let { emit(it) }
|
response.body()?.let { emit(Resource.Success(it)) }
|
||||||
|
} else {
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
package com.isolaatti.posting.likes.domain.repository
|
package com.isolaatti.posting.likes.domain.repository
|
||||||
|
|
||||||
import com.isolaatti.posting.likes.data.remote.LikeDto
|
import com.isolaatti.posting.likes.data.remote.LikeDto
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface LikesRepository {
|
interface LikesRepository {
|
||||||
fun likePost(postId: Long): Flow<LikeDto>
|
fun likePost(postId: Long): Flow<Resource<LikeDto>>
|
||||||
fun unLikePost(postId: Long): Flow<LikeDto>
|
fun unLikePost(postId: Long): Flow<Resource<LikeDto>>
|
||||||
}
|
}
|
||||||
@ -94,13 +94,16 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadPost(postId: Long): Flow<Resource<FeedDto.PostDto>> = flow {
|
override fun loadPost(postId: Long): Flow<Resource<Post>> = flow {
|
||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
try {
|
try {
|
||||||
val result = postApi.getPost(postId).execute()
|
val result = postApi.getPost(postId).execute()
|
||||||
if(result.isSuccessful) {
|
if(result.isSuccessful) {
|
||||||
emit(Resource.Success(result.body()))
|
val dto = result.body()
|
||||||
return@flow
|
if(dto != null) {
|
||||||
|
emit(Resource.Success(Post.fromPostDto(dto)))
|
||||||
|
return@flow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
|
emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
|
||||||
} catch(_: Exception) {
|
} catch(_: Exception) {
|
||||||
|
|||||||
@ -18,5 +18,5 @@ interface PostsRepository {
|
|||||||
fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>>
|
fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>>
|
||||||
fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>>
|
fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>>
|
||||||
fun deletePost(postId: Long): Flow<Resource<PostDeletedDto>>
|
fun deletePost(postId: Long): Flow<Resource<PostDeletedDto>>
|
||||||
fun loadPost(postId: Long): Flow<Resource<FeedDto.PostDto>>
|
fun loadPost(postId: Long): Flow<Resource<Post>>
|
||||||
}
|
}
|
||||||
@ -36,5 +36,22 @@ data class Post(
|
|||||||
)
|
)
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fromPostDto(postDto: FeedDto.PostDto): Post {
|
||||||
|
return Post(
|
||||||
|
id = postDto.post.id,
|
||||||
|
userId = postDto.post.userId,
|
||||||
|
textContent = postDto.post.textContent,
|
||||||
|
privacy = postDto.post.privacy,
|
||||||
|
date = postDto.post.date,
|
||||||
|
audioId = postDto.post.audioId,
|
||||||
|
squadId = postDto.post.squadId,
|
||||||
|
numberOfComments = postDto.numberOfComments,
|
||||||
|
numberOfLikes = postDto.numberOfLikes,
|
||||||
|
userName = postDto.userName,
|
||||||
|
squadName = postDto.squadName,
|
||||||
|
liked = postDto.liked
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,13 +1,13 @@
|
|||||||
package com.isolaatti.posting.posts.domain.use_case
|
package com.isolaatti.posting.posts.domain.use_case
|
||||||
|
|
||||||
import com.isolaatti.posting.posts.data.remote.FeedDto
|
|
||||||
import com.isolaatti.posting.posts.domain.PostsRepository
|
import com.isolaatti.posting.posts.domain.PostsRepository
|
||||||
|
import com.isolaatti.posting.posts.domain.entity.Post
|
||||||
import com.isolaatti.utils.Resource
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LoadSinglePost @Inject constructor(private val postsRepository: PostsRepository) {
|
class LoadSinglePost @Inject constructor(private val postsRepository: PostsRepository) {
|
||||||
operator fun invoke(postId: Long): Flow<Resource<FeedDto.PostDto>> {
|
operator fun invoke(postId: Long): Flow<Resource<Post>> {
|
||||||
return postsRepository.loadPost(postId)
|
return postsRepository.loadPost(postId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ class CreatePostViewModel @Inject constructor(private val makePost: MakePost, pr
|
|||||||
loadPost(postId).onEach { postRes ->
|
loadPost(postId).onEach { postRes ->
|
||||||
if(postRes is Resource.Success) {
|
if(postRes is Resource.Success) {
|
||||||
postRes.data?.let {
|
postRes.data?.let {
|
||||||
postToEdit.postValue(EditPostDto(PRIVACY_ISOLAATTI, content = it.post.textContent, postId = it.post.id))
|
postToEdit.postValue(EditPostDto(PRIVACY_ISOLAATTI, content = it.textContent, postId = it.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
|||||||
@ -43,36 +43,54 @@ abstract class PostListingViewModelBase : ViewModel() {
|
|||||||
|
|
||||||
fun likePost(postId: Long) {
|
fun likePost(postId: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
likesRepository.likePost(postId).onEach {likeDto ->
|
likesRepository.likePost(postId).onEach {like ->
|
||||||
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?.toMutableList()
|
|
||||||
Log.d("***", temp.toString())
|
|
||||||
temp?.set(index, likedPost!!.apply {
|
|
||||||
liked = true
|
|
||||||
numberOfLikes = likeDto.likesCount
|
|
||||||
|
|
||||||
})
|
when(like) {
|
||||||
|
is Resource.Error -> {}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
is Resource.Success -> {
|
||||||
|
val likedPost = posts.value?.first?.find { post -> post.id == like.data?.postId }
|
||||||
|
val index = posts.value?.first?.indexOf(likedPost)
|
||||||
|
if(index != null){
|
||||||
|
val temp = posts.value?.first?.toMutableList()
|
||||||
|
Log.d("***", temp.toString())
|
||||||
|
temp?.set(index, likedPost!!.apply {
|
||||||
|
liked = true
|
||||||
|
numberOfLikes = like.data?.likesCount ?: 0
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_LIKED, index)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_LIKED, index)))
|
|
||||||
|
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unLikePost(postId: Long) {
|
fun unLikePost(postId: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
likesRepository.unLikePost(postId).onEach {likeDto ->
|
likesRepository.unLikePost(postId).onEach {like ->
|
||||||
val likedPost = posts.value?.first?.find { post -> post.id == likeDto.postId }
|
|
||||||
val index = posts.value?.first?.indexOf(likedPost)
|
when(like) {
|
||||||
if(index != null){
|
is Resource.Error -> TODO()
|
||||||
val temp = posts.value?.first?.toMutableList()
|
is Resource.Loading -> TODO()
|
||||||
temp?.set(index, likedPost!!.apply {
|
is Resource.Success -> {
|
||||||
liked = false
|
val likedPost = posts.value?.first?.find { post -> post.id == like.data?.postId }
|
||||||
numberOfLikes = likeDto.likesCount
|
val index = posts.value?.first?.indexOf(likedPost)
|
||||||
})
|
if(index != null){
|
||||||
|
val temp = posts.value?.first?.toMutableList()
|
||||||
|
temp?.set(index, likedPost!!.apply {
|
||||||
|
liked = false
|
||||||
|
numberOfLikes = like.data?.likesCount ?: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_LIKED, index)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_LIKED, index)))
|
|
||||||
|
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,97 @@
|
|||||||
|
package com.isolaatti.posting.posts.viewer.presentation
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.isolaatti.posting.comments.domain.use_case.GetComments
|
||||||
|
import com.isolaatti.posting.likes.domain.repository.LikesRepository
|
||||||
|
import com.isolaatti.posting.posts.domain.entity.Post
|
||||||
|
import com.isolaatti.posting.posts.domain.use_case.LoadSinglePost
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class PostViewerViewModel @Inject constructor(private val loadSinglePost: LoadSinglePost, private val likesRepository: LikesRepository) : ViewModel() {
|
||||||
|
val error: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||||
|
val post: MutableLiveData<Post> = MutableLiveData()
|
||||||
|
var postId: Long = 0
|
||||||
|
|
||||||
|
private val toRetry: MutableList<Runnable> = mutableListOf()
|
||||||
|
|
||||||
|
val postLiked: MutableLiveData<Boolean> = MutableLiveData()
|
||||||
|
|
||||||
|
|
||||||
|
// runs the lists of "Runnable" one by one and clears list. After this is executed,
|
||||||
|
// caller should report as handled
|
||||||
|
fun retry() {
|
||||||
|
toRetry.forEach {
|
||||||
|
it.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
toRetry.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPost() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
loadSinglePost(postId).onEach {
|
||||||
|
when(it) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
error.postValue(it.errorType)
|
||||||
|
toRetry.add {
|
||||||
|
getPost()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
is Resource.Success -> {
|
||||||
|
if(it.data != null) {
|
||||||
|
post.postValue(it.data)
|
||||||
|
postLiked.postValue(it.data.liked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateLikesCount(likesCount: Int) {
|
||||||
|
val updatedPost = post.value?.copy(numberOfLikes = likesCount)
|
||||||
|
if(updatedPost != null) {
|
||||||
|
post.postValue(updatedPost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun likeDislikePost() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
if(postLiked.value == true) {
|
||||||
|
likesRepository.unLikePost(postId).onEach {
|
||||||
|
when(it) {
|
||||||
|
is Resource.Error -> TODO()
|
||||||
|
is Resource.Loading -> TODO()
|
||||||
|
is Resource.Success -> {
|
||||||
|
updateLikesCount(it.data?.likesCount ?: 0)
|
||||||
|
postLiked.postValue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
} else {
|
||||||
|
likesRepository.likePost(postId).onEach {
|
||||||
|
when(it) {
|
||||||
|
is Resource.Error -> TODO()
|
||||||
|
is Resource.Loading -> TODO()
|
||||||
|
is Resource.Success -> {
|
||||||
|
updateLikesCount(it.data?.likesCount ?: 0)
|
||||||
|
postLiked.postValue(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
package com.isolaatti.posting.posts.viewer.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.core.content.ContentProviderCompat.requireContext
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import com.isolaatti.BuildConfig
|
||||||
|
import com.isolaatti.R
|
||||||
|
import com.isolaatti.common.ErrorMessageViewModel
|
||||||
|
import com.isolaatti.common.IsolaattiBaseActivity
|
||||||
|
import com.isolaatti.databinding.ActivityPostViewerBinding
|
||||||
|
import com.isolaatti.posting.comments.ui.BottomSheetPostComments
|
||||||
|
import com.isolaatti.posting.posts.viewer.presentation.PostViewerViewModel
|
||||||
|
import com.isolaatti.profile.ui.ProfileActivity
|
||||||
|
import com.isolaatti.utils.PicassoImagesPluginDef
|
||||||
|
import com.isolaatti.utils.UrlGen
|
||||||
|
import com.squareup.picasso.Picasso
|
||||||
|
import io.noties.markwon.AbstractMarkwonPlugin
|
||||||
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.MarkwonConfiguration
|
||||||
|
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute
|
||||||
|
import io.noties.markwon.linkify.LinkifyPlugin
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class PostViewerActivity : IsolaattiBaseActivity() {
|
||||||
|
companion object {
|
||||||
|
const val POST_ID = "postId"
|
||||||
|
fun startActivity(context: Context, postId: Long) {
|
||||||
|
context.startActivity(Intent(context, PostViewerActivity::class.java).apply {
|
||||||
|
putExtra(POST_ID, postId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityPostViewerBinding
|
||||||
|
private val viewModel: PostViewerViewModel by viewModels()
|
||||||
|
private var postId: Long = 0
|
||||||
|
private lateinit var markwon: Markwon
|
||||||
|
|
||||||
|
private fun openComments() {
|
||||||
|
val modalBottomSheet = BottomSheetPostComments.getInstance(postId)
|
||||||
|
modalBottomSheet.show(supportFragmentManager, BottomSheetPostComments.TAG)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setObservers() {
|
||||||
|
viewModel.error.observe(this) {
|
||||||
|
errorViewModel.error.postValue(it)
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
errorViewModel.retry.collect {
|
||||||
|
viewModel.retry()
|
||||||
|
errorViewModel.handleRetry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.post.observe(this) {
|
||||||
|
markwon.setMarkdown(binding.markwonContainer, it.textContent)
|
||||||
|
binding.author.text = it.userName
|
||||||
|
Picasso.get().load(UrlGen.userProfileImage(it.userId)).into(binding.profileImageView)
|
||||||
|
binding.commentsInfo.text = getString(R.string.comments_info, it.numberOfComments)
|
||||||
|
binding.likesInfo.text = getString(R.string.likes_info, it.numberOfLikes)
|
||||||
|
binding.author.setOnClickListener {_ ->
|
||||||
|
ProfileActivity.startActivity(this@PostViewerActivity, it.userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.postLiked.observe(this) {
|
||||||
|
val color = if(it) R.color.purple_lighter else R.color.on_surface
|
||||||
|
val menuItem = binding.toolbar.menu.findItem(R.id.like)
|
||||||
|
|
||||||
|
menuItem.isEnabled = true
|
||||||
|
menuItem.icon?.setTint(ResourcesCompat.getColor(resources, color, null))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setListeners() {
|
||||||
|
binding.toolbar.setOnMenuItemClickListener {
|
||||||
|
when(it.itemId) {
|
||||||
|
R.id.like -> {
|
||||||
|
viewModel.likeDislikePost()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.comments -> {
|
||||||
|
openComments()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.commentsInfo.setOnClickListener {
|
||||||
|
openComments()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
postId = intent.getLongExtra(POST_ID, 0)
|
||||||
|
|
||||||
|
binding = ActivityPostViewerBinding.inflate(layoutInflater)
|
||||||
|
markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(object: AbstractMarkwonPlugin() {
|
||||||
|
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
|
||||||
|
builder
|
||||||
|
.imageDestinationProcessor(
|
||||||
|
ImageDestinationProcessorRelativeToAbsolute
|
||||||
|
.create(BuildConfig.backend))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.usePlugin(PicassoImagesPluginDef.picassoImagePlugin)
|
||||||
|
.usePlugin(LinkifyPlugin.create())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
if(postId!! > 0) {
|
||||||
|
viewModel.postId = postId
|
||||||
|
setObservers()
|
||||||
|
setListeners()
|
||||||
|
viewModel.getPost()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,7 +22,7 @@ import com.isolaatti.common.ErrorMessageViewModel
|
|||||||
import com.isolaatti.databinding.FragmentDiscussionsBinding
|
import com.isolaatti.databinding.FragmentDiscussionsBinding
|
||||||
import com.isolaatti.followers.domain.FollowingState
|
import com.isolaatti.followers.domain.FollowingState
|
||||||
import com.isolaatti.home.FeedFragment
|
import com.isolaatti.home.FeedFragment
|
||||||
import com.isolaatti.posting.PostViewerActivity
|
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
|
||||||
import com.isolaatti.posting.comments.ui.BottomSheetPostComments
|
import com.isolaatti.posting.comments.ui.BottomSheetPostComments
|
||||||
import com.isolaatti.posting.common.domain.Ownable
|
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.OptionClicked
|
||||||
|
|||||||
@ -4,6 +4,6 @@
|
|||||||
android:viewportWidth="640"
|
android:viewportWidth="640"
|
||||||
android:viewportHeight="512">
|
android:viewportHeight="512">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="@color/on_surface"
|
||||||
android:pathData="M208,352c114.9,0 208,-78.8 208,-176S322.9,0 208,0S0,78.8 0,176c0,38.6 14.7,74.3 39.6,103.4c-3.5,9.4 -8.7,17.7 -14.2,24.7c-4.8,6.2 -9.7,11 -13.3,14.3c-1.8,1.6 -3.3,2.9 -4.3,3.7c-0.5,0.4 -0.9,0.7 -1.1,0.8l-0.2,0.2 0,0 0,0C1,327.2 -1.4,334.4 0.8,340.9S9.1,352 16,352c21.8,0 43.8,-5.6 62.1,-12.5c9.2,-3.5 17.8,-7.4 25.3,-11.4C134.1,343.3 169.8,352 208,352zM448,176c0,112.3 -99.1,196.9 -216.5,207C255.8,457.4 336.4,512 432,512c38.2,0 73.9,-8.7 104.7,-23.9c7.5,4 16,7.9 25.2,11.4c18.3,6.9 40.3,12.5 62.1,12.5c6.9,0 13.1,-4.5 15.2,-11.1c2.1,-6.6 -0.2,-13.8 -5.8,-17.9l0,0 0,0 -0.2,-0.2c-0.2,-0.2 -0.6,-0.4 -1.1,-0.8c-1,-0.8 -2.5,-2 -4.3,-3.7c-3.6,-3.3 -8.5,-8.1 -13.3,-14.3c-5.5,-7 -10.7,-15.4 -14.2,-24.7c24.9,-29 39.6,-64.7 39.6,-103.4c0,-92.8 -84.9,-168.9 -192.6,-175.5c0.4,5.1 0.6,10.3 0.6,15.5z"/>
|
android:pathData="M208,352c114.9,0 208,-78.8 208,-176S322.9,0 208,0S0,78.8 0,176c0,38.6 14.7,74.3 39.6,103.4c-3.5,9.4 -8.7,17.7 -14.2,24.7c-4.8,6.2 -9.7,11 -13.3,14.3c-1.8,1.6 -3.3,2.9 -4.3,3.7c-0.5,0.4 -0.9,0.7 -1.1,0.8l-0.2,0.2 0,0 0,0C1,327.2 -1.4,334.4 0.8,340.9S9.1,352 16,352c21.8,0 43.8,-5.6 62.1,-12.5c9.2,-3.5 17.8,-7.4 25.3,-11.4C134.1,343.3 169.8,352 208,352zM448,176c0,112.3 -99.1,196.9 -216.5,207C255.8,457.4 336.4,512 432,512c38.2,0 73.9,-8.7 104.7,-23.9c7.5,4 16,7.9 25.2,11.4c18.3,6.9 40.3,12.5 62.1,12.5c6.9,0 13.1,-4.5 15.2,-11.1c2.1,-6.6 -0.2,-13.8 -5.8,-17.9l0,0 0,0 -0.2,-0.2c-0.2,-0.2 -0.6,-0.4 -1.1,-0.8c-1,-0.8 -2.5,-2 -4.3,-3.7c-3.6,-3.3 -8.5,-8.1 -13.3,-14.3c-5.5,-7 -10.7,-15.4 -14.2,-24.7c24.9,-29 39.6,-64.7 39.6,-103.4c0,-92.8 -84.9,-168.9 -192.6,-175.5c0.4,5.1 0.6,10.3 0.6,15.5z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@ -4,6 +4,6 @@
|
|||||||
android:viewportWidth="512"
|
android:viewportWidth="512"
|
||||||
android:viewportHeight="512">
|
android:viewportHeight="512">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="@color/on_surface"
|
||||||
android:pathData="M336,16L336,80c0,8.8 -7.2,16 -16,16s-16,-7.2 -16,-16L304,16c0,-8.8 7.2,-16 16,-16s16,7.2 16,16zM237.3,23.1l32,48c4.9,7.4 2.9,17.3 -4.4,22.2s-17.3,2.9 -22.2,-4.4l-32,-48c-4.9,-7.4 -2.9,-17.3 4.4,-22.2s17.3,-2.9 22.2,4.4zM135,119c9.4,-9.4 24.6,-9.4 33.9,0L292.7,242.7c10.1,10.1 27.3,2.9 27.3,-11.3L320,192c0,-17.7 14.3,-32 32,-32s32,14.3 32,32L384,345.6c0,57.1 -30,110 -78.9,139.4c-64,38.4 -145.8,28.3 -198.5,-24.4L7,361c-9.4,-9.4 -9.4,-24.6 0,-33.9s24.6,-9.4 33.9,0l53,53c6.1,6.1 16,6.1 22.1,0s6.1,-16 0,-22.1L23,265c-9.4,-9.4 -9.4,-24.6 0,-33.9s24.6,-9.4 33.9,0l93,93c6.1,6.1 16,6.1 22.1,0s6.1,-16 0,-22.1L55,185c-9.4,-9.4 -9.4,-24.6 0,-33.9s24.6,-9.4 33.9,0l117,117c6.1,6.1 16,6.1 22.1,0s6.1,-16 0,-22.1l-93,-93c-9.4,-9.4 -9.4,-24.6 0,-33.9zM433.1,484.9c-24.2,14.5 -50.9,22.1 -77.7,23.1c48.1,-39.6 76.6,-99 76.6,-162.4l0,-98.1c8.2,-0.1 16,-6.4 16,-16L448,192c0,-17.7 14.3,-32 32,-32s32,14.3 32,32L512,345.6c0,57.1 -30,110 -78.9,139.4zM424.9,18.7c7.4,4.9 9.3,14.8 4.4,22.2l-32,48c-4.9,7.4 -14.8,9.3 -22.2,4.4s-9.3,-14.8 -4.4,-22.2l32,-48c4.9,-7.4 14.8,-9.3 22.2,-4.4z"/>
|
android:pathData="M336,16L336,80c0,8.8 -7.2,16 -16,16s-16,-7.2 -16,-16L304,16c0,-8.8 7.2,-16 16,-16s16,7.2 16,16zM237.3,23.1l32,48c4.9,7.4 2.9,17.3 -4.4,22.2s-17.3,2.9 -22.2,-4.4l-32,-48c-4.9,-7.4 -2.9,-17.3 4.4,-22.2s17.3,-2.9 22.2,4.4zM135,119c9.4,-9.4 24.6,-9.4 33.9,0L292.7,242.7c10.1,10.1 27.3,2.9 27.3,-11.3L320,192c0,-17.7 14.3,-32 32,-32s32,14.3 32,32L384,345.6c0,57.1 -30,110 -78.9,139.4c-64,38.4 -145.8,28.3 -198.5,-24.4L7,361c-9.4,-9.4 -9.4,-24.6 0,-33.9s24.6,-9.4 33.9,0l53,53c6.1,6.1 16,6.1 22.1,0s6.1,-16 0,-22.1L23,265c-9.4,-9.4 -9.4,-24.6 0,-33.9s24.6,-9.4 33.9,0l93,93c6.1,6.1 16,6.1 22.1,0s6.1,-16 0,-22.1L55,185c-9.4,-9.4 -9.4,-24.6 0,-33.9s24.6,-9.4 33.9,0l117,117c6.1,6.1 16,6.1 22.1,0s6.1,-16 0,-22.1l-93,-93c-9.4,-9.4 -9.4,-24.6 0,-33.9zM433.1,484.9c-24.2,14.5 -50.9,22.1 -77.7,23.1c48.1,-39.6 76.6,-99 76.6,-162.4l0,-98.1c8.2,-0.1 16,-6.4 16,-16L448,192c0,-17.7 14.3,-32 32,-32s32,14.3 32,32L512,345.6c0,57.1 -30,110 -78.9,139.4zM424.9,18.7c7.4,4.9 9.3,14.8 4.4,22.2l-32,48c-4.9,7.4 -14.8,9.3 -22.2,4.4s-9.3,-14.8 -4.4,-22.2l32,-48c4.9,-7.4 14.8,-9.3 22.2,-4.4z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@ -1,21 +1,71 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appBarLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/Theme.Isolaatti"
|
style="@style/Theme.Isolaatti"
|
||||||
app:navigationIcon="@drawable/baseline_arrow_back_24"
|
app:navigationIcon="@drawable/baseline_arrow_back_24"
|
||||||
app:navigationIconTint="@color/on_surface"
|
app:navigationIconTint="@color/on_surface"
|
||||||
/>
|
app:title="@string/discussion"
|
||||||
|
app:menu="@menu/post_viewer_menu"/>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/profile_image_view"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
||||||
|
tools:srcCompat="@tools:sample/avatars"/>
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/author"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="username"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_horizontal">
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/comments_info"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_marginEnd="4dp"/>
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/likes_info"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/markwon_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
20
app/src/main/res/menu/post_viewer_menu.xml
Normal file
20
app/src/main/res/menu/post_viewer_menu.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/comments"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:title="@string/comments"
|
||||||
|
android:icon="@drawable/comments_solid"
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/like"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:title="@string/clap"
|
||||||
|
android:icon="@drawable/hands_clapping_solid"
|
||||||
|
android:enabled="false"
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
</menu>
|
||||||
@ -80,4 +80,8 @@
|
|||||||
<string name="retry">Retry</string>
|
<string name="retry">Retry</string>
|
||||||
<string name="not_found">The resource you are trying to load could not be found</string>
|
<string name="not_found">The resource you are trying to load could not be found</string>
|
||||||
<string name="unknown_error">An unkwnow error occurred</string>
|
<string name="unknown_error">An unkwnow error occurred</string>
|
||||||
|
<string name="clap">Clap</string>
|
||||||
|
<string name="comments">Comments</string>
|
||||||
|
<string name="likes_info">Claps: %d</string>
|
||||||
|
<string name="comments_info">Comments: %d</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user