Skip to content
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

Feature/#190 참여자 목록 탭 화면 구현 #193

Merged
6 changes: 5 additions & 1 deletion android/2023-emmsale/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ android {
buildConfigField(
"String",
"GITHUB_CLIENT_ID",
getApiKey("GH_CLIENT_ID")
getApiKey("GH_CLIENT_ID"),
)
}
buildFeatures {
Expand Down Expand Up @@ -87,6 +87,7 @@ dependencies {
implementation("com.squareup.okhttp3:mockwebserver:4.11.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.8.0")
implementation("com.github.bumptech.glide:glide:4.15.1")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")

Expand All @@ -101,4 +102,7 @@ dependencies {
testImplementation("io.mockk:mockk-android:1.13.5")
testImplementation("io.mockk:mockk-agent:1.13.5")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.2")

// imageview
implementation("de.hdodenhof:circleimageview:3.1.0")
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit

Expand All @@ -15,8 +16,11 @@ class ServiceFactory {
isLenient = true
}
private val jsonConverterFactory = json.asConverterFactory(jsonMediaType)

private val httpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
private val okhttpClient = OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(AuthInterceptor())
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.emmsale.data.participant

data class Participant(
val id: Long,
val memberId: Long,
val name: String,
val imageUrl: String,
val description: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.emmsale.data.participant

import com.emmsale.data.common.ApiResult

interface ParticipantRepository {

suspend fun fetchEventParticipants(
eventId: Long,
): ApiResult<List<Participant>>

suspend fun saveParticipant(eventId: Long): ApiResult<Unit>
suspend fun deleteParticipant(eventId: Long): ApiResult<Unit>

suspend fun requestCompanion(
eventId: Long,
memberId: Long,
message: String,
): ApiResult<Unit>

suspend fun checkParticipationStatus(eventId: Long): ApiResult<Boolean>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.emmsale.data.participant

import com.emmsale.data.common.ApiResult
import com.emmsale.data.common.handleApi
import com.emmsale.data.participant.dto.CompanionRequestBody
import com.emmsale.data.participant.dto.ParticipantApiModel
import com.emmsale.data.participant.dto.ParticipantRequestBody
import com.emmsale.data.participant.dto.toData
import com.emmsale.data.uid.UidRepository

class ParticipantRepositoryImpl(
private val participantService: ParticipantService,
uidRepository: UidRepository,
) : ParticipantRepository {

private val currentUid = uidRepository.getCurrentUid()

override suspend fun fetchEventParticipants(eventId: Long): ApiResult<List<Participant>> {
val response = participantService.getParticipants(eventId)
return handleApi(
response = response,
mapToDomain = List<ParticipantApiModel>::toData,
)
}

override suspend fun saveParticipant(eventId: Long): ApiResult<Unit> {
val requestBody = ParticipantRequestBody(currentUid)
val response = participantService.saveParticipant(eventId, requestBody)
return handleApi(response, mapToDomain = { })
}

override suspend fun deleteParticipant(eventId: Long): ApiResult<Unit> {
val response = participantService.deleteParticipant(eventId, currentUid)
return handleApi(response, mapToDomain = {})
}

override suspend fun requestCompanion(
eventId: Long,
memberId: Long,
message: String,
): ApiResult<Unit> {
val requestBody = CompanionRequestBody(
senderId = currentUid,
receiverId = memberId,
eventId = eventId,
message = message,
)
val response = participantService.postCompanion(requestBody)
return handleApi(response, mapToDomain = {})
}

override suspend fun checkParticipationStatus(eventId: Long): ApiResult<Boolean> {
val response = participantService.checkIsParticipated(eventId, currentUid)
return handleApi(response, mapToDomain = { response.body()!! })
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.emmsale.data.participant

import com.emmsale.data.participant.dto.CompanionRequestBody
import com.emmsale.data.participant.dto.ParticipantApiModel
import com.emmsale.data.participant.dto.ParticipantRequestBody
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query

interface ParticipantService {

@GET("events/{eventId}/participants")
suspend fun getParticipants(
@Path("eventId") eventId: Long,
): Response<List<ParticipantApiModel>>

@POST("events/{eventId}/participants")
suspend fun saveParticipant(
@Path("eventId") eventId: Long,
@Body participantRequestBody: ParticipantRequestBody,
): Response<Unit>

@DELETE("events/{eventId}/participants")
suspend fun deleteParticipant(
@Path("eventId") eventId: Long,
@Query("member-id") memberId: Long,
): Response<Unit>

@GET("events/{eventId}/participants/already-participate")
suspend fun checkIsParticipated(
@Path("eventId") eventId: Long,
@Query("member-id") memberId: Long,
): Response<Boolean>

@POST("notifications")
suspend fun postCompanion(
@Body companionRequestBody: CompanionRequestBody,
): Response<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.emmsale.data.participant.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CompanionRequestBody(
@SerialName("senderId")
val senderId: Long,
@SerialName("receiverId")
val receiverId: Long,
@SerialName("message")
val message: String,
@SerialName("eventId")
val eventId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.emmsale.data.participant.dto

import com.emmsale.data.participant.Participant
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ParticipantApiModel(
@SerialName("id")
val id: Long,
@SerialName("memberId")
val memberId: Long,
@SerialName("name")
val name: String,
@SerialName("imageUrl")
val imageUrl: String,
@SerialName("description")
val description: String?,
)

fun List<ParticipantApiModel>.toData(): List<Participant> = map {
Participant(
id = it.id,
memberId = it.memberId,
name = it.name,
imageUrl = it.imageUrl,
description = it.description,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.emmsale.data.participant.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ParticipantRequestBody(
@SerialName("memberId")
val memberId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.emmsale.data.uid

interface UidRepository {
fun getCurrentUid(): Long
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.emmsale.data.uid

import android.content.SharedPreferences

class UidRepositoryImpl(
private val sharedPreferences: SharedPreferences,
) : UidRepository {

override fun getCurrentUid(): Long {
return sharedPreferences.getLong(UID_KEY, DEFAULT_UID)
}

companion object {
private const val UID_KEY = "uid_key"
private const val DEFAULT_UID = 0L
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import com.emmsale.data.login.LoginRepository
import com.emmsale.data.login.LoginRepositoryImpl
import com.emmsale.data.member.MemberRepository
import com.emmsale.data.member.MemberRepositoryImpl
import com.emmsale.data.participant.ParticipantRepository
import com.emmsale.data.participant.ParticipantRepositoryImpl
import com.emmsale.data.token.TokenRepository
import com.emmsale.data.token.TokenRepositoryImpl
import com.emmsale.data.uid.UidRepository
import com.emmsale.data.uid.UidRepositoryImpl

class RepositoryContainer(
serviceContainer: ServiceContainer,
Expand All @@ -40,4 +44,13 @@ class RepositoryContainer(
val eventDetailRepository: EventDetailRepository by lazy {
EventDetailRepositoryImpl(eventDetailService = serviceContainer.eventDetailService)
}
val uidRepository: UidRepository by lazy {
UidRepositoryImpl(preferenceContainer.preference)
}
val participantRepository: ParticipantRepository by lazy {
ParticipantRepositoryImpl(
uidRepository = uidRepository,
participantService = serviceContainer.participantService,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.emmsale.data.eventdetail.EventDetailService
import com.emmsale.data.fcmToken.FcmTokenService
import com.emmsale.data.login.LoginService
import com.emmsale.data.member.MemberService
import com.emmsale.data.participant.ParticipantService

class ServiceContainer(serviceFactory: ServiceFactory) {
val loginService: LoginService by lazy { serviceFactory.create(LoginService::class.java) }
Expand All @@ -15,4 +16,5 @@ class ServiceContainer(serviceFactory: ServiceFactory) {
val eventService: EventService by lazy { serviceFactory.create(EventService::class.java) }
val fcmTokenService: FcmTokenService by lazy { serviceFactory.create(FcmTokenService::class.java) }
val eventDetailService: EventDetailService by lazy { serviceFactory.create(EventDetailService::class.java) }
val participantService: ParticipantService by lazy { serviceFactory.create(ParticipantService::class.java) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import com.emmsale.data.common.ApiError
import com.emmsale.data.common.ApiException
import com.emmsale.data.common.ApiSuccess
import com.emmsale.data.eventdetail.EventDetailRepository
import com.emmsale.data.member.MemberRepository
import com.emmsale.presentation.KerdyApplication
import com.emmsale.presentation.common.ViewModelFactory
import com.emmsale.presentation.ui.eventdetail.uistate.EventDetailUiState
import kotlinx.coroutines.launch

class EventDetailViewModel(
private val eventDetailRepository: EventDetailRepository,
private val memberRepository: MemberRepository,
) : ViewModel() {

private val _eventDetail: MutableLiveData<EventDetailUiState> =
Expand All @@ -39,6 +41,7 @@ class EventDetailViewModel(
val factory = ViewModelFactory {
EventDetailViewModel(
eventDetailRepository = KerdyApplication.repositoryContainer.eventDetailRepository,
memberRepository = KerdyApplication.repositoryContainer.memberRepository,
)
}
}
Expand Down
Loading
Loading