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