Skip to content

Commit c43c3fb

Browse files
authored
[Mod/#1622] SP3 솝레터 구조 개선 및 오류 수정 (#1625)
* [MOD#1622] AuthActivity 내 인증 및 업데이트 UI 처리 로직 개선 - `AuthScreen`과 업데이트 다이얼로그 로직을 `Box` 레이아웃으로 감싸 전체 화면 레이아웃 구조 개선 - `accessToken`이 비어있는 경우에만 `AuthScreen`을 렌더링하도록 분기 처리 추가 - 업데이트가 불필요한 상태(`else`)에서 `LaunchedEffect`를 사용하여 `navigateToMainActivity` 호출을 안전하게 처리하도록 변경 - `navigateToUnAuthenticatedHome` 콜백 내 `context` 참조를 `this@AuthActivity`로 명시적으로 수정하여 안정성 확보 * [MOD#1622] Sopletter 네비게이션 구조 개선 및 스낵바 위치,write 오류 수정 * [MOD#1622] 스타일 맞춰 flowWithLifecycle 로 수정 * [MOD#1622] 사용하지 않는 import 제거
1 parent 794d6d4 commit c43c3fb

11 files changed

Lines changed: 304 additions & 313 deletions

File tree

app/src/main/java/org/sopt/official/feature/auth/AuthActivity.kt

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ import android.os.Bundle
3333
import androidx.activity.compose.setContent
3434
import androidx.activity.viewModels
3535
import androidx.appcompat.app.AppCompatActivity
36+
import androidx.compose.foundation.layout.Box
37+
import androidx.compose.foundation.layout.fillMaxSize
3638
import androidx.compose.runtime.LaunchedEffect
3739
import androidx.compose.runtime.getValue
3840
import androidx.compose.runtime.mutableStateOf
3941
import androidx.compose.runtime.saveable.rememberSaveable
4042
import androidx.compose.runtime.setValue
43+
import androidx.compose.ui.Modifier
4144
import androidx.compose.ui.platform.LocalContext
4245
import androidx.core.app.NotificationCompat
4346
import androidx.lifecycle.compose.LocalLifecycleOwner
@@ -89,39 +92,6 @@ class AuthActivity : AppCompatActivity() {
8992
viewModel.getUpdateConfig(context.getVersionName())
9093
}
9194

92-
when (val state = updateState) {
93-
is UpdateState.Default -> LoadingIndicator()
94-
is UpdateState.PatchUpdateAvailable -> {
95-
var dialogVisibility by rememberSaveable { mutableStateOf(true) }
96-
97-
if (dialogVisibility) {
98-
UpdateDialog(
99-
description = state.message,
100-
onDismiss = {
101-
dialogVisibility = false
102-
navigateToMainActivity(accessToken)
103-
},
104-
onPositiveClick = this@AuthActivity::launchPlayStore,
105-
onNegativeClick = {
106-
dialogVisibility = false
107-
navigateToMainActivity(accessToken)
108-
}
109-
)
110-
}
111-
}
112-
113-
is UpdateState.UpdateRequired -> {
114-
UpdateDialog(
115-
description = state.message,
116-
onDismiss = this@AuthActivity::finishAffinity,
117-
onPositiveClick = this@AuthActivity::launchPlayStore,
118-
onNegativeClick = this@AuthActivity::finishAffinity,
119-
)
120-
}
121-
122-
else -> navigateToMainActivity(accessToken)
123-
}
124-
12595
LaunchedEffect(true) {
12696
NotificationChannel(
12797
getString(R.string.toolbar_notification),
@@ -151,33 +121,74 @@ class AuthActivity : AppCompatActivity() {
151121
}
152122
}
153123

154-
AuthScreen(
155-
navigateToHome = {
156-
try {
157-
if (accessToken.isNotEmpty()) {
124+
Box(modifier = Modifier.fillMaxSize()) {
125+
if (accessToken.isEmpty()) {
126+
AuthScreen(
127+
navigateToHome = {
128+
try {
129+
if (accessToken.isNotEmpty()) {
130+
startActivity(
131+
MainActivity.getIntent(
132+
context = context,
133+
args = MainActivity.StartArgs(UserStatus.ACTIVE)
134+
)
135+
)
136+
}
137+
} catch (e: Exception) {
138+
Timber.e(e)
139+
}
140+
},
141+
navigateToUnAuthenticatedHome = {
158142
startActivity(
159143
MainActivity.getIntent(
160-
context = context,
161-
args = MainActivity.StartArgs(UserStatus.ACTIVE)
144+
context = this@AuthActivity,
145+
args = MainActivity.StartArgs(UserStatus.UNAUTHENTICATED)
162146
)
163147
)
148+
},
149+
onContactChannelClick = { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(WebUrlConstant.OPINION_KAKAO_CHAT))) },
150+
onGoogleFormClick = { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(WebUrlConstant.SOPT_GOOGLE_FROM))) },
151+
platform = platform
152+
)
153+
}
154+
155+
when (val state = updateState) {
156+
is UpdateState.Default -> LoadingIndicator()
157+
is UpdateState.PatchUpdateAvailable -> {
158+
var dialogVisibility by rememberSaveable { mutableStateOf(true) }
159+
160+
if (dialogVisibility) {
161+
UpdateDialog(
162+
description = state.message,
163+
onDismiss = {
164+
dialogVisibility = false
165+
navigateToMainActivity(accessToken)
166+
},
167+
onPositiveClick = this@AuthActivity::launchPlayStore,
168+
onNegativeClick = {
169+
dialogVisibility = false
170+
navigateToMainActivity(accessToken)
171+
}
172+
)
164173
}
165-
} catch (e: Exception) {
166-
Timber.e(e)
167174
}
168-
},
169-
navigateToUnAuthenticatedHome = {
170-
startActivity(
171-
MainActivity.getIntent(
172-
context = this,
173-
args = MainActivity.StartArgs(UserStatus.UNAUTHENTICATED)
175+
176+
is UpdateState.UpdateRequired -> {
177+
UpdateDialog(
178+
description = state.message,
179+
onDismiss = this@AuthActivity::finishAffinity,
180+
onPositiveClick = this@AuthActivity::launchPlayStore,
181+
onNegativeClick = this@AuthActivity::finishAffinity,
174182
)
175-
)
176-
},
177-
onContactChannelClick = { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(WebUrlConstant.OPINION_KAKAO_CHAT))) },
178-
onGoogleFormClick = { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(WebUrlConstant.SOPT_GOOGLE_FROM))) },
179-
platform = platform
180-
)
183+
}
184+
185+
else -> {
186+
LaunchedEffect(accessToken) {
187+
navigateToMainActivity(accessToken)
188+
}
189+
}
190+
}
191+
}
181192
}
182193
}
183194
}

