Skip to content

Commit d5640aa

Browse files
authored
Merge pull request #188 from YAPP-admin/release/1.1.0
Release/1.1.0 to main
2 parents b32b6a4 + 9be6ff8 commit d5640aa

File tree

186 files changed

+6668
-613
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

186 files changed

+6668
-613
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ captures/
112112

113113
# Google Services (e.g. APIs or Firebase)
114114
app/google-services.json
115+
app/src/debug/google-services.json
116+
app/src/qa/google-services.json
115117

116118
# Android Patch
117119
gen-external-apklibs
@@ -171,3 +173,5 @@ fabric.properties
171173
yapp_attendance_keystore
172174
.github/api-play-store-key.json
173175
fastlane/.env
176+
177+
output-metadata.json

app/build.gradle.kts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,43 @@ android {
2323

2424
defaultConfig {
2525
applicationId = "com.yapp.app.official"
26-
versionCode = 2
27-
versionName = "1.0.0"
26+
versionCode = 5
27+
versionName = "1.1.0"
2828

2929
targetSdk = 35
3030
}
3131
buildTypes {
32-
getByName("release") {
33-
signingConfig = signingConfigs.getByName("release")
32+
getByName("debug") {
33+
applicationIdSuffix = ".dev"
34+
isDebuggable = true
3435
}
3536

3637
create("qa") {
3738
initWith(getByName("release"))
39+
applicationIdSuffix = ".qa"
3840
signingConfig = signingConfigs.getByName("release")
3941
matchingFallbacks += listOf("release")
42+
isDebuggable = true
43+
}
44+
45+
getByName("release") {
46+
signingConfig = signingConfigs.getByName("release")
4047
}
4148
}
4249
}
4350

4451
dependencies {
4552
implementation(projects.feature.home)
53+
implementation(projects.feature.schedule)
4654
implementation(projects.feature.notice)
55+
implementation(projects.feature.profile)
4756
implementation(projects.feature.signup)
4857
implementation(projects.feature.login)
58+
implementation(projects.feature.history)
59+
implementation(projects.feature.setting)
4960
implementation(projects.core.designsystem)
50-
implementation(projects.core.data) // For di
61+
implementation(projects.core.ui)
62+
implementation(projects.core.data)
5163
implementation(projects.core.dataApi)
5264
implementation(projects.core.domain)
5365
implementation(projects.core.commonAndroid)

app/src/debug/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<string name="app_name">Yapp Official DEV</string>
4+
</resources>
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package com.yapp.app
2+
3+
import android.widget.Toast
4+
import androidx.compose.animation.AnimatedVisibility
5+
import androidx.compose.animation.slideInVertically
6+
import androidx.compose.animation.slideOutVertically
7+
import androidx.compose.foundation.layout.WindowInsets
8+
import androidx.compose.foundation.layout.consumeWindowInsets
9+
import androidx.compose.foundation.layout.navigationBars
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.material3.Scaffold
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.Modifier
18+
import androidx.compose.ui.platform.LocalContext
19+
import androidx.compose.ui.platform.LocalUriHandler
20+
import androidx.compose.ui.unit.dp
21+
import androidx.hilt.navigation.compose.hiltViewModel
22+
import androidx.navigation.NavDestination
23+
import androidx.navigation.NavDestination.Companion.hasRoute
24+
import androidx.navigation.NavDestination.Companion.hierarchy
25+
import com.yapp.app.official.YappAppViewModel
26+
import com.yapp.app.official.navigation.TopLevelDestination
27+
import com.yapp.app.official.navigation.YappNavHost
28+
import com.yapp.app.official.ui.NavigatorState
29+
import com.yapp.core.designsystem.component.alert.YappAlertLongDialog
30+
import com.yapp.core.ui.R
31+
import com.yapp.core.ui.component.BottomNavigationBar
32+
import com.yapp.core.ui.component.BottomNavigationBarItem
33+
import com.yapp.core.ui.extension.safeOpenUri
34+
import kotlin.reflect.KClass
35+
36+
37+
@Composable
38+
fun YappApp(
39+
navigator: NavigatorState,
40+
viewModel: YappAppViewModel = hiltViewModel()
41+
) {
42+
43+
var showCommonErrorDialog by remember {
44+
mutableStateOf(false)
45+
}
46+
47+
val uriHandler = LocalUriHandler.current
48+
val context = LocalContext.current
49+
50+
Scaffold(
51+
bottomBar = {
52+
AnimatedVisibility(
53+
visible = navigator.shouldShowBottomBar,
54+
enter = slideInVertically { it },
55+
exit = slideOutVertically { it },
56+
) {
57+
val destinations = TopLevelDestination.entries
58+
YappBottomNavigationBar(
59+
destinations = destinations,
60+
currentDestination = navigator.currentDestination,
61+
onNavigateToDestination = { destination ->
62+
navigator.navigateToTopLevelDestination(destination)
63+
}
64+
)
65+
}
66+
},
67+
contentWindowInsets = WindowInsets(0.dp),
68+
) { padding ->
69+
YappNavHost(
70+
navigator = navigator,
71+
modifier = Modifier
72+
.padding(padding)
73+
.consumeWindowInsets(
74+
WindowInsets(0.dp).takeIf { !navigator.shouldShowBottomBar } ?: WindowInsets.navigationBars
75+
),
76+
handleException = { showCommonErrorDialog = true }
77+
)
78+
}
79+
80+
if (showCommonErrorDialog) {
81+
CommonErrorDialog(
82+
onDismissRequest = { showCommonErrorDialog = false },
83+
onActionButtonClick = { showCommonErrorDialog = false },
84+
onRecommendActionButtonClick = {
85+
viewModel.onCommonErrorDialogRecommendActionButtonClick(
86+
onSuccess = {
87+
uriHandler.safeOpenUri(it)
88+
},
89+
onError = {
90+
Toast.makeText(
91+
context,
92+
context.getString(R.string.toast_message_error_loading_url),
93+
Toast.LENGTH_SHORT
94+
).show()
95+
}
96+
)
97+
}
98+
)
99+
}
100+
}
101+
102+
@Composable
103+
private fun CommonErrorDialog(
104+
onDismissRequest: () -> Unit,
105+
onActionButtonClick: () -> Unit,
106+
onRecommendActionButtonClick: () -> Unit,
107+
) {
108+
YappAlertLongDialog(
109+
onDismissRequest = onDismissRequest,
110+
title = "앗! 예기치 못한 오류가 발생했어요.",
111+
body = "재시도 후에도 같은 문제가 발생한다면, \n" +
112+
"개발팀에 제보해주세요! \n" +
113+
"서비스 품질 향상에 큰 도움이 됩니다 :) ",
114+
recommendActionButtonText = "제보하러 가기",
115+
actionButtonText = "닫기",
116+
onRecommendActionButtonClick = onRecommendActionButtonClick,
117+
onActionButtonClick = onActionButtonClick,
118+
)
119+
}
120+
121+
@Composable
122+
private fun YappBottomNavigationBar(
123+
modifier: Modifier = Modifier,
124+
destinations: List<TopLevelDestination>,
125+
currentDestination: NavDestination?,
126+
onNavigateToDestination: (TopLevelDestination) -> Unit
127+
) {
128+
BottomNavigationBar(
129+
modifier = modifier
130+
) {
131+
destinations.forEach { destination ->
132+
val selected = currentDestination.isRouteInHierarchy(destination.baseRoute)
133+
134+
BottomNavigationBarItem(
135+
selected = selected,
136+
iconTextId = destination.iconTextId,
137+
selectedIcon = destination.selectedIcon,
138+
unselectedIcon = destination.unselectedIcon,
139+
onClick = {
140+
if (!selected) {
141+
onNavigateToDestination(destination)
142+
}
143+
},
144+
)
145+
}
146+
}
147+
}
148+
149+
private fun NavDestination?.isRouteInHierarchy(route: KClass<*>) =
150+
this?.hierarchy?.any {
151+
it.hasRoute(route)
152+
} ?: false

app/src/main/java/com/yapp/app/official/MainActivity.kt

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
package com.yapp.app.official
22

3+
import android.content.Intent
34
import android.content.pm.PackageManager
45
import android.os.Build
5-
import android.content.Intent
66
import android.os.Bundle
77
import androidx.activity.ComponentActivity
88
import androidx.activity.compose.setContent
99
import androidx.activity.enableEdgeToEdge
10+
import androidx.activity.result.contract.ActivityResultContracts
1011
import androidx.compose.runtime.Composable
1112
import androidx.compose.runtime.LaunchedEffect
1213
import androidx.compose.runtime.getValue
1314
import androidx.compose.runtime.mutableStateOf
1415
import androidx.compose.runtime.setValue
1516
import androidx.compose.ui.res.stringResource
16-
import androidx.activity.result.contract.ActivityResultContracts
1717
import androidx.core.content.ContextCompat
18-
import com.yapp.app.official.ui.YappApp
18+
import androidx.core.net.toUri
19+
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
20+
import com.yapp.app.YappApp
1921
import com.yapp.app.official.ui.rememberNavigator
20-
import com.yapp.core.designsystem.component.alert.YappAlertDialog
22+
import com.yapp.core.designsystem.component.alert.YappAlertShortDialog
2123
import com.yapp.core.designsystem.theme.YappTheme
2224
import com.yapp.dataapi.OperationsRepository
25+
import com.yapp.domain.CheckLoginStatusUseCase
2326
import com.yapp.domain.UpdateDeviceAlarmUseCase
2427
import com.yapp.domain.runCatchingIgnoreCancelled
28+
import com.yapp.feature.home.navigation.HomeRoute
2529
import dagger.hilt.android.AndroidEntryPoint
2630
import kotlinx.coroutines.MainScope
2731
import kotlinx.coroutines.launch
2832
import javax.inject.Inject
29-
import androidx.core.net.toUri
30-
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
31-
import com.yapp.domain.CheckLoginStatusUseCase
32-
import com.yapp.feature.home.navigation.HomeRoute
3333

3434

3535
@AndroidEntryPoint
@@ -43,12 +43,13 @@ class MainActivity : ComponentActivity() {
4343
@Inject
4444
lateinit var checkLoginStatusUseCase: CheckLoginStatusUseCase
4545

46-
private var showForceUpdateDialog by mutableStateOf(false)
47-
4846
private val scope = MainScope()
4947

48+
private var showForceUpdateDialog by mutableStateOf(false)
49+
5050
private var showSplashScreen by mutableStateOf(true)
5151

52+
5253
private val requestPermissionLauncher = registerForActivityResult(
5354
contract = ActivityResultContracts.RequestPermission(),
5455
callback = { }
@@ -95,7 +96,7 @@ class MainActivity : ComponentActivity() {
9596

9697
@Composable
9798
private fun ForceUpdateDialog() {
98-
YappAlertDialog(
99+
YappAlertShortDialog(
99100
onDismissRequest = {},
100101
title = stringResource(R.string.main_activity_force_update_dialog_title),
101102
body = stringResource(R.string.main_activity_force_update_dialog_body),
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.yapp.app.official
2+
3+
import androidx.lifecycle.ViewModel
4+
import androidx.lifecycle.viewModelScope
5+
import com.yapp.core.common.android.record
6+
import com.yapp.dataapi.OperationsRepository
7+
import com.yapp.domain.runCatchingIgnoreCancelled
8+
import dagger.hilt.android.lifecycle.HiltViewModel
9+
import kotlinx.coroutines.launch
10+
import javax.inject.Inject
11+
12+
@HiltViewModel
13+
class YappAppViewModel @Inject constructor(
14+
private val operationsRepository: OperationsRepository,
15+
) : ViewModel() {
16+
17+
fun onCommonErrorDialogRecommendActionButtonClick(
18+
onSuccess: (String) -> Unit,
19+
onError: (Throwable) -> Unit,
20+
) = viewModelScope.launch {
21+
runCatchingIgnoreCancelled {
22+
operationsRepository.getUsageInquiryLink()
23+
}.onSuccess(onSuccess)
24+
.onFailure {
25+
onError(it)
26+
it.record()
27+
}
28+
}
29+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.yapp.app.official.navigation
2+
3+
import androidx.annotation.DrawableRes
4+
import androidx.annotation.StringRes
5+
import com.yapp.app.official.R
6+
import kotlin.reflect.KClass
7+
8+
enum class TopLevelDestination(
9+
@DrawableRes val selectedIcon: Int,
10+
@DrawableRes val unselectedIcon: Int,
11+
@StringRes val iconTextId: Int,
12+
val route: KClass<*>,
13+
val baseRoute: KClass<*> = route,
14+
) {
15+
HOME(
16+
selectedIcon = R.drawable.icon_home_selected,
17+
unselectedIcon = R.drawable.icon_home_unselected,
18+
iconTextId = R.string.menu_home,
19+
route = com.yapp.feature.home.navigation.HomeRoute::class,
20+
),
21+
SCHEDULE(
22+
selectedIcon = R.drawable.icon_schedule_selected,
23+
unselectedIcon = R.drawable.icon_schedule_unselected,
24+
iconTextId = R.string.menu_schedule,
25+
route = com.yapp.feature.schedule.navigation.ScheduleRoute::class,
26+
),
27+
BOARD(
28+
selectedIcon = R.drawable.icon_board_selected,
29+
unselectedIcon = R.drawable.icon_board_unselected,
30+
iconTextId = R.string.menu_board,
31+
route = com.yapp.feature.notice.navigation.NoticeRoute::class,
32+
),
33+
PROFILE(
34+
selectedIcon = R.drawable.icon_profile_selected,
35+
unselectedIcon = R.drawable.icon_profile_unselected,
36+
iconTextId = R.string.menu_profile,
37+
route = com.yapp.feature.profile.navigation.ProfileRoute::class,
38+
)
39+
}

0 commit comments

Comments
 (0)