Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
Binary file added app/release/app-release.aab
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.room.TypeConverter
import com.example.chaining.domain.model.Application
import com.example.chaining.domain.model.LanguagePref
import com.example.chaining.domain.model.RecruitPost
import com.example.chaining.domain.model.User
import com.example.chaining.domain.model.UserSummary
import kotlinx.serialization.json.Json

Expand Down Expand Up @@ -43,4 +44,10 @@ class Converters {

@TypeConverter
fun toUserSummary(value: String?): UserSummary? = if (value.isNullOrEmpty()) null else Json.decodeFromString(value)

@TypeConverter
fun fromUser(value: User?): String = value?.let { Json.encodeToString(it) } ?: ""

@TypeConverter
fun toUser(value: String?): User? = if (value.isNullOrEmpty()) null else Json.decodeFromString(value)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.example.chaining.data.repository

import com.example.chaining.domain.model.Application
import com.example.chaining.domain.model.Notification
import com.example.chaining.domain.model.UserSummary
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
Expand Down Expand Up @@ -52,6 +54,32 @@ class ApplicationRepository
"/users/$uid/applications/$applicationId" to newApplication,
)

val postOwnerId = application.owner.id

val newNotificationKey =
rootRef.child("notifications")
.child(postOwnerId).push().key ?: error("알림 ID 생성 실패")

val notification =
Notification(
id = newNotificationKey,
type = "application",
sender =
UserSummary(
id = application.applicant.id,
nickname = application.applicant.nickname,
profileImageUrl = application.applicant.profileImageUrl,
country = application.applicant.country,
),
postId = application.postId,
applicationId = applicationId,
createdAt = System.currentTimeMillis(),
isRead = false,
uid = postOwnerId,
)

updates["/notifications/$postOwnerId/$newNotificationKey"] = notification

// 원자적 업데이트 수행
rootRef.updateChildren(updates).await()

Expand Down Expand Up @@ -137,20 +165,54 @@ class ApplicationRepository
suspend fun updateStatus(
application: Application,
value: String,
) {
// 멀티패스 업데이트 경로 구성
val updates =
hashMapOf<String, Any?>(
// 1. applications 노드에 지원서 저장
"/applications/${application.applicationId}/status" to value,
// 2. posts/{postId}/applications/{applicationId}
"/posts/${application.postId}/applications/${application.applicationId}/status" to value,
// 3. users/{uid}/myApplications/{applicationId}
"/users/${application.applicant.id}/applications/${application.applicationId}/status" to value,
)

// 원자적 업데이트 수행
rootRef.updateChildren(updates).await()
): Result<Unit> {
return try {
val applicationRef = applicationsRef().child(application.applicationId)
val currentAppSnapshot = applicationRef.get().await()
val currentStatus = currentAppSnapshot.child("status").getValue(String::class.java)

if (currentStatus != "PENDING") {
return Result.failure(Exception("이미 처리된 지원서입니다."))
}
// 멀티패스 업데이트 경로 구성
println("호시기" + application.applicant.id + application.applicationId)
val updates =
hashMapOf<String, Any?>(
// 1. applications 노드에 지원서 저장
"/applications/${application.applicationId}/status" to value,
// 2. posts/{postId}/applications/{applicationId}
"/posts/${application.postId}/applications/${application.applicationId}/status" to value,
// 3. users/{uid}/myApplications/{applicationId}
"/users/${application.applicant.id}/applications/${application.applicationId}/status" to value,
)

val newNotificationKey =
rootRef.child("notifications")
.child(application.applicant.id).push().key ?: error("알림 ID 생성 실패")
val notification =
Notification(
id = newNotificationKey,
type = "status_update",
sender =
UserSummary(
id = application.applicant.id,
nickname = application.applicant.nickname,
profileImageUrl = application.applicant.profileImageUrl,
country = application.applicant.country,
),
postId = application.postId,
applicationId = application.applicationId,
status = value,
createdAt = System.currentTimeMillis(),
isRead = false,
)
updates["/notifications/${application.applicant.id}/$newNotificationKey"] = notification
// 원자적 업데이트 수행
rootRef.updateChildren(updates).await()
return Result.success(Unit)
} catch (e: Exception) {
Result.failure(e)
}
}

