diff --git a/android/2023-emmsale/app/src/main/AndroidManifest.xml b/android/2023-emmsale/app/src/main/AndroidManifest.xml
index 24f2124ee..50eee8eed 100644
--- a/android/2023-emmsale/app/src/main/AndroidManifest.xml
+++ b/android/2023-emmsale/app/src/main/AndroidManifest.xml
@@ -22,6 +22,9 @@
android:theme="@style/Theme.Emmsale"
android:usesCleartextTraffic="true"
tools:targetApi="33">
+
+
-
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/FeedResponse.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/FeedResponse.kt
index b889c375c..090abc8af 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/FeedResponse.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/FeedResponse.kt
@@ -11,6 +11,8 @@ data class FeedResponse(
val id: Long,
@SerialName("eventId")
val eventId: Long,
+ @SerialName("eventName")
+ val eventName: String = "",
@SerialName("title")
val title: String,
@SerialName("content")
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/MyPostResponse.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/MyPostResponse.kt
deleted file mode 100644
index a0ab4e9e4..000000000
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/MyPostResponse.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.emmsale.data.apiModel.response
-
-import com.emmsale.data.apiModel.serializer.DateSerializer
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-import java.time.LocalDate
-
-@Serializable
-data class MyPostResponse(
- @SerialName("postId")
- val postId: Long,
- @SerialName("memberId")
- val memberId: Long,
- @SerialName("eventId")
- val eventId: Long,
- @SerialName("eventName")
- val eventName: String,
- @SerialName("content")
- val content: String,
- @SerialName("updatedAt")
- @Serializable(with = DateSerializer::class)
- val updatedAt: LocalDate,
-)
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/RecruitmentResponse.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/RecruitmentResponse.kt
index 7f38b58c4..6234e6846 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/RecruitmentResponse.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/apiModel/response/RecruitmentResponse.kt
@@ -20,6 +20,8 @@ data class RecruitmentResponse(
val member: MemberResponse,
@SerialName("eventId")
val eventId: Long,
+ @SerialName("eventName")
+ val eventName: String = "",
)
@Serializable
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/FeedMapper.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/FeedMapper.kt
index 246b5b7b7..d1615ea6f 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/FeedMapper.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/FeedMapper.kt
@@ -2,13 +2,17 @@ package com.emmsale.data.mapper
import com.emmsale.BuildConfig
import com.emmsale.data.apiModel.response.FeedResponse
+import com.emmsale.data.model.Event
import com.emmsale.data.model.Feed
fun List.toData(): List = map { it.toData() }
fun FeedResponse.toData(): Feed = Feed(
id = id,
- eventId = eventId,
+ event = Event(
+ id = eventId,
+ name = eventName,
+ ),
title = title,
content = content,
writer = writer.toData(),
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/MyPostMapper.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/MyPostMapper.kt
deleted file mode 100644
index b0dbbdbe3..000000000
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/MyPostMapper.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.emmsale.data.mapper
-
-import com.emmsale.data.apiModel.response.MyPostResponse
-import com.emmsale.data.model.Event
-import com.emmsale.data.model.Member
-import com.emmsale.data.model.Recruitment
-
-fun List.toData(): List = map { it.toData() }
-fun MyPostResponse.toData() = Recruitment(
- id = postId,
- writer = Member(),
- event = Event(id = eventId, name = eventName),
- content = content,
- updatedDate = updatedAt,
-)
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/RecruitmentMapper.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/RecruitmentMapper.kt
index 0918d7451..40d58aece 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/RecruitmentMapper.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/mapper/RecruitmentMapper.kt
@@ -13,5 +13,8 @@ fun RecruitmentResponse.toData(): Recruitment = Recruitment(
content = content,
updatedDate = updatedAt,
writer = member.toData(),
- event = Event(id = eventId),
+ event = Event(
+ id = eventId,
+ name = eventName,
+ ),
)
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/model/Feed.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/model/Feed.kt
index b2fd31b57..7c1b8ae0c 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/model/Feed.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/model/Feed.kt
@@ -4,7 +4,7 @@ import java.time.LocalDateTime
data class Feed(
val id: Long = -1,
- val eventId: Long = -1,
+ val event: Event = Event(),
val title: String = "",
val content: String = "",
val writer: Member = Member(),
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/repository/concretes/DefaultFeedRepository.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/repository/concretes/DefaultFeedRepository.kt
index c0e3ed3ab..1cf45805d 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/repository/concretes/DefaultFeedRepository.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/repository/concretes/DefaultFeedRepository.kt
@@ -36,6 +36,12 @@ class DefaultFeedRepository @Inject constructor(
.map(FeedResponse::toData)
}
+ override suspend fun getMyFeeds(): ApiResponse> = withContext(dispatcher) {
+ feedService
+ .getMyFeeds()
+ .map { it.toData() }
+ }
+
override suspend fun deleteFeed(
feedId: Long,
): ApiResponse = withContext(dispatcher) {
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/repository/interfaces/FeedRepository.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/repository/interfaces/FeedRepository.kt
index 19c5250e4..0e0f65543 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/repository/interfaces/FeedRepository.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/repository/interfaces/FeedRepository.kt
@@ -10,6 +10,8 @@ interface FeedRepository {
suspend fun getFeed(feedId: Long): ApiResponse
+ suspend fun getMyFeeds(): ApiResponse>
+
suspend fun deleteFeed(feedId: Long): ApiResponse
suspend fun uploadFeed(
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/FeedService.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/FeedService.kt
index fdedfb756..2e65e88ac 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/FeedService.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/FeedService.kt
@@ -20,6 +20,9 @@ interface FeedService {
@Query("event-id") eventId: Long,
): ApiResponse>
+ @GET("/feeds/my")
+ suspend fun getMyFeeds(): ApiResponse>
+
@GET("/feeds/{feedId}")
suspend fun getFeed(
@Path("feedId") feedId: Long,
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/MyPostService.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/MyPostService.kt
deleted file mode 100644
index 4750a0874..000000000
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/MyPostService.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.emmsale.data.service
-
-import com.emmsale.data.apiModel.response.MyPostResponse
-import com.emmsale.data.common.retrofit.callAdapter.ApiResponse
-import retrofit2.http.GET
-import retrofit2.http.Query
-
-interface MyPostService {
- @GET("/events/recruitment-posts")
- suspend fun getMyPosts(
- @Query("member-id") memberId: Long,
- ): ApiResponse>
-}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/RecruitmentService.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/RecruitmentService.kt
index 141304f5c..1d5cdc32a 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/RecruitmentService.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/service/RecruitmentService.kt
@@ -3,7 +3,6 @@ package com.emmsale.data.service
import com.emmsale.data.apiModel.request.RecruitmentCreateRequest
import com.emmsale.data.apiModel.request.RecruitmentDeleteRequest
import com.emmsale.data.apiModel.request.RecruitmentReportCreateRequest
-import com.emmsale.data.apiModel.response.MyPostResponse
import com.emmsale.data.apiModel.response.RecruitmentReportResponse
import com.emmsale.data.apiModel.response.RecruitmentResponse
import com.emmsale.data.common.retrofit.callAdapter.ApiResponse
@@ -31,7 +30,7 @@ interface RecruitmentService {
@GET("/events/recruitment-posts")
suspend fun getMemberRecruitments(
@Query("member-id") memberId: Long,
- ): ApiResponse>
+ ): ApiResponse>
@POST("/events/{eventId}/recruitment-posts")
suspend fun postRecruitment(
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/di/modules/service/ServiceModule.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/di/modules/service/ServiceModule.kt
index 24a00e194..0bf1213d2 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/di/modules/service/ServiceModule.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/di/modules/service/ServiceModule.kt
@@ -11,7 +11,6 @@ import com.emmsale.data.service.FeedService
import com.emmsale.data.service.LoginService
import com.emmsale.data.service.MemberService
import com.emmsale.data.service.MessageRoomService
-import com.emmsale.data.service.MyPostService
import com.emmsale.data.service.NotificationService
import com.emmsale.data.service.RecruitmentService
import dagger.Module
@@ -88,12 +87,6 @@ class ServiceModule {
serviceFactory: ServiceFactory,
): BlockedMemberService = serviceFactory.create(BlockedMemberService::class.java)
- @Provides
- @Singleton
- fun provideMyPostService(
- serviceFactory: ServiceFactory,
- ): MyPostService = serviceFactory.create(MyPostService::class.java)
-
@Provides
@Singleton
fun provideMessageRoomService(
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/MyFeedsFragment.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/MyFeedsFragment.kt
new file mode 100644
index 000000000..4ccec922c
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/MyFeedsFragment.kt
@@ -0,0 +1,55 @@
+package com.emmsale.presentation.ui.myFeedList
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.viewModels
+import com.emmsale.R
+import com.emmsale.databinding.FragmentMyFeedsBinding
+import com.emmsale.presentation.base.NetworkFragment
+import com.emmsale.presentation.common.recyclerView.DividerItemDecoration
+import com.emmsale.presentation.ui.feedDetail.FeedDetailActivity
+import com.emmsale.presentation.ui.myFeedList.recyclerView.MyFeedAdapter
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MyFeedsFragment : NetworkFragment(R.layout.fragment_my_feeds) {
+
+ override val viewModel: MyFeedsViewModel by viewModels()
+
+ private val myFeedAdapter: MyFeedAdapter by lazy {
+ MyFeedAdapter(onItemClick = ::navigateToDetail)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupDataBinding()
+ setupRecyclerView()
+
+ observeMyFeeds()
+ }
+
+ private fun setupDataBinding() {
+ binding.vm = viewModel
+ }
+
+ private fun setupRecyclerView() {
+ binding.rvMyFeeds.apply {
+ adapter = myFeedAdapter
+ itemAnimator = null
+ addItemDecoration(DividerItemDecoration(context = context))
+ }
+ }
+
+ private fun observeMyFeeds() {
+ viewModel.myFeeds.observe(viewLifecycleOwner) { myFeeds ->
+ myFeedAdapter.submitList(myFeeds)
+ }
+ }
+
+ private fun navigateToDetail(feedId: Long) {
+ FeedDetailActivity.startActivity(
+ requireContext(),
+ feedId = feedId,
+ )
+ }
+}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/MyFeedsViewModel.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/MyFeedsViewModel.kt
new file mode 100644
index 000000000..4214bd60d
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/MyFeedsViewModel.kt
@@ -0,0 +1,37 @@
+package com.emmsale.presentation.ui.myFeedList
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.emmsale.data.model.Feed
+import com.emmsale.data.repository.interfaces.FeedRepository
+import com.emmsale.presentation.base.RefreshableViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Job
+import javax.inject.Inject
+
+@HiltViewModel
+class MyFeedsViewModel @Inject constructor(
+ private val feedRepository: FeedRepository,
+) : RefreshableViewModel() {
+
+ private val _myFeeds: MutableLiveData> = MutableLiveData(emptyList())
+ val myFeeds: LiveData> = _myFeeds
+
+ init {
+ fetchMyFeeds()
+ }
+
+ private fun fetchMyFeeds(): Job = fetchData(
+ fetchData = { feedRepository.getMyFeeds() },
+ onSuccess = {
+ _myFeeds.value = it.sortedByDescending { feed -> feed.createdAt }
+ },
+ )
+
+ override fun refresh(): Job = refreshData(
+ refresh = { feedRepository.getMyFeeds() },
+ onSuccess = {
+ _myFeeds.value = it.sortedByDescending { feed -> feed.createdAt }
+ },
+ )
+}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/recyclerView/MyFeedAdapter.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/recyclerView/MyFeedAdapter.kt
new file mode 100644
index 000000000..7655ea704
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/recyclerView/MyFeedAdapter.kt
@@ -0,0 +1,31 @@
+package com.emmsale.presentation.ui.myFeedList.recyclerView
+
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import com.emmsale.data.model.Feed
+
+class MyFeedAdapter(
+ private val onItemClick: (recruitmentId: Long) -> Unit,
+) : ListAdapter(
+ object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: Feed,
+ newItem: Feed,
+ ): Boolean = oldItem == newItem
+
+ override fun areContentsTheSame(
+ oldItem: Feed,
+ newItem: Feed,
+ ): Boolean = oldItem.id == newItem.id
+ },
+) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyFeedViewHolder {
+ return MyFeedViewHolder.create(parent, onItemClick)
+ }
+
+ override fun onBindViewHolder(holder: MyFeedViewHolder, position: Int) {
+ holder.bind(getItem(position))
+ }
+}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/recyclerView/MyFeedViewHolder.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/recyclerView/MyFeedViewHolder.kt
new file mode 100644
index 000000000..3382b77e8
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myFeedList/recyclerView/MyFeedViewHolder.kt
@@ -0,0 +1,32 @@
+package com.emmsale.presentation.ui.myFeedList.recyclerView
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.emmsale.data.model.Feed
+import com.emmsale.databinding.ItemMyFeedBinding
+
+class MyFeedViewHolder(
+ private val binding: ItemMyFeedBinding,
+ navigateToDetail: (feedId: Long) -> Unit,
+) : RecyclerView.ViewHolder(binding.root) {
+
+ init {
+ itemView.setOnClickListener { navigateToDetail(binding.feed!!.id) }
+ }
+
+ fun bind(feed: Feed) {
+ binding.feed = feed
+ }
+
+ companion object {
+ fun create(
+ parent: ViewGroup,
+ onItemClick: (feedId: Long) -> Unit,
+ ): MyFeedViewHolder {
+ val binding =
+ ItemMyFeedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return MyFeedViewHolder(binding, onItemClick)
+ }
+ }
+}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myPostListPage/MyPostsActivity.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myPostListPage/MyPostsActivity.kt
new file mode 100644
index 000000000..80ec3d80a
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myPostListPage/MyPostsActivity.kt
@@ -0,0 +1,57 @@
+package com.emmsale.presentation.ui.myPostListPage
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import com.emmsale.R
+import com.emmsale.databinding.ActivityMyPostsBinding
+import com.emmsale.presentation.base.BaseActivity
+import com.google.android.material.tabs.TabLayout
+import com.google.android.material.tabs.TabLayoutMediator
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MyPostsActivity : BaseActivity(R.layout.activity_my_posts) {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(binding.root)
+ setupFragmentStateAdapter()
+ setupToolbar()
+ setupMyPostsTabLayoutSelectedListener()
+ }
+
+ private fun setupFragmentStateAdapter() {
+ binding.vpMyPosts.adapter =
+ MyPostsFragmentStateAdapter(this)
+ val tabNames = listOf(
+ getString(R.string.myposts_tab_feeds),
+ getString(R.string.myposts_tab_recruitments),
+ )
+ TabLayoutMediator(binding.tablayoutMyposts, binding.vpMyPosts) { tab, position ->
+ tab.text = tabNames[position]
+ }.attach()
+ }
+
+ private fun setupMyPostsTabLayoutSelectedListener() {
+ binding.tablayoutMyposts.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+ override fun onTabSelected(tab: TabLayout.Tab) {
+ binding.vpMyPosts.currentItem = tab.position
+ }
+
+ override fun onTabUnselected(tab: TabLayout.Tab) {}
+ override fun onTabReselected(tab: TabLayout.Tab) {}
+ })
+ }
+
+ private fun setupToolbar() {
+ binding.tbMyPost.setNavigationOnClickListener { onBackPressedDispatcher.onBackPressed() }
+ }
+
+ companion object {
+ fun startActivity(context: Context) {
+ val intent = Intent(context, MyPostsActivity::class.java)
+ context.startActivity(intent)
+ }
+ }
+}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myPostListPage/MyPostsFragmentStateAdapter.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myPostListPage/MyPostsFragmentStateAdapter.kt
new file mode 100644
index 000000000..776bfd839
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myPostListPage/MyPostsFragmentStateAdapter.kt
@@ -0,0 +1,23 @@
+package com.emmsale.presentation.ui.myPostListPage
+
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import com.emmsale.presentation.ui.myFeedList.MyFeedsFragment
+import com.emmsale.presentation.ui.myRecruitmentList.MyRecruitmentsFragment
+
+class MyPostsFragmentStateAdapter(activity: AppCompatActivity) : FragmentStateAdapter(activity) {
+ override fun getItemCount(): Int = TAB_SIZE
+
+ override fun createFragment(position: Int): Fragment = when (position) {
+ MY_FEEDS_POSITION -> MyFeedsFragment()
+ MY_RECRUITMENT_POSITION -> MyRecruitmentsFragment()
+ else -> throw IllegalArgumentException("올바르지 않은 fragment position 입니다.")
+ }
+
+ companion object {
+ private const val TAB_SIZE = 2
+ private const val MY_FEEDS_POSITION = 0
+ private const val MY_RECRUITMENT_POSITION = 1
+ }
+}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentActivity.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentActivity.kt
deleted file mode 100644
index 699a43f7e..000000000
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentActivity.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.emmsale.presentation.ui.myRecruitmentList
-
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.activity.viewModels
-import androidx.appcompat.app.AppCompatActivity
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.emmsale.R
-import com.emmsale.databinding.ActivityMyPostBinding
-import com.emmsale.presentation.common.extension.showToast
-import com.emmsale.presentation.ui.myRecruitmentList.recyclerView.MyRecruitmentAdapter
-import com.emmsale.presentation.ui.recruitmentDetail.RecruitmentDetailActivity
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class MyRecruitmentActivity : AppCompatActivity() {
- private val binding by lazy { ActivityMyPostBinding.inflate(layoutInflater) }
- private val viewModel: MyRecruitmentViewModel by viewModels()
-
- private val myRecruitmentAdapter: MyRecruitmentAdapter by lazy {
- MyRecruitmentAdapter(navigateToDetail = ::navigateToDetail)
- }
-
- private val fetchByResultActivityLauncher =
- registerForActivityResult(
- ActivityResultContracts.StartActivityForResult(),
- ) { result ->
- if (result == null || result.resultCode != RESULT_OK) return@registerForActivityResult
- viewModel.refresh()
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(binding.root)
- initRecyclerView()
- setUpMyPosts()
- initBackPressButton()
- }
-
- private fun setUpMyPosts() {
- viewModel.myRecruitments.observe(this) { myRecruitments ->
- if (myRecruitments.isError) {
- showToast(R.string.all_data_loading_failed_message)
- } else {
- myRecruitmentAdapter.submitList(myRecruitments.list)
- }
- }
- }
-
- private fun initRecyclerView() {
- binding.rvMyPost.layoutManager = LinearLayoutManager(this)
- binding.rvMyPost.adapter = myRecruitmentAdapter
- }
-
- private fun navigateToDetail(eventId: Long, recruitmentId: Long) {
- RecruitmentDetailActivity.startActivity(
- this,
- eventId = eventId,
- recruitmentId = recruitmentId,
- )
- }
-
- private fun initBackPressButton() {
- binding.tbMyPost.setNavigationOnClickListener {
- finish()
- }
- }
-
- companion object {
- fun startActivity(context: Context) {
- val intent = Intent(context, MyRecruitmentActivity::class.java)
- context.startActivity(intent)
- }
- }
-}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentViewModel.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentViewModel.kt
index 3c6f0521c..c046a789f 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentViewModel.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentViewModel.kt
@@ -1,47 +1,45 @@
package com.emmsale.presentation.ui.myRecruitmentList
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.emmsale.data.common.retrofit.callAdapter.Failure
-import com.emmsale.data.common.retrofit.callAdapter.NetworkError
-import com.emmsale.data.common.retrofit.callAdapter.Success
-import com.emmsale.data.common.retrofit.callAdapter.Unexpected
+import com.emmsale.data.model.Recruitment
import com.emmsale.data.repository.interfaces.RecruitmentRepository
import com.emmsale.data.repository.interfaces.TokenRepository
+import com.emmsale.presentation.base.RefreshableViewModel
import com.emmsale.presentation.common.livedata.NotNullLiveData
import com.emmsale.presentation.common.livedata.NotNullMutableLiveData
-import com.emmsale.presentation.common.viewModel.Refreshable
-import com.emmsale.presentation.ui.myRecruitmentList.uiState.MyRecruitmentsUiState
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.Job
import javax.inject.Inject
@HiltViewModel
class MyRecruitmentViewModel @Inject constructor(
private val tokenRepository: TokenRepository,
private val recruitmentRepository: RecruitmentRepository,
-) : ViewModel(), Refreshable {
+) : RefreshableViewModel() {
private val uid: Long by lazy { tokenRepository.getMyUid()!! }
- private val _myRecruitments: NotNullMutableLiveData =
- NotNullMutableLiveData(MyRecruitmentsUiState())
- val myRecruitments: NotNullLiveData = _myRecruitments
+ private val _myRecruitments: NotNullMutableLiveData> =
+ NotNullMutableLiveData(emptyList())
+ val myRecruitments: NotNullLiveData> = _myRecruitments
init {
- refresh()
+ fetchMyRecruitments()
}
- override fun refresh() {
- _myRecruitments.value = _myRecruitments.value.copy(isLoading = true)
- viewModelScope.launch {
- when (val result = recruitmentRepository.getMemberRecruitments(uid)) {
- is Failure, NetworkError ->
- _myRecruitments.value =
- _myRecruitments.value.copy(isLoading = false, isError = true)
-
- is Success -> _myRecruitments.value = MyRecruitmentsUiState.from(result.data)
- is Unexpected -> throw Throwable(result.error)
- }
- }
+ private fun fetchMyRecruitments() {
+ fetchData(
+ fetchData = { recruitmentRepository.getMemberRecruitments(uid) },
+ onSuccess = {
+ _myRecruitments.value =
+ it.sortedByDescending { recruitment -> recruitment.updatedDate }
+ },
+ )
}
+
+ override fun refresh(): Job = refreshData(
+ refresh = { recruitmentRepository.getMemberRecruitments(uid) },
+ onSuccess = {
+ _myRecruitments.value =
+ it.sortedByDescending { recruitment -> recruitment.updatedDate }
+ },
+ )
}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentsFragment.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentsFragment.kt
new file mode 100644
index 000000000..274a55c68
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/MyRecruitmentsFragment.kt
@@ -0,0 +1,57 @@
+package com.emmsale.presentation.ui.myRecruitmentList
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.viewModels
+import com.emmsale.R
+import com.emmsale.databinding.FragmentMyRecruitmentsBinding
+import com.emmsale.presentation.base.NetworkFragment
+import com.emmsale.presentation.common.recyclerView.DividerItemDecoration
+import com.emmsale.presentation.ui.myRecruitmentList.recyclerView.MyRecruitmentAdapter
+import com.emmsale.presentation.ui.recruitmentDetail.RecruitmentDetailActivity
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MyRecruitmentsFragment :
+ NetworkFragment(R.layout.fragment_my_recruitments) {
+
+ override val viewModel: MyRecruitmentViewModel by viewModels()
+
+ private val myRecruitmentAdapter: MyRecruitmentAdapter by lazy {
+ MyRecruitmentAdapter(onItemClick = ::navigateToDetail)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupBinding()
+ setupRecyclerView()
+
+ observeMyRecruitments()
+ }
+
+ private fun setupBinding() {
+ binding.vm = viewModel
+ }
+
+ private fun setupRecyclerView() {
+ binding.rvMyPost.apply {
+ adapter = myRecruitmentAdapter
+ itemAnimator = null
+ addItemDecoration(DividerItemDecoration(context = context))
+ }
+ }
+
+ private fun observeMyRecruitments() {
+ viewModel.myRecruitments.observe(viewLifecycleOwner) { myRecruitments ->
+ myRecruitmentAdapter.submitList(myRecruitments)
+ }
+ }
+
+ private fun navigateToDetail(eventId: Long, recruitmentId: Long) {
+ RecruitmentDetailActivity.startActivity(
+ requireContext(),
+ eventId = eventId,
+ recruitmentId = recruitmentId,
+ )
+ }
+}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/recyclerView/MyRecruitmentAdapter.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/recyclerView/MyRecruitmentAdapter.kt
index 7a682837b..7798fec61 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/recyclerView/MyRecruitmentAdapter.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/recyclerView/MyRecruitmentAdapter.kt
@@ -3,31 +3,29 @@ package com.emmsale.presentation.ui.myRecruitmentList.recyclerView
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
-import com.emmsale.presentation.ui.myRecruitmentList.uiState.MyRecruitmentUiState
+import com.emmsale.data.model.Recruitment
class MyRecruitmentAdapter(
- private val navigateToDetail: (eventId: Long, recruitmentId: Long) -> Unit,
-) : ListAdapter(diffUtil) {
+ private val onItemClick: (eventId: Long, recruitmentId: Long) -> Unit,
+) : ListAdapter(
+ object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: Recruitment,
+ newItem: Recruitment,
+ ): Boolean = oldItem == newItem
+
+ override fun areContentsTheSame(
+ oldItem: Recruitment,
+ newItem: Recruitment,
+ ): Boolean = (oldItem.id == newItem.id && oldItem.event.id == newItem.event.id)
+ },
+) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyRecruitmentViewHolder {
- return MyRecruitmentViewHolder.create(parent, navigateToDetail)
+ return MyRecruitmentViewHolder.create(parent, onItemClick)
}
override fun onBindViewHolder(holder: MyRecruitmentViewHolder, position: Int) {
holder.bind(getItem(position))
}
-
- companion object {
- val diffUtil = object : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldItem: MyRecruitmentUiState,
- newItem: MyRecruitmentUiState,
- ): Boolean = oldItem == newItem
-
- override fun areContentsTheSame(
- oldItem: MyRecruitmentUiState,
- newItem: MyRecruitmentUiState,
- ): Boolean = (oldItem.postId == newItem.postId && oldItem.eventId == newItem.eventId)
- }
- }
}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/recyclerView/MyRecruitmentViewHolder.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/recyclerView/MyRecruitmentViewHolder.kt
index 957c2f6cf..6a99bf39a 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/recyclerView/MyRecruitmentViewHolder.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/recyclerView/MyRecruitmentViewHolder.kt
@@ -3,35 +3,35 @@ package com.emmsale.presentation.ui.myRecruitmentList.recyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
-import com.emmsale.databinding.ItemMyPostBinding
-import com.emmsale.presentation.ui.myRecruitmentList.uiState.MyRecruitmentUiState
+import com.emmsale.data.model.Recruitment
+import com.emmsale.databinding.ItemMyRecruitmentBinding
class MyRecruitmentViewHolder(
- private val binding: ItemMyPostBinding,
+ private val binding: ItemMyRecruitmentBinding,
navigateToDetail: (eventId: Long, recruitmentId: Long) -> Unit,
) : RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
navigateToDetail(
- binding.recruitment!!.eventId,
- binding.recruitment!!.postId,
+ binding.recruitment!!.event.id,
+ binding.recruitment!!.id,
)
}
}
- fun bind(myRecruitment: MyRecruitmentUiState) {
+ fun bind(myRecruitment: Recruitment) {
binding.recruitment = myRecruitment
}
companion object {
fun create(
parent: ViewGroup,
- navigateToDetail: (eventId: Long, recruitmentId: Long) -> Unit,
+ onItemClick: (eventId: Long, recruitmentId: Long) -> Unit,
): MyRecruitmentViewHolder {
val binding =
- ItemMyPostBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- return MyRecruitmentViewHolder(binding, navigateToDetail)
+ ItemMyRecruitmentBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return MyRecruitmentViewHolder(binding, onItemClick)
}
}
}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/uiState/MyRecruitmentUiState.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/uiState/MyRecruitmentUiState.kt
deleted file mode 100644
index 58b469d4b..000000000
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/uiState/MyRecruitmentUiState.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.emmsale.presentation.ui.myRecruitmentList.uiState
-
-import com.emmsale.data.model.Recruitment
-
-data class MyRecruitmentUiState(
- val postId: Long = DEFAULT_ID,
- val eventId: Long = DEFAULT_ID,
- val eventName: String = "",
- val content: String = "",
- val updatedAt: String = "",
-) {
- companion object {
- private const val DEFAULT_ID = -1L
-
- fun from(recruitment: Recruitment) = MyRecruitmentUiState(
- postId = recruitment.id,
- eventId = recruitment.event.id,
- eventName = recruitment.event.name,
- content = recruitment.content,
- updatedAt = recruitment.updatedDate.toString(),
- )
- }
-}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/uiState/MyRecruitmentsUiState.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/uiState/MyRecruitmentsUiState.kt
deleted file mode 100644
index 16fcc8309..000000000
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/myRecruitmentList/uiState/MyRecruitmentsUiState.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.emmsale.presentation.ui.myRecruitmentList.uiState
-
-import com.emmsale.data.model.Recruitment
-
-data class MyRecruitmentsUiState(
- val list: List = listOf(),
- val isLoading: Boolean = false,
- val isError: Boolean = false,
-) {
- companion object {
- fun from(recruitments: List) = MyRecruitmentsUiState(
- list = recruitments.map { MyRecruitmentUiState.from(it) },
- isLoading = false,
- isError = false,
- )
- }
-}
diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/setting/SettingFragment.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/setting/SettingFragment.kt
index a4ed9ee6a..aae32785b 100644
--- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/setting/SettingFragment.kt
+++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/setting/SettingFragment.kt
@@ -15,7 +15,7 @@ import com.emmsale.presentation.common.views.WarningDialog
import com.emmsale.presentation.ui.blockMemberList.BlockedMembersActivity
import com.emmsale.presentation.ui.login.LoginActivity
import com.emmsale.presentation.ui.myCommentList.MyCommentsActivity
-import com.emmsale.presentation.ui.myRecruitmentList.MyRecruitmentActivity
+import com.emmsale.presentation.ui.myPostListPage.MyPostsActivity
import com.emmsale.presentation.ui.notificationConfig.NotificationConfigActivity
import com.emmsale.presentation.ui.setting.uiState.SettingUiEvent
import com.emmsale.presentation.ui.useTerm.UseTermWebViewActivity
@@ -43,7 +43,7 @@ class SettingFragment :
private fun setupDataBinding() {
binding.viewModel = viewModel
- binding.onWritingsButtonClick = { MyRecruitmentActivity.startActivity(requireContext()) }
+ binding.onWritingsButtonClick = { MyPostsActivity.startActivity(requireContext()) }
binding.onWrittenCommentsButtonClick =
{ MyCommentsActivity.startActivity(requireContext()) }
binding.onNotificationSettingButtonClick =
diff --git a/android/2023-emmsale/app/src/main/res/layout/activity_my_post.xml b/android/2023-emmsale/app/src/main/res/layout/activity_my_posts.xml
similarity index 50%
rename from android/2023-emmsale/app/src/main/res/layout/activity_my_post.xml
rename to android/2023-emmsale/app/src/main/res/layout/activity_my_posts.xml
index a7975cdfa..c76fe8990 100644
--- a/android/2023-emmsale/app/src/main/res/layout/activity_my_post.xml
+++ b/android/2023-emmsale/app/src/main/res/layout/activity_my_posts.xml
@@ -1,8 +1,6 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto">
@@ -16,8 +14,7 @@
+ android:background="@color/white">
-
-
-
+ app:layout_constraintEnd_toEndOf="parent" />
-
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/tablayout_myposts"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
diff --git a/android/2023-emmsale/app/src/main/res/layout/fragment_my_feeds.xml b/android/2023-emmsale/app/src/main/res/layout/fragment_my_feeds.xml
new file mode 100644
index 000000000..96e22c378
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/res/layout/fragment_my_feeds.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/2023-emmsale/app/src/main/res/layout/fragment_my_recruitments.xml b/android/2023-emmsale/app/src/main/res/layout/fragment_my_recruitments.xml
new file mode 100644
index 000000000..45acf56c9
--- /dev/null
+++ b/android/2023-emmsale/app/src/main/res/layout/fragment_my_recruitments.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/2023-emmsale/app/src/main/res/layout/fragment_setting.xml b/android/2023-emmsale/app/src/main/res/layout/fragment_setting.xml
index c2c0ecdf7..b80e5ad6a 100644
--- a/android/2023-emmsale/app/src/main/res/layout/fragment_setting.xml
+++ b/android/2023-emmsale/app/src/main/res/layout/fragment_setting.xml
@@ -94,12 +94,30 @@
app:layout_constraintTop_toBottomOf="@+id/tv_setting_member_name"
tools:text="git1234567@github.com" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/2023-emmsale/app/src/main/res/layout/item_my_post.xml b/android/2023-emmsale/app/src/main/res/layout/item_my_recruitment.xml
similarity index 60%
rename from android/2023-emmsale/app/src/main/res/layout/item_my_post.xml
rename to android/2023-emmsale/app/src/main/res/layout/item_my_recruitment.xml
index df1afb568..2c13e7128 100644
--- a/android/2023-emmsale/app/src/main/res/layout/item_my_post.xml
+++ b/android/2023-emmsale/app/src/main/res/layout/item_my_recruitment.xml
@@ -7,29 +7,18 @@
+ type="com.emmsale.data.model.Recruitment" />
+
+
-
-
-
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground">
-
-
-
+
diff --git a/android/2023-emmsale/app/src/main/res/layout/item_mycomments_comment.xml b/android/2023-emmsale/app/src/main/res/layout/item_mycomments_comment.xml
index 4d88041af..461f21888 100644
--- a/android/2023-emmsale/app/src/main/res/layout/item_mycomments_comment.xml
+++ b/android/2023-emmsale/app/src/main/res/layout/item_mycomments_comment.xml
@@ -48,6 +48,8 @@
android:lineSpacingExtra="3dp"
android:text="@{comment.content}"
android:textSize="12sp"
+ android:ellipsize="end"
+ android:maxLines="5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/tv_mycomments_event_name"
app:layout_constraintTop_toBottomOf="@+id/tv_mycomments_event_name"
diff --git a/android/2023-emmsale/app/src/main/res/values/strings.xml b/android/2023-emmsale/app/src/main/res/values/strings.xml
index 33717a9cb..5ac9268c8 100644
--- a/android/2023-emmsale/app/src/main/res/values/strings.xml
+++ b/android/2023-emmsale/app/src/main/res/values/strings.xml
@@ -51,6 +51,9 @@
오류
데이터를 불러올 수 없습니다.
알 수 없는 오류가 발생했습니다.
+ 이미지 권한 허용을 위해 설정창으로 이동하시겠습니까?
+ 이미지 권한이 거부되었어요
+ 갤러리 종류를 선택하세요
업데이트
새로운 버전이 출시되었어요!\n지금 바로 커디 스토어로 이동하시겠어요?
@@ -71,14 +74,6 @@
복수 선택이 최대 4개까지 가능합니다.
선택할 수 있는 개수를 초과했어요!
- %s ~ %s
- 게시판
- 행사 공유
- 친구로부터 행사를 공유받았어요!\n커디에서 게시글을 구경하고 동행자를 모집해보세요.
- 행사 구경하기!
- 카카오톡 공유에 실패했어요 🥲
- 지원되는 브라우저가 없어요 🥲
-
3/4
교육 활동
교육 이력이 있나요?
@@ -256,6 +251,13 @@
상세정보 보기 탭
스크랩 불가
스크랩 해제 불가
+ %s ~ %s
+ 게시판
+ 행사 공유
+ 친구로부터 행사를 공유받았어요!\n커디에서 게시글을 구경하고 동행자를 모집해보세요.
+ 행사 구경하기!
+ 카카오톡 공유에 실패했어요 🥲
+ 지원되는 브라우저가 없어요 🥲
비용
장소
@@ -318,6 +320,7 @@
차단 목록
작성한 글
오픈 프로필 등록
+ 작성한 글
작성한 댓글
회원 정보를 불러오는 데 실패했어요 😥
회원 탈퇴하는 데 실패했어요 😥
@@ -463,9 +466,9 @@
전체 삭제
전체 삭제
최근 검색어를 모두 삭제하시겠어요?
- 이미지 권한 허용을 위해 설정창으로 이동하시겠습니까?
- 이미지 권한이 거부되었어요
- 갤러리 종류를 선택하세요
+
+ 행사 게시판
+ 함께해요
플레이스토어가 설치되지 않았거나 로그인하지 않았어요 😥