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

[Feature/#63] : profile push alarm #64

Merged
merged 17 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions core/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ dependencies {
implementation(project(":core:model"))
implementation(project(":core:datastore"))
implementation(libs.paging)
implementation(libs.okhttp)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package com.teamwable.data.repository

import com.teamwable.model.Profile
import com.teamwable.model.profile.MemberDataModel
import com.teamwable.model.profile.MemberInfoEditModel
import java.io.File

interface ProfileRepository {
suspend fun getProfileInfo(userId: Long): Result<Profile>
suspend fun getMemberData(): Result<MemberDataModel>
suspend fun patchWithdrawal(deletedReason: List<String>): Result<Unit>
suspend fun patchProfileUriEdit(info: MemberInfoEditModel, file: File?): Result<Unit>
Eonji-sw marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import com.teamwable.data.mapper.toModel.toProfile
import com.teamwable.data.repository.ProfileRepository
import com.teamwable.model.Profile
import com.teamwable.model.profile.MemberDataModel
import com.teamwable.model.profile.MemberInfoEditModel
import com.teamwable.network.datasource.ProfileService
import com.teamwable.network.dto.request.RequestWithdrawalDto
import com.teamwable.network.util.handleThrowable
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import java.io.File
import javax.inject.Inject

class DefaultProfileRepository @Inject constructor(
Expand All @@ -31,4 +38,28 @@ class DefaultProfileRepository @Inject constructor(
Unit
}.onFailure { return it.handleThrowable() }
}

override suspend fun patchProfileUriEdit(info: MemberInfoEditModel, file: File?): Result<Unit> {
return runCatching {
val infoJson = JSONObject().apply {
put("nickname", info.nickname)
put("isAlarmAllowed", info.isAlarmAllowed)
put("memberIntro", info.memberIntro)
put("isPushAlarmAllowed", info.isPushAlarmAllowed)
put("fcmToken", info.fcmToken)
put("memberLckYears", info.memberLckYears)
put("memberFanTeam", info.memberFanTeam)
put("memberDefaultProfileImage", info.memberDefaultProfileImage)
}.toString()

val infoRequestBody = infoJson.toRequestBody("application/json".toMediaTypeOrNull())

val filePart = file?.let {
val requestBody = it.asRequestBody("image/jpeg".toMediaTypeOrNull())
MultipartBody.Part.createFormData("file", it.name, requestBody)
}
Eonji-sw marked this conversation as resolved.
Show resolved Hide resolved

apiService.patchUserProfile(infoRequestBody, filePart).success
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.teamwable.model.profile

data class MemberInfoEditModel(
val nickname: String? = "",
val isAlarmAllowed: Boolean? = false,
val memberIntro: String? = "",
val isPushAlarmAllowed: Boolean? = false,
val fcmToken: String? = "",
val memberLckYears: Int = 0,
val memberFanTeam: String? = "",
val memberDefaultProfileImage: String? = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import com.teamwable.network.dto.response.ResponseProfileInfoDto
import com.teamwable.network.dto.response.profile.ResponseMemberDataDto
import com.teamwable.network.util.BaseResponse
import com.teamwable.network.util.BaseUnitResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.PATCH
import retrofit2.http.Part
import retrofit2.http.Path

interface ProfileService {
Expand All @@ -21,4 +24,10 @@ interface ProfileService {

@PATCH("api/v1/withdrawal")
suspend fun patchWithdrawal(@Body requestWithdrawalDto: RequestWithdrawalDto): BaseUnitResponse<Unit>

@PATCH("api/v1/user-profile2")
suspend fun patchUserProfile(
@Part("info") requestProfileEdit: RequestBody,
@Part file: MultipartBody.Part?,
): BaseUnitResponse<Unit>
}
14 changes: 14 additions & 0 deletions core/ui/src/main/java/com/teamwable/ui/extensions/FragmentExt.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.teamwable.ui.extensions

import android.content.Intent
import android.net.Uri
import android.provider.Settings
import android.view.View
import android.widget.Toast
import androidx.annotation.ColorRes
Expand Down Expand Up @@ -48,3 +51,14 @@ fun Fragment.statusBarColorOf(
) {
requireActivity().statusBarColorOf(resId)
}

fun Fragment.navigateToAppSettings() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", requireContext().packageName, null)
intent.data = uri
startActivity(intent)
}
Eonji-sw marked this conversation as resolved.
Show resolved Hide resolved

fun Fragment.openUri(uri: String) {
Intent(Intent.ACTION_VIEW, Uri.parse(uri)).also { startActivity(it) }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.teamwable.news.rank

import android.content.Intent
import android.net.Uri
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
Expand All @@ -11,6 +13,7 @@ import com.teamwable.news.NewsViewModel
import com.teamwable.news.databinding.FragmentNewsRankBinding
import com.teamwable.ui.base.BindingFragment
import com.teamwable.ui.extensions.colorOf
import com.teamwable.ui.extensions.openUri
import com.teamwable.ui.extensions.viewLifeCycle
import com.teamwable.ui.extensions.viewLifeCycleScope
import dagger.hilt.android.AndroidEntryPoint
Expand Down Expand Up @@ -46,7 +49,7 @@ class NewsRankFragment : BindingFragment<FragmentNewsRankBinding>(FragmentNewsRa

private fun initOpinionBtnClickListener() {
binding.btnNewsRankOpinion.setOnClickListener {
// Todo : λ‚˜μ€‘μ— μΆ”κ°€ν•΄μ•Ό 함
openUri("https://forms.gle/WWfbHXvGNgXMxgZr5")
}
}

Expand Down
2 changes: 2 additions & 0 deletions feature/profile/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ dependencies {

// Third Party
implementation(libs.glide)
implementation(libs.google.play.services)
implementation(libs.firebase.messaging.ktx)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.teamwable.profile.R
import com.teamwable.profile.databinding.BottomsheetProfileHamburgerBinding
import com.teamwable.ui.base.BindingBottomSheetFragment
import com.teamwable.ui.component.TwoButtonDialog
import com.teamwable.ui.extensions.openUri
import com.teamwable.ui.extensions.viewLifeCycleScope
import com.teamwable.ui.type.DialogType
import com.teamwable.ui.util.Arg.DIALOG_RESULT
Expand Down Expand Up @@ -42,13 +43,13 @@ class ProfileHamburgerBottomSheet : BindingBottomSheetFragment<BottomsheetProfil

private fun initFeedbackBtnClickListener() {
binding.tvProfileHamburgerFeedback.setOnClickListener {
navigateToWeb("https://forms.gle/WWfbHXvGNgXMxgZr5")
openUri("https://forms.gle/WWfbHXvGNgXMxgZr5")
}
}

private fun initCustomerServiceBtnClickListener() {
binding.tvProfileHamburgerCustomerService.setOnClickListener {
navigateToWeb("https://forms.gle/WWfbHXvGNgXMxgZr5")
openUri("https://forms.gle/WWfbHXvGNgXMxgZr5")
}
}

Expand All @@ -74,8 +75,4 @@ class ProfileHamburgerBottomSheet : BindingBottomSheetFragment<BottomsheetProfil
)
)
}

private fun navigateToWeb(uri: String) {
Intent(Intent.ACTION_VIEW, Uri.parse(uri)).also { startActivity(it) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import com.teamwable.common.uistate.UiState
import com.teamwable.data.repository.ProfileRepository
import com.teamwable.data.repository.UserInfoRepository
import com.teamwable.model.profile.MemberDataModel
import com.teamwable.model.profile.MemberInfoEditModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import javax.inject.Inject

@HiltViewModel
Expand All @@ -23,6 +26,9 @@ class ProfileHamburgerViewModel @Inject constructor(
private val _withdrawalUiState = MutableStateFlow<UiState<Unit>>(UiState.Loading)
val withdrawalUiState = _withdrawalUiState.asStateFlow()

private var _pushAlarmAllowedState = MutableStateFlow(false)
val pushAlarmAllowedState = _pushAlarmAllowedState.asStateFlow()

fun getMemberData() {
viewModelScope.launch {
_memberDataUiState.value = UiState.Loading
Expand Down Expand Up @@ -50,4 +56,18 @@ class ProfileHamburgerViewModel @Inject constructor(
userInfoRepository.saveAutoLogin(check)
}
}

fun patchUserProfileUri(info: MemberInfoEditModel, url: File? = null) {
viewModelScope.launch {
profileRepository.patchProfileUriEdit(info, url)
.onSuccess { info.isPushAlarmAllowed?.let { _pushAlarmAllowedState.value = it } }
.onFailure { Timber.d("fail", it.message.toString()) }
}
}

fun saveIsPushAlarmAllowed(isPushAlarmAllowed: Boolean) =
viewModelScope.launch {
userInfoRepository.saveIsPushAlarmAllowed(isPushAlarmAllowed)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.teamwable.model.profile.MemberDataModel
import com.teamwable.profile.R
import com.teamwable.profile.databinding.FragmentProfileInformationBinding
import com.teamwable.ui.base.BindingFragment
import com.teamwable.ui.extensions.openUri
import com.teamwable.ui.extensions.stringOf
import com.teamwable.ui.extensions.viewLifeCycle
import com.teamwable.ui.extensions.viewLifeCycleScope
Expand All @@ -35,14 +36,10 @@ class ProfileInformationFragment : BindingFragment<FragmentProfileInformationBin

private fun initTermsOfServiceClickListener() {
binding.tvProfileInformationTermsOfServiceContent.setOnClickListener {
navigateToWeb("https://joyous-ghost-8c7.notion.site/c6e26919055a4ff98fd73a8f9b29cb36?pvs=4")
openUri("https://joyous-ghost-8c7.notion.site/c6e26919055a4ff98fd73a8f9b29cb36?pvs=4")
}
}

private fun navigateToWeb(uri: String) {
Intent(Intent.ACTION_VIEW, Uri.parse(uri)).also { startActivity(it) }
}

private fun setupMemberDataObserve() {
viewModel.memberDataUiState.flowWithLifecycle(viewLifeCycle).onEach {
when (it) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.teamwable.profile.hamburger

import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.flowWithLifecycle
import androidx.navigation.fragment.findNavController
import com.google.android.gms.tasks.OnCompleteListener
import com.google.firebase.messaging.FirebaseMessaging
import com.teamwable.model.profile.MemberInfoEditModel
import com.teamwable.profile.R
import com.teamwable.profile.databinding.FragmentPushAlarmBinding
import com.teamwable.ui.base.BindingFragment
import com.teamwable.ui.extensions.navigateToAppSettings
import com.teamwable.ui.extensions.stringOf
import com.teamwable.ui.extensions.viewLifeCycle
import com.teamwable.ui.extensions.viewLifeCycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber

@AndroidEntryPoint
class PushAlarmFragment : BindingFragment<FragmentPushAlarmBinding>(FragmentPushAlarmBinding::inflate) {
private val viewModel by viewModels<ProfileHamburgerViewModel>()

override fun initView() {
setAppbarText()
setPushAlarmText()

initPushAlarmSettingClickListener()
initBackBtnClickListener()

setupUserPushAlarmInfoObserve()
}

override fun onResume() {
super.onResume()
refreshPushAlarmPermission()
Copy link
Collaborator

Choose a reason for hiding this comment

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

p1 : 이 ν•¨μˆ˜ resume말고 initview에 λ„£μ–΄μ£Όμ„Έμš₯

Copy link
Member Author

Choose a reason for hiding this comment

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

κ·Έλ ‡κ²Œ ν–ˆλ”λ‹ˆ μ•ˆλ©λ‹ˆλ‹Ή..,,

Copy link
Collaborator

Choose a reason for hiding this comment

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

μ’€ 더 쒋은 μ½”λ“œ κ³ λ―Ό μ’€ ν•΄λ΄„ γ…‹.γ…‹

}

private fun refreshPushAlarmPermission() {
setPushAlarmText()
when (checkPushAlarmAllowed()) {
true -> handlePushAlarmPermissionGranted()
false -> handlePushAlarmPermissionDenied()
}
}

private fun handlePushAlarmPermissionGranted() {
FirebaseMessaging.getInstance().token.addOnCompleteListener(
OnCompleteListener { task ->
if (task.isSuccessful) {
viewModel.patchUserProfileUri(
MemberInfoEditModel(
isPushAlarmAllowed = true,
fcmToken = task.result
)
)
Timber.tag("fcm").d("fcm token: $task.result")
} else {
Timber.d(task.exception)
return@OnCompleteListener
}
}
)
}

private fun handlePushAlarmPermissionDenied() {
viewModel.patchUserProfileUri(MemberInfoEditModel(isPushAlarmAllowed = false))
}

private fun setupUserPushAlarmInfoObserve() {
viewModel.pushAlarmAllowedState.flowWithLifecycle(viewLifeCycle).onEach {
viewModel.saveIsPushAlarmAllowed(it)
}.launchIn(viewLifeCycleScope)
}

private fun initPushAlarmSettingClickListener() {
binding.tvPushAlarmContent.setOnClickListener {
navigateToAppSettings()
}
binding.btnPushAlarmMore.setOnClickListener {
navigateToAppSettings()
}
}


private fun setPushAlarmText() {
binding.tvPushAlarmContent.text =
if (checkPushAlarmAllowed()) getString(R.string.tv_push_alarm_content_on)
else getString(R.string.tv_push_alarm_content_off)
}

private fun checkPushAlarmAllowed(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
} else {
NotificationManagerCompat.from(requireContext()).areNotificationsEnabled()
}
}

private fun setAppbarText() {
binding.viewPushAlarmAppbar.tvProfileAppbarTitle.text = stringOf(R.string.appbar_push_alarm_title)
}

private fun initBackBtnClickListener() {
binding.viewPushAlarmAppbar.btnProfileAppbarBack.setOnClickListener {
findNavController().popBackStack()
}
}
}
Loading