/** Delete (Soft Delete) */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ data class Application(
// 모집글 제목 (캐싱)
val recruitPostTitle: String = "",
// 지원자 간단 프로필
val applicant: UserSummary = UserSummary(),
val applicant: User = User(),
// 자기 소개
val introduction: String = "",
val createdAt: Long = 0L,
Expand Down
28 changes: 19 additions & 9 deletions app/src/main/java/com/example/chaining/ui/component/CardItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,20 @@ fun CardItem(
onRightButtonClick: () -> Unit = {},
currentUserId: String? = "",
isLiked: Boolean? = false,
hasApplied: Boolean = false,
) {
val title =
when (type) {
"모집글" -> recruitPost?.title ?: stringResource(id = R.string.community_no_title)
"지원서" -> application?.recruitPostTitle ?: stringResource(id = R.string.community_no_title)
"지원서" ->
application?.recruitPostTitle
?: stringResource(id = R.string.community_no_title)

else -> stringResource(id = R.string.community_no_title)
}
}.replace("+", " ")

val remainingTimeText = remainingTime ?: stringResource(id = R.string.community_unknown)
val isAuthor = recruitPost?.owner?.id == currentUserId

val timeText =
when (type) {
Expand All @@ -79,11 +84,13 @@ fun CardItem(

val leftButtonText =
if (type == "모집글") {
stringResource(id = R.string.community_apply_button)
if (hasApplied == true) {
stringResource(id = R.string.community_application_complete)
} else {
stringResource(id = R.string.community_apply_button)
}
} else {
stringResource(
id = R.string.application_yes,
)
stringResource(id = R.string.application_yes)
}
val rightButtonText =
if (type == "모집글") {
Expand All @@ -95,7 +102,7 @@ fun CardItem(
}

val rightText =
if (type == "모집글") stringResource(id = R.string.community_see_post) else stringResource(id = R.string.view_application)
if (type == "모집글") stringResource(id = R.string.community_see_post) else stringResource(id = R.string.view_application_arrow)

val profile =
if (type == "모집글") {
Expand All @@ -105,7 +112,9 @@ fun CardItem(
recruitPost?.owner?.nickname
?: stringResource(id = R.string.community_unknown),
profileImageUrl = recruitPost?.owner?.profileImageUrl ?: "",
country = recruitPost?.owner?.country ?: stringResource(id = R.string.community_unknown),
country =
recruitPost?.owner?.country
?: stringResource(id = R.string.community_unknown),
)
} else {
UserSummary(
Expand Down Expand Up @@ -240,7 +249,6 @@ fun CardItem(
}

Spacer(modifier = Modifier.height(8.dp))

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
Expand All @@ -251,6 +259,7 @@ fun CardItem(
onClick = onLeftButtonClick,
modifier = Modifier.weight(3f),
shape = RoundedCornerShape(20.dp),
enabled = !isAuthor && !hasApplied,
colors =
ButtonDefaults.buttonColors(
containerColor = Color(0xFF4285F4),
Expand All @@ -269,6 +278,7 @@ fun CardItem(
.weight(2f)
.scale(scale.value),
shape = RoundedCornerShape(20.dp),
enabled = !isAuthor,
colors =
ButtonDefaults.buttonColors(
containerColor = buttonColor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import com.example.chaining.R
import com.example.chaining.data.model.FilterState
import com.example.chaining.ui.screen.PrimaryBlue
import com.example.chaining.ui.screen.SecondaryTextColor
import com.example.chaining.viewmodel.AreaViewModel

Expand Down Expand Up @@ -264,6 +265,8 @@ fun FilterDropdown(
.fillMaxWidth(),
colors =
ExposedDropdownMenuDefaults.outlinedTextFieldColors(
focusedLabelColor = PrimaryBlue,
unfocusedLabelColor = Color.Gray,
focusedBorderColor = Color(0xFF7282B4),
unfocusedBorderColor = Color.Gray.copy(alpha = 0.5f),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@ fun OwnerProfile(
userViewModel: UserViewModel = hiltViewModel(),
// "상세 보기"
type: String? = "",
showFollowButton: Boolean = false,
) {
val userState by userViewModel.user.collectAsState()
val nicknameInfo =
when (where) {
"카드뷰" -> 18.sp to 0xFF4A526A
"모집글 상세보기" -> 14.sp to 0xFFFFFFFF
"모집글 상세보기" -> {
if (showFollowButton) 14.sp to 0xFFFFFFFF else 16.sp to 0xFFFFFFFF // 버튼 없으면 폰트 크게
}
"지원서" -> 14.sp to 0xFF4A526A
else -> 14.sp to 0xFF4A526A
}
Expand Down Expand Up @@ -87,13 +90,16 @@ fun OwnerProfile(
fontSize = nicknameInfo.first,
color = Color(nicknameInfo.second),
)
Text(
text = owner.country,
fontSize = countryInfo.first,
color = Color(countryInfo.second),
)
if (where != "모집글 상세보기") {
Text(
text = owner.country,
fontSize = countryInfo.first,
color = Color(countryInfo.second),
)
}
}
if (type == "상세 보기") {
if (showFollowButton) {
Spacer(modifier = Modifier.width(8.dp))
Box(
modifier =
Modifier
Expand Down
Loading
Loading