Skip to content

Commit 6b547ac

Browse files
authored
Merge pull request #102 from Nexters/release/0.3.0
Release 0.3.0 to develop
2 parents 6882d85 + fd8ec7d commit 6b547ac

69 files changed

Lines changed: 2912 additions & 954 deletions

File tree

Some content is hidden

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

β€Ž.gitignoreβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,4 @@ lint/tmp/
8484
@gemini-skills/
8585
.gemini/logs/
8686
.gemini/cache/
87+
docs/superpowers/

β€Žapp/build.gradle.ktsβ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import java.util.Properties
33
plugins {
44
id("buyornot.android.application")
55
alias(libs.plugins.kotlin.compose)
6+
alias(libs.plugins.kotlin.serialization)
67
alias(libs.plugins.hilt)
78
alias(libs.plugins.ksp)
89
alias(libs.plugins.google.services)
@@ -29,8 +30,8 @@ android {
2930

3031
defaultConfig {
3132
applicationId = "com.sseotdabwa.buyornot"
32-
versionCode = 5
33-
versionName = "0.2.0"
33+
versionCode = 6
34+
versionName = "0.3.0"
3435

3536
buildConfigField("String", "KAKAO_NATIVE_APP_KEY", "\"${localProperties.getProperty("kakao.nativeAppKey", "")}\"")
3637
manifestPlaceholders["NATIVE_APP_KEY"] = localProperties.getProperty("kakao.nativeAppKey", "")

β€Žapp/src/main/AndroidManifest.xmlβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
<uses-permission android:name="android.permission.INTERNET" />
55
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
6+
<uses-permission android:name="android.permission.CAMERA" />
67
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
78

89
<application

β€Žapp/src/main/java/com/sseotdabwa/buyornot/navigation/BuyOrNotNavHost.ktβ€Ž

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ import androidx.navigation.compose.NavHost
88
import com.sseotdabwa.buyornot.BuildConfig
99
import com.sseotdabwa.buyornot.core.network.AuthEvent
1010
import com.sseotdabwa.buyornot.core.network.AuthEventBus
11+
import com.sseotdabwa.buyornot.core.ui.imageviewer.imageViewerScreen
12+
import com.sseotdabwa.buyornot.core.ui.imageviewer.navigateToImageViewer
1113
import com.sseotdabwa.buyornot.core.ui.snackbar.LocalSnackbarState
1214
import com.sseotdabwa.buyornot.core.ui.webview.navigateToPrivacyPolicy
1315
import com.sseotdabwa.buyornot.core.ui.webview.navigateToTerms
16+
import com.sseotdabwa.buyornot.core.ui.webview.navigateToWebView
1417
import com.sseotdabwa.buyornot.core.ui.webview.webViewScreen
15-
import com.sseotdabwa.buyornot.feature.auth.navigation.AUTH_ROUTE
16-
import com.sseotdabwa.buyornot.feature.auth.navigation.SPLASH_ROUTE
18+
import com.sseotdabwa.buyornot.feature.auth.navigation.AuthRoute
19+
import com.sseotdabwa.buyornot.feature.auth.navigation.SplashRoute
1720
import com.sseotdabwa.buyornot.feature.auth.navigation.authScreen
1821
import com.sseotdabwa.buyornot.feature.auth.navigation.navigateForceToLogin
1922
import com.sseotdabwa.buyornot.feature.auth.navigation.navigateToLogin
@@ -27,17 +30,10 @@ import com.sseotdabwa.buyornot.feature.mypage.navigation.navigateToMyPage
2730
import com.sseotdabwa.buyornot.feature.notification.navigation.navigateToNotification
2831
import com.sseotdabwa.buyornot.feature.notification.navigation.navigateToNotificationDetail
2932
import com.sseotdabwa.buyornot.feature.notification.navigation.notificationGraph
30-
import com.sseotdabwa.buyornot.feature.upload.navigation.UPLOAD_ROUTE
33+
import com.sseotdabwa.buyornot.feature.upload.navigation.UploadRoute
3134
import com.sseotdabwa.buyornot.feature.upload.navigation.navigateToUpload
3235
import com.sseotdabwa.buyornot.feature.upload.navigation.uploadScreen
3336

34-
/**
35-
* BuyOrNot μ•±μ˜ 메인 λ„€λΉ„κ²Œμ΄μ…˜ 호슀트
36-
*
37-
* @param navController λ„€λΉ„κ²Œμ΄μ…˜ 컨트둀러
38-
* @param authEventBus 인증 κ΄€λ ¨ κΈ€λ‘œλ²Œ 이벀트λ₯Ό μˆ˜μ‹ ν•˜λŠ” λ²„μŠ€
39-
* @param modifier λ ˆμ΄μ•„μ›ƒ μˆ˜μ •μž
40-
*/
4137
@Composable
4238
fun BuyOrNotNavHost(
4339
navController: NavHostController,
@@ -47,7 +43,6 @@ fun BuyOrNotNavHost(
4743
) {
4844
val snackbarState = LocalSnackbarState.current
4945

50-
// κ°•μ œ λ‘œκ·Έμ•„μ›ƒ 이벀트 처리
5146
LaunchedEffect(authEventBus) {
5247
authEventBus.events.collect { event ->
5348
if (event == AuthEvent.FORCE_LOGOUT) {
@@ -61,7 +56,7 @@ fun BuyOrNotNavHost(
6156

6257
NavHost(
6358
navController = navController,
64-
startDestination = SPLASH_ROUTE,
59+
startDestination = SplashRoute,
6560
modifier = modifier,
6661
) {
6762
splashScreen(
@@ -70,7 +65,7 @@ fun BuyOrNotNavHost(
7065
navController.navigateToHome(
7166
navOptions =
7267
androidx.navigation.navOptions {
73-
popUpTo(SPLASH_ROUTE) { inclusive = true }
68+
popUpTo<SplashRoute> { inclusive = true }
7469
launchSingleTop = true
7570
},
7671
)
@@ -83,7 +78,7 @@ fun BuyOrNotNavHost(
8378
navController.navigateToHome(
8479
navOptions =
8580
androidx.navigation.navOptions {
86-
popUpTo(AUTH_ROUTE) { inclusive = true }
81+
popUpTo<AuthRoute> { inclusive = true }
8782
launchSingleTop = true
8883
},
8984
)
@@ -97,10 +92,14 @@ fun BuyOrNotNavHost(
9792
onNotificationClick = navController::navigateToNotification,
9893
onProfileClick = navController::navigateToMyPage,
9994
onUploadClick = navController::navigateToUpload,
95+
onLinkClick = { url -> navController.navigateToWebView("", url) },
96+
onImageClick = { urls, page -> navController.navigateToImageViewer(urls, page) },
10097
)
10198
notificationGraph(
10299
onBackClick = navController::popBackStack,
103100
onNotificationClick = navController::navigateToNotificationDetail,
101+
onLinkClick = { url -> navController.navigateToWebView("", url) },
102+
onImageClick = { urls, page -> navController.navigateToImageViewer(urls, page) },
104103
)
105104
uploadScreen(
106105
onNavigateBack = navController::popBackStack,
@@ -109,7 +108,7 @@ fun BuyOrNotNavHost(
109108
tab = HomeTab.MY_FEED,
110109
navOptions =
111110
androidx.navigation.navOptions {
112-
popUpTo(UPLOAD_ROUTE) {
111+
popUpTo<UploadRoute> {
113112
inclusive = true
114113
}
115114
launchSingleTop = true
@@ -122,6 +121,9 @@ fun BuyOrNotNavHost(
122121
versionName = BuildConfig.VERSION_NAME,
123122
onNavigateToLogin = navController::navigateForceToLogin,
124123
)
124+
imageViewerScreen(
125+
onBackClick = navController::popBackStack,
126+
)
125127
webViewScreen(
126128
onBackClick = navController::popBackStack,
127129
)

β€Žapp/src/main/java/com/sseotdabwa/buyornot/ui/BuyOrNotApp.ktβ€Ž

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import androidx.compose.runtime.getValue
1212
import androidx.compose.ui.Modifier
1313
import androidx.hilt.navigation.compose.hiltViewModel
1414
import androidx.lifecycle.compose.collectAsStateWithLifecycle
15-
import androidx.navigation.NavDestination
1615
import androidx.navigation.compose.currentBackStackEntryAsState
1716
import androidx.navigation.compose.rememberNavController
1817
import com.sseotdabwa.buyornot.core.designsystem.components.BuyOrNotSnackBarHost
@@ -21,25 +20,11 @@ import com.sseotdabwa.buyornot.core.network.AuthEventBus
2120
import com.sseotdabwa.buyornot.core.ui.permission.rememberNotificationPermission
2221
import com.sseotdabwa.buyornot.core.ui.snackbar.LocalSnackbarState
2322
import com.sseotdabwa.buyornot.core.ui.snackbar.rememberBuyOrNotSnackbarState
24-
import com.sseotdabwa.buyornot.feature.auth.navigation.AUTH_ROUTE
25-
import com.sseotdabwa.buyornot.feature.auth.navigation.SPLASH_ROUTE
26-
import com.sseotdabwa.buyornot.feature.home.navigation.HOME_ROUTE
23+
import com.sseotdabwa.buyornot.feature.auth.navigation.AuthRoute
24+
import com.sseotdabwa.buyornot.feature.auth.navigation.SplashRoute
25+
import com.sseotdabwa.buyornot.feature.home.navigation.HomeRoute
2726
import com.sseotdabwa.buyornot.navigation.BuyOrNotNavHost
2827

29-
/**
30-
* BuyOrNot μ•±μ˜ 메인 컴포저블
31-
*
32-
* λ„€λΉ„κ²Œμ΄μ…˜κ³Ό ν•˜λ‹¨ λ„€λΉ„κ²Œμ΄μ…˜ λ°”λ₯Ό ν¬ν•¨ν•œ μ•±μ˜ 전체 ꡬ쑰λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
33-
* μŠ€ν”Œλž˜μ‹œ 및 둜그인 ν™”λ©΄μ—μ„œλŠ” ν•˜λ‹¨ λ°”κ°€ ν‘œμ‹œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
34-
*
35-
* 전체 화면이 ν•„μš”ν•˜λ©΄ β†’ bottomBarPadding() ν•¨μˆ˜μ˜ λ¦¬μŠ€νŠΈμ— 라우트 μΆ”κ°€
36-
* 일반 화면이면 β†’ 아무 것도 ν•˜μ§€ μ•Šμ•„λ„ μžλ™μœΌλ‘œ νŒ¨λ”© 적용
37-
*
38-
* @param authEventBus 인증 κ΄€λ ¨ 이벀트 λ²„μŠ€
39-
* @param onBackPressed ν™ˆ ν™”λ©΄μ—μ„œ λ’€λ‘œκ°€κΈ° μ‹œ μ•± μ’…λ£Œλ₯Ό μœ„ν•œ 콜백
40-
* @param onFinish μ•± μ’…λ£Œ 콜백 (κ°•μ œ μ—…λ°μ΄νŠΈ μ‹œ "μ’…λ£Œ" λ²„νŠΌ)
41-
* @param viewModel μ•± 곡톡 ViewModel
42-
*/
4328
@Composable
4429
fun BuyOrNotApp(
4530
authEventBus: AuthEventBus,
@@ -54,12 +39,10 @@ fun BuyOrNotApp(
5439

5540
val isFirstRun by viewModel.isFirstRun.collectAsStateWithLifecycle()
5641

57-
// ν™ˆ ν™”λ©΄μ—μ„œ λ’€λ‘œκ°€κΈ° μ‹œ μ•± μ’…λ£Œ
58-
BackHandler(enabled = currentDestination?.route == HOME_ROUTE) {
42+
BackHandler(enabled = currentDestination?.route?.startsWith(HomeRoute::class.qualifiedName ?: "") == true) {
5943
onBackPressed()
6044
}
6145

62-
// μ•± μ§„μž… μ‹œ 졜초 1회만 μ•Œλ¦Ό κΆŒν•œ μžλ™ μš”μ²­
6346
val (hasNotificationPermission, requestNotificationPermission) = rememberNotificationPermission()
6447

6548
LaunchedEffect(isFirstRun) {
@@ -71,6 +54,11 @@ fun BuyOrNotApp(
7154
}
7255
}
7356

57+
val isFullscreen =
58+
currentDestination?.route.let { route ->
59+
route == SplashRoute::class.qualifiedName || route == AuthRoute::class.qualifiedName
60+
}
61+
7462
CompositionLocalProvider(LocalSnackbarState provides snackbarState) {
7563
Scaffold(
7664
containerColor = BuyOrNotTheme.colors.gray0,
@@ -83,27 +71,17 @@ fun BuyOrNotApp(
8371
modifier =
8472
Modifier
8573
.consumeWindowInsets(innerPadding)
86-
.bottomBarPadding(currentDestination, innerPadding),
74+
.bottomBarPadding(isFullscreen, innerPadding),
8775
)
8876
}
8977
}
9078
}
9179

92-
/**
93-
* νŠΉμ • ν™”λ©΄(μŠ€ν”Œλž˜μ‹œ, 둜그인)μ—μ„œλŠ” μ‹œμŠ€ν…œ νŒ¨λ”©μ„ μ œκ±°ν•˜λŠ” ν™•μž₯ ν•¨μˆ˜
94-
*
95-
* NavHost에 μ μš©λ˜μ–΄, 전체 화면이 ν•„μš”ν•œ μŠ€ν”Œλž˜μ‹œ/둜그인 ν™”λ©΄μ—μ„œλŠ”
96-
* μ‹œμŠ€ν…œ λ°” μ˜μ—­κΉŒμ§€ ν™•μž₯되고, 일반 ν™”λ©΄μ—μ„œλŠ” ν•˜λ‹¨ λ°” νŒ¨λ”©μ„ μ μš©ν•©λ‹ˆλ‹€.
97-
*
98-
* @param currentDestination ν˜„μž¬ λ„€λΉ„κ²Œμ΄μ…˜ λͺ©μ μ§€
99-
* @param padding Scaffold의 innerPadding (ν•˜λ‹¨ λ°” 높이 포함)
100-
* @return 쑰건에 따라 νŒ¨λ”©μ΄ μ μš©λ˜κ±°λ‚˜ 제거된 Modifier
101-
*/
10280
private fun Modifier.bottomBarPadding(
103-
currentDestination: NavDestination?,
81+
isFullscreen: Boolean,
10482
padding: PaddingValues,
10583
): Modifier =
106-
if (currentDestination?.route in listOf(SPLASH_ROUTE, AUTH_ROUTE)) {
84+
if (isFullscreen) {
10785
this
10886
} else {
10987
this.padding(padding)

β€Žbuild-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.ktβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
1515
apply("com.android.library")
1616
apply("org.jetbrains.kotlin.android")
1717
apply("org.jetbrains.kotlin.plugin.compose")
18+
apply("org.jetbrains.kotlin.plugin.serialization")
1819
}
1920

2021
extensions.configure<LibraryExtension> {
@@ -30,7 +31,6 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
3031
add("implementation", libs.findLibrary("androidx.lifecycle.runtime.compose").get())
3132
add("implementation", libs.findLibrary("androidx.navigation.compose").get())
3233
add("implementation", libs.findLibrary("hilt.navigation.compose").get())
33-
3434
}
3535
}
3636
}

β€Žbuild.gradle.ktsβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44
alias(libs.plugins.android.library) apply false
55
alias(libs.plugins.kotlin.android) apply false
66
alias(libs.plugins.kotlin.compose) apply false
7+
alias(libs.plugins.kotlin.serialization) apply false
78
alias(libs.plugins.hilt) apply false
89
alias(libs.plugins.ksp) apply false
910
alias(libs.plugins.ktlint) apply false

β€Žcore/data/src/main/java/com/sseotdabwa/buyornot/core/data/repository/FeedRepositoryImpl.ktβ€Ž

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package com.sseotdabwa.buyornot.core.data.repository
22

33
import com.sseotdabwa.buyornot.core.network.api.FeedApiService
4+
import com.sseotdabwa.buyornot.core.network.dto.request.FeedImageRequest
45
import com.sseotdabwa.buyornot.core.network.dto.request.FeedRequest
56
import com.sseotdabwa.buyornot.core.network.dto.request.PresignedUrlRequest
67
import com.sseotdabwa.buyornot.core.network.dto.request.VoteRequest
78
import com.sseotdabwa.buyornot.core.network.dto.response.AuthorDto
9+
import com.sseotdabwa.buyornot.core.network.dto.response.FeedImageDto
810
import com.sseotdabwa.buyornot.core.network.dto.response.FeedItemDto
911
import com.sseotdabwa.buyornot.core.network.dto.response.FeedListResponse
1012
import com.sseotdabwa.buyornot.core.network.dto.response.VoteResponse
1113
import com.sseotdabwa.buyornot.core.network.dto.response.getOrThrow
1214
import com.sseotdabwa.buyornot.domain.model.Author
1315
import com.sseotdabwa.buyornot.domain.model.Feed
1416
import com.sseotdabwa.buyornot.domain.model.FeedCategory
17+
import com.sseotdabwa.buyornot.domain.model.FeedImage
1518
import com.sseotdabwa.buyornot.domain.model.FeedStatus
1619
import com.sseotdabwa.buyornot.domain.model.UploadInfo
1720
import com.sseotdabwa.buyornot.domain.model.VoteChoice
@@ -29,9 +32,10 @@ class FeedRepositoryImpl @Inject constructor(
2932
cursor: Long?,
3033
size: Int,
3134
feedStatus: String?,
35+
category: List<String>?,
3236
): FeedList =
3337
feedApiService
34-
.getFeedList(cursor, size, feedStatus)
38+
.getFeedList(cursor, size, feedStatus, category)
3539
.getOrThrow()
3640
.toDomain()
3741

@@ -87,19 +91,26 @@ class FeedRepositoryImpl @Inject constructor(
8791
category: FeedCategory,
8892
price: Int,
8993
content: String,
90-
s3ObjectKey: String,
91-
imageWidth: Int,
92-
imageHeight: Int,
94+
images: List<FeedImage>,
95+
title: String?,
96+
link: String?,
9397
): Long =
9498
feedApiService
9599
.createFeed(
96100
FeedRequest(
97101
category = category.name,
98102
price = price,
99103
content = content,
100-
s3ObjectKey = s3ObjectKey,
101-
imageWidth = imageWidth,
102-
imageHeight = imageHeight,
104+
images =
105+
images.map { image ->
106+
FeedImageRequest(
107+
s3ObjectKey = image.s3ObjectKey,
108+
imageWidth = image.imageWidth,
109+
imageHeight = image.imageHeight,
110+
)
111+
},
112+
title = title,
113+
link = link,
103114
),
104115
).getOrThrow()
105116
.feedId
@@ -149,21 +160,28 @@ private fun FeedListResponse.toDomain(): FeedList =
149160
private fun FeedItemDto.toDomain(): Feed =
150161
Feed(
151162
feedId = feedId,
163+
title = title ?: "",
152164
content = content,
153165
price = String.format(java.util.Locale.KOREA, "%,d", price),
154166
category = category.toFeedCategory(),
155167
yesCount = yesCount,
156168
noCount = noCount,
157169
totalCount = totalCount,
158170
feedStatus = feedStatus.toFeedStatus(),
159-
s3ObjectKey = s3ObjectKey,
160-
viewUrl = viewUrl,
161-
imageWidth = imageWidth,
162-
imageHeight = imageHeight,
171+
images = images.map { it.toDomain() },
163172
author = author.toDomain(),
164173
createdAt = createdAt,
165174
hasVoted = hasVoted ?: false,
166175
myVoteChoice = myVoteChoice?.toVoteChoice(),
176+
productLink = link,
177+
)
178+
179+
private fun FeedImageDto.toDomain(): FeedImage =
180+
FeedImage(
181+
s3ObjectKey = s3ObjectKey,
182+
imageUrl = imageUrl,
183+
imageWidth = imageWidth,
184+
imageHeight = imageHeight,
167185
)
168186

169187
private fun String.toFeedCategory(): FeedCategory =

β€Žcore/data/src/main/java/com/sseotdabwa/buyornot/core/data/repository/NotificationRepositoryImpl.ktβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ class NotificationRepositoryImpl @Inject constructor(
3030
resultPercent = resultPercent,
3131
resultLabel = resultLabel,
3232
viewUrl = viewUrl,
33+
feedTitle = feedTitle.orEmpty(),
3334
)
3435
}

0 commit comments

Comments
Β (0)