Skip to content

Commit db7ff70

Browse files
committed
feat: 사진관 동적조회 추가
1 parent 40a2e37 commit db7ff70

File tree

14 files changed

+228
-111
lines changed

14 files changed

+228
-111
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ android {
4747
minSdk = 26
4848
targetSdk = 35
4949
versionCode = 11015
50-
versionName = "1.4.1"
50+
versionName = "1.5.0"
5151

5252
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
5353
vectorDrawables {

app/src/main/java/com/no5ing/bbibbi/data/datasource/network/RestAPI.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import com.no5ing.bbibbi.data.model.notification.NotificationModel
3636
import com.no5ing.bbibbi.data.model.post.AIImageCount
3737
import com.no5ing.bbibbi.data.model.post.AIImageResponse
3838
import com.no5ing.bbibbi.data.model.post.AIPost
39+
import com.no5ing.bbibbi.data.model.post.AIPostType
3940
import com.no5ing.bbibbi.data.model.post.CalendarBanner
4041
import com.no5ing.bbibbi.data.model.post.CalendarElement
4142
import com.no5ing.bbibbi.data.model.post.DailyCalendarElement
@@ -204,6 +205,7 @@ interface RestAPI {
204205
suspend fun createAiPost(
205206
@Body body: CreatePostRequest,
206207
@Query("type") type: String? = null,
208+
@Query("aiPostType") aiPostType: String? = null,
207209
): ApiResponse<AIPost>
208210

209211
@POST("v1/posts/image-upload-request")
@@ -334,10 +336,16 @@ interface RestAPI {
334336
@Query("size") size: Int?,
335337
@Query("memberId") memberId: String?,
336338
@Query("sort") sort: String? = "DESC",
339+
@Query("aiPostType") aiPostType: String? = null,
337340
): ApiResponse<Pagination<AIPost>>
338341

339342
@GET("v1/posts/ai-images/count")
340-
suspend fun getAiImagePostCount(): ApiResponse<AIImageCount>
343+
suspend fun getAiImagePostCount(
344+
@Query("aiPostType") aiPostType: String? = null,
345+
): ApiResponse<AIImageCount>
346+
347+
@GET("v1/posts/ai-images/types")
348+
suspend fun getAiImageTypes(): ApiResponse<ArrayResponse<AIPostType>>
341349
}
342350

343351
/**
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.no5ing.bbibbi.data.model.post
2+
3+
import com.no5ing.bbibbi.data.model.BaseModel
4+
5+
data class AIPostType(
6+
val aiPostType: String,
7+
val imageUrl: String,
8+
val startDate: String,
9+
val name: String?,
10+
val endDate: String,
11+
val postCount: Int,
12+
) : BaseModel() {
13+
fun getTypeName(): String {
14+
if (name != null) {
15+
return name
16+
}
17+
return when (aiPostType.lowercase()) {
18+
"chuseok_2025" -> "추석"
19+
"christmas_2025" -> "크리스마스"
20+
else -> "알 수 없는 유형"
21+
}
22+
}
23+
}

app/src/main/java/com/no5ing/bbibbi/data/repository/post/GetAIPostsRepository.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class GetAIPostPagingSource @Inject constructor(
6363
memberId = null,
6464
page = loadParams.key ?: 1,
6565
size = loadParams.loadSize,
66+
aiPostType = arguments.get("aiPostType"),
6667
).mapSuccess {
6768
Pagination(
6869
currentPage = currentPage,

app/src/main/java/com/no5ing/bbibbi/presentation/feature/view/main/family_studio/FamilyStudioPage.kt

Lines changed: 78 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import androidx.compose.ui.tooling.preview.Preview
3535
import androidx.compose.ui.unit.dp
3636
import androidx.hilt.navigation.compose.hiltViewModel
3737
import com.no5ing.bbibbi.R
38+
import coil.compose.AsyncImage
3839
import com.no5ing.bbibbi.data.model.post.AIPost
3940
import com.no5ing.bbibbi.data.repository.Arguments
4041
import com.no5ing.bbibbi.presentation.component.AIPhotoInfoBaloon
@@ -45,39 +46,46 @@ import com.no5ing.bbibbi.presentation.component.button.CustomCTAButton
4546
import com.no5ing.bbibbi.presentation.feature.view.common.CustomAlertDialog
4647
import com.no5ing.bbibbi.presentation.feature.view_model.post.GetAIPostsViewModel
4748
import com.no5ing.bbibbi.presentation.feature.view_model.post.GetAiImageCountViewModel
49+
import com.no5ing.bbibbi.presentation.feature.view_model.post.GetAiImageTypesViewModel
4850
import com.no5ing.bbibbi.presentation.theme.bbibbiScheme
4951
import com.no5ing.bbibbi.presentation.theme.bbibbiTypo
5052
import com.no5ing.bbibbi.util.LocalSessionState
53+
import com.no5ing.bbibbi.util.asyncImagePainter
5154
import java.time.LocalDate
5255

5356

5457
@Composable
5558
fun FamilyStudioPage(
59+
aiPostType: String = "",
5660
onDispose: () -> Unit = {},
5761
onTapCreateImage: () -> Unit = {},
5862
onTapAiPost: (AIPost) -> Unit = {},
5963
postsViewModel: GetAIPostsViewModel = hiltViewModel(),
6064
aiImageCountViewModel: GetAiImageCountViewModel = hiltViewModel(),
65+
aiImageTypesViewModel: GetAiImageTypesViewModel = hiltViewModel(),
6166
isTermDialogEnabled: State<Boolean> = mutableStateOf(true),
6267
onDisagreeTerm: () -> Unit = {},
6368
onAgreeTerm: () -> Unit = {},
6469
onClickTerm: () -> Unit = {},
6570
) {
6671
val aiImageState = aiImageCountViewModel.uiState.collectAsState()
72+
val typesState = aiImageTypesViewModel.uiState.collectAsState()
6773
LaunchedEffect(Unit) {
74+
val postArgs = Arguments(
75+
arguments = if (aiPostType.isNotEmpty()) mapOf("aiPostType" to aiPostType.uppercase()) else emptyMap()
76+
)
6877
if (postsViewModel.isInitialize()) {
69-
postsViewModel.invoke(Arguments())
78+
postsViewModel.invoke(postArgs)
7079
} else {
7180
postsViewModel.refresh()
7281
}
7382

74-
aiImageCountViewModel.invoke(Arguments())
75-
}
76-
val photoCount = if(aiImageState.value.isReady()) {
77-
aiImageState.value.data.familyAiImageCount
78-
} else {
79-
0
83+
aiImageCountViewModel.invoke(postArgs)
84+
aiImageTypesViewModel.invoke(Arguments())
8085
}
86+
val matchedType = if (typesState.value.isReady()) {
87+
typesState.value.data.results.find { it.aiPostType == aiPostType }
88+
} else null
8189
CustomAlertDialog(
8290
title = "이미지사용약관",
8391
description = "AI 이미지 기능을 사용하려면\n약관에 대한 동의가 필요해요",
@@ -101,11 +109,8 @@ fun FamilyStudioPage(
101109
onDispose = onDispose,
102110
title = "가족 사진관"
103111
)
104-
val scrollState = rememberScrollState()
105-
Column(
106-
modifier = Modifier
107-
.verticalScroll(state = scrollState)
108-
) {
112+
if (matchedType != null) {
113+
val dateRange = formatDateRange(matchedType.startDate, matchedType.endDate)
109114
Column(
110115
modifier = Modifier.padding(vertical = 20.dp, horizontal = 20.dp)
111116
) {
@@ -122,78 +127,80 @@ fun FamilyStudioPage(
122127
.padding(vertical = 2.dp, horizontal = 6.dp)
123128
) {
124129
Text(
125-
text = "추석",
130+
text = matchedType.getTypeName(),
126131
color = MaterialTheme.bbibbiScheme.backgroundPrimary,
127132
style = MaterialTheme.bbibbiTypo.bodyTwoBold,
128133
)
129134
}
130135
Box(modifier = Modifier.width(6.dp))
131136
Text(
132-
text = "9/29~10/27",
137+
text = dateRange,
133138
color = MaterialTheme.bbibbiScheme.textPrimary,
134139
style = MaterialTheme.bbibbiTypo.headTwoBold,
135140
)
136141
Spacer(modifier = Modifier.width(4.dp))
137142
AIPhotoInfoBaloon()
138143
}
139144
Text(
140-
text = "${photoCount}개의 추억",
145+
text = "${matchedType.postCount}개의 추억",
141146
color = MaterialTheme.bbibbiScheme.textPrimary,
142147
style = MaterialTheme.bbibbiTypo.bodyOneRegular,
143148
)
144-
145149
}
146150
Box(modifier = Modifier.height(16.dp))
147-
Image(
148-
painter = painterResource(id = R.drawable.family_studio_banner),
151+
AsyncImage(
152+
model = asyncImagePainter(source = matchedType.imageUrl),
149153
contentDescription = null,
150154
modifier = Modifier.fillMaxWidth(),
151-
contentScale = ContentScale.FillWidth
155+
contentScale = ContentScale.FillWidth,
152156
)
153157
}
154-
155158
}
156159
FamilyStudioPageFeed(
157160
postItemsState = postsViewModel.uiState,
158161
onTapContent = onTapAiPost,
159162
onPullToRefresh = {
160-
aiImageCountViewModel.invoke(Arguments())
163+
val refreshArgs = Arguments(
164+
arguments = if (aiPostType.isNotEmpty()) mapOf("aiPostType" to aiPostType.uppercase()) else emptyMap()
165+
)
166+
aiImageCountViewModel.invoke(refreshArgs)
161167
}
162168
)
163169
}
164170

165-
Box(
166-
modifier = Modifier
167-
.padding(horizontal = 12.dp, vertical = 15.dp)
168-
.navigationBarsPadding()
169-
.align(Alignment.BottomCenter)
170-
) {
171-
172-
CustomCTAButton(
173-
modifier = Modifier.fillMaxWidth(),
174-
contentPadding = PaddingValues(vertical = 18.dp),
175-
onClick = onTapCreateImage,
176-
isActive = aiImageState.value.isReady() && aiImageState.value.data.hasAvailableImage()
171+
if (isWithinDateRange(matchedType?.startDate, matchedType?.endDate)) {
172+
Box(
173+
modifier = Modifier
174+
.padding(horizontal = 12.dp, vertical = 15.dp)
175+
.navigationBarsPadding()
176+
.align(Alignment.BottomCenter)
177177
) {
178-
Text(
179-
text = "이미지 만들기",
180-
color = MaterialTheme.bbibbiScheme.backgroundPrimary,
181-
style = MaterialTheme.bbibbiTypo.bodyOneBold,
182-
)
183-
Image(
184-
painter = painterResource(id = R.drawable.ai),
185-
contentDescription = null,
186-
modifier = Modifier.size(22.dp),
187-
contentScale = ContentScale.FillWidth
188-
)
189-
if (aiImageState.value.isReady()) {
190-
val count = aiImageState.value.data
191-
Spacer(modifier = Modifier.width(3.dp))
178+
CustomCTAButton(
179+
modifier = Modifier.fillMaxWidth(),
180+
contentPadding = PaddingValues(vertical = 18.dp),
181+
onClick = onTapCreateImage,
182+
isActive = aiImageState.value.isReady() && aiImageState.value.data.hasAvailableImage()
183+
) {
192184
Text(
193-
text = "(${count.availableAiImageCount}/3)",
185+
text = "이미지 만들기",
194186
color = MaterialTheme.bbibbiScheme.backgroundPrimary,
195-
style = MaterialTheme.bbibbiTypo.bodyTwoRegular,
187+
style = MaterialTheme.bbibbiTypo.bodyOneBold,
196188
)
189+
Image(
190+
painter = painterResource(id = R.drawable.ai),
191+
contentDescription = null,
192+
modifier = Modifier.size(22.dp),
193+
contentScale = ContentScale.FillWidth
194+
)
195+
if (aiImageState.value.isReady()) {
196+
val count = aiImageState.value.data
197+
Spacer(modifier = Modifier.width(3.dp))
198+
Text(
199+
text = "(${count.availableAiImageCount}/3)",
200+
color = MaterialTheme.bbibbiScheme.backgroundPrimary,
201+
style = MaterialTheme.bbibbiTypo.bodyTwoRegular,
202+
)
203+
}
197204
}
198205
}
199206
}
@@ -204,6 +211,28 @@ fun FamilyStudioPage(
204211
}
205212
}
206213

214+
private fun isWithinDateRange(startDate: String?, endDate: String?): Boolean {
215+
if (startDate == null || endDate == null) return false
216+
return try {
217+
val today = LocalDate.now()
218+
val start = LocalDate.parse(startDate)
219+
val end = LocalDate.parse(endDate)
220+
!today.isBefore(start) && !today.isAfter(end)
221+
} catch (e: Exception) {
222+
false
223+
}
224+
}
225+
226+
private fun formatDateRange(startDate: String, endDate: String): String {
227+
return try {
228+
val start = LocalDate.parse(startDate)
229+
val end = LocalDate.parse(endDate)
230+
"${start.monthValue}/${start.dayOfMonth}~${end.monthValue}/${end.dayOfMonth}"
231+
} catch (e: Exception) {
232+
"$startDate~$endDate"
233+
}
234+
}
235+
207236
@Preview(
208237
showBackground = true,
209238
name = "FamilyStudioPage",

app/src/main/java/com/no5ing/bbibbi/presentation/feature/view/main/family_studio_upload/FamilyStudioUploadPage.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import java.util.UUID
6565

6666
@Composable
6767
fun FamilyStudioUploadPage(
68+
aiPostType: String = "",
6869
onDispose: () -> Unit,
6970
imageUrl: State<Uri?>,
7071
convertAIImageViewModel: ConvertAIImageViewModel = hiltViewModel(),
@@ -141,14 +142,14 @@ fun FamilyStudioUploadPage(
141142
isSaveIdle = convertResult.value.isReady(),
142143
onClickUpload = {
143144
mixPanel.track("Click_UploadPhoto")
144-
createPostViewModel.invoke(
145-
Arguments(
146-
arguments = mapOf(
147-
"imageUrl" to convertResult.value.data.imageUrl,
148-
"type" to "AI_IMAGE"
149-
)
150-
)
145+
val args = mutableMapOf(
146+
"imageUrl" to convertResult.value.data.imageUrl,
147+
"type" to "AI_IMAGE",
151148
)
149+
if (aiPostType.isNotEmpty()) {
150+
args["aiPostType"] = aiPostType
151+
}
152+
createPostViewModel.invoke(Arguments(arguments = args))
152153
},
153154
onClickSave = {
154155
coroutineScope.launch {

app/src/main/java/com/no5ing/bbibbi/presentation/feature/view/main/home/HomePage.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ fun HomePage(
5353
onTapViewPost: (LocalDate) -> Unit = {},
5454
onTapPick: (MainPageTopBarModel) -> Unit = {},
5555
onTapNight: () -> Unit = {},
56-
onTapFamilyStudio: () -> Unit = {},
56+
onTapFamilyStudio: (String) -> Unit = {},
5757
) {
5858
val postViewType by postViewTypeState
5959
val mainPageState = mainPageViewModel.uiState.collectAsState()

0 commit comments

Comments
 (0)