feature/main/src/main/java/org/sopt/official/feature/main/MainScreen.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,10 +406,10 @@ fun MainScreen(
406406
}
407407

408408
sopletterGraph(
409-
paddingValues = innerPadding,
410-
navController = navigator.navController,
411409
navigateToHome = {
412-
navigator.navigateAndClear(MainTab.Home, userStatus)
410+
if (!navigator.navController.popBackStack()) {
411+
navigator.navigateAndClear(MainTab.Home, userStatus)
412+
}
413413
}
414414
)
415415
}

feature/sopletter/src/main/java/org/sopt/official/feature/sopletter/main/SopletterMainScreen.kt

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator
4444
import androidx.compose.material.pullrefresh.PullRefreshState
4545
import androidx.compose.material.pullrefresh.pullRefresh
4646
import androidx.compose.material.pullrefresh.rememberPullRefreshState
47-
import androidx.compose.material3.SnackbarHostState
4847
import androidx.compose.runtime.Composable
4948
import androidx.compose.runtime.LaunchedEffect
5049
import androidx.compose.runtime.getValue
@@ -65,9 +64,8 @@ import org.sopt.official.common.util.onBottomReached
6564
import org.sopt.official.designsystem.SoptTheme
6665
import org.sopt.official.designsystem.component.dialog.NetworkErrorDialog
6766
import org.sopt.official.domain.sopletter.model.SopletterMessage
68-
import org.sopt.official.feature.sopletter.common.component.SopletterScaffold
6967
import org.sopt.official.feature.sopletter.common.component.SopletterPrintDialog
70-
import org.sopt.official.feature.sopletter.common.component.SopletterSnackbarHost
68+
import org.sopt.official.feature.sopletter.common.model.SopletterSnackbarVisuals
7169
import org.sopt.official.feature.sopletter.main.component.EmptySopletterContent
7270
import org.sopt.official.feature.sopletter.main.component.SopletterDeleteDialog
7371
import org.sopt.official.feature.sopletter.main.component.SopletterMainTopBar
@@ -85,6 +83,7 @@ import org.sopt.official.webview.view.WebViewActivity
8583
@OptIn(ExperimentalMaterialApi::class)
8684
@Composable
8785
fun SopletterMainRoute(
86+
onShowSnackbar: (SopletterSnackbarVisuals) -> Unit,
8887
viewModel: SopletterMainViewModel = hiltViewModel(),
8988
navigateUp: () -> Unit = {},
9089
navigateToTopic: () -> Unit = {},
@@ -93,7 +92,6 @@ fun SopletterMainRoute(
9392
navigateToPrint: (Long?) -> Unit = {},
9493
) {
9594
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
96-
val snackbarHostState = remember { SnackbarHostState() }
9795
val dialogActions: SopletterMemoDetailDialogContract.Actions = viewModel
9896
val context = LocalContext.current
9997
val lifecycleOwner = LocalLifecycleOwner.current
@@ -111,52 +109,44 @@ fun SopletterMainRoute(
111109
}
112110

113111
is SopletterMainSideEffect.ShowSnackbar -> {
114-
snackbarHostState.showSnackbar(sideEffect.visuals)
112+
onShowSnackbar(sideEffect.visuals)
115113
}
116114
}
117115
}
118116
}
119117

