diff --git a/app/src/main/java/com/egobook/app/data/api/FriendsApiService.kt b/app/src/main/java/com/egobook/app/data/api/FriendsApiService.kt index 4d6d118b..7f198ccb 100644 --- a/app/src/main/java/com/egobook/app/data/api/FriendsApiService.kt +++ b/app/src/main/java/com/egobook/app/data/api/FriendsApiService.kt @@ -2,7 +2,7 @@ package com.egobook.app.data.api import com.egobook.app.data.model.ApiResponse import com.egobook.app.data.model.square.friend.FriendRequestResponse -import com.egobook.app.data.model.square.friend.FriendResponse +import com.egobook.app.data.model.square.friend.FriendListResponse import com.egobook.app.data.model.square.friend.FriendshipRequest import com.egobook.app.data.model.square.friend.SearchUserResponse import retrofit2.http.Body @@ -14,10 +14,10 @@ import retrofit2.http.Query interface FriendsApiService { @GET("/friends") - suspend fun fetchFriendList(): ApiResponse> + suspend fun fetchFriendList(): ApiResponse @DELETE("/friends/{friendId}") - suspend fun deleteFriend(@Path("friendId") friendId: Int): ApiResponse + suspend fun deleteFriend(@Path("friendId") friendId: Long): ApiResponse @GET("/friends/requests/incoming") suspend fun fetchIncomingFriendsRequests(): ApiResponse> diff --git a/app/src/main/java/com/egobook/app/data/model/square/friend/FriendListResponse.kt b/app/src/main/java/com/egobook/app/data/model/square/friend/FriendListResponse.kt new file mode 100644 index 00000000..288beff8 --- /dev/null +++ b/app/src/main/java/com/egobook/app/data/model/square/friend/FriendListResponse.kt @@ -0,0 +1,31 @@ +package com.egobook.app.data.model.square.friend + +import com.egobook.app.domain.model.Friend +import com.egobook.app.domain.model.FriendList +import com.google.gson.annotations.SerializedName + +data class FriendListResponse( + val count: Int, + val friends: List, +) + +data class FriendResponse( + @SerializedName("friendId") + val id: Long, + @SerializedName("nickname") + val name: String, + @SerializedName("level") + val level: Long +) + +fun FriendListResponse.toDomain(): FriendList = FriendList( + count = count, + friends = friends.map { it.toDomain() } +) + +fun FriendResponse.toDomain(): Friend = Friend( + id = id, + name = name, + level = level +) + diff --git a/app/src/main/java/com/egobook/app/data/model/square/friend/FriendResponse.kt b/app/src/main/java/com/egobook/app/data/model/square/friend/FriendResponse.kt deleted file mode 100644 index 386aa0d0..00000000 --- a/app/src/main/java/com/egobook/app/data/model/square/friend/FriendResponse.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.egobook.app.data.model.square.friend - -import com.egobook.app.domain.model.Friend -import com.google.gson.annotations.SerializedName - -data class FriendResponse( - @SerializedName("friendId") - val id: Long, - @SerializedName("nickname") - val name: String, -) - -fun FriendResponse.toDomain(): Friend = Friend( - id = id, - name = name -) diff --git a/app/src/main/java/com/egobook/app/data/repository/CounselingRepositoryImpl.kt b/app/src/main/java/com/egobook/app/data/repository/CounselingRepositoryImpl.kt index d50824ca..f4e252c6 100644 --- a/app/src/main/java/com/egobook/app/data/repository/CounselingRepositoryImpl.kt +++ b/app/src/main/java/com/egobook/app/data/repository/CounselingRepositoryImpl.kt @@ -1,5 +1,6 @@ package com.egobook.app.data.repository +import android.util.Log import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData @@ -91,103 +92,26 @@ class CounselingRepositoryImpl @Inject constructor(private val apiService: Couns } override fun getWeeklyReports(size: Int): Flow> { -// return Pager( -// config = PagingConfig( -// pageSize = size, -// initialLoadSize = size, -// enablePlaceholders = false -// ), -// pagingSourceFactory = { -// WeeklyReportsPagingSource(apiService = apiService) -// } -// ).flow - - val dummyWeeklyReports = listOf( - WeeklyReport( - id = 1L, - startDate = "2024.02.03", - endDate = "2024.02.09", - isRead = true, - isLocked = false - ), - WeeklyReport( - id = 2L, - startDate = "2024.01.27", - endDate = "2024.02.02", - isRead = false, - isLocked = false - ), - WeeklyReport( - id = 3L, - startDate = "2024.01.20", - endDate = "2024.01.26", - isRead = false, - isLocked = true // 잠긴 상태 테스트 + return Pager( + config = PagingConfig( + pageSize = size, + initialLoadSize = size, + enablePlaceholders = false ), - WeeklyReport( - id = 4L, - startDate = "2024.01.13", - endDate = "2024.01.19", - isRead = false, - isLocked = true - ) - ) - - // 2. Pager 대신 flowOf와 PagingData.from을 사용하여 즉시 반환 - return flowOf( - PagingData.from(dummyWeeklyReports) - ) + pagingSourceFactory = { + WeeklyReportsPagingSource(apiService = apiService) + } + ).flow } override suspend fun getWeeklyReportByDate(startDate: String): Result = try { -// val response = apiService.fetchWeeklyReportByDate(startDate = startDate) -// if (response.isSuccessful && response.body() != null) { -// Result.success(response.body()!!.toDomain()) -// } else { -// Result.failure(Exception("Error: ${response.code()}")) -// } - val dummyDetail = WeeklyReportDetail( - startDate = startDate, // 클릭한 아이템의 날짜를 그대로 사용 - endDate = "2024.02.09", - summary = """ - 이번 한 주 동안 사용자님은 정말 꾸준한 감정 기록을 보여주셨습니다. - 특히 주 초반에 겪었던 업무적 스트레스를 수요일 이후 스스로 잘 극복해내는 과정이 인상 깊었습니다. - 금요일에는 예상치 못한 즐거운 소식으로 인해 긍정적인 에너지가 최고조에 달했으며, - 이러한 긍정적인 흐름이 주말까지 이어져 안정적인 심리 상태를 유지하셨습니다. - 전반적으로 감정의 기복이 있었으나 스스로를 잘 돌보신 한 주였습니다. - """.trimIndent(), - praisePoints = """ - 가장 칭찬하고 싶은 점은 스트레스 상황에서도 기록을 포기하지 않은 끈기입니다. - 자신의 감정을 외면하지 않고 있는 그대로 마주하려는 태도가 매우 훌륭합니다. - 부정적인 감정이 들 때마다 짧은 명상을 시도한 점이 심리적 회복 탄력성을 높였습니다. - 또한 주변 사람들과 긍정적인 대화를 나누며 에너지를 얻으려 노력한 모습도 보기 좋습니다. - 작은 성취들을 기록하며 자신감을 되찾으려는 모습이 사용자님의 가장 큰 강점입니다. - """.trimIndent(), - improvementPoints = """ - 화요일 밤에 나타난 수면 부족 현상이 수요일 오전 집중력 저하로 이어진 것으로 보입니다. - 감정이 격해질 때 가끔 식사를 거르는 습관이 체력적인 부담을 줄 수 있으니 주의가 필요합니다. - 타인의 시선을 과도하게 신경 써서 본인의 진심을 숨기려는 경향이 간혹 관찰됩니다. - 불안함이 느껴질 때 너무 빠르게 결론을 내리려 하기보다는 시간을 두고 지켜보는 여유가 필요합니다. - 일과 삶의 경계가 모호해지지 않도록 명확한 휴식 시간을 설정하는 것을 추천드립니다. - """.trimIndent(), - managementAdvice = """ - 다음 주에는 하루에 10분이라도 온전히 스마트폰을 멀리하고 혼자만의 시간을 가져보세요. - 특히 감정이 복잡할 때는 글로 직접 써 내려가는 '감정 쓰기'가 큰 도움이 될 것입니다. - 점심시간을 활용한 가벼운 산책이 오후 시간의 집중력과 기분 전환에 효과적일 것입니다. - 불안한 생각이 들 때는 호흡에 집중하며 현재의 감각을 느껴보는 연습을 지속해 보세요. - 스스로에게 너무 엄격한 잣대를 대기보다는 '그럴 수도 있지'라는 마음가짐이 필요합니다. - """.trimIndent(), - supportMessage = """ - 사용자님, 이번 주도 정말 고생 많으셨습니다. 당신은 이미 충분히 잘하고 있습니다. - 때로는 비가 오기도 하지만, 그 비가 땅을 단단하게 만들어 더 아름다운 꽃을 피우게 할 거예요. - 작은 감정의 변화에 일희일비하기보다, 꾸준히 나아가고 있는 자신의 발걸음을 믿어보세요. - 어떤 순간에도 저는 사용자님의 편이 되어 응원하고 기록을 도와드릴 것입니다. - 따뜻한 차 한 잔과 함께 편안한 저녁 보내시길 바라며, 내일도 힘차게 시작해 봐요! - """.trimIndent(), - isRead = true - ) - Result.success(dummyDetail) + val response = apiService.fetchWeeklyReportByDate(startDate = startDate) + if (response.isSuccessful && response.body() != null) { + Result.success(response.body()!!.toDomain()) + } else { + Result.failure(Exception("Error: ${response.code()}")) + } } catch (e: Exception) { Result.failure(e) } @@ -232,7 +156,7 @@ class CounselingRepositoryImpl @Inject constructor(private val apiService: Couns } override suspend fun getStatistics(): Result = try { - // val response = apiService.fetchStatistics() +// val response = apiService.fetchStatistics() // if (response.isSuccessful && response.body() != null) { // Result.success(response.body()!!.toDomain()) // } else { diff --git a/app/src/main/java/com/egobook/app/data/repository/FriendsRepositoryImpl.kt b/app/src/main/java/com/egobook/app/data/repository/FriendsRepositoryImpl.kt index bbb786d6..9dd54daf 100644 --- a/app/src/main/java/com/egobook/app/data/repository/FriendsRepositoryImpl.kt +++ b/app/src/main/java/com/egobook/app/data/repository/FriendsRepositoryImpl.kt @@ -1,188 +1,110 @@ package com.egobook.app.data.repository import com.egobook.app.data.api.FriendsApiService -import com.egobook.app.data.model.square.friend.FriendRequestResponse -import com.egobook.app.data.model.square.friend.FriendResponse -import com.egobook.app.data.model.square.friend.SearchUserResponse +import com.egobook.app.data.model.square.friend.FriendshipRequest import com.egobook.app.data.model.square.friend.toDomain -import com.egobook.app.domain.model.Friend +import com.egobook.app.domain.model.FriendList import com.egobook.app.domain.model.FriendRequest import com.egobook.app.domain.model.SearchUser import com.egobook.app.domain.repository.FriendsRepository import javax.inject.Inject class FriendsRepositoryImpl @Inject constructor(private val apiService: FriendsApiService): FriendsRepository { - override suspend fun fetchFriendList(): Result> = try { -// val response = apiService.fetchFriendList() -// if(response.status == 200) { -// Result.success(response.data.map { it.toDomain() }) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - val dummyData = listOf( - FriendResponse(id = 1, name = "소프트웨어마법사"), - FriendResponse(id = 2, name = "야근하는다람쥐"), - FriendResponse(id = 3, name = "커피중독자"), - FriendResponse(id = 4, name = "안드로이드마스터"), - FriendResponse(id = 5, name = "코딩하는고양이"), - FriendResponse(id = 6, name = "말랑카우"), - FriendResponse(id = 7, name = "개발하는진돗개"), - FriendResponse(id = 8, name = "배고픈거북이"), - FriendResponse(id = 9, name = "잠자는사자") - ) - val emptyDummyData = listOf() - Result.success(dummyData.map { it.toDomain()}) + override suspend fun fetchFriendList(): Result = try { + val response = apiService.fetchFriendList() + if(response.status == 200) { + Result.success(response.data.toDomain()) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun fetchIncomingFriendRequestList(): Result> = try { -// val response = apiService.fetchIncomingFriendsRequests() -// if(response.status == 200) { -// Result.success(response.data.map { it.toDomain() }) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - val dummyData = listOf( - FriendRequestResponse( - requestId = 9007199254740991L, - userId = 1000000000000001L, - nickname = "말랑카우", - requestedAt = "2026-01-24T09:34:26.191Z" - ), - FriendRequestResponse( - requestId = 9007199254740992L, - userId = 1000000000000002L, - nickname = "코딩하는고양이", - requestedAt = "2026-01-24T10:15:00.000Z" - ), - FriendRequestResponse( - requestId = 9007199254740993L, - userId = 1000000000000003L, - nickname = "안드로이드마스터", - requestedAt = "2026-01-24T11:45:12.555Z" - ) - ) - Result.success(dummyData.map { it.toDomain() }) + val response = apiService.fetchIncomingFriendsRequests() + if(response.status == 200) { + Result.success(response.data.map { it.toDomain() }) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun fetchOutgoingFriendRequestList(): Result> = try { -// val response = apiService.fetchOutgoingFriendsRequests() -// if(response.status == 200) { -// Result.success(response.data.map { it.toDomain() }) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - val dummyData = listOf( - FriendRequestResponse( - requestId = 8000000000000001L, - userId = 2000000000000001L, - nickname = "개발하는진돗개", - requestedAt = "2026-01-24T10:00:00.000Z" - ), - FriendRequestResponse( - requestId = 8000000000000002L, - userId = 2000000000000002L, - nickname = "소프트웨어마법사", - requestedAt = "2026-01-24T11:30:00.000Z" - ), - FriendRequestResponse( - requestId = 8000000000000003L, - userId = 2000000000000003L, - nickname = "커피중독자", - requestedAt = "2026-01-24T13:15:00.000Z" - ), - FriendRequestResponse( - requestId = 8000000000000004L, - userId = 2000000000000004L, - nickname = "야근하는다람쥐", - requestedAt = "2026-01-24T15:45:00.000Z" - ) - ) - Result.success(dummyData.map { it.toDomain() }) + val response = apiService.fetchOutgoingFriendsRequests() + if(response.status == 200) { + Result.success(response.data.map { it.toDomain() }) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun searchUser(keyword: String): Result> = try { -// val response = apiService.searchUser(keyword = keyword) -// if(response.status == 200) { -// Result.success(response.data.map { it.toDomain() }) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - val dummySearchData = listOf( - SearchUserResponse( - userId = 1000000000000005L, - nickname = "개발하는 거북이", - level = 42, - profileImageUrl = "https://images.unsplash.com/photo-1541344999736-83eca272f6fc?q=80" - ) - ) - val dummySearchEmptyData = emptyList() - Result.success(dummySearchData.map { it.toDomain() }) + val response = apiService.searchUser(keyword = keyword) + if(response.status == 200) { + Result.success(response.data.map { it.toDomain() }) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun requestFriendship(receiverId: Long): Result = try { -// val response = apiService.requestFriendship(request = FriendshipRequest(receiverId = receiverId)) -// if(response.status == 200) { -// Result.success(Unit) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - Result.success(Unit) + val response = apiService.requestFriendship(request = FriendshipRequest(receiverId = receiverId)) + if(response.status == 200) { + Result.success(Unit) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun rejectFriendRequest(requestId: Long): Result = try { -// val response = apiService.rejectFriendRequest(requestId = requestId) -// if(response.status == 200) { -// Result.success(requestId) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - Result.success(requestId) + val response = apiService.rejectFriendRequest(requestId = requestId) + if(response.status == 200) { + Result.success(requestId) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun acceptFriendRequest(requestId: Long): Result = try { -// val response = apiService.acceptFriendRequest(requestId = requestId) -// if(response.status == 200) { -// Result.success(requestId) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - Result.success(requestId) + val response = apiService.acceptFriendRequest(requestId = requestId) + if(response.status == 200) { + Result.success(requestId) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun deleteFriend(deleteId: Long): Result = try { -// val response = apiService.deleteFriend(friendId = deleteId) -// if(response.status == 200) { -// Result.success(deleteId) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - Result.success(deleteId) + val response = apiService.deleteFriend(friendId = deleteId) + if(response.status == 200) { + Result.success(deleteId) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun cancelFriendRequest(requestId: Long): Result = try { -// val response = apiService.cancelFriendRequest(requestId = requestId) -// if(response.status == 200) { -// Result.success(requestId) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - Result.success(requestId) + val response = apiService.cancelFriendRequest(requestId = requestId) + if(response.status == 200) { + Result.success(requestId) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } diff --git a/app/src/main/java/com/egobook/app/data/repository/LetterRepositoryImpl.kt b/app/src/main/java/com/egobook/app/data/repository/LetterRepositoryImpl.kt index a6717aa4..151f18d4 100644 --- a/app/src/main/java/com/egobook/app/data/repository/LetterRepositoryImpl.kt +++ b/app/src/main/java/com/egobook/app/data/repository/LetterRepositoryImpl.kt @@ -5,16 +5,13 @@ import androidx.paging.PagingConfig import androidx.paging.PagingData import com.egobook.app.data.api.AIApiService import com.egobook.app.data.api.LetterApiService +import com.egobook.app.data.model.square.letter.DetectAbusiveContentRequest import com.egobook.app.data.model.square.letter.ReplyLetterRequest import com.egobook.app.data.model.square.letter.toData import com.egobook.app.data.model.square.letter.toDomain import com.egobook.app.data.repository.paging.SentLettersPagingSource import com.egobook.app.domain.model.square.letter.AbusiveContentAnalysis import com.egobook.app.domain.model.square.letter.ArrivedPendingLetter -import com.egobook.app.domain.model.square.letter.ArrivedPendingLetterItem -import com.egobook.app.domain.model.square.letter.LetterBackgroundColor -import com.egobook.app.domain.model.square.letter.LetterMode -import com.egobook.app.domain.model.square.letter.LetterStatus import com.egobook.app.domain.model.square.letter.ReplyLetter import com.egobook.app.domain.model.square.letter.ReportLetter import com.egobook.app.domain.model.square.letter.SendLetter @@ -40,47 +37,23 @@ class LetterRepositoryImpl @Inject constructor( } override suspend fun detectAbusiveContent(text: String): Result = try { -// val response = aiApiService.detectAbusiveContent(request = DetectAbusiveContentRequest(text = text)) -// if(response.isSuccessful && response.body() != null) { -// Result.success(response.body()!!.toDomain()) -// } else { -// Result.failure(Exception("Error: ${response.code()}")) -// } - val dummyData = AbusiveContentAnalysis( - text = "테스트 중입니다", - riskScore = 40.0, - isHarmful = true, - label = "응?", - detectedBadWords = emptyList() - ) - Result.success(dummyData) + val response = aiApiService.detectAbusiveContent(request = DetectAbusiveContentRequest(text = text)) + if(response.isSuccessful && response.body() != null) { + Result.success(response.body()!!.toDomain()) + } else { + Result.failure(Exception("Error: ${response.code()}")) + } } catch (e: Exception) { Result.failure(e) } override suspend fun fetchArrivedPendingLetter(): Result = try { -// val response = letterApiService.fetchArrivedPendingLetter() -// if(response.status == 200) { -// Result.success(response.data.toDomain()) -// } else { -// Result.failure(Exception("Error: ${response.status}")) -// } - val mockData = ArrivedPendingLetter( - letter = ArrivedPendingLetterItem( - letterId = 1L, - status = LetterStatus.ARRIVED, - mode = LetterMode.RANDOM, - fromLabel = "익명", - content = "안녕하세요! 요즘 날씨가 부쩍 추워졌는데 잘 지내고 계신가요? 오늘 우연히 당신의 이야기를 듣고 문득 위로의 말을 전하고 싶어 펜을 들었습니다. 누구나 가끔은 마음이 무겁고 모든 게 버겁게 느껴지는 날이 있잖아요. 그럴 때일수록 스스로를 너무 다그치지 말고, 따뜻한 차 한 잔 마시며 쉬어갔으면 좋겠어요. 당신은 충분히 잘해내고 있고, 존재만으로도 소중한 사람이라는 걸 잊지 마세요. 내일은 오늘보다 조금 더 웃을 수 있는 여유가 생기길 진심으로 응원하겠습니다! 답장 기다릴게요.", - arrivedAt = "2026-02-02T10:35:00+09:00", - replyDeadlineAt = "2026-02-04T21:40:00+09:00", - letterColor = LetterBackgroundColor.WHITE - ) - ) - val emptyMockData = ArrivedPendingLetter( - letter = null - ) - Result.success(emptyMockData) + val response = letterApiService.fetchArrivedPendingLetter() + if(response.status == 200) { + Result.success(response.data.toDomain()) + } else { + Result.failure(Exception("Error: ${response.status}")) + } } catch (e: Exception) { Result.failure(e) } @@ -141,25 +114,6 @@ class LetterRepositoryImpl @Inject constructor( } else { Result.failure(Exception("Error: ${response.status}")) } -// val mockSentLetterWithReply = SentLetterWithReply( -// letterId = letterId, -// threadId = letterId, -// status = LetterStatus.ARRIVED, // 답장이 도착함 -// mode = LetterMode.RANDOM, -// sentContent = "안녕하세요, 고민이 있어 편지를 보냅니다. 요즘 업무량이 너무 많아서 번아웃이 온 것 같아요. 어떻게 극복하면 좋을까요?", -// backgroundColor = LetterBackgroundColor.WHITE, -// createdAt = "2026-02-06T03:42:43.162307Z", -// arrivedAt = "2026-02-06T03:42:43.162307Z", -// // 답장 데이터 -// reply = LetterReply( -// replyId = 101L, -// replyContent = "보내주신 편지 잘 읽었습니다. 번아웃 때문에 많이 힘드시겠어요. 저도 비슷한 경험이 있었는데, 그럴 땐 완벽하게 해내려는 마음을 조금 내려놓고 하루에 딱 10분이라도 온전히 자신만을 위해 산책을 하는 게 큰 도움이 되더라고요. 당신은 이미 충분히 잘하고 있습니다. 너무 스스로를 몰아세우지 마세요.", -// isAIGenerated = true, -// isReported = false, -// repliedAt = "2026-02-06T03:42:43.162307Z" -// ) -// ) -// Result.success(mockSentLetterWithReply) } catch (e: Exception) { Result.failure(e) } diff --git a/app/src/main/java/com/egobook/app/data/repository/paging/AllUserRepliesPagingSource.kt b/app/src/main/java/com/egobook/app/data/repository/paging/AllUserRepliesPagingSource.kt index 1e3e98e9..7c7b5cfc 100644 --- a/app/src/main/java/com/egobook/app/data/repository/paging/AllUserRepliesPagingSource.kt +++ b/app/src/main/java/com/egobook/app/data/repository/paging/AllUserRepliesPagingSource.kt @@ -17,27 +17,11 @@ class AllUserRepliesPagingSource(private val apiService: QuestionApiService): Pa return try { val page = params.key ?: 1 val size = params.loadSize -// val result = apiService.fetchTodayAllUserReplies(page = page, size = size).data -// LoadResult.Page( -// data = result.content.map { it.toDomain() }, -// prevKey = if(result.page == 1) null else result.page - 1, -// nextKey = if(result.hasNext) result.page + 1 else null -// ) - if(page > 1) delay(1000) - val mockData = List(size) { index -> - UserTodayQuestionAnswerItem( - answerId = (page * size + index).toLong(), - userId = (page * size + index).toLong(), - nickname = "다른 유저${page*size+index}", - content = "다른 유저의 답이 보이는 자리 다른 유저의 답이 보이는 자리다른 유저의 답이 보이는 자리 다른 유저의 답이 보이는 자리 다른 유저의 답이 보이는 자리다른 유저의 답이 보이는 자리다른 유저의 답이 보이는 자리다른 유저의 답이 보이는 자리 다른 유저의 답이 보이는 자리 다른 유저의 답이 보이는 자리 다른 유저의 답이 보이는 자리", - createdAt = "2026-01-28T07:30:00Z" - ) - } - val hasNext = page < 5 + val result = apiService.fetchTodayAllUserReplies(page = page, size = size).data LoadResult.Page( - data = mockData, - prevKey = if(page == 1) null else page - 1, - nextKey = if(hasNext) page + 1 else null + data = result.content.map { it.toDomain() }, + prevKey = if(result.page == 1) null else result.page - 1, + nextKey = if(result.hasNext) result.page + 1 else null ) } catch (e: Exception) { LoadResult.Error(e) diff --git a/app/src/main/java/com/egobook/app/data/repository/paging/FriendRepliesPagingSource.kt b/app/src/main/java/com/egobook/app/data/repository/paging/FriendRepliesPagingSource.kt index f86be0f0..efde8932 100644 --- a/app/src/main/java/com/egobook/app/data/repository/paging/FriendRepliesPagingSource.kt +++ b/app/src/main/java/com/egobook/app/data/repository/paging/FriendRepliesPagingSource.kt @@ -3,6 +3,7 @@ package com.egobook.app.data.repository.paging import androidx.paging.PagingSource import androidx.paging.PagingState import com.egobook.app.data.api.QuestionApiService +import com.egobook.app.data.model.square.question.toDomain import com.egobook.app.domain.model.square.question.UserTodayQuestionAnswerItem import kotlinx.coroutines.delay @@ -16,32 +17,12 @@ class FriendRepliesPagingSource(private val apiService: QuestionApiService) : return try { val page = params.key ?: 1 val size = params.loadSize -// val result = apiService.fetchTodayFriendReplies(page = page, size = size).data -// LoadResult.Page( -// data = result.content.map { it.toDomain() }, -// prevKey = if(result.page == 1) null else result.page - 1, -// nextKey = if(result.hasNext) result.page + 1 else null -// ) - - if (page > 1) delay(1000) - - val mockContent = List(size) { index -> - UserTodayQuestionAnswerItem( - answerId = (page * size + index).toLong(), - userId = (page * size + index).toLong(), - nickname = "아무개씨", - content = "친구의 답이 보이는 자리 친구의 답이 보이는 자리친구의 답이 보이는 자리 친구의 답이 보이는 자리 친구의 답이 보이는 자리친구의 답이 보이는 자리친구의 답이 보이는 자리친구의 답이 보이는 자리 친구의 답이 보이는 자리 친구의 답이 보이는 자리 친구의 답이 보이는 자리", - createdAt = "2026-01-28T07:30:00Z" - ) - } - - val hasNext = page < 2 + val result = apiService.fetchTodayFriendReplies(page = page, size = size).data LoadResult.Page( - data = mockContent, - prevKey = if (page == 1) null else page - 1, - nextKey = if (hasNext) page + 1 else null + data = result.content.map { it.toDomain() }, + prevKey = if(result.page == 1) null else result.page - 1, + nextKey = if(result.hasNext) result.page + 1 else null ) - } catch (e: Exception) { LoadResult.Error(e) } diff --git a/app/src/main/java/com/egobook/app/domain/model/Friend.kt b/app/src/main/java/com/egobook/app/domain/model/Friend.kt deleted file mode 100644 index 442b63f9..00000000 --- a/app/src/main/java/com/egobook/app/domain/model/Friend.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.egobook.app.domain.model - -data class Friend( - val id: Long, - val name: String -) \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/domain/model/FriendList.kt b/app/src/main/java/com/egobook/app/domain/model/FriendList.kt new file mode 100644 index 00000000..3c8548c2 --- /dev/null +++ b/app/src/main/java/com/egobook/app/domain/model/FriendList.kt @@ -0,0 +1,12 @@ +package com.egobook.app.domain.model + +data class FriendList( + val count: Int, + val friends: List, +) + +data class Friend( + val id: Long, + val name: String, + val level: Long +) \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/domain/repository/FriendsRepository.kt b/app/src/main/java/com/egobook/app/domain/repository/FriendsRepository.kt index dbda0bde..8dd70f9b 100644 --- a/app/src/main/java/com/egobook/app/domain/repository/FriendsRepository.kt +++ b/app/src/main/java/com/egobook/app/domain/repository/FriendsRepository.kt @@ -1,12 +1,12 @@ package com.egobook.app.domain.repository -import com.egobook.app.domain.model.Friend +import com.egobook.app.domain.model.FriendList import com.egobook.app.domain.model.FriendRequest import com.egobook.app.domain.model.SearchUser interface FriendsRepository { suspend fun deleteFriend(deleteId: Long): Result - suspend fun fetchFriendList(): Result> + suspend fun fetchFriendList(): Result suspend fun fetchIncomingFriendRequestList(): Result> suspend fun fetchOutgoingFriendRequestList(): Result> suspend fun searchUser(keyword: String): Result> diff --git a/app/src/main/java/com/egobook/app/domain/usecase/GetFriendListUseCase.kt b/app/src/main/java/com/egobook/app/domain/usecase/GetFriendListUseCase.kt index fa2a3562..349ea118 100644 --- a/app/src/main/java/com/egobook/app/domain/usecase/GetFriendListUseCase.kt +++ b/app/src/main/java/com/egobook/app/domain/usecase/GetFriendListUseCase.kt @@ -1,11 +1,11 @@ package com.egobook.app.domain.usecase -import com.egobook.app.domain.model.Friend +import com.egobook.app.domain.model.FriendList import com.egobook.app.domain.repository.FriendsRepository import javax.inject.Inject class GetFriendListUseCase @Inject constructor( private val repository: FriendsRepository ) { - suspend operator fun invoke(): Result> = repository.fetchFriendList() + suspend operator fun invoke(): Result = repository.fetchFriendList() } \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/domain/usecase/GetUserIdUseCase.kt b/app/src/main/java/com/egobook/app/domain/usecase/GetUserIdUseCase.kt new file mode 100644 index 00000000..12169083 --- /dev/null +++ b/app/src/main/java/com/egobook/app/domain/usecase/GetUserIdUseCase.kt @@ -0,0 +1,10 @@ +package com.egobook.app.domain.usecase + +import com.egobook.app.domain.repository.account.AccountRepository +import javax.inject.Inject + +class GetUserIdUseCase @Inject constructor(private val repository: AccountRepository) { + suspend operator fun invoke(): Result { + return repository.getUserId() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/ui/counseling/adapter/CounselingWeeklyReportAdapter.kt b/app/src/main/java/com/egobook/app/ui/counseling/adapter/CounselingWeeklyReportAdapter.kt index 8f7b2933..803eeed4 100644 --- a/app/src/main/java/com/egobook/app/ui/counseling/adapter/CounselingWeeklyReportAdapter.kt +++ b/app/src/main/java/com/egobook/app/ui/counseling/adapter/CounselingWeeklyReportAdapter.kt @@ -1,5 +1,6 @@ package com.egobook.app.ui.counseling.adapter +import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.paging.PagingDataAdapter @@ -10,7 +11,7 @@ import com.egobook.app.R import com.egobook.app.databinding.ItemCounselingWeeklyReportBinding import com.egobook.app.ui.counseling.model.WeeklyReportModel -class CounselingWeeklyReportAdapter(private val onItemClick: (WeeklyReportModel) -> Unit) : PagingDataAdapter (diffUtil) { +class CounselingWeeklyReportAdapter(private val onItemClick: (WeeklyReportModel) -> Unit) : PagingDataAdapter(diffUtil) { inner class WeeklyReportViewHolder(private val binding: ItemCounselingWeeklyReportBinding) : RecyclerView.ViewHolder(binding.root) { @@ -36,6 +37,7 @@ class CounselingWeeklyReportAdapter(private val onItemClick: (WeeklyReportModel) override fun onBindViewHolder(holder: WeeklyReportViewHolder, position: Int) { val item = getItem(position) + Log.e("TTEST", "pagingData adapter: $item") if(item != null) (holder.bind(item)) } diff --git a/app/src/main/java/com/egobook/app/ui/counseling/view/EgoRoomWeeklyReportFragment.kt b/app/src/main/java/com/egobook/app/ui/counseling/view/EgoRoomWeeklyReportFragment.kt index fa2b67dc..74ace4e1 100644 --- a/app/src/main/java/com/egobook/app/ui/counseling/view/EgoRoomWeeklyReportFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/counseling/view/EgoRoomWeeklyReportFragment.kt @@ -6,7 +6,6 @@ import android.widget.Toast import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -17,18 +16,14 @@ import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import com.egobook.app.R import com.egobook.app.databinding.FragmentEgoRoomWeeklyReportBinding -import com.egobook.app.domain.model.NotificationType import com.egobook.app.domain.model.ReportStyle import com.egobook.app.ui.counseling.adapter.CounselingWeeklyReportAdapter import com.egobook.app.ui.counseling.model.DailyAndWeeklyNotificationModel -import com.egobook.app.ui.counseling.model.WeeklyReportStyleModel import com.egobook.app.ui.counseling.viewmodel.WeeklyReportViewModel -import com.egobook.app.ui.notification.model.NotificationModel import com.egobook.app.util.UiState import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import kotlin.getValue @AndroidEntryPoint class EgoRoomWeeklyReportFragment : Fragment(R.layout.fragment_ego_room_weekly_report) { diff --git a/app/src/main/java/com/egobook/app/ui/counseling/viewmodel/WeeklyReportViewModel.kt b/app/src/main/java/com/egobook/app/ui/counseling/viewmodel/WeeklyReportViewModel.kt index ccc8fb88..6d6d963b 100644 --- a/app/src/main/java/com/egobook/app/ui/counseling/viewmodel/WeeklyReportViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/counseling/viewmodel/WeeklyReportViewModel.kt @@ -136,16 +136,16 @@ class WeeklyReportViewModel @Inject constructor( } } - private val _userInfo = MutableStateFlow>(UiState.Idle) - val userInfo = _userInfo.asStateFlow() + private val _userInfo = MutableSharedFlow>() + val userInfo = _userInfo.asSharedFlow() fun getUserInfo() { viewModelScope.launch { - _userInfo.value = UiState.Loading + _userInfo.emit(UiState.Loading) getUserInfoUseCase().onSuccess { domain -> - _userInfo.value = UiState.Success(domain) + _userInfo.emit( UiState.Success(domain)) }.onFailure { error -> - _userInfo.value = UiState.Failure(error.message) + _userInfo.emit(UiState.Failure(error.message)) } } } diff --git a/app/src/main/java/com/egobook/app/ui/square/adapter/FriendPopupListAdapter.kt b/app/src/main/java/com/egobook/app/ui/square/adapter/FriendPopupListAdapter.kt index d15efb07..8770c75b 100644 --- a/app/src/main/java/com/egobook/app/ui/square/adapter/FriendPopupListAdapter.kt +++ b/app/src/main/java/com/egobook/app/ui/square/adapter/FriendPopupListAdapter.kt @@ -6,6 +6,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.egobook.app.databinding.ItemFriendPopupListBinding +import com.egobook.app.ui.square.model.friend.FriendListModel import com.egobook.app.ui.square.model.friend.FriendModel class FriendPopupListAdapter(private val onClicked: (FriendModel) -> Unit): ListAdapter(diffUtil) { diff --git a/app/src/main/java/com/egobook/app/ui/square/adapter/FriendsListAdapter.kt b/app/src/main/java/com/egobook/app/ui/square/adapter/FriendsListAdapter.kt index 0089b196..1f05eb81 100644 --- a/app/src/main/java/com/egobook/app/ui/square/adapter/FriendsListAdapter.kt +++ b/app/src/main/java/com/egobook/app/ui/square/adapter/FriendsListAdapter.kt @@ -6,6 +6,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.egobook.app.databinding.ItemSquareFriendListBinding +import com.egobook.app.ui.square.model.friend.FriendListModel import com.egobook.app.ui.square.model.friend.FriendModel class FriendsListAdapter(private val onDeleted: (FriendModel) -> Unit): ListAdapter(diffUtil) { @@ -28,6 +29,7 @@ class FriendsListAdapter(private val onDeleted: (FriendModel) -> Unit): ListAdap inner class FriendsListViewHolder(private val binding: ItemSquareFriendListBinding): RecyclerView.ViewHolder(binding.root) { fun bind(item: FriendModel) = with(binding) { tvItemFriendListName.text = item.name + tvItemFriendListLevel.text = "LV ${item.level}" ivItemSquareFriendListDelete.setOnClickListener { onDeleted(item) } } } diff --git a/app/src/main/java/com/egobook/app/ui/square/model/friend/FriendListModel.kt b/app/src/main/java/com/egobook/app/ui/square/model/friend/FriendListModel.kt new file mode 100644 index 00000000..59bf5081 --- /dev/null +++ b/app/src/main/java/com/egobook/app/ui/square/model/friend/FriendListModel.kt @@ -0,0 +1,25 @@ +package com.egobook.app.ui.square.model.friend + +import com.egobook.app.domain.model.Friend +import com.egobook.app.domain.model.FriendList + +data class FriendListModel( + val count: Int, + val friends: List, +) +data class FriendModel( + val id: Long, + val name: String, + val level: Long +) + +fun FriendList.toPresentation(): FriendListModel = FriendListModel( + count = count, + friends = friends.map { it.toPresentation() } +) + +fun Friend.toPresentation(): FriendModel = FriendModel( + id = id, + name = name, + level = level +) \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/ui/square/model/friend/FriendModel.kt b/app/src/main/java/com/egobook/app/ui/square/model/friend/FriendModel.kt deleted file mode 100644 index a1890d02..00000000 --- a/app/src/main/java/com/egobook/app/ui/square/model/friend/FriendModel.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.egobook.app.ui.square.model.friend - -import com.egobook.app.domain.model.Friend - -data class FriendModel( - val id: Long, - val name: String -) - -fun Friend.toPresentation(): FriendModel = FriendModel( - id = id, - name = name -) \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/ui/square/view/FriendAddDialog.kt b/app/src/main/java/com/egobook/app/ui/square/view/FriendAddDialog.kt index c524e999..5755bbc5 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/FriendAddDialog.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/FriendAddDialog.kt @@ -1,18 +1,22 @@ package com.egobook.app.ui.square.view import android.app.Dialog +import android.content.ClipData +import android.content.Context import android.graphics.Color import android.os.Bundle import android.view.View import android.widget.Toast +import android.content.ClipboardManager +import androidx.core.content.getSystemService import androidx.core.graphics.drawable.toDrawable -import com.egobook.app.R import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.bumptech.glide.Glide +import com.egobook.app.R import com.egobook.app.databinding.DialogSquareAddFriendBinding import com.egobook.app.removeScreenBlur import com.egobook.app.ui.square.model.friend.SearchUserModel @@ -35,10 +39,15 @@ class FriendAddDialog: DialogFragment(R.layout.dialog_square_add_friend) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = DialogSquareAddFriendBinding.bind(view) + fetchData() initListeners() initObservers() } + private fun fetchData() { + viewModel.getUserId() + } + private fun initListeners() = with(binding) { ivAddFriendBack.setOnClickListener { removeScreenBlur() @@ -46,11 +55,22 @@ class FriendAddDialog: DialogFragment(R.layout.dialog_square_add_friend) { } tvAddFriendSearch.setOnClickListener { val keyword = etAddFriendUserKeyword.text.toString() - viewModel.searchUser(keyword = keyword) + if(keyword.isEmpty()) { + Toast.makeText(context, "ID를 입력해주세요!", Toast.LENGTH_SHORT).show() + } else { + viewModel.searchUser(keyword = keyword) + } } btnAddFriendSearchResultApply.setOnClickListener { viewModel.requestFriendship(receiverId = receiverId ?: -1L) } + btnAddFriendCopyId.setOnClickListener { + val copyId = tvAddFriendAccountId.text.toString() + val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("User ID", copyId) + clipboard.setPrimaryClip(clip) + Toast.makeText(context, "계정 ID가 복사되었습니다.", Toast.LENGTH_SHORT).show() + } } private fun initObservers() = with(binding) { @@ -65,6 +85,7 @@ class FriendAddDialog: DialogFragment(R.layout.dialog_square_add_friend) { is UiState.Success -> { val searchUserResult = state.data if(searchUserResult == null) { + Toast.makeText(context, "검색 결과가 존재하지 않습니다!", Toast.LENGTH_SHORT).show() clAddFriendSearchResultFound.visibility = View.GONE tvAddFriendSearchResultEmpty.visibility = View.VISIBLE } else { @@ -94,6 +115,19 @@ class FriendAddDialog: DialogFragment(R.layout.dialog_square_add_friend) { } } } + launch { + viewModel.userId.collect { state -> + when (state) { + is UiState.Failure -> {} + UiState.Idle -> {} + UiState.Loading -> {} + is UiState.Success -> { + val userId = state.data + tvAddFriendAccountId.text = userId + } + } + } + } } } } diff --git a/app/src/main/java/com/egobook/app/ui/square/view/FriendDeleteDialog.kt b/app/src/main/java/com/egobook/app/ui/square/view/FriendDeleteDialog.kt index 65ad4550..0dd4407a 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/FriendDeleteDialog.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/FriendDeleteDialog.kt @@ -5,9 +5,9 @@ import android.graphics.Color import android.os.Bundle import android.view.View import androidx.core.graphics.drawable.toDrawable -import com.egobook.app.R import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels +import com.egobook.app.R import com.egobook.app.databinding.DialogDeleteFriendBinding import com.egobook.app.removeScreenBlur import com.egobook.app.ui.square.model.friend.FriendModel diff --git a/app/src/main/java/com/egobook/app/ui/square/view/FriendsFragment.kt b/app/src/main/java/com/egobook/app/ui/square/view/FriendsFragment.kt index 0b5fd87b..638e828a 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/FriendsFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/FriendsFragment.kt @@ -36,7 +36,9 @@ class FriendsFragment : Fragment(R.layout.fragment_square_friends) { } btnSquareFriendsAdd.setOnClickListener { applyScreenBlur(BlurLevel.BASE) - val dialog = FriendAddDialog() + val dialog = FriendAddDialog().apply { + isCancelable = false + } dialog.show(childFragmentManager, FriendAddDialog.TAG) } } diff --git a/app/src/main/java/com/egobook/app/ui/square/view/FriendsListFragment.kt b/app/src/main/java/com/egobook/app/ui/square/view/FriendsListFragment.kt index c3aa6b3d..9a1ae729 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/FriendsListFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/FriendsListFragment.kt @@ -3,6 +3,7 @@ package com.egobook.app.ui.square.view import android.os.Bundle import android.view.View import android.widget.Toast +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle @@ -14,7 +15,7 @@ import com.egobook.app.applyScreenBlur import com.egobook.app.databinding.FragmentFriendsListBinding import com.egobook.app.removeScreenBlur import com.egobook.app.ui.square.adapter.FriendsListAdapter -import com.egobook.app.ui.square.model.friend.FriendModel +import com.egobook.app.ui.square.model.friend.FriendListModel import com.egobook.app.ui.square.viewmodel.FriendsViewModel import com.egobook.app.util.UiState import kotlinx.coroutines.launch @@ -56,10 +57,17 @@ class FriendsListFragment : Fragment(R.layout.fragment_friends_list) { is UiState.Failure -> {} UiState.Idle -> {} UiState.Loading -> {} - is UiState.Success> -> { + is UiState.Success -> { val friendList = state.data - adapter.submitList(friendList) - tvFriendsValue.text = "${friendList.size}/${MAX_FRIEND_COUNT}명" + tvFriendsValue.text = "${friendList.count}/${MAX_FRIEND_COUNT}명" + if(friendList.count == 0) { + tvFriendsListPlaceholder.isVisible = true + rvFriendsList.isVisible = false + } else { + tvFriendsListPlaceholder.isVisible = false + rvFriendsList.isVisible = true + adapter.submitList(friendList.friends) + } } } } diff --git a/app/src/main/java/com/egobook/app/ui/square/view/FriendsPendingListFragment.kt b/app/src/main/java/com/egobook/app/ui/square/view/FriendsPendingListFragment.kt index 79bba607..6e13865f 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/FriendsPendingListFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/FriendsPendingListFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import androidx.fragment.app.Fragment import android.view.View import android.widget.Toast +import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -45,26 +46,32 @@ class FriendsPendingListFragment : Fragment(R.layout.fragment_friends_pending_li UiState.Loading -> {} is UiState.Success> -> { val friendRequestList = state.data - llFriendsPendingListReceived.removeAllViews() // 기존에 추가되어 있던 뷰들 모두 제거 - friendRequestList.forEach { friendRequest -> - val itemView = layoutInflater.inflate( - R.layout.item_square_friend_pending_received_list, - llFriendsPendingListReceived, - false - ).apply { - tag = friendRequest.requestId - } - val itemBinding = ItemSquareFriendPendingReceivedListBinding.bind(itemView) - itemBinding.tvItemFriendPendingListName.text = friendRequest.nickname - itemBinding.btnItemSquareFriendPendingListDeny.setOnClickListener { - viewModel.rejectFriendRequest(requestId = friendRequest.requestId) - } - itemBinding.btnItemSquareFriendPendingListAccept.setOnClickListener { - viewModel.acceptFriendRequest(requestId = friendRequest.requestId) + tvFriendsPendingListReceivedNum.text = friendRequestList.size.toString() + if(friendRequestList.isEmpty()) { + llFriendsPendingListReceived.removeAllViews() + tvFriendsPendingListReceivedEmpty.isVisible = true + } else { + tvFriendsPendingListReceivedEmpty.isVisible = false + llFriendsPendingListReceived.removeAllViews() // 기존에 추가되어 있던 뷰들 모두 제거 + friendRequestList.forEach { friendRequest -> + val itemView = layoutInflater.inflate( + R.layout.item_square_friend_pending_received_list, + llFriendsPendingListReceived, + false + ).apply { + tag = friendRequest.requestId + } + val itemBinding = ItemSquareFriendPendingReceivedListBinding.bind(itemView) + itemBinding.tvItemFriendPendingListName.text = friendRequest.nickname + itemBinding.btnItemSquareFriendPendingListDeny.setOnClickListener { + viewModel.rejectFriendRequest(requestId = friendRequest.requestId) + } + itemBinding.btnItemSquareFriendPendingListAccept.setOnClickListener { + viewModel.acceptFriendRequest(requestId = friendRequest.requestId) + } + llFriendsPendingListReceived.addView(itemView) } - llFriendsPendingListReceived.addView(itemView) } - tvFriendsPendingListReceivedNum.text = friendRequestList.size.toString() } } } @@ -77,21 +84,27 @@ class FriendsPendingListFragment : Fragment(R.layout.fragment_friends_pending_li UiState.Loading -> {} is UiState.Success> -> { val friendRequestList = state.data - llFriendsPendingListSent.removeAllViews() // 기존에 추가되어 있던 뷰들 모두 제거 - friendRequestList.forEach { friendRequest -> - val itemView = layoutInflater.inflate( - R.layout.item_square_friend_pending_sent_list, - llFriendsPendingListSent, - false - ).apply { - tag = friendRequest.requestId - } - val itemBinding = ItemSquareFriendPendingSentListBinding.bind(itemView) - itemBinding.tvItemFriendPendingSentListName.text = friendRequest.nickname - itemBinding.btnItemSquareFriendPendingSentListCancel.setOnClickListener { - viewModel.cancelFriendRequest(requestId = friendRequest.requestId) + if(friendRequestList.isEmpty()) { + llFriendsPendingListSent.removeAllViews() + tvFriendsPendingListSentEmpty.isVisible = true + } else { + tvFriendsPendingListSentEmpty.isVisible = false + llFriendsPendingListSent.removeAllViews() // 기존에 추가되어 있던 뷰들 모두 제거 + friendRequestList.forEach { friendRequest -> + val itemView = layoutInflater.inflate( + R.layout.item_square_friend_pending_sent_list, + llFriendsPendingListSent, + false + ).apply { + tag = friendRequest.requestId + } + val itemBinding = ItemSquareFriendPendingSentListBinding.bind(itemView) + itemBinding.tvItemFriendPendingSentListName.text = friendRequest.nickname + itemBinding.btnItemSquareFriendPendingSentListCancel.setOnClickListener { + viewModel.cancelFriendRequest(requestId = friendRequest.requestId) + } + llFriendsPendingListSent.addView(itemView) } - llFriendsPendingListSent.addView(itemView) } } } diff --git a/app/src/main/java/com/egobook/app/ui/square/view/LetterSendDialog.kt b/app/src/main/java/com/egobook/app/ui/square/view/LetterSendDialog.kt index 96b80b05..df383395 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/LetterSendDialog.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/LetterSendDialog.kt @@ -14,12 +14,12 @@ import com.egobook.app.BlurLevel import com.egobook.app.R import com.egobook.app.applyScreenBlur import com.egobook.app.databinding.DialogLetterSendBinding -import com.egobook.app.removeScreenBlur +import com.egobook.app.domain.model.square.letter.LetterBackgroundColor import com.egobook.app.domain.model.square.letter.LetterMode +import com.egobook.app.domain.model.square.letter.LetterStatus +import com.egobook.app.removeScreenBlur import com.egobook.app.ui.square.model.friend.FriendModel import com.egobook.app.ui.square.model.letter.AbusiveContentModel -import com.egobook.app.domain.model.square.letter.LetterBackgroundColor -import com.egobook.app.domain.model.square.letter.LetterStatus import com.egobook.app.ui.square.model.letter.SendLetterModel import com.egobook.app.ui.square.viewmodel.LetterViewModel import com.egobook.app.util.UiState diff --git a/app/src/main/java/com/egobook/app/ui/square/view/LetterWriteFragment.kt b/app/src/main/java/com/egobook/app/ui/square/view/LetterWriteFragment.kt index b9af78e9..8fb73212 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/LetterWriteFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/LetterWriteFragment.kt @@ -23,7 +23,7 @@ import com.egobook.app.databinding.FragmentLetterWriteBinding import com.egobook.app.databinding.LayoutPopupFriendListBinding import com.egobook.app.domain.model.square.letter.LetterMode import com.egobook.app.ui.square.adapter.FriendPopupListAdapter -import com.egobook.app.ui.square.model.friend.FriendModel +import com.egobook.app.ui.square.model.friend.FriendListModel import com.egobook.app.domain.model.square.letter.LetterBackgroundColor import com.egobook.app.ui.square.viewmodel.LetterViewModel import com.egobook.app.util.UiState @@ -39,7 +39,7 @@ class LetterWriteFragment : Fragment(R.layout.fragment_letter_write) { private val viewModel: LetterViewModel by activityViewModels() - private lateinit var friendList: List + private lateinit var friendList: FriendListModel private var letterColor: LetterBackgroundColor = LetterBackgroundColor.WHITE override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -153,7 +153,7 @@ class LetterWriteFragment : Fragment(R.layout.fragment_letter_write) { } }) } - (popupBinding.rvPopupFriendList.adapter as FriendPopupListAdapter).submitList(friendList) + (popupBinding.rvPopupFriendList.adapter as FriendPopupListAdapter).submitList(friendList.friends) popupWindow.showAsDropDown( anchorView, 0, @@ -169,9 +169,9 @@ class LetterWriteFragment : Fragment(R.layout.fragment_letter_write) { is UiState.Failure -> {} UiState.Idle -> {} UiState.Loading -> {} - is UiState.Success> -> { + is UiState.Success -> { val friendList = state.data - if(friendList.isEmpty()) { + if(friendList.friends.isEmpty()) { btnLetterSendFriend.isVisible = false val params = btnLetterSendAnonymous.layoutParams as ConstraintLayout.LayoutParams params.startToEnd = ConstraintLayout.LayoutParams.UNSET diff --git a/app/src/main/java/com/egobook/app/ui/square/view/PremiumLetterBuyDialog.kt b/app/src/main/java/com/egobook/app/ui/square/view/PremiumLetterBuyDialog.kt index 66b92b7c..6f0dbf95 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/PremiumLetterBuyDialog.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/PremiumLetterBuyDialog.kt @@ -4,15 +4,28 @@ import android.app.Dialog import android.graphics.Color import android.os.Bundle import android.view.View +import android.widget.Toast import androidx.core.graphics.drawable.toDrawable import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.egobook.app.R import com.egobook.app.databinding.DialogPremiumLetterBuyBinding +import com.egobook.app.domain.model.counseling.WeeklyReportUnlockType import com.egobook.app.domain.model.square.letter.LetterBackgroundColor import com.egobook.app.removeScreenBlur +import com.egobook.app.ui.counseling.view.WeeklyReportUnlockDialog.Companion.INK_PRICE +import com.egobook.app.ui.home.user.User +import com.egobook.app.ui.square.viewmodel.LetterViewModel +import com.egobook.app.util.UiState +import kotlinx.coroutines.launch -class PremiumLetterBuyDialog(private val letterColor: LetterBackgroundColor): DialogFragment(R.layout.dialog_premium_letter_buy) { +class PremiumLetterBuyDialog(private val letterColor: LetterBackgroundColor) : + DialogFragment(R.layout.dialog_premium_letter_buy) { private lateinit var binding: DialogPremiumLetterBuyBinding + private val viewModel: LetterViewModel by activityViewModels() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return Dialog(requireContext()).apply { @@ -25,22 +38,23 @@ class PremiumLetterBuyDialog(private val letterColor: LetterBackgroundColor): Di binding = DialogPremiumLetterBuyBinding.bind(view) initViews() initListeners() + initObservers() } private fun initViews() = with(binding) { - val backgroundColor = when(letterColor) { + val backgroundColor = when (letterColor) { LetterBackgroundColor.PINK -> R.color.letter_bg_pink LetterBackgroundColor.GREEN -> R.color.letter_bg_green LetterBackgroundColor.BLUE -> R.color.letter_bg_blue else -> R.color.letter_bg_purple } - val letterBanner = when(letterColor) { + val letterBanner = when (letterColor) { LetterBackgroundColor.PINK -> R.drawable.img_letter_pink LetterBackgroundColor.GREEN -> R.drawable.img_letter_green LetterBackgroundColor.BLUE -> R.drawable.img_letter_blue else -> R.drawable.img_letter_purple } - cvPremiumLetterColor.backgroundTintList = resources.getColorStateList(backgroundColor,null) + cvPremiumLetterColor.backgroundTintList = resources.getColorStateList(backgroundColor, null) ivPremiumLetterBanner.setImageResource(letterBanner) } @@ -50,7 +64,32 @@ class PremiumLetterBuyDialog(private val letterColor: LetterBackgroundColor): Di removeScreenBlur() } btnPremiumLetterBuy.setOnClickListener { - // TODO: 유저 정보 연동 필요 + viewModel.getUserInfo() + } + } + + private fun initObservers() { + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.userInfo.collect { state -> + when (state) { + is UiState.Failure -> {} + UiState.Idle -> {} + UiState.Loading -> {} + is UiState.Success -> { + val userInfo = state.data + val currentInk = userInfo.ink.value + if (currentInk >= INK_PRICE) { + Toast.makeText(context, "구매 가능한 상태지만, 서버 점검 중입니다!", Toast.LENGTH_SHORT).show() + // TODO: 편지 잉크 구매 API 연동하기 + } else { + Toast.makeText(context, "현재 잉크가 부족합니다!", Toast.LENGTH_SHORT).show() + } + } + } + } + + } } } diff --git a/app/src/main/java/com/egobook/app/ui/square/view/SquareAllRepliesFragment.kt b/app/src/main/java/com/egobook/app/ui/square/view/SquareAllRepliesFragment.kt index 4bcd3410..9839d70c 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/SquareAllRepliesFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/SquareAllRepliesFragment.kt @@ -2,6 +2,7 @@ package com.egobook.app.ui.square.view import android.os.Bundle import android.view.View +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle @@ -9,6 +10,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import androidx.paging.LoadState import com.egobook.app.BlurLevel import com.egobook.app.R import com.egobook.app.applyScreenBlur @@ -59,8 +61,18 @@ class SquareAllRepliesFragment : Fragment(R.layout.fragment_square_all_replies) private fun initObservers() = with(binding) { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.todayAllUserReplies.collectLatest { pagingData -> - if(pagingData != null) adapter.submitData(lifecycle, pagingData) + launch { + viewModel.todayAllUserReplies.collectLatest { pagingData -> + if(pagingData != null) adapter.submitData(lifecycle, pagingData) + } + } + launch { + adapter.loadStateFlow.collectLatest { loadStates -> + val isRefreshing = loadStates.refresh is LoadState.Loading + val isListEmpty = loadStates.refresh is LoadState.NotLoading && adapter.itemCount == 0 + tvSquareAllRepliesPlaceholder.isVisible = isListEmpty + rvSquareAllReplies.isVisible = !isListEmpty + } } } } diff --git a/app/src/main/java/com/egobook/app/ui/square/view/SquareFragment.kt b/app/src/main/java/com/egobook/app/ui/square/view/SquareFragment.kt index 7d9f442b..4f81fe21 100644 --- a/app/src/main/java/com/egobook/app/ui/square/view/SquareFragment.kt +++ b/app/src/main/java/com/egobook/app/ui/square/view/SquareFragment.kt @@ -14,6 +14,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController +import androidx.paging.LoadState import com.egobook.app.BlurLevel import com.egobook.app.R import com.egobook.app.applyScreenBlur @@ -255,6 +256,23 @@ class SquareFragment : Fragment(R.layout.fragment_square) { } } } + launch { + todayQuestionAdapter.loadStateFlow.collectLatest { loadStates -> + // 1. 현재 '새로고침(refresh)' 중인지 확인 + val isRefreshing = loadStates.refresh is LoadState.Loading + + // 2. '로딩 중이 아니면서' + '아이템이 0개'일 때만 진짜 비어있는 것으로 간주 + val isListEmpty = loadStates.refresh is LoadState.NotLoading && todayQuestionAdapter.itemCount == 0 + + // 로딩 중일 때는 플레이스홀더와 리사이클러뷰를 모두 숨기거나, + // 데이터가 없을 때만 플레이스홀더를 보여줍니다. + tvSquareTodayQuestionFriendReplyPlaceholderMain.isVisible = isListEmpty + tvSquareTodayQuestionFriendReplyPlaceholderSub.isVisible = isListEmpty + + // 데이터가 있고 로딩 중이 아닐 때만 리사이클러뷰를 보여줌 + rvSquareTodayQuestionFriendReply.isVisible = !isListEmpty && !isRefreshing + } + } launch { questionViewModel.updateTodayAnswerResult.collect { state -> when(state) { @@ -293,6 +311,14 @@ class SquareFragment : Fragment(R.layout.fragment_square) { } } } + launch { + sentLetterAdapter.loadStateFlow.collectLatest { loadStates -> + val isRefreshing = loadStates.refresh is LoadState.Loading + val isListEmpty = loadStates.refresh is LoadState.NotLoading && sentLetterAdapter.itemCount == 0 + tvSquareSentLetterPlaceholder.isVisible = isListEmpty + rvSquareSentLetter.visibility = if(isListEmpty) View.INVISIBLE else View.VISIBLE + } + } } } } diff --git a/app/src/main/java/com/egobook/app/ui/square/viewmodel/FriendsViewModel.kt b/app/src/main/java/com/egobook/app/ui/square/viewmodel/FriendsViewModel.kt index 15e7b974..c55e6f40 100644 --- a/app/src/main/java/com/egobook/app/ui/square/viewmodel/FriendsViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/square/viewmodel/FriendsViewModel.kt @@ -8,10 +8,11 @@ import com.egobook.app.domain.usecase.DeleteFriendUseCase import com.egobook.app.domain.usecase.GetFriendListUseCase import com.egobook.app.domain.usecase.GetIncomingFriendRequestsUseCase import com.egobook.app.domain.usecase.GetOutgoingFriendRequestsUseCase +import com.egobook.app.domain.usecase.GetUserIdUseCase import com.egobook.app.domain.usecase.RejectFriendRequestUseCase import com.egobook.app.domain.usecase.RequestFriendshipUseCase import com.egobook.app.domain.usecase.SearchUserUseCase -import com.egobook.app.ui.square.model.friend.FriendModel +import com.egobook.app.ui.square.model.friend.FriendListModel import com.egobook.app.ui.square.model.friend.FriendRequestModel import com.egobook.app.ui.square.model.friend.SearchUserModel import com.egobook.app.ui.square.model.friend.toPresentation @@ -34,16 +35,17 @@ class FriendsViewModel @Inject constructor( private val requestFriendshipUseCase: RequestFriendshipUseCase, private val rejectFriendRequestUseCase: RejectFriendRequestUseCase, private val acceptFriendRequestUseCase: AcceptFriendRequestUseCase, - private val cancelFriendRequestUseCase: CancelFriendRequestUseCase + private val cancelFriendRequestUseCase: CancelFriendRequestUseCase, + private val getUserIdUseCase: GetUserIdUseCase ): ViewModel() { - private val _friendList = MutableStateFlow>>(UiState.Idle) + private val _friendList = MutableStateFlow>(UiState.Idle) val friendList = _friendList.asStateFlow() fun fetchFriendList() { viewModelScope.launch { getFriendListUseCase().onSuccess { domainList -> - _friendList.value = UiState.Success(domainList.map { it.toPresentation() }) + _friendList.value = UiState.Success(domainList.toPresentation()) }.onFailure { error -> _friendList.value = UiState.Failure(error.message) } @@ -162,4 +164,18 @@ class FriendsViewModel @Inject constructor( } } + private val _userId = MutableStateFlow>(UiState.Idle) + val userId = _userId.asSharedFlow() + + fun getUserId() { + viewModelScope.launch { + _userId.emit(UiState.Loading) + getUserIdUseCase().onSuccess { userId -> + _userId.emit( UiState.Success(userId)) + }.onFailure { error -> + _userId.emit(UiState.Failure(error.message)) + } + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/egobook/app/ui/square/viewmodel/LetterViewModel.kt b/app/src/main/java/com/egobook/app/ui/square/viewmodel/LetterViewModel.kt index cfe7054f..88c9c14b 100644 --- a/app/src/main/java/com/egobook/app/ui/square/viewmodel/LetterViewModel.kt +++ b/app/src/main/java/com/egobook/app/ui/square/viewmodel/LetterViewModel.kt @@ -6,6 +6,7 @@ import androidx.paging.PagingData import androidx.paging.cachedIn import androidx.paging.map import com.egobook.app.domain.usecase.GetFriendListUseCase +import com.egobook.app.domain.usecase.GetUserInfoUseCase import com.egobook.app.domain.usecase.letter.DeferReplyLetterUseCase import com.egobook.app.domain.usecase.letter.DeleteLetterThreadUseCase import com.egobook.app.domain.usecase.letter.DetectAbusiveContentUseCase @@ -16,7 +17,8 @@ import com.egobook.app.domain.usecase.letter.GiveUpReplyLetterUseCase import com.egobook.app.domain.usecase.letter.ReplyLetterUseCase import com.egobook.app.domain.usecase.letter.ReportRepliedLetterUseCase import com.egobook.app.domain.usecase.letter.SendLetterUseCase -import com.egobook.app.ui.square.model.friend.FriendModel +import com.egobook.app.ui.home.user.User +import com.egobook.app.ui.square.model.friend.FriendListModel import com.egobook.app.ui.square.model.friend.toPresentation import com.egobook.app.ui.square.model.letter.AbusiveContentModel import com.egobook.app.ui.square.model.letter.ArrivedPendingLetterModel @@ -49,17 +51,18 @@ class LetterViewModel @Inject constructor( private val getSentLettersUseCase: GetSentLettersUseCase, private val getSentLetterWithReplyUseCase: GetSentLetterWithReplyUseCase, private val reportRepliedLetterUseCase: ReportRepliedLetterUseCase, - private val deleteLetterThreadUseCase: DeleteLetterThreadUseCase + private val deleteLetterThreadUseCase: DeleteLetterThreadUseCase, + private val getUserInfoUseCase: GetUserInfoUseCase ): ViewModel() { - private val _friendList = MutableStateFlow>>(UiState.Idle) + private val _friendList = MutableStateFlow>(UiState.Idle) val friendList = _friendList.asStateFlow() fun getFriendList() { viewModelScope.launch { _friendList.value = UiState.Loading getFriendListUseCase().onSuccess { domainList -> - _friendList.value = UiState.Success(domainList.map { it.toPresentation() }) + _friendList.value = UiState.Success(domainList.toPresentation()) }.onFailure { error -> _friendList.value = UiState.Failure(error.message) } @@ -202,4 +205,18 @@ class LetterViewModel @Inject constructor( } } } + + private val _userInfo = MutableSharedFlow>() + val userInfo = _userInfo.asSharedFlow() + + fun getUserInfo() { + viewModelScope.launch { + _userInfo.emit(UiState.Loading) + getUserInfoUseCase().onSuccess { domain -> + _userInfo.emit( UiState.Success(domain)) + }.onFailure { error -> + _userInfo.emit(UiState.Failure(error.message)) + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_detect_abusive_content_loading.xml b/app/src/main/res/layout/dialog_detect_abusive_content_loading.xml index 4a9c03cc..1721331c 100644 --- a/app/src/main/res/layout/dialog_detect_abusive_content_loading.xml +++ b/app/src/main/res/layout/dialog_detect_abusive_content_loading.xml @@ -4,7 +4,6 @@ android:layout_height="wrap_content" android:background="@drawable/bg_round_and_white_dialog" android:paddingVertical="24dp" - android:paddingHorizontal="16dp" xmlns:app="http://schemas.android.com/apk/res-auto"> diff --git a/app/src/main/res/layout/dialog_square_add_friend.xml b/app/src/main/res/layout/dialog_square_add_friend.xml index d840a1ac..ebe0ddd8 100644 --- a/app/src/main/res/layout/dialog_square_add_friend.xml +++ b/app/src/main/res/layout/dialog_square_add_friend.xml @@ -55,15 +55,17 @@ android:textSize="14sp" android:fontFamily="@font/arita_semibold"/> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_friends_pending_list.xml b/app/src/main/res/layout/fragment_friends_pending_list.xml index 68fe36af..657ebeda 100644 --- a/app/src/main/res/layout/fragment_friends_pending_list.xml +++ b/app/src/main/res/layout/fragment_friends_pending_list.xml @@ -29,7 +29,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" - android:text="3" + tools:text="3" android:textColor="@color/neutral_subtle" android:textSize="14sp" android:fontFamily="@font/arita_semibold" @@ -37,6 +37,23 @@ app:layout_constraintStart_toEndOf="@id/tv_friends_pending_list_received_title" app:layout_constraintTop_toTopOf="@id/tv_friends_pending_list_received_title" /> + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -288,6 +165,20 @@ android:src="@drawable/ic_square_view_all" /> + + 3 + + + + + + + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools">