From 86dc367837ca2ad35f694e2fd30197e683d833d0 Mon Sep 17 00:00:00 2001 From: Erik Cavazos Date: Sat, 8 Jul 2023 02:17:19 -0600 Subject: [PATCH] WIP --- .idea/assetWizardSettings.xml | 46 +++++ .idea/compiler.xml | 2 +- .idea/misc.xml | 3 +- .idea/render.experimental.xml | 6 + app/build.gradle | 8 +- .../comments/data/remote/CommentDto.kt | 8 - .../comments/data/remote/CommentsApi.kt | 10 -- .../data/repository/CommentsRepositoryImpl.kt | 8 - .../comments/domain/CommentsRepository.kt | 5 - .../connectivity/AuthenticationInterceptor.kt | 6 +- .../com/isolaatti/connectivity/ClientId.kt | 7 + .../isolaatti/connectivity/NetworkStatus.kt | 5 + .../isolaatti/connectivity/RetrofitClient.kt | 1 + .../{home => }/feed/data/remote/FeedApi.kt | 2 +- .../com/isolaatti/feed/data/remote/FeedDto.kt | 6 + .../com/isolaatti/feed/data/remote/PostDto.kt | 12 ++ .../data/repository/FeedRepositoryImpl.kt | 8 +- .../{home => }/feed/domain/model/Post.kt | 6 +- .../feed/domain/repository/FeedRepository.kt | 4 +- .../{home => }/feed/ui/FeedFragment.kt | 52 ++++-- .../followers/data/FollowersRepository.kt | 8 - .../followers/data/FollowersRepositoryImpl.kt | 33 ++++ .../followers/domain/FollowersRepository.kt | 7 + .../java/com/isolaatti/home/HomeActivity.kt | 14 +- .../main/java/com/isolaatti/home/Module.kt | 6 +- .../home/feed/data/remote/FeedDto.kt | 6 - .../home/feed/data/remote/PostDto.kt | 12 -- .../home/feed/presentation/FeedViewModel.kt | 29 --- .../home/feed/ui/FeedRecyclerViewAdapter.kt | 62 ------- .../isolaatti/{posts => posting}/Module.kt | 8 +- .../{ => posting}/comments/Module.kt | 8 +- .../comments/data/remote/CommentDto.kt | 8 + .../comments/data/remote/CommentToPostDto.kt | 2 +- .../comments/data/remote/CommentsApi.kt | 19 ++ .../comments/data/remote/FeedCommentsDto.kt | 3 + .../data/repository/CommentsRepositoryImpl.kt | 33 ++++ .../comments/domain/CommentsRepository.kt | 12 ++ .../comments/domain/model/Comment.kt | 2 +- .../comments/domain/use_case/GetComments.kt | 12 ++ .../presentation/BottomSheetPostComments.kt | 100 +++++++++++ .../CommentsRecyclerViewAdapter.kt | 45 +++++ .../presentation/CommentsViewModel.kt | 58 ++++++ .../common/domain/OnUserInteractedCallback.kt | 6 + .../OnUserInteractedWithPostCallback.kt | 7 + .../options_bottom_sheet/domain/Options.kt | 29 +++ .../BottomSheetPostOptionsViewModel.kt | 15 ++ .../ui/BottomSheetPostOptionsFragment.kt | 59 ++++++ .../com/isolaatti/posting/likes/Module.kt | 25 +++ .../posting/likes/data/LikesRepositoryImpl.kt | 28 +++ .../posting/likes/data/remote/LikeDto.kt | 3 + .../posting/likes/data/remote/LikesApi.kt | 14 ++ .../domain/repository/LikesRepository.kt | 9 + .../posts/data/remote/PostsApi.kt | 6 +- .../data/repository/PostsRepositoryImpl.kt | 8 + .../posting/posts/domain/PostsRepository.kt | 4 + .../posting/posts/domain/use_case/MakePost.kt | 4 + .../presentation/PostsRecyclerViewAdapter.kt | 168 ++++++++++++++++++ .../posts/presentation/PostsViewModel.kt | 89 ++++++++++ .../posts/ui/CreatePostDialogFragment.kt | 4 + .../data/repository/PostsRepositoryImpl.kt | 8 - .../isolaatti/posts/domain/PostsRepository.kt | 4 - .../profile/data/remote/ProfileApi.kt | 3 +- .../profile/domain/use_case/GetProfile.kt | 4 + .../isolaatti/settings/ui/SettingsFragment.kt | 4 + ...ionWrapper.kt => IdentificationWrapper.kt} | 1 + .../main/java/com/isolaatti/utils/Resource.kt | 9 + .../main/java/com/isolaatti/utils/UrlGen.kt | 2 +- .../main/res/drawable/baseline_close_24.xml | 5 + .../main/res/drawable/baseline_delete_24.xml | 5 + .../main/res/drawable/baseline_edit_24.xml | 5 + .../res/drawable/baseline_more_horiz_24.xml | 5 + .../res/drawable/baseline_open_in_full_24.xml | 5 + .../main/res/drawable/baseline_report_24.xml | 5 + .../main/res/drawable/baseline_send_24.xml | 5 + .../main/res/layout-land/fragment_feed.xml | 98 ++++++++++ app/src/main/res/layout/activity_profile.xml | 2 +- app/src/main/res/layout/activity_settings.xml | 1 + .../res/layout/bottom_sheet_post_comments.xml | 70 ++++++++ .../res/layout/bottom_sheet_post_options.xml | 40 +++++ app/src/main/res/layout/comment_layout.xml | 72 ++++++++ .../main/res/layout/fragment_discussions.xml | 12 +- app/src/main/res/layout/fragment_feed.xml | 152 ++++++++-------- app/src/main/res/layout/fragment_settings.xml | 13 +- app/src/main/res/layout/post_layout.xml | 37 ++-- .../main/res/navigation/home_navigation.xml | 2 +- app/src/main/res/values/strings.xml | 8 + 86 files changed, 1426 insertions(+), 321 deletions(-) create mode 100644 .idea/assetWizardSettings.xml create mode 100644 .idea/render.experimental.xml delete mode 100644 app/src/main/java/com/isolaatti/comments/data/remote/CommentDto.kt delete mode 100644 app/src/main/java/com/isolaatti/comments/data/remote/CommentsApi.kt delete mode 100644 app/src/main/java/com/isolaatti/comments/data/repository/CommentsRepositoryImpl.kt delete mode 100644 app/src/main/java/com/isolaatti/comments/domain/CommentsRepository.kt create mode 100644 app/src/main/java/com/isolaatti/connectivity/ClientId.kt create mode 100644 app/src/main/java/com/isolaatti/connectivity/NetworkStatus.kt rename app/src/main/java/com/isolaatti/{home => }/feed/data/remote/FeedApi.kt (82%) create mode 100644 app/src/main/java/com/isolaatti/feed/data/remote/FeedDto.kt create mode 100644 app/src/main/java/com/isolaatti/feed/data/remote/PostDto.kt rename app/src/main/java/com/isolaatti/{home => }/feed/data/repository/FeedRepositoryImpl.kt (69%) rename app/src/main/java/com/isolaatti/{home => }/feed/domain/model/Post.kt (65%) rename app/src/main/java/com/isolaatti/{home => }/feed/domain/repository/FeedRepository.kt (55%) rename app/src/main/java/com/isolaatti/{home => }/feed/ui/FeedFragment.kt (58%) delete mode 100644 app/src/main/java/com/isolaatti/followers/data/FollowersRepository.kt create mode 100644 app/src/main/java/com/isolaatti/followers/data/FollowersRepositoryImpl.kt delete mode 100644 app/src/main/java/com/isolaatti/home/feed/data/remote/FeedDto.kt delete mode 100644 app/src/main/java/com/isolaatti/home/feed/data/remote/PostDto.kt delete mode 100644 app/src/main/java/com/isolaatti/home/feed/presentation/FeedViewModel.kt delete mode 100644 app/src/main/java/com/isolaatti/home/feed/ui/FeedRecyclerViewAdapter.kt rename app/src/main/java/com/isolaatti/{posts => posting}/Module.kt (70%) rename app/src/main/java/com/isolaatti/{ => posting}/comments/Module.kt (69%) create mode 100644 app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentDto.kt rename app/src/main/java/com/isolaatti/{ => posting}/comments/data/remote/CommentToPostDto.kt (61%) create mode 100644 app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentsApi.kt create mode 100644 app/src/main/java/com/isolaatti/posting/comments/data/remote/FeedCommentsDto.kt create mode 100644 app/src/main/java/com/isolaatti/posting/comments/data/repository/CommentsRepositoryImpl.kt create mode 100644 app/src/main/java/com/isolaatti/posting/comments/domain/CommentsRepository.kt rename app/src/main/java/com/isolaatti/{ => posting}/comments/domain/model/Comment.kt (82%) create mode 100644 app/src/main/java/com/isolaatti/posting/comments/domain/use_case/GetComments.kt create mode 100644 app/src/main/java/com/isolaatti/posting/comments/presentation/BottomSheetPostComments.kt create mode 100644 app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsRecyclerViewAdapter.kt create mode 100644 app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsViewModel.kt create mode 100644 app/src/main/java/com/isolaatti/posting/common/domain/OnUserInteractedCallback.kt create mode 100644 app/src/main/java/com/isolaatti/posting/common/domain/OnUserInteractedWithPostCallback.kt create mode 100644 app/src/main/java/com/isolaatti/posting/common/options_bottom_sheet/domain/Options.kt create mode 100644 app/src/main/java/com/isolaatti/posting/common/options_bottom_sheet/presentation/BottomSheetPostOptionsViewModel.kt create mode 100644 app/src/main/java/com/isolaatti/posting/common/options_bottom_sheet/ui/BottomSheetPostOptionsFragment.kt create mode 100644 app/src/main/java/com/isolaatti/posting/likes/Module.kt create mode 100644 app/src/main/java/com/isolaatti/posting/likes/data/LikesRepositoryImpl.kt create mode 100644 app/src/main/java/com/isolaatti/posting/likes/data/remote/LikeDto.kt create mode 100644 app/src/main/java/com/isolaatti/posting/likes/data/remote/LikesApi.kt create mode 100644 app/src/main/java/com/isolaatti/posting/likes/domain/repository/LikesRepository.kt rename app/src/main/java/com/isolaatti/{ => posting}/posts/data/remote/PostsApi.kt (82%) create mode 100644 app/src/main/java/com/isolaatti/posting/posts/data/repository/PostsRepositoryImpl.kt create mode 100644 app/src/main/java/com/isolaatti/posting/posts/domain/PostsRepository.kt create mode 100644 app/src/main/java/com/isolaatti/posting/posts/domain/use_case/MakePost.kt create mode 100644 app/src/main/java/com/isolaatti/posting/posts/presentation/PostsRecyclerViewAdapter.kt create mode 100644 app/src/main/java/com/isolaatti/posting/posts/presentation/PostsViewModel.kt create mode 100644 app/src/main/java/com/isolaatti/posting/posts/ui/CreatePostDialogFragment.kt delete mode 100644 app/src/main/java/com/isolaatti/posts/data/repository/PostsRepositoryImpl.kt delete mode 100644 app/src/main/java/com/isolaatti/posts/domain/PostsRepository.kt create mode 100644 app/src/main/java/com/isolaatti/profile/domain/use_case/GetProfile.kt rename app/src/main/java/com/isolaatti/utils/{IntIdentificationWrapper.kt => IdentificationWrapper.kt} (60%) create mode 100644 app/src/main/java/com/isolaatti/utils/Resource.kt create mode 100644 app/src/main/res/drawable/baseline_close_24.xml create mode 100644 app/src/main/res/drawable/baseline_delete_24.xml create mode 100644 app/src/main/res/drawable/baseline_edit_24.xml create mode 100644 app/src/main/res/drawable/baseline_more_horiz_24.xml create mode 100644 app/src/main/res/drawable/baseline_open_in_full_24.xml create mode 100644 app/src/main/res/drawable/baseline_report_24.xml create mode 100644 app/src/main/res/drawable/baseline_send_24.xml create mode 100644 app/src/main/res/layout-land/fragment_feed.xml create mode 100644 app/src/main/res/layout/bottom_sheet_post_comments.xml create mode 100644 app/src/main/res/layout/bottom_sheet_post_options.xml create mode 100644 app/src/main/res/layout/comment_layout.xml diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml new file mode 100644 index 0000000..03b92aa --- /dev/null +++ b/.idea/assetWizardSettings.xml @@ -0,0 +1,46 @@ + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8..b589d56 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 54d5acd..773fe0f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,6 @@ - - + diff --git a/.idea/render.experimental.xml b/.idea/render.experimental.xml new file mode 100644 index 0000000..8ec256a --- /dev/null +++ b/.idea/render.experimental.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 0af2361..be6c315 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,8 +40,9 @@ android { dependencies { - implementation 'androidx.core:core-ktx:1.9.0' + implementation 'androidx.core:core-ktx:1.10.1' implementation 'androidx.appcompat:appcompat:1.6.0' + implementation "androidx.recyclerview:recyclerview:1.3.0" implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' @@ -66,7 +67,7 @@ dependencies { implementation "com.google.android.material:material:1.8.0" // Navigation - def nav_version = "2.5.3" + def nav_version = "2.6.0" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version" @@ -81,6 +82,9 @@ dependencies { // Markwon final def markwon_version = '4.6.2' + // Customtabs + implementation 'androidx.browser:browser:1.5.0' + implementation "io.noties.markwon:core:$markwon_version" implementation "io.noties.markwon:editor:$markwon_version" implementation "io.noties.markwon:image-picasso:$markwon_version" diff --git a/app/src/main/java/com/isolaatti/comments/data/remote/CommentDto.kt b/app/src/main/java/com/isolaatti/comments/data/remote/CommentDto.kt deleted file mode 100644 index f8be4ef..0000000 --- a/app/src/main/java/com/isolaatti/comments/data/remote/CommentDto.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.isolaatti.comments.data.remote - -import com.isolaatti.comments.domain.model.Comment - -data class CommentDto( - val comment: Comment, - val username: String -) diff --git a/app/src/main/java/com/isolaatti/comments/data/remote/CommentsApi.kt b/app/src/main/java/com/isolaatti/comments/data/remote/CommentsApi.kt deleted file mode 100644 index db7a6d8..0000000 --- a/app/src/main/java/com/isolaatti/comments/data/remote/CommentsApi.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.isolaatti.comments.data.remote - -import retrofit2.Call -import retrofit2.http.Body -import retrofit2.http.POST - -interface CommentsApi { - @POST("Posting/Post/{postId}/Comment") - fun postComment(@Body commentToPost: CommentToPostDto): Call -} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/comments/data/repository/CommentsRepositoryImpl.kt b/app/src/main/java/com/isolaatti/comments/data/repository/CommentsRepositoryImpl.kt deleted file mode 100644 index e361ceb..0000000 --- a/app/src/main/java/com/isolaatti/comments/data/repository/CommentsRepositoryImpl.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.isolaatti.comments.data.repository - -import com.isolaatti.comments.data.remote.CommentsApi -import com.isolaatti.comments.domain.CommentsRepository -import javax.inject.Inject - -class CommentsRepositoryImpl @Inject constructor(private val commentsApi: CommentsApi) : CommentsRepository { -} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/comments/domain/CommentsRepository.kt b/app/src/main/java/com/isolaatti/comments/domain/CommentsRepository.kt deleted file mode 100644 index 81b7de5..0000000 --- a/app/src/main/java/com/isolaatti/comments/domain/CommentsRepository.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.isolaatti.comments.domain - -interface CommentsRepository { - -} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/connectivity/AuthenticationInterceptor.kt b/app/src/main/java/com/isolaatti/connectivity/AuthenticationInterceptor.kt index e488b1d..8bdeb50 100644 --- a/app/src/main/java/com/isolaatti/connectivity/AuthenticationInterceptor.kt +++ b/app/src/main/java/com/isolaatti/connectivity/AuthenticationInterceptor.kt @@ -3,7 +3,6 @@ package com.isolaatti.connectivity import com.isolaatti.auth.domain.AuthRepository import okhttp3.Interceptor import okhttp3.Response - class AuthenticationInterceptor(private val authRepository: dagger.Lazy) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { @@ -16,7 +15,10 @@ class AuthenticationInterceptor(private val authRepository: dagger.Lazy, + val moreContent: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/feed/data/remote/PostDto.kt b/app/src/main/java/com/isolaatti/feed/data/remote/PostDto.kt new file mode 100644 index 0000000..3db1d40 --- /dev/null +++ b/app/src/main/java/com/isolaatti/feed/data/remote/PostDto.kt @@ -0,0 +1,12 @@ +package com.isolaatti.feed.data.remote + +import com.isolaatti.feed.domain.model.Post + +data class PostDto( + val post: Post, + var numberOfLikes: Int, + var numberOfComments: Int, + val userName: String, + val squadName: String?, + var liked: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/home/feed/data/repository/FeedRepositoryImpl.kt b/app/src/main/java/com/isolaatti/feed/data/repository/FeedRepositoryImpl.kt similarity index 69% rename from app/src/main/java/com/isolaatti/home/feed/data/repository/FeedRepositoryImpl.kt rename to app/src/main/java/com/isolaatti/feed/data/repository/FeedRepositoryImpl.kt index dd9fb5e..2f5cb5e 100644 --- a/app/src/main/java/com/isolaatti/home/feed/data/repository/FeedRepositoryImpl.kt +++ b/app/src/main/java/com/isolaatti/feed/data/repository/FeedRepositoryImpl.kt @@ -1,8 +1,8 @@ -package com.isolaatti.home.feed.data.repository +package com.isolaatti.feed.data.repository -import com.isolaatti.home.feed.data.remote.FeedApi -import com.isolaatti.home.feed.data.remote.FeedDto -import com.isolaatti.home.feed.domain.repository.FeedRepository +import com.isolaatti.feed.data.remote.FeedApi +import com.isolaatti.feed.data.remote.FeedDto +import com.isolaatti.feed.domain.repository.FeedRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import retrofit2.awaitResponse diff --git a/app/src/main/java/com/isolaatti/home/feed/domain/model/Post.kt b/app/src/main/java/com/isolaatti/feed/domain/model/Post.kt similarity index 65% rename from app/src/main/java/com/isolaatti/home/feed/domain/model/Post.kt rename to app/src/main/java/com/isolaatti/feed/domain/model/Post.kt index 89b51f1..5c79842 100644 --- a/app/src/main/java/com/isolaatti/home/feed/domain/model/Post.kt +++ b/app/src/main/java/com/isolaatti/feed/domain/model/Post.kt @@ -1,12 +1,12 @@ -package com.isolaatti.home.feed.domain.model +package com.isolaatti.feed.domain.model data class Post( val id: Long, - val textContent: String, + var textContent: String, val userId: Int, val privacy: Int, val date: String, - val audioId: String, + var audioId: String, val squadId: String, val linkedDiscussionId: Long, val linkedCommentId: Long diff --git a/app/src/main/java/com/isolaatti/home/feed/domain/repository/FeedRepository.kt b/app/src/main/java/com/isolaatti/feed/domain/repository/FeedRepository.kt similarity index 55% rename from app/src/main/java/com/isolaatti/home/feed/domain/repository/FeedRepository.kt rename to app/src/main/java/com/isolaatti/feed/domain/repository/FeedRepository.kt index df9c9ba..9a4db92 100644 --- a/app/src/main/java/com/isolaatti/home/feed/domain/repository/FeedRepository.kt +++ b/app/src/main/java/com/isolaatti/feed/domain/repository/FeedRepository.kt @@ -1,6 +1,6 @@ -package com.isolaatti.home.feed.domain.repository +package com.isolaatti.feed.domain.repository -import com.isolaatti.home.feed.data.remote.FeedDto +import com.isolaatti.feed.data.remote.FeedDto import kotlinx.coroutines.flow.Flow interface FeedRepository { diff --git a/app/src/main/java/com/isolaatti/home/feed/ui/FeedFragment.kt b/app/src/main/java/com/isolaatti/feed/ui/FeedFragment.kt similarity index 58% rename from app/src/main/java/com/isolaatti/home/feed/ui/FeedFragment.kt rename to app/src/main/java/com/isolaatti/feed/ui/FeedFragment.kt index 8e3b741..d82a3eb 100644 --- a/app/src/main/java/com/isolaatti/home/feed/ui/FeedFragment.kt +++ b/app/src/main/java/com/isolaatti/feed/ui/FeedFragment.kt @@ -1,8 +1,8 @@ -package com.isolaatti.home.feed.ui +package com.isolaatti.feed.ui import android.content.Intent -import androidx.lifecycle.ViewModelProvider import android.os.Bundle +import android.util.Log import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View @@ -11,7 +11,11 @@ import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.LinearLayoutManager import com.isolaatti.R import com.isolaatti.databinding.FragmentFeedBinding -import com.isolaatti.home.feed.presentation.FeedViewModel +import com.isolaatti.posting.posts.presentation.PostsViewModel +import com.isolaatti.posting.comments.presentation.BottomSheetPostComments +import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback +import com.isolaatti.posting.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment +import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter import com.isolaatti.profile.ui.ProfileActivity import com.isolaatti.settings.ui.SettingsActivity import com.isolaatti.utils.PicassoImagesPluginDef @@ -23,15 +27,15 @@ import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAb import io.noties.markwon.linkify.LinkifyPlugin @AndroidEntryPoint -class FeedFragment : Fragment() { +class FeedFragment : Fragment(), OnUserInteractedWithPostCallback { companion object { fun newInstance() = FeedFragment() } - private val viewModel: FeedViewModel by activityViewModels() + private val viewModel: PostsViewModel by activityViewModels() private lateinit var viewBinding: FragmentFeedBinding - private lateinit var adapter: FeedRecyclerViewAdapter + private lateinit var adapter: PostsRecyclerViewAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -45,9 +49,10 @@ class FeedFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewBinding.topAppBar.setNavigationOnClickListener { - viewBinding.drawerLayout.openDrawer(viewBinding.homeDrawer) + viewBinding.drawerLayout?.openDrawer(viewBinding.homeDrawer) } + viewBinding.homeDrawer.setNavigationItemSelectedListener { when(it.itemId) { R.id.my_profile_menu_item -> { @@ -73,12 +78,37 @@ class FeedFragment : Fragment() { .usePlugin(PicassoImagesPluginDef.picassoImagePlugin) .usePlugin(LinkifyPlugin.create()) .build() - adapter = FeedRecyclerViewAdapter(markwon) + adapter = PostsRecyclerViewAdapter(markwon, this, listOf()) viewBinding.feedRecyclerView.adapter = adapter viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext()) - viewModel.feed.observe(viewLifecycleOwner){ - adapter.submitList(it.data) - } + + viewModel.posts.observe(viewLifecycleOwner){ + Log.d("recycler", it.data.toString()) + adapter.updateList(it.data.toList(),null) + } + viewModel.postLiked.observe(viewLifecycleOwner) { + adapter.updateList(viewModel.posts.value?.data, PostsRecyclerViewAdapter.UpdateEvent( + PostsRecyclerViewAdapter.UpdateEvent.UpdateType.POST_LIKED, it.postId)) + } } + + override fun onLiked(postId: Long) = viewModel.likePost(postId) + + override fun onUnLiked(postId: Long) = viewModel.unLikePost(postId) + + override fun onOptions(postId: Long) { + val modalBottomSheet = BottomSheetPostOptionsFragment() + modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG) + } + + override fun onComment(postId: Long) { + val modalBottomSheet = BottomSheetPostComments.getInstance(postId) + modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostComments.TAG) + } + + override fun onProfileClick(userId: Int) { + TODO("Not yet implemented") + } + } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/followers/data/FollowersRepository.kt b/app/src/main/java/com/isolaatti/followers/data/FollowersRepository.kt deleted file mode 100644 index 4c0b6ea..0000000 --- a/app/src/main/java/com/isolaatti/followers/data/FollowersRepository.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.isolaatti.followers.data - -import com.isolaatti.followers.data.remote.FollowersApi -import com.isolaatti.followers.domain.FollowersRepository -import javax.inject.Inject - -class FollowersRepositoryImpl @Inject constructor(followersApi: FollowersApi) : FollowersRepository { -} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/followers/data/FollowersRepositoryImpl.kt b/app/src/main/java/com/isolaatti/followers/data/FollowersRepositoryImpl.kt new file mode 100644 index 0000000..adbf523 --- /dev/null +++ b/app/src/main/java/com/isolaatti/followers/data/FollowersRepositoryImpl.kt @@ -0,0 +1,33 @@ +package com.isolaatti.followers.data + +import com.isolaatti.followers.data.remote.FollowersApi +import com.isolaatti.followers.domain.FollowersRepository +import com.isolaatti.profile.data.remote.ProfileListItemDto +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import retrofit2.awaitResponse +import javax.inject.Inject + +class FollowersRepositoryImpl @Inject constructor(private val followersApi: FollowersApi) : FollowersRepository { + override fun getFollowersOfUser(userId: Int): Flow> = flow { + val response = followersApi.getFollowersOfUser(userId).awaitResponse() + if(response.isSuccessful) { + response.body()?.let { emit(response.body()!!) } + } + } + + override fun getFollowingsOfUser(userId: Int): Flow> = flow { + val response = followersApi.getFollowingsOfUser(userId).awaitResponse() + if(response.isSuccessful) { + + } + } + + override fun followUser(userId: Int): Flow = flow { + + } + + override fun unfollowUser(userId: Int): Flow = flow { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/followers/domain/FollowersRepository.kt b/app/src/main/java/com/isolaatti/followers/domain/FollowersRepository.kt index 2758bc6..3bfe091 100644 --- a/app/src/main/java/com/isolaatti/followers/domain/FollowersRepository.kt +++ b/app/src/main/java/com/isolaatti/followers/domain/FollowersRepository.kt @@ -1,4 +1,11 @@ package com.isolaatti.followers.domain +import com.isolaatti.profile.data.remote.ProfileListItemDto +import kotlinx.coroutines.flow.Flow + interface FollowersRepository { + fun getFollowersOfUser(userId: Int): Flow> + fun getFollowingsOfUser(userId: Int): Flow> + fun followUser(userId: Int): Flow + fun unfollowUser(userId: Int): Flow } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/home/HomeActivity.kt b/app/src/main/java/com/isolaatti/home/HomeActivity.kt index 1fe5c43..fedb0fe 100644 --- a/app/src/main/java/com/isolaatti/home/HomeActivity.kt +++ b/app/src/main/java/com/isolaatti/home/HomeActivity.kt @@ -4,22 +4,17 @@ import android.os.Bundle import android.view.Menu import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.content.res.AppCompatResources -import androidx.core.app.ActivityCompat -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController -import com.google.android.material.search.SearchBar import com.isolaatti.R import com.isolaatti.databinding.ActivityHomeBinding -import com.isolaatti.home.feed.presentation.FeedViewModel +import com.isolaatti.posting.posts.presentation.PostsViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class HomeActivity : AppCompatActivity() { lateinit var viewBinding: ActivityHomeBinding - val feedViewModel: FeedViewModel by viewModels() + val postsViewModel: PostsViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -28,7 +23,10 @@ class HomeActivity : AppCompatActivity() { val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment viewBinding.bottomNavigation.setupWithNavController(navHostFragment.navController) - feedViewModel.getFeed() + if(savedInstanceState == null) { + postsViewModel.getFeed() + } + } override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/app/src/main/java/com/isolaatti/home/Module.kt b/app/src/main/java/com/isolaatti/home/Module.kt index 13eaf9b..e1655c3 100644 --- a/app/src/main/java/com/isolaatti/home/Module.kt +++ b/app/src/main/java/com/isolaatti/home/Module.kt @@ -1,9 +1,9 @@ package com.isolaatti.home import com.isolaatti.connectivity.RetrofitClient -import com.isolaatti.home.feed.data.remote.FeedApi -import com.isolaatti.home.feed.data.repository.FeedRepositoryImpl -import com.isolaatti.home.feed.domain.repository.FeedRepository +import com.isolaatti.feed.data.remote.FeedApi +import com.isolaatti.feed.data.repository.FeedRepositoryImpl +import com.isolaatti.feed.domain.repository.FeedRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/app/src/main/java/com/isolaatti/home/feed/data/remote/FeedDto.kt b/app/src/main/java/com/isolaatti/home/feed/data/remote/FeedDto.kt deleted file mode 100644 index 9e6d795..0000000 --- a/app/src/main/java/com/isolaatti/home/feed/data/remote/FeedDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.isolaatti.home.feed.data.remote - -data class FeedDto( - val data: List, - val moreContent: Boolean -) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/home/feed/data/remote/PostDto.kt b/app/src/main/java/com/isolaatti/home/feed/data/remote/PostDto.kt deleted file mode 100644 index 7f055fd..0000000 --- a/app/src/main/java/com/isolaatti/home/feed/data/remote/PostDto.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.isolaatti.home.feed.data.remote - -import com.isolaatti.home.feed.domain.model.Post - -data class PostDto( - val post: Post, - val numberOfLikes: Int, - val numberOfComments: Int, - val userName: String, - val squadName: String?, - val liked: Boolean -) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/home/feed/presentation/FeedViewModel.kt b/app/src/main/java/com/isolaatti/home/feed/presentation/FeedViewModel.kt deleted file mode 100644 index 1e235f2..0000000 --- a/app/src/main/java/com/isolaatti/home/feed/presentation/FeedViewModel.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.isolaatti.home.feed.presentation - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.isolaatti.home.feed.data.remote.FeedDto -import com.isolaatti.home.feed.domain.repository.FeedRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class FeedViewModel @Inject constructor(private val feedRepository: FeedRepository) : ViewModel() { - - private val _feed: MutableLiveData = MutableLiveData() - val feed: LiveData get() = _feed - - private fun getLastId(): Long = try {_feed.value?.data?.last()?.post?.id ?: 0} catch (e: NoSuchElementException) { 0 } - - fun getFeed() { - viewModelScope.launch { - feedRepository.getNextPage(0, 20).collect { - _feed.postValue(it) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/home/feed/ui/FeedRecyclerViewAdapter.kt b/app/src/main/java/com/isolaatti/home/feed/ui/FeedRecyclerViewAdapter.kt deleted file mode 100644 index 869ccc5..0000000 --- a/app/src/main/java/com/isolaatti/home/feed/ui/FeedRecyclerViewAdapter.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.isolaatti.home.feed.ui - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView.ViewHolder -import com.isolaatti.R -import com.isolaatti.home.feed.data.remote.PostDto -import com.isolaatti.utils.UrlGen.userProfileImage -import com.squareup.picasso.Picasso -import io.noties.markwon.Markwon -import java.text.DateFormat -import java.util.Date - -class FeedRecyclerViewAdapter(private val markwon: Markwon) : ListAdapter(DIFF_CALLBACK) { - inner class FeedViewHolder(itemView: View) : ViewHolder(itemView) { - fun bindView(postDto: PostDto) { - val username: TextView = itemView.findViewById(R.id.text_view_username) - username.text = postDto.userName - - val profileImageView: ImageView = itemView.findViewById(R.id.avatar_picture) - Picasso.get().load(userProfileImage(postDto.post.userId)).into(profileImageView) - - val dateTextView: TextView = itemView.findViewById(R.id.text_view_date) - dateTextView.text = postDto.post.date - - val content: TextView = itemView.findViewById(R.id.post_content) - markwon.setMarkdown(content, postDto.post.textContent) - - } - } - - - - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.post_layout, parent, false) - - return FeedViewHolder(view) - } - - override fun onBindViewHolder(holder: FeedViewHolder, position: Int) { - holder.bindView(getItem(position)) - } - - companion object { - val DIFF_CALLBACK = object: DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: PostDto, newItem: PostDto): Boolean { - return oldItem.post.id == newItem.post.id - } - - override fun areContentsTheSame(oldItem: PostDto, newItem: PostDto): Boolean { - return oldItem.post.id == newItem.post.id && oldItem.post.textContent == newItem.post.textContent - } - - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posts/Module.kt b/app/src/main/java/com/isolaatti/posting/Module.kt similarity index 70% rename from app/src/main/java/com/isolaatti/posts/Module.kt rename to app/src/main/java/com/isolaatti/posting/Module.kt index 09cb3e2..771cac5 100644 --- a/app/src/main/java/com/isolaatti/posts/Module.kt +++ b/app/src/main/java/com/isolaatti/posting/Module.kt @@ -1,9 +1,9 @@ -package com.isolaatti.posts +package com.isolaatti.posting import com.isolaatti.connectivity.RetrofitClient -import com.isolaatti.posts.data.remote.PostsApi -import com.isolaatti.posts.data.repository.PostsRepositoryImpl -import com.isolaatti.posts.domain.PostsRepository +import com.isolaatti.posting.posts.data.remote.PostsApi +import com.isolaatti.posting.posts.data.repository.PostsRepositoryImpl +import com.isolaatti.posting.posts.domain.PostsRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/app/src/main/java/com/isolaatti/comments/Module.kt b/app/src/main/java/com/isolaatti/posting/comments/Module.kt similarity index 69% rename from app/src/main/java/com/isolaatti/comments/Module.kt rename to app/src/main/java/com/isolaatti/posting/comments/Module.kt index f7bfde6..2d44693 100644 --- a/app/src/main/java/com/isolaatti/comments/Module.kt +++ b/app/src/main/java/com/isolaatti/posting/comments/Module.kt @@ -1,8 +1,8 @@ -package com.isolaatti.comments +package com.isolaatti.posting.comments -import com.isolaatti.comments.data.remote.CommentsApi -import com.isolaatti.comments.data.repository.CommentsRepositoryImpl -import com.isolaatti.comments.domain.CommentsRepository +import com.isolaatti.posting.comments.data.remote.CommentsApi +import com.isolaatti.posting.comments.data.repository.CommentsRepositoryImpl +import com.isolaatti.posting.comments.domain.CommentsRepository import com.isolaatti.connectivity.RetrofitClient import dagger.Module import dagger.Provides diff --git a/app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentDto.kt b/app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentDto.kt new file mode 100644 index 0000000..31fb834 --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentDto.kt @@ -0,0 +1,8 @@ +package com.isolaatti.posting.comments.data.remote + +import com.isolaatti.posting.comments.domain.model.Comment + +data class CommentDto( + val comment: Comment, + val username: String +) diff --git a/app/src/main/java/com/isolaatti/comments/data/remote/CommentToPostDto.kt b/app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentToPostDto.kt similarity index 61% rename from app/src/main/java/com/isolaatti/comments/data/remote/CommentToPostDto.kt rename to app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentToPostDto.kt index 67512ea..9a2ce31 100644 --- a/app/src/main/java/com/isolaatti/comments/data/remote/CommentToPostDto.kt +++ b/app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentToPostDto.kt @@ -1,4 +1,4 @@ -package com.isolaatti.comments.data.remote +package com.isolaatti.posting.comments.data.remote data class CommentToPostDto( val content: String, diff --git a/app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentsApi.kt b/app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentsApi.kt new file mode 100644 index 0000000..be68cb3 --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/data/remote/CommentsApi.kt @@ -0,0 +1,19 @@ +package com.isolaatti.posting.comments.data.remote + +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.Query + +interface CommentsApi { + @POST("Posting/Post/{postId}/Comment") + fun postComment(@Body commentToPost: CommentToPostDto): Call + + @GET("Fetch/Post/{postId}/Comments") + fun getCommentsOfPosts(@Path("postId") postId: Long, @Query("lastId") lastId: Long, @Query("take") count: Int): Call + + @GET("Fetch/Comments/{commentId}") + fun getComment(@Path("commentId") commentId: Long): Call +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/comments/data/remote/FeedCommentsDto.kt b/app/src/main/java/com/isolaatti/posting/comments/data/remote/FeedCommentsDto.kt new file mode 100644 index 0000000..631e149 --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/data/remote/FeedCommentsDto.kt @@ -0,0 +1,3 @@ +package com.isolaatti.posting.comments.data.remote + +data class FeedCommentsDto(val data: List, val moreContent: Boolean) diff --git a/app/src/main/java/com/isolaatti/posting/comments/data/repository/CommentsRepositoryImpl.kt b/app/src/main/java/com/isolaatti/posting/comments/data/repository/CommentsRepositoryImpl.kt new file mode 100644 index 0000000..a02d19d --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/data/repository/CommentsRepositoryImpl.kt @@ -0,0 +1,33 @@ +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 kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import retrofit2.awaitResponse +import javax.inject.Inject + +class CommentsRepositoryImpl @Inject constructor(private val commentsApi: CommentsApi) : + CommentsRepository { + override fun getComments(postId: Long, lastId: Long): Flow = flow { + val response = commentsApi.getCommentsOfPosts(postId, lastId, 15).awaitResponse() + if(response.isSuccessful){ + response.body()?.let { emit(it) } + } + } + + override fun getComment(commentId: Long): Flow = flow { + val response = commentsApi.getComment(commentId).awaitResponse() + if(response.isSuccessful) { + response.body()?.let { emit(it) } + } + } + + override fun postComment(commentToPostDto: CommentToPostDto, postId: Long): Flow = flow { + val response = commentsApi.postComment(commentToPostDto).awaitResponse() + emit(response.isSuccessful) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/comments/domain/CommentsRepository.kt b/app/src/main/java/com/isolaatti/posting/comments/domain/CommentsRepository.kt new file mode 100644 index 0000000..894b761 --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/domain/CommentsRepository.kt @@ -0,0 +1,12 @@ +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 kotlinx.coroutines.flow.Flow + +interface CommentsRepository { + fun getComments(postId: Long, lastId: Long): Flow + fun getComment(commentId: Long): Flow + fun postComment(commentToPostDto: CommentToPostDto, postId: Long): Flow +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/comments/domain/model/Comment.kt b/app/src/main/java/com/isolaatti/posting/comments/domain/model/Comment.kt similarity index 82% rename from app/src/main/java/com/isolaatti/comments/domain/model/Comment.kt rename to app/src/main/java/com/isolaatti/posting/comments/domain/model/Comment.kt index d33fd1c..9f88182 100644 --- a/app/src/main/java/com/isolaatti/comments/domain/model/Comment.kt +++ b/app/src/main/java/com/isolaatti/posting/comments/domain/model/Comment.kt @@ -1,4 +1,4 @@ -package com.isolaatti.comments.domain.model +package com.isolaatti.posting.comments.domain.model data class Comment( val id: Long, diff --git a/app/src/main/java/com/isolaatti/posting/comments/domain/use_case/GetComments.kt b/app/src/main/java/com/isolaatti/posting/comments/domain/use_case/GetComments.kt new file mode 100644 index 0000000..4914455 --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/domain/use_case/GetComments.kt @@ -0,0 +1,12 @@ +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 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 = + commentsRepository.getComments(postId, lastId ?: 0) +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/comments/presentation/BottomSheetPostComments.kt b/app/src/main/java/com/isolaatti/posting/comments/presentation/BottomSheetPostComments.kt new file mode 100644 index 0000000..ec5a25b --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/presentation/BottomSheetPostComments.kt @@ -0,0 +1,100 @@ +package com.isolaatti.posting.comments.presentation + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.isolaatti.databinding.BottomSheetPostCommentsBinding +import com.isolaatti.posting.common.domain.OnUserInteractedCallback +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.utils.PicassoImagesPluginDef +import dagger.hilt.android.AndroidEntryPoint +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 + +@AndroidEntryPoint +class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedCallback { + private lateinit var viewBinding: BottomSheetPostCommentsBinding + val viewModel: CommentsViewModel by viewModels() + + val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val postId = arguments?.getLong(ARG_POST_ID) + if (postId != null) { + viewModel.postId = postId + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + viewBinding = BottomSheetPostCommentsBinding.inflate(inflater) + + return viewBinding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewBinding.recyclerComments.isNestedScrollingEnabled = true + + val markwon = Markwon.builder(requireContext()) + .usePlugin(object: AbstractMarkwonPlugin() { + override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { + builder + .imageDestinationProcessor( + ImageDestinationProcessorRelativeToAbsolute + .create("https://isolaatti.com/")) + } + }) + .usePlugin(PicassoImagesPluginDef.picassoImagePlugin) + .usePlugin(LinkifyPlugin.create()) + .build() + + val adapter = CommentsRecyclerViewAdapter(listOf(), markwon, this) + viewBinding.recyclerComments.adapter = adapter + viewBinding.recyclerComments.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + + viewModel.comments.observe(viewLifecycleOwner) { + adapter.submitList(it) + } + } + + companion object { + const val TAG = "BottomSheetPostComments" + + const val ARG_POST_ID = "postId" + + fun getInstance(postId: Long): BottomSheetPostComments { + return BottomSheetPostComments().apply { + arguments = Bundle().apply { + putLong(ARG_POST_ID, postId) + } + } + } + } + + + override fun onOptions(postId: Long) { + val fragment = BottomSheetPostOptionsFragment() + fragment.show(parentFragmentManager, BottomSheetPostOptionsFragment.TAG) + optionsViewModel.setOptions(Options.commentOptions) + } + + override fun onProfileClick(userId: Int) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsRecyclerViewAdapter.kt b/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsRecyclerViewAdapter.kt new file mode 100644 index 0000000..8b05895 --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsRecyclerViewAdapter.kt @@ -0,0 +1,45 @@ +package com.isolaatti.posting.comments.presentation + +import android.view.LayoutInflater +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.common.domain.OnUserInteractedCallback +import com.isolaatti.utils.UrlGen +import com.squareup.picasso.Picasso +import io.noties.markwon.Markwon + +class CommentsRecyclerViewAdapter(private var list: List, private val markwon: Markwon, private val callback: OnUserInteractedCallback) : RecyclerView.Adapter() { + + inner class CommentViewHolder(val viewBinding: CommentLayoutBinding) : RecyclerView.ViewHolder(viewBinding.root) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentViewHolder { + return CommentViewHolder(CommentLayoutBinding.inflate(LayoutInflater.from(parent.context))) + } + + override fun getItemCount(): Int = list.count() + + 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.textViewUsername.text = comment.username + holder.viewBinding.textViewUsername.setOnClickListener { + callback.onProfileClick(comment.comment.userId) + } + holder.viewBinding.moreButton.setOnClickListener { + callback.onOptions(comment.comment.id) + } + Picasso.get() + .load(UrlGen.userProfileImage(comment.comment.userId)) + .into(holder.viewBinding.avatarPicture) + } + + fun submitList(commentDtoList: List) { + val lastIndex = if(list.count() - 1 < 1) 0 else list.count() - 1 + list = commentDtoList + notifyItemRangeChanged(lastIndex, commentDtoList.count()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsViewModel.kt b/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsViewModel.kt new file mode 100644 index 0000000..e63c758 --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/comments/presentation/CommentsViewModel.kt @@ -0,0 +1,58 @@ +package com.isolaatti.posting.comments.presentation + +import androidx.lifecycle.LiveData +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.use_case.GetComments +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 CommentsViewModel @Inject constructor(private val getComments: GetComments) : ViewModel() { + private val _comments: MutableLiveData> = MutableLiveData() + + val comments: LiveData> get() = _comments + + /** + * postId to query comments for. First page will be fetched when set. + * Call getContent() to get more content if available + */ + var postId: Long = 0 + set(value) { + field = value + getContent() + } + + private var lastId: Long = 0L + + fun getContent() { + viewModelScope.launch { + getComments(postId, lastId).onEach { + val newList = _comments.value?.toMutableList() ?: mutableListOf() + newList.addAll(it.data) + _comments.postValue(newList) + if(it.data.isNotEmpty()){ + lastId = it.data.last().comment.id + } + + }.flowOn(Dispatchers.IO).launchIn(this) + } + } + + /** + * Use when new comment has been posted + */ + fun putCommentAtTheBeginning(commentDto: CommentDto) { + val newList: MutableList = mutableListOf(commentDto) + newList.addAll(_comments.value ?: mutableListOf()) + _comments.postValue(newList) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/common/domain/OnUserInteractedCallback.kt b/app/src/main/java/com/isolaatti/posting/common/domain/OnUserInteractedCallback.kt new file mode 100644 index 0000000..f71ab16 --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/common/domain/OnUserInteractedCallback.kt @@ -0,0 +1,6 @@ +package com.isolaatti.posting.common.domain + +interface OnUserInteractedCallback { + fun onOptions(postId: Long) + fun onProfileClick(userId: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/common/domain/OnUserInteractedWithPostCallback.kt b/app/src/main/java/com/isolaatti/posting/common/domain/OnUserInteractedWithPostCallback.kt new file mode 100644 index 0000000..23d0aeb --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/common/domain/OnUserInteractedWithPostCallback.kt @@ -0,0 +1,7 @@ +package com.isolaatti.posting.common.domain + +interface OnUserInteractedWithPostCallback : OnUserInteractedCallback { + fun onLiked(postId: Long) + fun onUnLiked(postId: Long) + fun onComment(postId: Long) +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/posting/common/options_bottom_sheet/domain/Options.kt b/app/src/main/java/com/isolaatti/posting/common/options_bottom_sheet/domain/Options.kt new file mode 100644 index 0000000..b0954fd --- /dev/null +++ b/app/src/main/java/com/isolaatti/posting/common/options_bottom_sheet/domain/Options.kt @@ -0,0 +1,29 @@ +package com.isolaatti.posting.common.options_bottom_sheet.domain + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import com.isolaatti.R + +data class Options( + @StringRes val title: Int, + val items: List