diff --git a/app/src/main/java/com/egobook/app/MainActivity.kt b/app/src/main/java/com/egobook/app/MainActivity.kt index 30f81503..107187d0 100644 --- a/app/src/main/java/com/egobook/app/MainActivity.kt +++ b/app/src/main/java/com/egobook/app/MainActivity.kt @@ -3,6 +3,7 @@ package com.egobook.app import android.os.Bundle import android.util.Log import android.view.View +import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat @@ -51,7 +52,8 @@ class MainActivity : AppCompatActivity(), BlurController, NotificationController R.id.diaryWriteFragment, // 일기 작성 화면 R.id.calenderFragment, // 달력 화면 R.id.storeFragment, - R.id.accountFragment //계정 화면 + R.id.accountFragment, //계정 화면 + R.id.psychologyFragment -> { binding.bottomNavigation.visibility = View.GONE } @@ -95,7 +97,8 @@ class MainActivity : AppCompatActivity(), BlurController, NotificationController RewardedAd.load(this, adUnitId, adRequest, object : RewardedAdLoadCallback() { override fun onAdFailedToLoad(adError: LoadAdError) { rewardedAd = null - Log.d("AdMob", "광고 로드 실패") + Toast.makeText(this@MainActivity, "광고를 불러오는데 실패했습니다", Toast.LENGTH_SHORT).show() + Log.d("AdMob", "광고 로드 실패, $adError") } override fun onAdLoaded(ad: RewardedAd) { diff --git a/app/src/main/java/com/egobook/app/ui/home/PsychologyViewModel.kt b/app/src/main/java/com/egobook/app/ui/home/PsychologyViewModel.kt index af18932a..4f79dc3b 100644 --- a/app/src/main/java/com/egobook/app/ui/home/PsychologyViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/home/PsychologyViewModel.kt @@ -1,18 +1,12 @@ package com.egobook.app.ui.home -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.egobook.app.ui.home.repository.DailyPsychologyDto import com.egobook.app.ui.home.repository.PsychologyKnowledge import com.egobook.app.ui.home.repository.PsychologyReward +import com.egobook.app.ui.home.repository.SavedPsychologyDto import com.egobook.app.ui.home.repository.UserPsychologyRepository -import com.egobook.app.ui.home.repository.UserRepository -import com.egobook.app.ui.home.user.Ink -import com.egobook.app.ui.home.user.Level -import com.egobook.app.ui.home.user.User -import com.egobook.app.ui.shop.CustomItem -import com.egobook.app.ui.shop.StoreRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -25,6 +19,14 @@ class PsychologyViewModel @Inject constructor( private val psychologyRepository: UserPsychologyRepository ) : ViewModel() { + init { + loadDailyPsychology() + loadSavedPsychology() + } + + private val _savedPsychologies = MutableStateFlow(emptyList()) + val savedPsychologies: StateFlow> = _savedPsychologies.asStateFlow() + private val _dailyPhycologyDto = MutableStateFlow( DailyPsychologyDto( date = "0000-00-00", @@ -50,6 +52,27 @@ class PsychologyViewModel @Inject constructor( viewModelScope.launch { _dailyPhycologyDto.value = psychologyRepository.loadDailyPsychology() } + } + + fun savePsychology(knowledgeId: Int) { + viewModelScope.launch { + psychologyRepository.saveDailyPsychology(knowledgeId) + loadDailyPsychology() + loadSavedPsychology() + } + } + fun deletePsychology(knowledgeId: Int) { + viewModelScope.launch { + psychologyRepository.deletePsychology(knowledgeId) + loadDailyPsychology() + loadSavedPsychology() + } + } + + fun loadSavedPsychology() { + viewModelScope.launch { + _savedPsychologies.value = psychologyRepository.loadSavedPsychology() + } } } diff --git a/app/src/main/java/com/egobook/app/ui/home/repository/UserRepository.kt b/app/src/main/java/com/egobook/app/ui/home/repository/UserRepository.kt index 145705e1..638f3e6c 100644 --- a/app/src/main/java/com/egobook/app/ui/home/repository/UserRepository.kt +++ b/app/src/main/java/com/egobook/app/ui/home/repository/UserRepository.kt @@ -6,8 +6,10 @@ import com.egobook.app.ui.home.user.Tendency import com.egobook.app.ui.home.user.User import retrofit2.Retrofit import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.POST +import retrofit2.http.Path import javax.inject.Inject import javax.inject.Singleton @@ -28,6 +30,11 @@ interface UserTendencyRepository { interface UserPsychologyRepository { suspend fun isReadDailyPsychology(): Boolean suspend fun loadDailyPsychology(): DailyPsychologyDto + suspend fun saveDailyPsychology(knowledgeId: Int): SavingPsychologyLogDto + + suspend fun deletePsychology(knowledgeId: Int): DeletingPsychologyLogDto + + suspend fun loadSavedPsychology(): List } interface NetworkUserService { @@ -95,12 +102,48 @@ data class DailyPsychologyDto( ) +data class SavingPsychologyLogDto( + val knowledgeId: Long, + val saved: Boolean, + val toastMessage: String +) + +data class DeletingPsychologyLogDto( + val saved: Boolean, + val knowledgeId: Long, + val toastMessage: String +) + +data class SavedPsychologyDto( + val knowledgeId: Int, + val preview: String, + val savedAt: String, + val source: String, + val title: String +) + +data class SavedPsychologyGroupDto( + val values: List, + val hasNext: Boolean, + val nextCursor: Int +) + interface NetworkPsychologyService { @GET("/psychology/daily/status") suspend fun isReadDailyPsychology(): BaseResponse @GET("/psychology/daily") suspend fun loadDailyPsychology(): BaseResponse + + @POST("/psychology/{knowledgeId}/save") + suspend fun savePsychology(@Path("knowledgeId") knowledgeId: Int): BaseResponse + + @DELETE("/psychology/{knowledgeId}/save") + suspend fun deletePsychology(@Path("knowledgeId") knowledgeId: Int): BaseResponse + + @GET("/psychology/saved") + suspend fun loadSavedPsychology(): BaseResponse + } @Singleton @@ -145,7 +188,7 @@ class NetworkUserRepository @Inject constructor( ) return watchingAdResponse.message } - + override suspend fun isReadDailyPsychology(): Boolean { val psychologyResponse: BaseResponse = psychologyService.isReadDailyPsychology() @@ -159,4 +202,22 @@ class NetworkUserRepository @Inject constructor( return psychologyResponse.data } + override suspend fun saveDailyPsychology(knowledgeId: Int): SavingPsychologyLogDto { + val psychologyResponse: BaseResponse = + psychologyService.savePsychology(knowledgeId) + return psychologyResponse.data + } + + override suspend fun deletePsychology(knowledgeId: Int): DeletingPsychologyLogDto { + val psychologyResponse: BaseResponse = + psychologyService.deletePsychology(knowledgeId) + return psychologyResponse.data + } + + override suspend fun loadSavedPsychology(): List { + val psychologyResponse: BaseResponse = + psychologyService.loadSavedPsychology() + return psychologyResponse.data.values + } + } diff --git a/app/src/main/java/com/egobook/app/ui/home/ui/HomeFragment.kt b/app/src/main/java/com/egobook/app/ui/home/ui/HomeFragment.kt index cb0ec097..ac1f2211 100644 --- a/app/src/main/java/com/egobook/app/ui/home/ui/HomeFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/home/ui/HomeFragment.kt @@ -92,17 +92,7 @@ class HomeFragment(): Fragment() { } binding.ivBottle.setOnClickListener { - applyScreenBlur(BlurLevel.BASE) - val dialog = PsychologyDialog() - dialog.isCancelable = false - dialog.show(parentFragmentManager, "DailyPsychologyDialog") - binding.ivDailyBottle.visibility = View.INVISIBLE - - parentFragmentManager.setFragmentResultListener("psychology_key", viewLifecycleOwner) { _, _ -> - Log.d("jang", "다이얼로그 닫힘 감지 - 데이터 갱신") - viewModel.fetchUser() - viewModel.fetchDailyPhycologyReadState() - } + findNavController().navigate(R.id.action_homeFragment_to_psychologyFragment) } binding.ivAd.setOnClickListener { diff --git a/app/src/main/java/com/egobook/app/ui/home/ui/PsychologyDialog.kt b/app/src/main/java/com/egobook/app/ui/home/ui/PsychologyDialog.kt index 21626819..dd502887 100644 --- a/app/src/main/java/com/egobook/app/ui/home/ui/PsychologyDialog.kt +++ b/app/src/main/java/com/egobook/app/ui/home/ui/PsychologyDialog.kt @@ -11,18 +11,22 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.setFragmentResult import androidx.lifecycle.lifecycleScope +import com.egobook.app.R import com.egobook.app.databinding.DialogAdBinding import com.egobook.app.databinding.DialogPsychologyBinding import com.egobook.app.removeScreenBlur import com.egobook.app.ui.home.HomeViewModel import com.egobook.app.ui.home.PsychologyViewModel import kotlinx.coroutines.launch +import kotlin.math.max class PsychologyDialog() : DialogFragment() { private var _binding: DialogPsychologyBinding? = null private val binding get() = checkNotNull(_binding) { "Fragment가 제거되었습니다." } + private var ink: Int = 0 + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -38,19 +42,26 @@ class PsychologyDialog() : DialogFragment() { val viewModel: PsychologyViewModel by activityViewModels() - viewModel.loadDailyPsychology() - viewLifecycleOwner.lifecycleScope.launch { viewModel.dailyPhycologyDto.collect { dailyPhycologyDto -> binding.tvPsychologyContent.text = dailyPhycologyDto.knowledge.content binding.tvPsychologySource.text = dailyPhycologyDto.knowledge.source binding.tvPsychologyDate.text = dailyPhycologyDto.date - if(dailyPhycologyDto.reward == null) { - binding.btnReward.text = "메인으로 돌아가기" + ink = max(ink, dailyPhycologyDto.reward?.inkGranted ?: 0) + binding.btnReward.text = "잉크 ${ink}개 획득!" + if (dailyPhycologyDto.isBookmarked) { + binding.ivBookmark.setImageResource(R.drawable.ic_bookmark_clicked) } else { - binding.btnReward.text = "잉크 ${dailyPhycologyDto.reward.inkGranted}개 획득!" + binding.ivBookmark.setImageResource(R.drawable.ic_bookmark_unclicked) } + } + } + binding.ivBookmark.setOnClickListener { + if (viewModel.dailyPhycologyDto.value.isBookmarked) { + viewModel.deletePsychology(viewModel.dailyPhycologyDto.value.knowledge.knowledgeId) + } else { + viewModel.savePsychology(viewModel.dailyPhycologyDto.value.knowledge.knowledgeId) } } diff --git a/app/src/main/java/com/egobook/app/ui/home/ui/PsychologyFragment.kt b/app/src/main/java/com/egobook/app/ui/home/ui/PsychologyFragment.kt new file mode 100644 index 00000000..b59f7983 --- /dev/null +++ b/app/src/main/java/com/egobook/app/ui/home/ui/PsychologyFragment.kt @@ -0,0 +1,78 @@ +package com.egobook.app.ui.home.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import com.egobook.app.R +import com.egobook.app.databinding.FragmentPsychologyBinding +import com.egobook.app.ui.home.PsychologyViewModel +import com.egobook.app.ui.home.repository.SavedPsychologyDto +import com.egobook.app.ui.shop.ItemAdapter +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class PsychologyFragment(): Fragment() { + private lateinit var binding: FragmentPsychologyBinding + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentPsychologyBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val viewModel: PsychologyViewModel by activityViewModels() + viewLifecycleOwner.lifecycleScope.launch { + viewModel.dailyPhycologyDto.collect { dailyPhycologyDto -> + binding.tvPsychologyContent.text = dailyPhycologyDto.knowledge.content + binding.tvPsychologySource.text = dailyPhycologyDto.knowledge.source + binding.tvPsychologyDate.text = dailyPhycologyDto.date + if (dailyPhycologyDto.isBookmarked) { + binding.ivBookmark.setImageResource(R.drawable.ic_bookmark_clicked) + } else { + binding.ivBookmark.setImageResource(R.drawable.ic_bookmark_unclicked) + } + } + } + binding.ivBookmark.setOnClickListener { + if (viewModel.dailyPhycologyDto.value.isBookmarked) { + viewModel.deletePsychology(viewModel.dailyPhycologyDto.value.knowledge.knowledgeId) + } else { + viewModel.savePsychology(viewModel.dailyPhycologyDto.value.knowledge.knowledgeId) + } + } + binding.ivBackButton.setOnClickListener { + findNavController().navigate(R.id.action_psychologyFragment_to_homeFragment) + } + + val itemAdapter = SavedPsychologyAdapter { savedPsychologyDto -> + viewModel.deletePsychology(savedPsychologyDto.knowledgeId) + } + + binding.rvSavedPsychologies.apply { + adapter = itemAdapter + layoutManager = LinearLayoutManager(context) + } + + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.savedPsychologies.collect { psychologies -> + itemAdapter.submitList(psychologies) + } + } + } + } +} diff --git a/app/src/main/java/com/egobook/app/ui/home/ui/SavedPsychologyAdapter.kt b/app/src/main/java/com/egobook/app/ui/home/ui/SavedPsychologyAdapter.kt new file mode 100644 index 00000000..900b838f --- /dev/null +++ b/app/src/main/java/com/egobook/app/ui/home/ui/SavedPsychologyAdapter.kt @@ -0,0 +1,57 @@ +package com.egobook.app.ui.home.ui + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.egobook.app.R +import com.egobook.app.ui.home.repository.SavedPsychologyDto +import com.egobook.app.ui.shop.CustomItem + +class SavedPsychologyAdapter( + private val onItemClick: (SavedPsychologyDto) -> Unit +) : ListAdapter(ItemDiffCallback) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.view_holder_psychology, parent, false) + return ItemViewHolder(view) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = getItem(position) + holder.bind(item, onItemClick) + } + + class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val dateTextView: TextView = view.findViewById(R.id.tv_psychology_date) + val contentTextView: TextView = view.findViewById(R.id.tv_psychology_content) + val sourceTextView: TextView = view.findViewById(R.id.tv_psychology_source) + fun bind(item: SavedPsychologyDto, onItemClick: (SavedPsychologyDto) -> Unit) { + dateTextView.text = item.savedAt + contentTextView.text = item.title + sourceTextView.text = item.source + contentTextView.text = item.preview + itemView.setOnClickListener { + onItemClick(item) + } + } + } + + companion object { + private val ItemDiffCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: SavedPsychologyDto, newItem: SavedPsychologyDto): Boolean { + return oldItem.knowledgeId == newItem.knowledgeId + } + + override fun areContentsTheSame(oldItem: SavedPsychologyDto, newItem: SavedPsychologyDto): Boolean { + return oldItem == newItem // 전체 객체 내용 비교 + } + } + } +} diff --git a/app/src/main/java/com/egobook/app/ui/square/view/LetterSendDialog.kt b/app/src/main/java/com/egobook/app/ui/square/view/LetterSendDialog.kt index df383395..4de761be 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/LetterSendDialog.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/LetterSendDialog.kt @@ -4,6 +4,7 @@ import android.app.Dialog import android.graphics.Color import android.os.Bundle import android.view.View +import android.widget.Toast import androidx.core.graphics.drawable.toDrawable import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels @@ -64,7 +65,10 @@ class LetterSendDialog(private val mode: LetterMode, private val friendInfo: Fri dismiss() } btnLetterSendApply.setOnClickListener { - viewModel.detectAbusiveContent(text = letterContent) +// viewModel.detectAbusiveContent(text = letterContent) + Toast.makeText(context, "서버 점검중입니다.", Toast.LENGTH_SHORT).show() + removeScreenBlur() + dismiss() } } @@ -141,4 +145,4 @@ class LetterSendDialog(private val mode: LetterMode, private val friendInfo: Fri companion object { const val TAG = "LetterSendDialog" } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/egobook/app/ui/square/viewmodel/LetterViewModel.kt b/app/src/main/java/com/egobook/app/ui/square/viewmodel/LetterViewModel.kt index 88c9c14b..e2b244ad 100644 --- a/app/src/main/java/com/egobook/app/ui/square/viewmodel/LetterViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/square/viewmodel/LetterViewModel.kt @@ -219,4 +219,4 @@ class LetterViewModel @Inject constructor( } } } -} \ No newline at end of file +} diff --git a/app/src/main/res/drawable/ic_bookmark_clicked.xml b/app/src/main/res/drawable/ic_bookmark_clicked.xml new file mode 100644 index 00000000..57a7994c --- /dev/null +++ b/app/src/main/res/drawable/ic_bookmark_clicked.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_crown.png b/app/src/main/res/drawable/ic_crown.png new file mode 100644 index 00000000..aa2ba03f Binary files /dev/null and b/app/src/main/res/drawable/ic_crown.png differ diff --git a/app/src/main/res/drawable/ic_shine_bottle.png b/app/src/main/res/drawable/ic_shine_bottle.png new file mode 100644 index 00000000..af2d0745 Binary files /dev/null and b/app/src/main/res/drawable/ic_shine_bottle.png differ diff --git a/app/src/main/res/layout/dialog_psychology.xml b/app/src/main/res/layout/dialog_psychology.xml index 4c3b017b..849c7c36 100644 --- a/app/src/main/res/layout/dialog_psychology.xml +++ b/app/src/main/res/layout/dialog_psychology.xml @@ -38,7 +38,7 @@ android:layout_weight="1" /> @@ -84,7 +84,6 @@ android:textAlignment="center" android:text="잉크 획득" /> - diff --git a/app/src/main/res/layout/fragment_ego_room_statistics.xml b/app/src/main/res/layout/fragment_ego_room_statistics.xml index a313b89d..562e32ad 100644 --- a/app/src/main/res/layout/fragment_ego_room_statistics.xml +++ b/app/src/main/res/layout/fragment_ego_room_statistics.xml @@ -22,6 +22,8 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 10b92e7e..b35032c9 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -150,10 +150,11 @@ diff --git a/app/src/main/res/layout/fragment_psychology.xml b/app/src/main/res/layout/fragment_psychology.xml new file mode 100644 index 00000000..bd9b0df3 --- /dev/null +++ b/app/src/main/res/layout/fragment_psychology.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_square_question_reply.xml b/app/src/main/res/layout/item_square_question_reply.xml index 26d5e912..17dc9e5b 100644 --- a/app/src/main/res/layout/item_square_question_reply.xml +++ b/app/src/main/res/layout/item_square_question_reply.xml @@ -49,6 +49,7 @@ app:layout_constraintStart_toEndOf="@id/civ_item_square_question_reply_user_image"/> - \ No newline at end of file + diff --git a/app/src/main/res/layout/view_holder_psychology.xml b/app/src/main/res/layout/view_holder_psychology.xml new file mode 100644 index 00000000..ecf9d740 --- /dev/null +++ b/app/src/main/res/layout/view_holder_psychology.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/bottom_navigation.xml b/app/src/main/res/navigation/bottom_navigation.xml index 731dc5b5..2d345c1f 100644 --- a/app/src/main/res/navigation/bottom_navigation.xml +++ b/app/src/main/res/navigation/bottom_navigation.xml @@ -14,12 +14,27 @@ android:id="@+id/action_homeFragment_to_storeFragment" app:destination="@id/storeFragment" /> + + + + + + + + #D2EAA2 + #ECF2F9