Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat : 대학/학과 공지 뷰 api 연결 및 무한 스크롤 적용 #167

Merged
merged 16 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package com.dongyang.android.youdongknowme.data.remote.entity
import com.google.gson.annotations.SerializedName

data class Notice(
@SerializedName("num")
var num: Int,
@SerializedName("title")
var title: String,
@SerializedName("writer")
var writer: String,
val title: String,
@SerializedName("author")
val author: String,
@SerializedName("date")
var date: String,
val date: String,
@SerializedName("url")
val url: String,
)

data class NoticeDetail(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package com.dongyang.android.youdongknowme.data.remote.service

import com.dongyang.android.youdongknowme.data.remote.entity.Notice
import com.dongyang.android.youdongknowme.data.remote.entity.NoticeDetail
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query

interface NoticeService {
@GET("/notice/{code}")
suspend fun getList(
@Path("code") code : Int
) : List<Notice>

@GET("/notice/{code}/search")
suspend fun getSearchList(
@Path("code") code : Int,
@Query("keyword") keyword : String
) : List<Notice>
@GET("api/v1/dmu/universityNotice")
suspend fun getUniversityNotice(
@Query("page") page: Int,
@Query("size") size: Int
): List<Notice>

@GET("api/v1/dmu/departmentNotice/{department}")
suspend fun getDepartmentNotice(
@Path("department") department: String,
@Query("page") page: Int,
@Query("size") size: Int
): List<Notice>
}

Original file line number Diff line number Diff line change
@@ -1,54 +1,38 @@
package com.dongyang.android.youdongknowme.data.repository

import com.dongyang.android.youdongknowme.data.local.SharedPreference
import com.dongyang.android.youdongknowme.data.local.dao.AlarmDao
import com.dongyang.android.youdongknowme.data.remote.entity.Notice
import com.dongyang.android.youdongknowme.data.remote.service.NoticeService
import com.dongyang.android.youdongknowme.standard.network.ErrorResponseHandler
import com.dongyang.android.youdongknowme.standard.network.NetworkResult
import com.dongyang.android.youdongknowme.standard.network.RetrofitObject
import kotlinx.coroutines.flow.Flow
import retrofit2.Response

class NoticeRepository(
private val alarmDao: AlarmDao,
private val errorResponseHandler: ErrorResponseHandler
) {
suspend fun fetchAllNotices(): NetworkResult<Map<String,List<Notice>>> {
suspend fun fetchUniversityNotices(page: Int): NetworkResult<List<Notice>> {
return try {
val departNotices = RetrofitObject.getNetwork().create(NoticeService::class.java).getList(SharedPreference.getCode())
val schoolNotices = RetrofitObject.getNetwork().create(NoticeService::class.java).getList(CODE.SCHOOL_CODE)
val universityNotices = RetrofitObject.getNetwork().create(NoticeService::class.java)
.getUniversityNotice(page, DEFAULT_SIZE)

val result : Map<String,List<Notice>> = mapOf("school" to schoolNotices, "depart" to departNotices)

NetworkResult.Success(result)
NetworkResult.Success(universityNotices)
} catch (exception: Exception) {
val error = errorResponseHandler.getError(exception)
NetworkResult.Error(error)
}
}

suspend fun fetchNotices(code: Int): NetworkResult<List<Notice>> {
suspend fun fetchDepartmentNotices(department: String, page: Int): NetworkResult<List<Notice>> {
return try {
val response = RetrofitObject.getNetwork().create(NoticeService::class.java).getList(code)
NetworkResult.Success(response)
val departmentNotices = RetrofitObject.getNetwork().create(NoticeService::class.java)
.getDepartmentNotice(department, page, DEFAULT_SIZE)
NetworkResult.Success(departmentNotices)
} catch (exception: Exception) {
val error = errorResponseHandler.getError(exception)
NetworkResult.Error(error)
}
}

suspend fun fetchSearchNotices(code: Int, keyword: String): NetworkResult<List<Notice>> {
return try {
val response = RetrofitObject.getNetwork().create(NoticeService::class.java).getSearchList(code, keyword)
NetworkResult.Success(response)
} catch (exception: Exception) {
val error = errorResponseHandler.getError(exception)
NetworkResult.Error(error)
}
companion object {
private const val DEFAULT_SIZE = 20
}

fun getUnVisitedAlarmCount(): Flow<Int> = alarmDao.getUnVisitedAlarmCount()

fun getDepartmentCode(): Int = SharedPreference.getCode()
m6z1 marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ val viewModelModule = module {

val repositoryModule = module {
single {
NoticeRepository(get(), get())
NoticeRepository(get())
}
single {
DetailRepository(get())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ object RetrofitObject {

fun getNetwork(): Retrofit {

val baseUrl = "http://3.38.118.184:8000"
val baseUrl = "https://triphub.kr/"
m6z1 marked this conversation as resolved.
Show resolved Hide resolved

val baseInterceptor = Interceptor { chain ->
val request = chain.request().newBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,10 @@ class NoticeAdapter : RecyclerView.Adapter<NoticeAdapter.ViewHolder>() {
}

private var noticeList = arrayListOf<Notice>()
private var itemClickListener : NoticeClickListener? = null

inner class ViewHolder(private val binding: ItemNoticeBinding)
: RecyclerView.ViewHolder(binding.root) {
fun bind(item : Notice) {
binding.notice = item
binding.itemClickListener = itemClickListener
}
}
private var itemClickListener: NoticeClickListener? = null

@SuppressLint("NotifyDataSetChanged")
fun submitList(item : List<Notice>) {
fun submitList(item: List<Notice>) {
noticeList.clear()
noticeList.addAll(item)
notifyDataSetChanged()
Expand All @@ -50,7 +42,17 @@ class NoticeAdapter : RecyclerView.Adapter<NoticeAdapter.ViewHolder>() {

override fun getItemCount(): Int = noticeList.size

fun setItemClickListener(listener : NoticeClickListener) {
fun setItemClickListener(listener: NoticeClickListener) {
itemClickListener = listener
}

class ViewHolder(private val binding: ItemNoticeBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(item: Notice) {
binding.tvNoticeTitle.text = item.title
binding.tvNoticeDate.text = item.date
binding.tvNoticeAuthor.text = item.author
}
}
}
Original file line number Diff line number Diff line change
@@ -1,65 +1,55 @@
package com.dongyang.android.youdongknowme.ui.view.notice

import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.dongyang.android.youdongknowme.R
import com.dongyang.android.youdongknowme.databinding.FragmentNoticeBinding
import com.dongyang.android.youdongknowme.standard.base.BaseFragment
import com.dongyang.android.youdongknowme.standard.util.ACTION
import com.dongyang.android.youdongknowme.ui.adapter.NoticeAdapter
import com.dongyang.android.youdongknowme.ui.view.detail.DetailActivity
import com.dongyang.android.youdongknowme.ui.view.util.EventObserver
import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.tabs.TabLayout
import org.koin.androidx.viewmodel.ext.android.viewModel

/* 공지 사항 화면 */
class NoticeFragment : BaseFragment<FragmentNoticeBinding, NoticeViewModel>(), NoticeClickListener {

override val layoutResourceId: Int = R.layout.fragment_notice
override val viewModel: NoticeViewModel by viewModel()

private val badgeDrawable: BadgeDrawable by lazy {
BadgeDrawable.create(requireActivity())
}

private lateinit var adapter: NoticeAdapter

private val localBroadCast = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
viewModel.refreshNotices()
}
}

override fun initStartView() {
binding.vm = viewModel
adapter = NoticeAdapter().apply { setItemClickListener(this@NoticeFragment) }
binding.noticeRvList.apply {
this.adapter = [email protected]
this.layoutManager = LinearLayoutManager(requireActivity())
this.itemAnimator = null
this.setHasFixedSize(true)
this.addItemDecoration(DividerItemDecoration(requireActivity(), 1))
this.addItemDecoration(DividerItemDecoration(requireContext(), 1))
}
setupTabLayout()
setupInfinityScroll()
}

@SuppressLint("UnsafeOptInUsageError")
override fun initDataBinding() {

viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) showLoading()
else dismissLoading()
}

viewModel.noticeList.observe(viewLifecycleOwner) {
adapter.submitList(it)
viewModel.universityNotices.observe(viewLifecycleOwner) { response ->
if (response != null) {
adapter.submitList(response)
}
}

viewModel.departmentNotices.observe(viewLifecycleOwner) { response ->
if (response != null) {
adapter.submitList(response)
}
}

viewModel.errorState.observe(viewLifecycleOwner, EventObserver { resId ->
Expand All @@ -68,36 +58,35 @@ class NoticeFragment : BaseFragment<FragmentNoticeBinding, NoticeViewModel>(), N
}

override fun initAfterBinding() {
viewModel.getUnVisitedAlarmCount()

// 새로고침 했을 때 동작
m6z1 marked this conversation as resolved.
Show resolved Hide resolved
binding.noticeSwipe.setOnRefreshListener {
viewModel.refreshNotices()
binding.noticeSwipe.isRefreshing = false
}

// 각각 탭 버튼 눌렀을 때 동작
binding.noticeTab.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {

binding.noticeRvList.scrollToPosition(0)

if (tab.text == getString(R.string.notice_tab_university))
if (tab.text == getString(R.string.notice_tab_university)) {
viewModel.updateSelectedTabType(NoticeTabType.SCHOOL)
else
viewModel.fetchUniversityNotices()
} else {
viewModel.updateSelectedTabType(NoticeTabType.FACULTY)
viewModel.fetchDepartmentNotices()
}
}

override fun onTabUnselected(tab: TabLayout.Tab?) {}

// 다시 클릭시 스크롤되게 설정
override fun onTabReselected(tab: TabLayout.Tab?) {
binding.noticeRvList.smoothScrollToPosition(-10)
}
})

binding.noticeErrorContainer.refresh.setOnClickListener {
viewModel.refreshNotices()
viewModel.fetchUniversityNotices()
}
}

Expand All @@ -106,24 +95,29 @@ class NoticeFragment : BaseFragment<FragmentNoticeBinding, NoticeViewModel>(), N
binding.noticeTab.getTabAt(1)?.select()
}

override fun onResume() {
super.onResume()
val intentFilter = IntentFilter(ACTION.FCM_ACTION_NAME)
LocalBroadcastManager.getInstance(requireContext())
.registerReceiver(localBroadCast, intentFilter)
}

override fun onStop() {
super.onStop()
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(localBroadCast)
Comment on lines -109 to -118
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

단순 궁금증 : 이 파트가 왜 지워진건지 궁금합니당

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fcm 관련 코드인 것 같아서 지웠는데 fcm 제가 구현해보고 싶어서요 ㅎㅅㅎ

private fun setupInfinityScroll() {
binding.noticeRvList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)

val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val lastVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition()
val totalItemCount = layoutManager.itemCount

if (!viewModel.isLoading.value!! && lastVisibleItemPosition >= totalItemCount - 1) {
viewModel.selectedTab.value?.peekContent()?.let { currentTab ->
when (currentTab) {
NoticeTabType.FACULTY -> viewModel.fetchDepartmentNotices()
NoticeTabType.SCHOOL -> viewModel.fetchUniversityNotices()
m6z1 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
})
}

// 아이템 클릭시 자세히 보기 화면으로 이동
override fun itemClick(num: Int) {
val departCode = viewModel.departmentCode.value

val intent = Intent(requireActivity(), DetailActivity::class.java)
intent.putExtra("departCode", departCode)
val intent = Intent(requireContext(), DetailActivity::class.java)
intent.putExtra("boardNum", num)
startActivity(intent)
}
Expand Down
Loading
Loading