Skip to content

Commit d471b69

Browse files
authored
Merge pull request #116 from YAPP-Github/feat/#114-onboarding-marketing
온보딩 과정 마케팅 알림 동의 플로우 추가
2 parents d8a71a5 + 751921b commit d471b69

File tree

11 files changed

+253
-21
lines changed

11 files changed

+253
-21
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package com.twix.designsystem.components.dialog
2+
3+
import androidx.compose.foundation.Image
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.height
9+
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.layout.size
11+
import androidx.compose.foundation.layout.width
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.getValue
14+
import androidx.compose.runtime.mutableStateOf
15+
import androidx.compose.runtime.remember
16+
import androidx.compose.runtime.setValue
17+
import androidx.compose.ui.Alignment
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.res.painterResource
20+
import androidx.compose.ui.res.stringResource
21+
import androidx.compose.ui.text.style.TextAlign
22+
import androidx.compose.ui.tooling.preview.Preview
23+
import androidx.compose.ui.unit.dp
24+
import com.twix.designsystem.R
25+
import com.twix.designsystem.components.dialog.CommonDialog
26+
import com.twix.designsystem.components.text.AppText
27+
import com.twix.designsystem.theme.GrayColor
28+
import com.twix.designsystem.theme.TwixTheme
29+
import com.twix.domain.model.enums.AppTextStyle
30+
import com.twix.ui.extension.noRippleClickable
31+
32+
@Composable
33+
fun MarketingDialog(
34+
visible: Boolean,
35+
onConfirm: (Boolean, Boolean) -> Unit,
36+
) {
37+
var isMarketingChecked by remember { mutableStateOf(true) }
38+
var isNightMarketingChecked by remember { mutableStateOf(true) }
39+
40+
CommonDialog(
41+
visible = visible,
42+
confirmText = stringResource(R.string.word_confirm),
43+
dismissText = null,
44+
onDismissRequest = { },
45+
onConfirm = {
46+
onConfirm(isMarketingChecked, isNightMarketingChecked)
47+
},
48+
content = {
49+
MarketingDialogContent(
50+
isMarketingChecked = isMarketingChecked,
51+
isNightMarketingChecked = isNightMarketingChecked,
52+
onMarketingToggle = {
53+
isMarketingChecked = !isMarketingChecked
54+
},
55+
onNightMarketingToggle = {
56+
isNightMarketingChecked = !isNightMarketingChecked
57+
},
58+
)
59+
},
60+
)
61+
}
62+
63+
@Composable
64+
private fun MarketingDialogContent(
65+
isMarketingChecked: Boolean,
66+
isNightMarketingChecked: Boolean,
67+
onMarketingToggle: () -> Unit,
68+
onNightMarketingToggle: () -> Unit,
69+
) {
70+
Column {
71+
AppText(
72+
text = stringResource(R.string.marketing_dialog_title),
73+
style = AppTextStyle.T1,
74+
color = GrayColor.C500,
75+
textAlign = TextAlign.Center,
76+
modifier = Modifier.fillMaxWidth(),
77+
)
78+
79+
Spacer(Modifier.height(24.dp))
80+
81+
MarketingCheckItem(
82+
text = stringResource(R.string.marketing_dialog_marketing),
83+
checked = isMarketingChecked,
84+
onClick = onMarketingToggle,
85+
)
86+
87+
Spacer(Modifier.height(12.dp))
88+
89+
MarketingCheckItem(
90+
text = stringResource(R.string.marketing_dialog_night_marketing),
91+
checked = isNightMarketingChecked,
92+
onClick = onNightMarketingToggle,
93+
)
94+
95+
Spacer(Modifier.height(14.dp))
96+
97+
AppText(
98+
text = stringResource(R.string.marketing_dialog_description),
99+
style = AppTextStyle.C2,
100+
color = GrayColor.C300,
101+
modifier = Modifier.padding(start = 10.dp),
102+
)
103+
}
104+
}
105+
106+
@Composable
107+
private fun MarketingCheckItem(
108+
text: String,
109+
checked: Boolean,
110+
onClick: () -> Unit,
111+
) {
112+
val icon = if (checked) R.drawable.ic_checked_you else R.drawable.ic_empty_check
113+
114+
Row(
115+
verticalAlignment = Alignment.CenterVertically,
116+
modifier =
117+
Modifier
118+
.fillMaxWidth()
119+
.noRippleClickable(onClick = onClick),
120+
) {
121+
Image(
122+
painter = painterResource(icon),
123+
contentDescription = null,
124+
modifier =
125+
Modifier
126+
.size(24.dp),
127+
)
128+
129+
Spacer(Modifier.width(8.dp))
130+
131+
AppText(
132+
text = text,
133+
style = AppTextStyle.B2,
134+
color = GrayColor.C500,
135+
)
136+
}
137+
}
138+
139+
@Preview(showBackground = true, showSystemUi = true)
140+
@Composable
141+
private fun MarketingDialogPreview() {
142+
TwixTheme {
143+
MarketingDialog(
144+
visible = true,
145+
onConfirm = { _, _ -> },
146+
)
147+
}
148+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M12.001,12.001m-9.857,0a9.857,9.857 0,1 1,19.714 0a9.857,9.857 0,1 1,-19.714 0"
8+
android:strokeWidth="0.857143"
9+
android:fillColor="#ffffff"
10+
android:strokeColor="#C6C6C6"/>
11+
<path
12+
android:pathData="M7.715,11.099L11.286,15.429L16.286,8.572"
13+
android:strokeLineJoin="round"
14+
android:strokeWidth="1.02857"
15+
android:fillColor="#00000000"
16+
android:strokeColor="#ffffff"
17+
android:strokeLineCap="round"/>
18+
</vector>

core/design-system/src/main/res/values/strings.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<string name="word_cancel">취소</string>
1818
<string name="word_delete">삭제</string>
1919
<string name="word_modify">수정</string>
20+
<string name="word_confirm">확인</string>
2021
<string name="word_save">저장</string>
2122
<string name="word_setting">설정</string>
2223
<string name="word_account">계정</string>
@@ -122,6 +123,12 @@
122123
<string name="dialog_withdraw_account_title">정말 탈퇴하시겠어요?</string>
123124
<string name="dialog_withdraw_account_content">커플 연결이 끊어집니다.\n데이터는 전부 삭제되며 복구가 불가능합니다.</string>
124125

126+
<!-- 마케팅 다이얼로그 -->
127+
<string name="marketing_dialog_title">도움이 되는 정보를\n알림으로 받아보시겠어요?</string>
128+
<string name="marketing_dialog_marketing">[선택] 마케팅 정보 알림</string>
129+
<string name="marketing_dialog_night_marketing">[선택] 야간 마케팅 정보 알림</string>
130+
<string name="marketing_dialog_description">* 언제든지 설정 > 알림 설정에서 변경 가능해요</string>
131+
125132
<!-- 인증샷 촬영 화면 !-->
126133
<string name="task_certification_upload">업로드</string>
127134
<string name="task_certification_image_capture_fail">이미지 캡처에 실패했습니다. 다시 시도해 주세요.</string>

feature/onboarding/src/main/java/com/twix/onboarding/vm/OnBoardingViewModel.kt renamed to feature/onboarding/src/main/java/com/twix/onboarding/OnBoardingViewModel.kt

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.twix.onboarding.vm
1+
package com.twix.onboarding
22

33
import androidx.lifecycle.viewModelScope
44
import com.twix.domain.model.OnboardingStatus
@@ -16,10 +16,6 @@ class OnBoardingViewModel(
1616
private val onBoardingRepository: OnBoardingRepository,
1717
private val notificationRepository: NotificationRepository,
1818
) : BaseViewModel<OnBoardingUiState, OnBoardingIntent, OnBoardingSideEffect>(OnBoardingUiState()) {
19-
init {
20-
initNotificationSettings()
21-
}
22-
2319
fun fetchMyInviteCode() {
2420
launchResult(
2521
block = { onBoardingRepository.fetchInviteCode() },
@@ -43,6 +39,9 @@ class OnBoardingViewModel(
4339
// 디데이 설정 화면
4440
is OnBoardingIntent.SelectDate -> reduceDday(intent.value)
4541
OnBoardingIntent.SubmitDday -> anniversarySetup()
42+
43+
is OnBoardingIntent.SubmitMarketingConsent ->
44+
initNotificationSettings(intent.isPushEnabled, intent.isMarketingEnabled, intent.isNightMarketingEnabled)
4645
}
4746
}
4847

@@ -145,9 +144,19 @@ class OnBoardingViewModel(
145144
)
146145
}
147146

148-
private fun initNotificationSettings() {
147+
private fun initNotificationSettings(
148+
isPushEnabled: Boolean,
149+
isMarketingEnabled: Boolean,
150+
isNightMarketingEnabled: Boolean,
151+
) {
149152
launchResult(
150-
block = { notificationRepository.initNotificationSettings(true, true, true) },
153+
block = {
154+
notificationRepository.initNotificationSettings(
155+
isPushEnabled,
156+
isMarketingEnabled,
157+
isNightMarketingEnabled,
158+
)
159+
},
151160
onSuccess = {},
152161
)
153162
}

feature/onboarding/src/main/java/com/twix/onboarding/couple/CoupleConnectRoute.kt

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package com.twix.onboarding.couple
22

3+
import android.Manifest
4+
import android.content.Context
5+
import android.content.pm.PackageManager
6+
import android.os.Build
37
import androidx.compose.foundation.Image
48
import androidx.compose.foundation.background
59
import androidx.compose.foundation.layout.Box
@@ -13,18 +17,23 @@ import androidx.compose.runtime.Composable
1317
import androidx.compose.runtime.LaunchedEffect
1418
import androidx.compose.runtime.getValue
1519
import androidx.compose.runtime.mutableStateOf
20+
import androidx.compose.runtime.rememberUpdatedState
1621
import androidx.compose.runtime.saveable.rememberSaveable
1722
import androidx.compose.runtime.setValue
1823
import androidx.compose.ui.Alignment
1924
import androidx.compose.ui.Modifier
2025
import androidx.compose.ui.graphics.vector.ImageVector
26+
import androidx.compose.ui.platform.LocalContext
2127
import androidx.compose.ui.res.stringResource
2228
import androidx.compose.ui.res.vectorResource
2329
import androidx.compose.ui.text.style.TextAlign
2430
import androidx.compose.ui.tooling.preview.Preview
2531
import androidx.compose.ui.unit.dp
32+
import androidx.core.app.NotificationManagerCompat
33+
import androidx.core.content.ContextCompat
2634
import com.twix.designsystem.components.bottomsheet.CommonBottomSheet
2735
import com.twix.designsystem.components.bottomsheet.model.CommonBottomSheetConfig
36+
import com.twix.designsystem.components.dialog.MarketingDialog
2837
import com.twix.designsystem.components.text.AppText
2938
import com.twix.designsystem.components.toast.ToastManager
3039
import com.twix.designsystem.components.toast.model.ToastData
@@ -33,11 +42,12 @@ import com.twix.designsystem.theme.CommonColor
3342
import com.twix.designsystem.theme.GrayColor
3443
import com.twix.designsystem.theme.TwixTheme
3544
import com.twix.domain.model.enums.AppTextStyle
45+
import com.twix.onboarding.OnBoardingViewModel
3646
import com.twix.onboarding.R
3747
import com.twix.onboarding.couple.component.ConnectButton
3848
import com.twix.onboarding.couple.component.RestoreCoupleBottomSheetContent
49+
import com.twix.onboarding.model.OnBoardingIntent
3950
import com.twix.onboarding.model.OnBoardingSideEffect
40-
import com.twix.onboarding.vm.OnBoardingViewModel
4151
import com.twix.ui.base.ObserveAsEvents
4252
import com.twix.ui.extension.noRippleClickable
4353
import org.koin.compose.koinInject
@@ -48,7 +58,10 @@ fun CoupleConnectRoute(
4858
toastManager: ToastManager = koinInject(),
4959
navigateToNext: () -> Unit,
5060
) {
61+
var showMarketingDialog by rememberSaveable { mutableStateOf(true) }
5162
var showRestoreSheet by rememberSaveable { mutableStateOf(false) }
63+
val context = LocalContext.current
64+
val currentContext by rememberUpdatedState(context)
5265

5366
LaunchedEffect(Unit) {
5467
viewModel.fetchMyInviteCode()
@@ -71,13 +84,30 @@ fun CoupleConnectRoute(
7184
}
7285
}
7386

74-
CoupleConnectScreen(
75-
showRestoreSheet = showRestoreSheet,
76-
onClickSend = { },
77-
onClickConnect = navigateToNext,
78-
onClickRestore = { showRestoreSheet = true },
79-
onDismissSheet = { showRestoreSheet = false },
80-
)
87+
Box {
88+
CoupleConnectScreen(
89+
showRestoreSheet = showRestoreSheet,
90+
onClickSend = { },
91+
onClickConnect = navigateToNext,
92+
onClickRestore = { showRestoreSheet = true },
93+
onDismissSheet = { showRestoreSheet = false },
94+
)
95+
96+
MarketingDialog(
97+
visible = showMarketingDialog,
98+
onConfirm = { marketing, nightMarketing ->
99+
showMarketingDialog = false
100+
val isPushEnabled = isNotificationPermissionGranted(context)
101+
viewModel.dispatch(
102+
OnBoardingIntent.SubmitMarketingConsent(
103+
isPushEnabled = isPushEnabled,
104+
isMarketingEnabled = marketing,
105+
isNightMarketingEnabled = nightMarketing,
106+
),
107+
)
108+
},
109+
)
110+
}
81111
}
82112

83113
@Composable
@@ -142,9 +172,23 @@ fun CoupleConnectScreen(
142172
}
143173
}
144174

175+
private fun isNotificationPermissionGranted(context: Context): Boolean {
176+
val notificationsEnabled = NotificationManagerCompat.from(context).areNotificationsEnabled()
177+
if (!notificationsEnabled) return false
178+
179+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
180+
ContextCompat.checkSelfPermission(
181+
context,
182+
Manifest.permission.POST_NOTIFICATIONS,
183+
) == PackageManager.PERMISSION_GRANTED
184+
} else {
185+
true
186+
}
187+
}
188+
145189
@Preview(showBackground = true)
146190
@Composable
147-
fun CoupleConnectScreenPreview() {
191+
private fun CoupleConnectScreenPreview() {
148192
TwixTheme {
149193
CoupleConnectScreen(
150194
showRestoreSheet = false,

feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ import com.twix.designsystem.theme.CommonColor
3030
import com.twix.designsystem.theme.GrayColor
3131
import com.twix.designsystem.theme.TwixTheme
3232
import com.twix.domain.model.enums.AppTextStyle
33+
import com.twix.onboarding.OnBoardingViewModel
3334
import com.twix.onboarding.R
3435
import com.twix.onboarding.dday.component.DDayField
3536
import com.twix.onboarding.dday.component.DdayTopBar
3637
import com.twix.onboarding.model.OnBoardingIntent
3738
import com.twix.onboarding.model.OnBoardingSideEffect
38-
import com.twix.onboarding.vm.OnBoardingViewModel
3939
import com.twix.ui.base.ObserveAsEvents
4040
import org.koin.compose.koinInject
4141
import java.time.LocalDate

feature/onboarding/src/main/java/com/twix/onboarding/di/OnBoardingModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package com.twix.onboarding.di
22

33
import com.twix.navigation.NavRoutes
44
import com.twix.navigation.base.NavGraphContributor
5+
import com.twix.onboarding.OnBoardingViewModel
56
import com.twix.onboarding.navigation.OnboardingNavGraph
6-
import com.twix.onboarding.vm.OnBoardingViewModel
77
import org.koin.core.module.dsl.viewModelOf
88
import org.koin.core.qualifier.named
99
import org.koin.dsl.module

feature/onboarding/src/main/java/com/twix/onboarding/invite/InviteCodeScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ import com.twix.designsystem.theme.CommonColor
4747
import com.twix.designsystem.theme.GrayColor
4848
import com.twix.designsystem.theme.TwixTheme
4949
import com.twix.domain.model.enums.AppTextStyle
50+
import com.twix.onboarding.OnBoardingViewModel
5051
import com.twix.onboarding.R
5152
import com.twix.onboarding.invite.component.InviteCodeTextField
5253
import com.twix.onboarding.model.OnBoardingIntent
5354
import com.twix.onboarding.model.OnBoardingSideEffect
54-
import com.twix.onboarding.vm.OnBoardingViewModel
5555
import com.twix.ui.base.ObserveAsEvents
5656
import com.twix.ui.extension.noRippleClickable
5757
import com.twix.ui.keyboard.Keyboard

0 commit comments

Comments
 (0)