120-
SopletterScaffold(
121-
snackbarHostState = snackbarHostState,
122-
isSnackbarHostVisible = false,
123-
) { paddingValues ->
124-
SopletterMainScreen(
125-
uiState = uiState,
126-
snackbarHostState = snackbarHostState,
127-
onMemoClick = {
128-
viewModel.fetchMemoDetail(
129-
messageId = it.messageId,
130-
memoColor = it.memoColor(),
131-
)
132-
},
133-
onRefresh = viewModel::refreshMainContent,
134-
onLoadMore = { viewModel.fetchMessages(isLoadMore = true) },
135-
dialogActions = dialogActions,
136-
onBackClick = navigateUp,
137-
onDownloadClick = { isPrintDialogVisible = true },
138-
isPrintDialogVisible = isPrintDialogVisible,
139-
onPrintDialogDismiss = { isPrintDialogVisible = false },
140-
onPrintConfirm = {
141-
isPrintDialogVisible = false
142-
navigateToPrint(uiState.selectedTopicId)
143-
},
144-
printDialogTitle = uiState.topicTitle,
145-
onTopicClick = navigateToTopic,
146-
onTopicCtaClick = navigateToTopicDetail,
147-
onReportClick = viewModel::openReportForm,
148-
onErrorConfirm = viewModel::dismissErrorDialog,
149-
onWriteFABClick = { navigateToWrite(uiState.selectedTopicId) },
150-
modifier = Modifier.padding(paddingValues),
151-
)
152-
}
118+
SopletterMainScreen(
119+
uiState = uiState,
120+
onMemoClick = {
121+
viewModel.fetchMemoDetail(
122+
messageId = it.messageId,
123+
memoColor = it.memoColor(),
124+
)
125+
},
126+
onRefresh = viewModel::refreshMainContent,
127+
onLoadMore = { viewModel.fetchMessages(isLoadMore = true) },
128+
dialogActions = dialogActions,
129+
onBackClick = navigateUp,
130+
onDownloadClick = { isPrintDialogVisible = true },
131+
isPrintDialogVisible = isPrintDialogVisible,
132+
onPrintDialogDismiss = { isPrintDialogVisible = false },
133+
onPrintConfirm = {
134+
isPrintDialogVisible = false
135+
navigateToPrint(uiState.selectedTopicId)
136+
},
137+
printDialogTitle = uiState.topicTitle,
138+
onTopicClick = navigateToTopic,
139+
onTopicCtaClick = navigateToTopicDetail,
140+
onReportClick = viewModel::openReportForm,
141+
onErrorConfirm = viewModel::dismissErrorDialog,
142+
onWriteFABClick = { navigateToWrite(uiState.selectedTopicId) },
143+
)
153144
}
154145

155146
@OptIn(ExperimentalMaterialApi::class)
156147
@Composable
157148
private fun SopletterMainScreen(
158149
uiState: SopletterMainUiState,
159-
snackbarHostState: SnackbarHostState,
160150
onMemoClick: (SopletterMessage) -> Unit,
161151
onRefresh: () -> Unit,
162152
onLoadMore: () -> Unit,
@@ -327,12 +317,6 @@ private fun SopletterMainScreen(
327317
)
328318
}
329319

330-
SopletterSnackbarHost(
331-
snackbarHostState = snackbarHostState,
332-
modifier = Modifier
333-
.align(Alignment.TopCenter)
334-
.padding(top = 16.dp, start = 16.dp, end = 16.dp),
335-
)
336320
}
337321
}
338322

