-
Notifications
You must be signed in to change notification settings - Fork 2
feat: 공동 카테고리 함께하는 사람들, 친구 초대 구현 #783 #825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
91da369
34eec8e
57e5587
7b50804
cab580e
79c42aa
a861161
fdbf751
ba85efd
603916c
75aba4b
28f6ec9
f579ccc
1537a88
7efeb1e
14460dc
1e29f61
0b4deec
ebd4880
2bcb8b7
9ff9e6f
f3097fc
9f69340
8cde712
42b6337
59626aa
5aa883f
80305b0
aa1046f
1333f7d
da2c451
70a66db
af149c0
100da19
99cc83e
187f334
9ead2cb
7106520
59cdd6f
eca2d2d
0f66b42
74916e3
173f41b
b557fe4
98a74d1
9b8e3f1
4c3f669
105741d
099bc59
24e21b8
ed7392a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.on.staccato.data.dto.invitation | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class InvitationRequest( | ||
@SerialName("categoryId") val categoryId: Long, | ||
@SerialName("inviteeIds") val inviteeIds: List<Long>, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.on.staccato.data.dto.invitation | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class InvitationResponse( | ||
@SerialName("invitationIds") val invitationIds: List<Long>, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.on.staccato.data.dto.member | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class MemberSearchResponse( | ||
@SerialName("members") val members: List<MemberDto>, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.on.staccato.data.dto.member | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class ParticipantDto( | ||
@SerialName("memberId") val id: Long, | ||
@SerialName("nickname") val nickname: String, | ||
@SerialName("memberImageUrl") val imageUrl: String? = null, | ||
@SerialName("memberRole") val role: String, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.on.staccato.data.invitation | ||
|
||
import com.on.staccato.data.dto.invitation.InvitationRequest | ||
import com.on.staccato.data.dto.invitation.InvitationResponse | ||
import com.on.staccato.data.network.ApiResult | ||
import retrofit2.http.Body | ||
import retrofit2.http.POST | ||
|
||
interface InvitationApiService { | ||
@POST(INVITATION_PATH) | ||
suspend fun postInvitation( | ||
@Body invitation: InvitationRequest, | ||
): ApiResult<InvitationResponse> | ||
|
||
companion object { | ||
const val INVITATION_PATH = "/invitations" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.on.staccato.data.invitation | ||
|
||
import com.on.staccato.data.dto.invitation.InvitationRequest | ||
import com.on.staccato.data.network.ApiResult | ||
import com.on.staccato.data.network.handle | ||
import com.on.staccato.domain.repository.InvitationRepository | ||
import javax.inject.Inject | ||
|
||
class InvitationDefaultRepository | ||
@Inject | ||
constructor( | ||
private val invitationApiService: InvitationApiService, | ||
) : InvitationRepository { | ||
override suspend fun invite( | ||
categoryId: Long, | ||
inviteeIds: List<Long>, | ||
): ApiResult<List<Long>> = | ||
invitationApiService.postInvitation(InvitationRequest(categoryId, inviteeIds)) | ||
.handle { it.invitationIds } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
package com.on.staccato.data.member | ||
|
||
import com.on.staccato.data.dto.member.MemberSearchResponse | ||
import com.on.staccato.data.dto.member.RecoveryCodeResponse | ||
import com.on.staccato.data.network.ApiResult | ||
import retrofit2.http.GET | ||
import retrofit2.http.POST | ||
import retrofit2.http.Query | ||
|
||
|
@@ -11,8 +13,15 @@ interface MemberApiService { | |
@Query(RECOVERY_CODE) recoveryCode: String, | ||
): ApiResult<RecoveryCodeResponse> | ||
|
||
@GET(MEMBERS_SEARCH_PATH) | ||
suspend fun getMembersBy( | ||
@Query(NICKNAME) nickname: String, | ||
): ApiResult<MemberSearchResponse> | ||
|
||
companion object { | ||
private const val MEMBERS_PATH = "/members" | ||
private const val RECOVERY_CODE = "code" | ||
private const val NICKNAME = "nickname" | ||
private const val MEMBERS_SEARCH_PATH = "$MEMBERS_PATH/search" | ||
Comment on lines
+16
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API Path를 동반객체의 문자열 상수로 깔끔하게 잘 표현해주셨네요! 👍 |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.on.staccato.domain.model | ||
|
||
data class Members(val members: List<Member>) { | ||
fun addFirst(member: Member): Members = Members((listOf(member) + members).distinct()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. distinct 메서드로 중복을 제거해주셨네요! |
||
|
||
fun filter(member: Member): Members = Members(members.filterNot { it.memberId == member.memberId }) | ||
|
||
fun contains(target: Member) = members.contains(target) | ||
} | ||
|
||
val emptyMembers = Members(emptyList()) | ||
|
||
val dummyMembers = | ||
Members( | ||
listOf( | ||
dummyMember, | ||
longNameMember.copy(memberId = 1L), | ||
dummyMember.copy(memberId = 2L), | ||
longNameMember.copy(memberId = 3L), | ||
dummyMember.copy(memberId = 4L), | ||
longNameMember.copy(memberId = 5L), | ||
dummyMember.copy(memberId = 6L), | ||
longNameMember.copy(memberId = 7L), | ||
), | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.on.staccato.domain.model | ||
|
||
data class Participant( | ||
val member: Member, | ||
val role: Role, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.on.staccato.domain.model | ||
|
||
data class Participants(val members: List<Participant>) | ||
|
||
val emptyParticipants = Participants(emptyList()) | ||
|
||
fun Participants.toMembers() = Members(members.map { it.member }) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.on.staccato.domain.model | ||
|
||
enum class Role(val value: String) { | ||
HOST("host"), | ||
GUEST("guest"), | ||
; | ||
|
||
companion object { | ||
fun of(value: String) = | ||
when (value) { | ||
"host" -> HOST | ||
"guest" -> GUEST | ||
else -> throw IllegalArgumentException("유효하지 않은 Role 입니다.") | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.on.staccato.domain.repository | ||
|
||
import com.on.staccato.data.network.ApiResult | ||
|
||
interface InvitationRepository { | ||
suspend fun invite( | ||
categoryId: Long, | ||
inviteeIds: List<Long>, | ||
): ApiResult<List<Long>> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,11 @@ | ||
package com.on.staccato.domain.repository | ||
|
||
import com.on.staccato.data.network.ApiResult | ||
import com.on.staccato.domain.model.Members | ||
import kotlinx.coroutines.flow.Flow | ||
|
||
interface MemberRepository { | ||
suspend fun fetchTokenWithRecoveryCode(recoveryCode: String): ApiResult<Unit> | ||
|
||
suspend fun searchMembersBy(nickname: String): Flow<ApiResult<Members>> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,18 +4,30 @@ import android.content.res.ColorStateList | |
import android.graphics.PorterDuff | ||
import android.graphics.drawable.Drawable | ||
import android.net.Uri | ||
import android.view.View | ||
import android.widget.ImageButton | ||
import android.widget.ImageView | ||
import androidx.annotation.ColorRes | ||
import androidx.annotation.DrawableRes | ||
import androidx.core.content.ContextCompat.getColor | ||
import androidx.core.view.isGone | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗 isGone이 사용되지 않고 있어요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오잉 린트가 잠깐 졸았나... 이유는 따로 없습니다! 반영 완! |
||
import androidx.databinding.BindingAdapter | ||
import coil.load | ||
import coil.transform.RoundedCornersTransformation | ||
import com.on.staccato.R | ||
import com.on.staccato.presentation.timeline.model.FilterType | ||
import com.on.staccato.presentation.util.dpToPx | ||
|
||
@BindingAdapter("isGone") | ||
fun ImageView.setIsGone(isGone: Boolean) { | ||
this.isGone = isGone | ||
} | ||
|
||
@BindingAdapter("isInvisible") | ||
fun ImageView.setVisibility(isInvisible: Boolean) { | ||
visibility = if (isInvisible) View.INVISIBLE else View.VISIBLE | ||
} | ||
|
||
@BindingAdapter("imageButtonIcon") | ||
fun ImageButton.setColorSelectionIcon( | ||
@DrawableRes drawableRes: Int, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분은 호두가 DataSource를 만들었다고 했던 것 같아서 충돌이 발생할 수도 있겠네요..! 머지 전에 한 번 같이 의논해보면 좋을 것 같아요~
@Junyoung-WON
@s6m1n
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇군요! 저는 Repository 패턴에서 DataSource를 분리하는 이유는 데이터 출처가 여러 개일 수 있기 때문이라고 생각해요.
하지만 초대(Invite) 기능이 로컬 캐시의 필요도가 낮고, 초대는 보통 서버에서만 처리된다고 생각해서 DataSource 없이 바로 ApiService를 주입해주었습니다!