diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/dialog/MarketingDialog.kt b/core/design-system/src/main/java/com/twix/designsystem/components/dialog/MarketingDialog.kt
new file mode 100644
index 00000000..e6e9e811
--- /dev/null
+++ b/core/design-system/src/main/java/com/twix/designsystem/components/dialog/MarketingDialog.kt
@@ -0,0 +1,148 @@
+package com.twix.designsystem.components.dialog
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.twix.designsystem.R
+import com.twix.designsystem.components.dialog.CommonDialog
+import com.twix.designsystem.components.text.AppText
+import com.twix.designsystem.theme.GrayColor
+import com.twix.designsystem.theme.TwixTheme
+import com.twix.domain.model.enums.AppTextStyle
+import com.twix.ui.extension.noRippleClickable
+
+@Composable
+fun MarketingDialog(
+ visible: Boolean,
+ onConfirm: (Boolean, Boolean) -> Unit,
+) {
+ var isMarketingChecked by remember { mutableStateOf(true) }
+ var isNightMarketingChecked by remember { mutableStateOf(true) }
+
+ CommonDialog(
+ visible = visible,
+ confirmText = stringResource(R.string.word_confirm),
+ dismissText = null,
+ onDismissRequest = { },
+ onConfirm = {
+ onConfirm(isMarketingChecked, isNightMarketingChecked)
+ },
+ content = {
+ MarketingDialogContent(
+ isMarketingChecked = isMarketingChecked,
+ isNightMarketingChecked = isNightMarketingChecked,
+ onMarketingToggle = {
+ isMarketingChecked = !isMarketingChecked
+ },
+ onNightMarketingToggle = {
+ isNightMarketingChecked = !isNightMarketingChecked
+ },
+ )
+ },
+ )
+}
+
+@Composable
+private fun MarketingDialogContent(
+ isMarketingChecked: Boolean,
+ isNightMarketingChecked: Boolean,
+ onMarketingToggle: () -> Unit,
+ onNightMarketingToggle: () -> Unit,
+) {
+ Column {
+ AppText(
+ text = stringResource(R.string.marketing_dialog_title),
+ style = AppTextStyle.T1,
+ color = GrayColor.C500,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.fillMaxWidth(),
+ )
+
+ Spacer(Modifier.height(24.dp))
+
+ MarketingCheckItem(
+ text = stringResource(R.string.marketing_dialog_marketing),
+ checked = isMarketingChecked,
+ onClick = onMarketingToggle,
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ MarketingCheckItem(
+ text = stringResource(R.string.marketing_dialog_night_marketing),
+ checked = isNightMarketingChecked,
+ onClick = onNightMarketingToggle,
+ )
+
+ Spacer(Modifier.height(14.dp))
+
+ AppText(
+ text = stringResource(R.string.marketing_dialog_description),
+ style = AppTextStyle.C2,
+ color = GrayColor.C300,
+ modifier = Modifier.padding(start = 10.dp),
+ )
+ }
+}
+
+@Composable
+private fun MarketingCheckItem(
+ text: String,
+ checked: Boolean,
+ onClick: () -> Unit,
+) {
+ val icon = if (checked) R.drawable.ic_checked_you else R.drawable.ic_empty_check
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .noRippleClickable(onClick = onClick),
+ ) {
+ Image(
+ painter = painterResource(icon),
+ contentDescription = null,
+ modifier =
+ Modifier
+ .size(24.dp),
+ )
+
+ Spacer(Modifier.width(8.dp))
+
+ AppText(
+ text = text,
+ style = AppTextStyle.B2,
+ color = GrayColor.C500,
+ )
+ }
+}
+
+@Preview(showBackground = true, showSystemUi = true)
+@Composable
+private fun MarketingDialogPreview() {
+ TwixTheme {
+ MarketingDialog(
+ visible = true,
+ onConfirm = { _, _ -> },
+ )
+ }
+}
diff --git a/core/design-system/src/main/res/drawable/ic_empty_check.xml b/core/design-system/src/main/res/drawable/ic_empty_check.xml
new file mode 100644
index 00000000..536425ab
--- /dev/null
+++ b/core/design-system/src/main/res/drawable/ic_empty_check.xml
@@ -0,0 +1,18 @@
+
+
+
+
diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml
index 3cabb0b8..f796c47b 100644
--- a/core/design-system/src/main/res/values/strings.xml
+++ b/core/design-system/src/main/res/values/strings.xml
@@ -17,6 +17,7 @@
취소
삭제
수정
+ 확인
저장
설정
계정
@@ -122,6 +123,12 @@
정말 탈퇴하시겠어요?
커플 연결이 끊어집니다.\n데이터는 전부 삭제되며 복구가 불가능합니다.
+
+ 도움이 되는 정보를\n알림으로 받아보시겠어요?
+ [선택] 마케팅 정보 알림
+ [선택] 야간 마케팅 정보 알림
+ * 언제든지 설정 > 알림 설정에서 변경 가능해요
+
업로드
이미지 캡처에 실패했습니다. 다시 시도해 주세요.
diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/vm/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/twix/onboarding/OnBoardingViewModel.kt
similarity index 90%
rename from feature/onboarding/src/main/java/com/twix/onboarding/vm/OnBoardingViewModel.kt
rename to feature/onboarding/src/main/java/com/twix/onboarding/OnBoardingViewModel.kt
index 68ab9e67..51f9543a 100644
--- a/feature/onboarding/src/main/java/com/twix/onboarding/vm/OnBoardingViewModel.kt
+++ b/feature/onboarding/src/main/java/com/twix/onboarding/OnBoardingViewModel.kt
@@ -1,4 +1,4 @@
-package com.twix.onboarding.vm
+package com.twix.onboarding
import androidx.lifecycle.viewModelScope
import com.twix.domain.model.OnboardingStatus
@@ -16,10 +16,6 @@ class OnBoardingViewModel(
private val onBoardingRepository: OnBoardingRepository,
private val notificationRepository: NotificationRepository,
) : BaseViewModel(OnBoardingUiState()) {
- init {
- initNotificationSettings()
- }
-
fun fetchMyInviteCode() {
launchResult(
block = { onBoardingRepository.fetchInviteCode() },
@@ -43,6 +39,9 @@ class OnBoardingViewModel(
// 디데이 설정 화면
is OnBoardingIntent.SelectDate -> reduceDday(intent.value)
OnBoardingIntent.SubmitDday -> anniversarySetup()
+
+ is OnBoardingIntent.SubmitMarketingConsent ->
+ initNotificationSettings(intent.isPushEnabled, intent.isMarketingEnabled, intent.isNightMarketingEnabled)
}
}
@@ -145,9 +144,19 @@ class OnBoardingViewModel(
)
}
- private fun initNotificationSettings() {
+ private fun initNotificationSettings(
+ isPushEnabled: Boolean,
+ isMarketingEnabled: Boolean,
+ isNightMarketingEnabled: Boolean,
+ ) {
launchResult(
- block = { notificationRepository.initNotificationSettings(true, true, true) },
+ block = {
+ notificationRepository.initNotificationSettings(
+ isPushEnabled,
+ isMarketingEnabled,
+ isNightMarketingEnabled,
+ )
+ },
onSuccess = {},
)
}
diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/couple/CoupleConnectRoute.kt b/feature/onboarding/src/main/java/com/twix/onboarding/couple/CoupleConnectRoute.kt
index fda3dbbb..51a31c09 100644
--- a/feature/onboarding/src/main/java/com/twix/onboarding/couple/CoupleConnectRoute.kt
+++ b/feature/onboarding/src/main/java/com/twix/onboarding/couple/CoupleConnectRoute.kt
@@ -1,5 +1,9 @@
package com.twix.onboarding.couple
+import android.Manifest
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -13,18 +17,23 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.content.ContextCompat
import com.twix.designsystem.components.bottomsheet.CommonBottomSheet
import com.twix.designsystem.components.bottomsheet.model.CommonBottomSheetConfig
+import com.twix.designsystem.components.dialog.MarketingDialog
import com.twix.designsystem.components.text.AppText
import com.twix.designsystem.components.toast.ToastManager
import com.twix.designsystem.components.toast.model.ToastData
@@ -33,11 +42,12 @@ import com.twix.designsystem.theme.CommonColor
import com.twix.designsystem.theme.GrayColor
import com.twix.designsystem.theme.TwixTheme
import com.twix.domain.model.enums.AppTextStyle
+import com.twix.onboarding.OnBoardingViewModel
import com.twix.onboarding.R
import com.twix.onboarding.couple.component.ConnectButton
import com.twix.onboarding.couple.component.RestoreCoupleBottomSheetContent
+import com.twix.onboarding.model.OnBoardingIntent
import com.twix.onboarding.model.OnBoardingSideEffect
-import com.twix.onboarding.vm.OnBoardingViewModel
import com.twix.ui.base.ObserveAsEvents
import com.twix.ui.extension.noRippleClickable
import org.koin.compose.koinInject
@@ -48,7 +58,10 @@ fun CoupleConnectRoute(
toastManager: ToastManager = koinInject(),
navigateToNext: () -> Unit,
) {
+ var showMarketingDialog by rememberSaveable { mutableStateOf(true) }
var showRestoreSheet by rememberSaveable { mutableStateOf(false) }
+ val context = LocalContext.current
+ val currentContext by rememberUpdatedState(context)
LaunchedEffect(Unit) {
viewModel.fetchMyInviteCode()
@@ -71,13 +84,30 @@ fun CoupleConnectRoute(
}
}
- CoupleConnectScreen(
- showRestoreSheet = showRestoreSheet,
- onClickSend = { },
- onClickConnect = navigateToNext,
- onClickRestore = { showRestoreSheet = true },
- onDismissSheet = { showRestoreSheet = false },
- )
+ Box {
+ CoupleConnectScreen(
+ showRestoreSheet = showRestoreSheet,
+ onClickSend = { },
+ onClickConnect = navigateToNext,
+ onClickRestore = { showRestoreSheet = true },
+ onDismissSheet = { showRestoreSheet = false },
+ )
+
+ MarketingDialog(
+ visible = showMarketingDialog,
+ onConfirm = { marketing, nightMarketing ->
+ showMarketingDialog = false
+ val isPushEnabled = isNotificationPermissionGranted(context)
+ viewModel.dispatch(
+ OnBoardingIntent.SubmitMarketingConsent(
+ isPushEnabled = isPushEnabled,
+ isMarketingEnabled = marketing,
+ isNightMarketingEnabled = nightMarketing,
+ ),
+ )
+ },
+ )
+ }
}
@Composable
@@ -142,9 +172,23 @@ fun CoupleConnectScreen(
}
}
+private fun isNotificationPermissionGranted(context: Context): Boolean {
+ val notificationsEnabled = NotificationManagerCompat.from(context).areNotificationsEnabled()
+ if (!notificationsEnabled) return false
+
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.POST_NOTIFICATIONS,
+ ) == PackageManager.PERMISSION_GRANTED
+ } else {
+ true
+ }
+}
+
@Preview(showBackground = true)
@Composable
-fun CoupleConnectScreenPreview() {
+private fun CoupleConnectScreenPreview() {
TwixTheme {
CoupleConnectScreen(
showRestoreSheet = false,
diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt b/feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt
index a15bffbd..02cb7871 100644
--- a/feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt
+++ b/feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt
@@ -30,12 +30,12 @@ import com.twix.designsystem.theme.CommonColor
import com.twix.designsystem.theme.GrayColor
import com.twix.designsystem.theme.TwixTheme
import com.twix.domain.model.enums.AppTextStyle
+import com.twix.onboarding.OnBoardingViewModel
import com.twix.onboarding.R
import com.twix.onboarding.dday.component.DDayField
import com.twix.onboarding.dday.component.DdayTopBar
import com.twix.onboarding.model.OnBoardingIntent
import com.twix.onboarding.model.OnBoardingSideEffect
-import com.twix.onboarding.vm.OnBoardingViewModel
import com.twix.ui.base.ObserveAsEvents
import org.koin.compose.koinInject
import java.time.LocalDate
diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/di/OnBoardingModule.kt b/feature/onboarding/src/main/java/com/twix/onboarding/di/OnBoardingModule.kt
index 71a3aca8..47473f21 100644
--- a/feature/onboarding/src/main/java/com/twix/onboarding/di/OnBoardingModule.kt
+++ b/feature/onboarding/src/main/java/com/twix/onboarding/di/OnBoardingModule.kt
@@ -2,8 +2,8 @@ package com.twix.onboarding.di
import com.twix.navigation.NavRoutes
import com.twix.navigation.base.NavGraphContributor
+import com.twix.onboarding.OnBoardingViewModel
import com.twix.onboarding.navigation.OnboardingNavGraph
-import com.twix.onboarding.vm.OnBoardingViewModel
import org.koin.core.module.dsl.viewModelOf
import org.koin.core.qualifier.named
import org.koin.dsl.module
diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/invite/InviteCodeScreen.kt b/feature/onboarding/src/main/java/com/twix/onboarding/invite/InviteCodeScreen.kt
index 343f7f8c..fd000099 100644
--- a/feature/onboarding/src/main/java/com/twix/onboarding/invite/InviteCodeScreen.kt
+++ b/feature/onboarding/src/main/java/com/twix/onboarding/invite/InviteCodeScreen.kt
@@ -47,11 +47,11 @@ import com.twix.designsystem.theme.CommonColor
import com.twix.designsystem.theme.GrayColor
import com.twix.designsystem.theme.TwixTheme
import com.twix.domain.model.enums.AppTextStyle
+import com.twix.onboarding.OnBoardingViewModel
import com.twix.onboarding.R
import com.twix.onboarding.invite.component.InviteCodeTextField
import com.twix.onboarding.model.OnBoardingIntent
import com.twix.onboarding.model.OnBoardingSideEffect
-import com.twix.onboarding.vm.OnBoardingViewModel
import com.twix.ui.base.ObserveAsEvents
import com.twix.ui.extension.noRippleClickable
import com.twix.ui.keyboard.Keyboard
diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/model/OnBoardingIntent.kt b/feature/onboarding/src/main/java/com/twix/onboarding/model/OnBoardingIntent.kt
index 49cf5b7d..e7ae7655 100644
--- a/feature/onboarding/src/main/java/com/twix/onboarding/model/OnBoardingIntent.kt
+++ b/feature/onboarding/src/main/java/com/twix/onboarding/model/OnBoardingIntent.kt
@@ -4,6 +4,12 @@ import com.twix.ui.base.Intent
import java.time.LocalDate
sealed interface OnBoardingIntent : Intent {
+ data class SubmitMarketingConsent(
+ val isPushEnabled: Boolean,
+ val isMarketingEnabled: Boolean,
+ val isNightMarketingEnabled: Boolean,
+ ) : OnBoardingIntent
+
data class WriteNickName(
val value: String,
) : OnBoardingIntent
diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/navigation/OnboardingNavGraph.kt b/feature/onboarding/src/main/java/com/twix/onboarding/navigation/OnboardingNavGraph.kt
index 247b3b96..1408fb1f 100644
--- a/feature/onboarding/src/main/java/com/twix/onboarding/navigation/OnboardingNavGraph.kt
+++ b/feature/onboarding/src/main/java/com/twix/onboarding/navigation/OnboardingNavGraph.kt
@@ -7,11 +7,11 @@ import androidx.navigation.navigation
import com.twix.navigation.NavRoutes
import com.twix.navigation.base.NavGraphContributor
import com.twix.navigation.graphViewModel
+import com.twix.onboarding.OnBoardingViewModel
import com.twix.onboarding.couple.CoupleConnectRoute
import com.twix.onboarding.dday.DdayRoute
import com.twix.onboarding.invite.InviteCodeRoute
import com.twix.onboarding.profile.ProfileRoute
-import com.twix.onboarding.vm.OnBoardingViewModel
object OnboardingNavGraph : NavGraphContributor {
override val graphRoute: NavRoutes
diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/profile/ProfileScreen.kt b/feature/onboarding/src/main/java/com/twix/onboarding/profile/ProfileScreen.kt
index 639ce981..ff6c037f 100644
--- a/feature/onboarding/src/main/java/com/twix/onboarding/profile/ProfileScreen.kt
+++ b/feature/onboarding/src/main/java/com/twix/onboarding/profile/ProfileScreen.kt
@@ -37,10 +37,10 @@ import com.twix.designsystem.theme.GrayColor
import com.twix.designsystem.theme.SystemColor
import com.twix.designsystem.theme.TwixTheme
import com.twix.domain.model.enums.AppTextStyle
+import com.twix.onboarding.OnBoardingViewModel
import com.twix.onboarding.R
import com.twix.onboarding.model.OnBoardingIntent
import com.twix.onboarding.model.OnBoardingSideEffect
-import com.twix.onboarding.vm.OnBoardingViewModel
import com.twix.ui.base.ObserveAsEvents
import com.twix.ui.extension.noRippleClickable
import org.koin.compose.koinInject