@@ -394,7 +378,6 @@ private fun SopletterMainScreenPreview(
394378

395379
SopletterMainScreen(
396380
uiState = previewState,
397-
snackbarHostState = remember { SnackbarHostState() },
398381
onMemoClick = { _ -> },
399382
onRefresh = {},
400383
onLoadMore = {},

feature/sopletter/src/main/java/org/sopt/official/feature/sopletter/name/SopletterNameScreen.kt

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,23 @@ package org.sopt.official.feature.sopletter.name
2626

2727
import androidx.compose.foundation.background
2828
import androidx.compose.foundation.layout.Column
29-
import androidx.compose.foundation.layout.PaddingValues
3029
import androidx.compose.foundation.layout.Spacer
3130
import androidx.compose.foundation.layout.fillMaxSize
3231
import androidx.compose.foundation.layout.fillMaxWidth
3332
import androidx.compose.foundation.layout.padding
34-
import androidx.compose.material3.SnackbarHostState
3533
import androidx.compose.runtime.Composable
3634
import androidx.compose.runtime.LaunchedEffect
3735
import androidx.compose.runtime.getValue
38-
import androidx.compose.runtime.remember
3936
import androidx.compose.ui.Modifier
4037
import androidx.compose.ui.tooling.preview.Preview
4138
import androidx.compose.ui.unit.dp
4239
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
40+
import androidx.lifecycle.Lifecycle
4341
import androidx.lifecycle.compose.LocalLifecycleOwner
4442
import androidx.lifecycle.compose.collectAsStateWithLifecycle
45-
import androidx.lifecycle.flowWithLifecycle
43+
import androidx.lifecycle.repeatOnLifecycle
4644
import org.sopt.official.designsystem.SoptTheme
4745
import org.sopt.official.feature.sopletter.common.component.SopletterButton
48-
import org.sopt.official.feature.sopletter.common.component.SopletterScaffold
4946
import org.sopt.official.feature.sopletter.common.component.SopletterTopbar
5047
import org.sopt.official.feature.sopletter.name.component.SopletterNameInfoHolder
5148
import org.sopt.official.sopletter.R
@@ -57,43 +54,35 @@ fun SopletterNameRoute(
5754
viewModel: SopletterNameViewModel = hiltViewModel()
5855
) {
5956
val lifeCycleOwner = LocalLifecycleOwner.current
60-
6157
val state by viewModel.state.collectAsStateWithLifecycle()
62-
val snackbarHostState = remember { SnackbarHostState() }
6358

6459
LaunchedEffect(Unit) {
65-
viewModel.sideEffect.flowWithLifecycle(lifeCycleOwner.lifecycle)
66-
.collect { sideEffect ->
60+
lifeCycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
61+
viewModel.sideEffect.collect { sideEffect ->
6762
when (sideEffect) {
6863
NameSideEffect.NavigateToSopletterMain -> navigateToSopletterMain()
6964
}
7065
}
66+
}
7167
}
7268

73-
SopletterScaffold(
74-
snackbarHostState = snackbarHostState,
75-
) { innerPadding ->
76-
SopletterNameScreen(
77-
paddingValues = innerPadding,
78-
state = state,
79-
navigateToSopletterMain = navigateToSopletterMain,
80-
navigateToHome = navigateToHome,
81-
)
82-
}
69+
SopletterNameScreen(
70+
state = state,
71+
navigateToSopletterMain = navigateToSopletterMain,
72+
navigateToHome = navigateToHome,
73+
)
8374
}
8475

8576
@Composable
8677
private fun SopletterNameScreen(
87-
paddingValues: PaddingValues,
8878
state: NameState,
89-
navigateToSopletterMain: () -> Unit= {},
79+
navigateToSopletterMain: () -> Unit = {},
9080
navigateToHome: () -> Unit = {}
9181
) {
9282
Column(
9383
modifier = Modifier
9484
.fillMaxSize()
9585
.background(SoptTheme.colors.onSurface950)
96-
.padding(paddingValues)
9786
) {
9887
SopletterTopbar(
9988
onBackClick = navigateToHome,
@@ -130,7 +119,6 @@ private fun SopletterNameScreen(
130119
private fun SopletterNameScreenPreview() {
131120
SoptTheme {
132121
SopletterNameScreen(
133-
paddingValues = PaddingValues(),
134122
state = NameState(
135123
name = "익명의 김솝트",
136124
generation = 38

0 commit comments

Comments
 (0)