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

#218 : 새로운 기능 소개 기능 구현해요 #230

Draft
wants to merge 35 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
66141a3
[FEATURE]#218 : 서버드리븐 Ui Component DTO Model 구현
jeongjaino Jan 30, 2025
fd1745d
[FEATURE]#218 : FeatureOverview 서버 드리븐 API 구현
jeongjaino Jan 30, 2025
326cc20
[FEATURE]#218 : FeatureOverview API 구현
jeongjaino Jan 30, 2025
6108df0
[FEATURE]#218 : FeatureOverview UI 구현 및 로직 적용
jeongjaino Jan 30, 2025
45a1e15
[FEATURE]#218 : coil 라이브러리 적용
jeongjaino Jan 30, 2025
766cc65
[FEATURE]#218 : NotificationType 추가
jeongjaino Jan 30, 2025
56ca1de
[FEATURE]#218 : Notification 전환 SideEffect 처리
jeongjaino Jan 30, 2025
5f8ebda
[CHORE]#218 : VersionUpdateDialogState 삭제
jeongjaino Jan 30, 2025
2b3ba86
[CHORE]#218 : description 추가
jeongjaino Jan 30, 2025
78c51e4
[CHORE]#218 : NotificationNavigatorImpl 주석 추가
jeongjaino Jan 30, 2025
616724b
[FEATURE]#218 : ProfileEdit Destination 딥링크 추가
jeongjaino Jan 30, 2025
7aeaf6c
[FEATURE]#218 : FeatureOverview 모달 외부로 변경 및 로딩 애니메이션 적용
jeongjaino Jan 31, 2025
ec9edc8
[FEATURE]#218 : FeatureOverview 이벤트 Action-SideEffect로 처리하도록 수정
jeongjaino Jan 31, 2025
ff70458
[CHORE]#218 : ImageComponent 내부 함수 소거
jeongjaino Jan 31, 2025
a7cc876
Merge branch 'develop' into feature/jaino/#218
jeongjaino Jan 31, 2025
5b67dd7
[CHORE]#218 : convertType에 UPDATE_REQUIRED 추가
jeongjaino Jan 31, 2025
20ebe4e
[CHORE]#218 : reduce 구문 내 공백 추가
jeongjaino Jan 31, 2025
d155e73
[feat]#232: 온보딩 model 추가
flash159483 Feb 1, 2025
e41f177
[feat]#232: 온보딩 데이터 가져와요
flash159483 Feb 1, 2025
9bbd42e
[feat]#232: 서버 드리븐 모듈 추가해요
flash159483 Feb 1, 2025
1bbf884
[feat]#232: UI 그리는데 필요한 섹션을 구현해요
flash159483 Feb 1, 2025
066be0b
[feat]#232: 화면에 그려요
flash159483 Feb 1, 2025
f42c9c1
[feat]#232: 바텀시트가 뜨도록 추가해요
flash159483 Feb 1, 2025
3fbeaf2
Merge branch 'feature/flash159483/#232' into feature/jaino/#218
jeongjaino Feb 2, 2025
3200340
[CHORE]#218 : DynamicUi to UpdateOverView
jeongjaino Feb 5, 2025
31bc215
[CHORE]#218 : 업데이트 개요 ComponentDto / Component 구현
jeongjaino Feb 5, 2025
cdeb2a1
[CHORE]#218 : 업데이트 개요 ComponentDto / Component 구현
jeongjaino Feb 5, 2025
9e4760d
[CHORE]#218 : 서버드리븐 섹션 구현
jeongjaino Feb 5, 2025
d176b80
[FEATURE]#218 : 업데이트 개요 모달 서버드리븐 구현
jeongjaino Feb 5, 2025
8ef7548
[FEATURE]#218 : 업데이트 푸시 알림 수신 시, 업데이트 개요 모달 전환
jeongjaino Feb 5, 2025
13b8a9e
[CHORE]#218 : 의존성 수정
jeongjaino Feb 5, 2025
6484197
[CHORE]#218 : 의존성 주입 설정
jeongjaino Feb 5, 2025
356093f
[CHORE]#218 : 버튼 디자인 시스템 내 커스텀 타입 구현
jeongjaino Feb 5, 2025
c284e0c
[CHORE]#218 : 리소스 추가
jeongjaino Feb 5, 2025
6958839
[CHORE]#218 : 타이틀 컴포넌트 수정
jeongjaino Feb 5, 2025
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 app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ dependencies {
implementation(project(":feature:message"))
implementation(project(":feature:entire"))
implementation(project(":feature:notification"))
implementation(project(":feature:server-driven"))

implementation(libs.splash.screen)
implementation(platform(libs.firebase.bom))
Expand Down
186 changes: 104 additions & 82 deletions app/src/main/kotlin/com/bff/wespot/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SheetValue
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
Expand All @@ -51,7 +52,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.rememberNavController
Expand All @@ -68,18 +72,19 @@ import com.bff.wespot.designsystem.theme.WeSpotThemeManager
import com.bff.wespot.entire.screen.destinations.SettingScreenDestination
import com.bff.wespot.model.common.RestrictionType
import com.bff.wespot.model.notification.NotificationType
import com.bff.wespot.model.serverDriven.OnBoardingCategory
import com.bff.wespot.navigation.Navigator
import com.bff.wespot.navigation.util.EXTRA_DATE
import com.bff.wespot.navigation.util.EXTRA_TARGET_ID
import com.bff.wespot.navigation.util.EXTRA_TYPE
import com.bff.wespot.navigation.util.EXTRA_USER_ID
import com.bff.wespot.notification.screen.NotificationNavigator
import com.bff.wespot.state.MainAction
import com.bff.wespot.state.MainUiState
import com.bff.wespot.data.remote.extensions.toLocalDateFromDashPattern
import com.bff.wespot.designsystem.component.modal.WSDialog
import com.bff.wespot.model.VersionUpdateDialogState
import com.bff.wespot.navigation.util.EXTRA_DATE
import com.bff.wespot.model.VersionUpdateType
import com.bff.wespot.state.MainSideEffect
import com.bff.wespot.server.driven.onboarding.OnBoardingBottomSheet
import com.bff.wespot.server.driven.overview.UpdateOverviewScreen
import com.bff.wespot.ui.component.TopToast
import com.bff.wespot.ui.component.WSBottomSheet
import com.bff.wespot.ui.model.ToastState
Expand All @@ -93,7 +98,6 @@ import kotlinx.coroutines.launch
import org.orbitmvi.orbit.compose.collectAsState
import org.orbitmvi.orbit.compose.collectSideEffect
import timber.log.Timber
import java.time.LocalDate
import javax.inject.Inject

@AndroidEntryPoint
Expand All @@ -117,15 +121,12 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestNotificationPermission()

val navArgs = getMainScreenArgsFromIntent()
checkEnteredFromPushNotification(navArgs)
handleIntentData()

setContent {
WeSpotTheme {
MainScreen(
navigator = navigator,
navArgs = navArgs,
analyticsHelper = analyticsHelper,
viewModel = viewModel,
)
Expand All @@ -146,66 +147,73 @@ class MainActivity : ComponentActivity() {
}
}

private fun checkEnteredFromPushNotification(data: MainScreenNavArgs) {
if (data.type != NotificationType.IDLE) {
viewModel.onAction(MainAction.OnEnteredByPushNotification(data))
}
}
private fun handleIntentData() = with(intent) {
val type = NotificationType.convertNotificationType(getStringExtra(EXTRA_TYPE).orEmpty())
if (type == NotificationType.IDLE) return

private fun getMainScreenArgsFromIntent(): MainScreenNavArgs = with(intent) {
val targetId = getIntExtra(EXTRA_TARGET_ID, -1)
val userId = getStringExtra(EXTRA_USER_ID).orEmpty()
val type = NotificationType.convertNotificationType(getStringExtra(EXTRA_TYPE).orEmpty())
val date = getStringExtra(EXTRA_DATE).orEmpty()

removeExtra(EXTRA_TARGET_ID)
removeExtra(EXTRA_USER_ID)
removeExtra(EXTRA_TYPE)
removeExtra(EXTRA_DATE)

MainScreenNavArgs(
targetId = targetId,
userId = userId,
type = type,
date = date,
viewModel.onAction(
MainAction.OnEnteredByPushNotification(
type = type,
targetId = targetId,
userId = userId,
date = date,
appVersion = [email protected](),
),
)
}
}

data class MainScreenNavArgs(
val type: NotificationType,
val userId: String,
val targetId: Int,
val date: String,
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun MainScreen(
navigator: Navigator,
navArgs: MainScreenNavArgs,
analyticsHelper: AnalyticsHelper,
viewModel: MainViewModel,
) {
val state by viewModel.collectAsState()
val action = viewModel::onAction

val navController = rememberNavController()
val notificationNavigator by remember { mutableStateOf(NotificationNavigatorImpl(navController)) }

val context = LocalContext.current
var toast by remember { mutableStateOf(ToastState()) }
var showVersionUpdateDialog by remember { mutableStateOf(VersionUpdateDialogState()) }

val isTopNavigationScreen by navController.checkCurrentScreen(NavigationBarPosition.TOP)
val isBottomNavigationScreen by navController.checkCurrentScreen(NavigationBarPosition.BOTTOM)

viewModel.collectSideEffect {
when (it) {
is MainSideEffect.ShowVersionUpdateDialog -> {
showVersionUpdateDialog = VersionUpdateDialogState(
show = true,
versionUpdateType = it.versionUpdateType,
is MainSideEffect.NavigateToVoteResultScreen -> {
notificationNavigator.navigateToVoteResultScreen(
isNavigateFromNotification = false,
isTodayVoteResult = it.isTodayVoteResult
)
}
is MainSideEffect.NavigateToMessageScreen -> {
notificationNavigator.navigateToMessageScreen(type = it.type, messageId = it.messageId)
}
is MainSideEffect.NavigateToDeepLink -> {
navController.navigate(it.deepLink.toUri())
}
MainSideEffect.NavigateToVoteStorageScreen -> {
notificationNavigator.navigateToVoteStorageScreen()
}
MainSideEffect.NavigateToReceiverSelectionScreen -> {
notificationNavigator.navigateToReceiverSelectionScreen()
}
MainSideEffect.NavigateToVotingScreen -> {
notificationNavigator.navigateToVotingScreen()
}
}
}

Expand Down Expand Up @@ -306,11 +314,6 @@ private fun MainScreen(
restricted = state.restriction.restrictionType != RestrictionType.NONE,
)
}

if (state.isPushNotificationNavigation) {
action(MainAction.OnNavigateByPushNotification)
navigateScreenFromNavArgs(navArgs, NotificationNavigatorImpl(navController))
}
}

TopToast(
Expand All @@ -321,17 +324,17 @@ private fun MainScreen(
toast = toast.copy(show = false)
}

if (showVersionUpdateDialog.show) {
if (state.versionUpdateType != VersionUpdateType.NONE) {
WSDialog(
title = showVersionUpdateDialog.versionUpdateType.title,
subTitle = showVersionUpdateDialog.versionUpdateType.subTitle,
title = state.versionUpdateType.title,
subTitle = state.versionUpdateType.subTitle,
okButtonText = stringResource(string.update),
cancelButtonText = stringResource(string.next_time_update),
okButtonClick = {
navigator.navigateToWebLink(context, state.playStoreLink)
},
cancelButtonClick = {
showVersionUpdateDialog = showVersionUpdateDialog.copy(show = false)
action(MainAction.OnVersionUpdateDialogDismiss)
},
onDismissRequest = {},
)
Expand All @@ -350,6 +353,29 @@ private fun MainScreen(
)
}

if (state.notificationType.isUpdateOverviewType()) {
Dialog(
onDismissRequest = { },
properties = DialogProperties(usePlatformDefaultWidth = false),
) {
UpdateOverviewScreen(
notificationType = state.notificationType,
onDismiss = {
action(MainAction.OnUpdateOverviewDismiss)
},
onNavigate = { deepLink ->
action(MainAction.OnUpdateOverviewNavigate(deepLink))
},
)
}
}

OnBoardingSheet(
state = state,
navController = navController,
action = action,
)

LaunchedEffect(Unit) {
action(MainAction.OnMainScreenEntered(context.getAppVersionName()))
}
Expand Down Expand Up @@ -385,6 +411,41 @@ private fun BottomNavigationTab(
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun OnBoardingSheet(
state: MainUiState,
navController: NavController,
action: (MainAction) -> Unit,
) {
val current by navController.currentScreenAsState()
val category = when {
current == BottomBarDestinations.Vote.screen && state.showVoteOnBoarding -> {
OnBoardingCategory.VOTE
}
current == BottomBarDestinations.Message.screen && state.showMessageOnBoarding -> {
OnBoardingCategory.MESSAGE
}
else -> null
}

category?.let {
WSBottomSheet(
closeSheet = {},
sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true,
confirmValueChange = { it != SheetValue.Hidden })
) {
OnBoardingBottomSheet(
category = it,
closeOnBoarding = {
action(MainAction.CloseOnBoarding(it))
}
)
}
}
}

@Stable
@Composable
private fun NavController.currentScreenAsState(): State<NavGraphSpec> {
Expand Down Expand Up @@ -466,45 +527,6 @@ private fun RowScope.TabItem(
}
}

private fun navigateScreenFromNavArgs(
navArgs: MainScreenNavArgs,
navigator: NotificationNavigator
) {
when (navArgs.type) {
NotificationType.MESSAGE -> {
navigator.navigateToReceiverSelectionScreen()
}

NotificationType.MESSAGE_SENT, NotificationType.MESSAGE_RECEIVED -> {
navigator.navigateToMessageScreen(type = navArgs.type, messageId = navArgs.targetId)
}

NotificationType.VOTE -> {
navigator.navigateToVotingScreen()
}

NotificationType.VOTE_RESULT -> {
val voteResultDate = navArgs.date.toLocalDateFromDashPattern()
val isTodayVoteResult = LocalDate.now().equals(voteResultDate)
navigator.navigateToVoteResultScreen(
isNavigateFromNotification = false,
isTodayVoteResult = isTodayVoteResult
)
}

NotificationType.VOTE_RECEIVED -> {
navigator.navigateToVoteStorageScreen()
}

NotificationType.PROFILE_UPDATE -> {
navigator.navigateToProfileEditScreen()
}

NotificationType.IDLE -> {
}
}
}

private fun NavController.navigateToNavGraph(navGraph: NavGraphSpec) {
this.navigate(navGraph) {
launchSingleTop = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import com.bff.wespot.vote.screen.destinations.VotingScreenDestination
import com.ramcosta.composedestinations.dynamic.within
import com.ramcosta.composedestinations.navigation.navigate

/**
* 알림 화면에서의 전환과 푸시 알림 전환에서 사용되는 Navigator
*/
class NotificationNavigatorImpl(private val navController: NavController): NotificationNavigator {
override fun navigateUp() {
navController.navigateUp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@ import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.res.stringResource
import com.bff.wespot.R

data class VersionUpdateDialogState(
val show: Boolean = false,
val versionUpdateType: VersionUpdateType = VersionUpdateType.USABILITY_IMPROVEMENT,
)

/**
* @property [USABILITY_IMPROVEMENT] 사용성 개선
* @property [NEW_FEATURE_ADDED] 새로운 기능 추가
*/
enum class VersionUpdateType {
USABILITY_IMPROVEMENT,
NEW_FEATURE_ADDED,
NONE,
;

val title: String
Expand All @@ -25,6 +21,7 @@ enum class VersionUpdateType {
get() = when(this) {
USABILITY_IMPROVEMENT -> stringResource(id = R.string.update_title_usability_improvement)
NEW_FEATURE_ADDED -> stringResource(id = R.string.update_title_new_feature_added)
NONE -> ""
}

val subTitle: String
Expand All @@ -33,6 +30,7 @@ enum class VersionUpdateType {
get() = when(this) {
USABILITY_IMPROVEMENT -> stringResource(id = R.string.update_subtitle_usability_improvement)
NEW_FEATURE_ADDED -> stringResource(id = R.string.update_subtitle_new_feature_added)
NONE -> ""
}

companion object {
Expand Down
16 changes: 13 additions & 3 deletions app/src/main/kotlin/com/bff/wespot/state/MainAction.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
package com.bff.wespot.state

import com.bff.wespot.MainScreenNavArgs
import com.bff.wespot.model.notification.NotificationType
import com.bff.wespot.model.serverDriven.OnBoardingCategory

sealed class MainAction {
data object OnNavigateByPushNotification : MainAction()
data class OnMainScreenEntered(val appVersionName: String) : MainAction()
data class OnEnteredByPushNotification(val data: MainScreenNavArgs) : MainAction()
data class OnNotificationSet(val isEnableNotification: Boolean) : MainAction()
data class OnEnteredByPushNotification(
val type: NotificationType,
val userId: String,
val targetId: Int,
val date: String,
val appVersion: String,
) : MainAction()
data object OnVersionUpdateDialogDismiss : MainAction()
data object OnUpdateOverviewDismiss : MainAction()
data class OnUpdateOverviewNavigate(val deepLink: String) : MainAction()
data class CloseOnBoarding(val category: OnBoardingCategory) : MainAction()
}
Loading
Loading