From c99d02162383f48922772b30ab160eb20968075e Mon Sep 17 00:00:00 2001 From: znayeonzn Date: Fri, 23 Aug 2024 06:50:47 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0?= =?UTF-8?q?=EC=99=80=20=EC=B1=84=ED=8C=85=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/note/NoteRepository.kt | 4 +- .../view/community/CommunityDetailActivity.kt | 96 +++++++ .../presentation/view/note/NoteAdapter.kt | 4 +- .../view/note/NoteLiveChatActivity.kt | 250 +++++------------- 4 files changed, 171 insertions(+), 183 deletions(-) diff --git a/app/src/main/java/com/example/energy/data/repository/note/NoteRepository.kt b/app/src/main/java/com/example/energy/data/repository/note/NoteRepository.kt index fe27f52..4389e92 100644 --- a/app/src/main/java/com/example/energy/data/repository/note/NoteRepository.kt +++ b/app/src/main/java/com/example/energy/data/repository/note/NoteRepository.kt @@ -26,9 +26,11 @@ class NoteRepository { Log.d("NoteRepository", "메시지 전송 성공: ${response.body()?.message}") callback(response.body()) } else { - Log.e("NoteRepository", "메시지 전송 실패: ${response.errorBody()?.string()}") + Log.e("NoteRepository", "메시지 전송 실패: ${response.errorBody()?.string()}!") callback(null) } + + } override fun onFailure(call: Call, t: Throwable) { diff --git a/app/src/main/java/com/example/energy/presentation/view/community/CommunityDetailActivity.kt b/app/src/main/java/com/example/energy/presentation/view/community/CommunityDetailActivity.kt index b3d6374..4217036 100644 --- a/app/src/main/java/com/example/energy/presentation/view/community/CommunityDetailActivity.kt +++ b/app/src/main/java/com/example/energy/presentation/view/community/CommunityDetailActivity.kt @@ -3,6 +3,7 @@ package com.example.energy.presentation.view.community import android.annotation.SuppressLint import android.app.Dialog import android.content.Context +import android.content.Context.INPUT_METHOD_SERVICE import android.content.Intent import android.graphics.Color import android.graphics.drawable.ColorDrawable @@ -13,6 +14,8 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.InputMethodManager import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getSystemService import androidx.recyclerview.widget.LinearLayoutManager import com.example.energy.R import com.example.energy.data.repository.community.BoardModel @@ -21,6 +24,10 @@ import com.example.energy.data.repository.community.CommunityRepository import com.example.energy.data.repository.community.HelpStatusRequest import com.example.energy.data.repository.community.PostBoardRequest import com.example.energy.data.repository.community.WriteCommentRequest +import com.example.energy.data.repository.note.ChatThreadsResponse +import com.example.energy.data.repository.note.MessageResponse +import com.example.energy.data.repository.note.NoteRepository +import com.example.energy.data.repository.note.RecentMessage import com.example.energy.databinding.ActivityCommunityDetailBinding import com.example.energy.databinding.DialogCommunityBlockBinding import com.example.energy.databinding.DialogCommunityCommentWriterSeeMoreBinding @@ -29,7 +36,9 @@ import com.example.energy.databinding.DialogCommunityWriterSeeMoreBinding import com.example.energy.databinding.DialogHelpStatusBinding import com.example.energy.databinding.DialogLoadingBinding import com.example.energy.databinding.DialogPostCommunitySuccessBinding +import com.example.energy.presentation.view.note.NoteLiveChatActivity import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.internal.ViewUtils.hideKeyboard class CommunityDetailActivity : AppCompatActivity(){ private lateinit var binding: ActivityCommunityDetailBinding @@ -121,8 +130,45 @@ class CommunityDetailActivity : AppCompatActivity(){ binding.communityDetailBackIcon.setOnClickListener { finish() //현재 Activity 종료 } + + + + // 채팅하기 버튼 클릭 시 + binding.communityDetailChattingBtn.setOnClickListener { + NoteRepository.communityGetMessages(accessToken!!, writerId!!) { response -> + + if (response != null) { + Log.d("커뮤니티채팅버튼","채팅으로 이동 성공 ${writerId}") + + //if (response.result?.threadId == null) { + + //} + + val intent = Intent(this, NoteLiveChatActivity::class.java) + + intent.putExtra("Username", writerId) + intent.putExtra("Id","dfdkssf") + intent.putExtra("threadId", writerId) + intent.putExtra("receiverId", writerId) + + + //intent.putExtra("cursor", ) + + + + startActivity(intent) + + + + } + } + } } + + + + override fun onResume() { super.onResume() showLoading() //데이터 로딩 페이지 @@ -176,6 +222,8 @@ class CommunityDetailActivity : AppCompatActivity(){ var likeCount = response.likeNum writerStatus = response.helpStatus + + //binding.communityDetailUserProfile.setImageResource(postInfo.userProfile!!) binding.communityDetailUserName.text = response.memberName binding.communityDetailTitle.text = response.title @@ -199,6 +247,54 @@ class CommunityDetailActivity : AppCompatActivity(){ } } + + // 채팅하기 버튼 클릭 시 + binding.communityDetailChattingBtn.setOnClickListener { + NoteRepository.communityGetMessages(accessToken!!, writerId!!) { responseMessage -> + + if (responseMessage != null) { + Log.d("커뮤니티채팅버튼","채팅으로 이동 성공 ${writerId}") + + //if (response.result?.threadId == null) { + + //} + + val intent = Intent(this, NoteLiveChatActivity::class.java) + + intent.putExtra("Username", response.memberName) + intent.putExtra("Id",response.memberId) + intent.putExtra("threadId", responseMessage.result?.threadId) + intent.putExtra("receiverId", writerId) + intent.putExtra("unreadMessageCount", 0) + intent.putExtra("cursor", 0) + + + startActivity(intent) + + + + //if threadid = null값이면 그냥 맨 페이지 + + + + /* + + val intent = Intent(itemView.context, NoteLiveChatActivity::class.java) + + intent.putExtra("Username", note.name) + intent.putExtra("Id",note.nickname) + intent.putExtra("threadId", note.threadId) + intent.putExtra("receiverId", note.receiverId) + intent.putExtra("cursor", note.recentMessage?.messageId) + intent.putExtra("unreadMessageCount", note.unreadMessageCount) + ContextCompat.startActivity(itemView.context, intent, null) + + */ + } + } + } + + // 좋아요 아이콘 클릭 시 binding.communityDetailLikeIcon.setOnClickListener { isLiked = !isLiked diff --git a/app/src/main/java/com/example/energy/presentation/view/note/NoteAdapter.kt b/app/src/main/java/com/example/energy/presentation/view/note/NoteAdapter.kt index fe7cb6a..ca11c9b 100644 --- a/app/src/main/java/com/example/energy/presentation/view/note/NoteAdapter.kt +++ b/app/src/main/java/com/example/energy/presentation/view/note/NoteAdapter.kt @@ -93,7 +93,7 @@ class NoteAdapter( intent.putExtra("Id",note.nickname) intent.putExtra("threadId", note.threadId) intent.putExtra("receiverId", note.receiverId) - intent.putExtra("cursor", note.recentMessage?.messageId) + intent.putExtra("cursor", note.recentMessage?.messageId ?:0) intent.putExtra("unreadMessageCount", note.unreadMessageCount) ContextCompat.startActivity(itemView.context, intent, null) } @@ -109,7 +109,7 @@ class NoteAdapter( if (getClamped()) { // 스와이프 상태에서만 버튼 클릭이 유효 note.threadId?.let { threadId -> leaveChatRoom(threadId) } removeData(this.layoutPosition) - Toast.makeText(binding.root.context, "삭제했습니다.", Toast.LENGTH_SHORT).show() + Toast.makeText(binding.root.context, "채팅방 나가기", Toast.LENGTH_SHORT).show() onSwipeClickListener(note, layoutPosition) } } diff --git a/app/src/main/java/com/example/energy/presentation/view/note/NoteLiveChatActivity.kt b/app/src/main/java/com/example/energy/presentation/view/note/NoteLiveChatActivity.kt index e32ccb7..eda7dbb 100644 --- a/app/src/main/java/com/example/energy/presentation/view/note/NoteLiveChatActivity.kt +++ b/app/src/main/java/com/example/energy/presentation/view/note/NoteLiveChatActivity.kt @@ -16,39 +16,24 @@ import com.example.energy.R import com.example.energy.data.repository.note.GetDetailMessage import com.example.energy.data.repository.note.MessageRequest import com.example.energy.data.repository.note.NoteRepository -import com.example.energy.data.repository.note.NoteRepository.Companion.sendMessage -import com.example.energy.data.repository.note.NoteRepository.Companion.updateReadMessage import com.example.energy.databinding.ActivityNoteLiveChatBinding import java.text.SimpleDateFormat -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.time.format.DateTimeParseException import java.util.Date import java.util.Locale class NoteLiveChatActivity : AppCompatActivity() { - private lateinit var binding: ActivityNoteLiveChatBinding - - private var cursor: Int = 0 - - private var getunreadmessage: Int = 0 - + private var cursor: Int? = 0 + private var getUnreadMessage: Int = 0 private var lastDisplayedDate: String? = null - private var lastTimeTextView: TextView? = null - + private val allMessages = mutableListOf() // 모든 메시지를 저장할 리스트 @RequiresApi(Build.VERSION_CODES.O) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - // 토큰 가져오기 - val sharedPreferences = getSharedPreferences("userToken", Context.MODE_PRIVATE) - val accessToken = sharedPreferences?.getString("accessToken", "none") - binding = ActivityNoteLiveChatBinding.inflate(layoutInflater) setContentView(binding.root) @@ -57,38 +42,35 @@ class NoteLiveChatActivity : AppCompatActivity() { val receiverId = intent.getIntExtra("receiverId", -1) val username = intent.getStringExtra("Username") ?: "Unknown User" val userId = intent.getStringExtra("Id") ?: "Unknown ID" - getunreadmessage = intent.getIntExtra("unreadMessageCount", -1) + getUnreadMessage = intent.getIntExtra("unreadMessageCount", -1) cursor = intent.getIntExtra("cursor", 0) - if (getunreadmessage == 0) { - GetMessages(threadId, cursor+1, receiverId) + + // 메시지 로딩 + if (getUnreadMessage == 0) { + //val initialCursor = cursor ?: 0 // cursor가 null이면 0으로 설정 + GetMessages(threadId, cursor, receiverId) } else { - UpdateReadMessages(threadId, cursor+1, receiverId) + UpdateReadMessages(threadId, cursor, receiverId) } - - - - binding.usernameTextView.text = username binding.userIdTextView.text = userId - - //뒤로 가기 버튼 클릭 시 + // 뒤로 가기 버튼 클릭 시 binding.backbutton.setOnClickListener { finish() } - - //전송 버튼 클릭 시 - + // 전송 버튼 클릭 시 binding.sendButton.setOnClickListener { + val accessToken = getSharedPreferences("userToken", Context.MODE_PRIVATE) + .getString("accessToken", "none") sendMessage(accessToken, threadId, receiverId) } - - // 프로필로 전환하는 클릭 리스너 + // 프로필 전환 클릭 리스너 val clickListener = View.OnClickListener { val intent = Intent(this, NoteUserProfileActivity::class.java).apply { putExtra("Username", username) @@ -97,149 +79,106 @@ class NoteLiveChatActivity : AppCompatActivity() { startActivity(intent) } - - //프로필 전환 클릭 리스너 적용 + // 프로필 전환 클릭 리스너 적용 binding.usernameTextView.setOnClickListener(clickListener) binding.userIdTextView.setOnClickListener(clickListener) binding.UserProfile.setOnClickListener(clickListener) binding.showMoreButton.setOnClickListener(clickListener) - - - } - // 쪽지 목록 조회 - private fun GetMessages(threadId: Int, cursor: Int, receiverId: Int) - { - - // 토큰 가져오기 + // 쪽지 목록 조회 + private fun GetMessages(threadId: Int, cursor: Int?, receiverId: Int) { val sharedPreferences = getSharedPreferences("userToken", Context.MODE_PRIVATE) val accessToken = sharedPreferences?.getString("accessToken", "none") - //threadId = threadId +1 - NoteRepository.getMessages(accessToken!!, threadId, cursor , 100) { response -> - if (response.result?.messages != null) { - - - - // 메시지 리스트를 역순으로 displayChatMessages 함수로 전달하여 UI에 표시 - displayChatMessages(response.result.messages.reversed(), receiverId) - - + fun fetchMessages(currentCursor: Int?) { + NoteRepository.getMessages(accessToken!!, threadId, currentCursor, 10) { response -> + if (response.result?.messages != null) { + // 수신된 메시지를 리스트에 추가하고 역순으로 정렬하여 UI에 표시 + allMessages.addAll(response.result.messages) + displayChatMessages(allMessages.sortedBy { it.createdAt }, receiverId) + if (response.result.hasNext == true) { + fetchMessages(response.result.nextCursor) + } + } else { + Toast.makeText(this, "쪽지 목록을 불러오지 못했습니다.", Toast.LENGTH_SHORT).show() - } else { - Toast.makeText(this, "쪽지 목록을 불러오지 못했습니다.", Toast.LENGTH_SHORT).show() + } } - - } + fetchMessages(cursor) } // 안 읽은 메시지가 있을 때 - private fun UpdateReadMessages(threadId: Int, cursor: Int, receiverId: Int) - { - - // 토큰 가져오기 + private fun UpdateReadMessages(threadId: Int, cursor: Int?, receiverId: Int) { val sharedPreferences = getSharedPreferences("userToken", Context.MODE_PRIVATE) val accessToken = sharedPreferences?.getString("accessToken", "none") - //threadId = threadId +1 - NoteRepository.updateReadMessage(accessToken!!, threadId, cursor , 100) { response -> - if (response.result?.messages != null) { - - - - // 메시지 리스트를 역순으로 displayChatMessages 함수로 전달하여 UI에 표시 - displayChatMessages(response.result.messages.reversed(), receiverId) - - + fun fetchMessages(currentCursor: Int?) { + NoteRepository.getMessages(accessToken!!, threadId, currentCursor, 10) { response -> + if (response.result?.messages != null) { + // 수신된 메시지를 리스트에 추가하고 역순으로 정렬하여 UI에 표시 + allMessages.addAll(response.result.messages) + displayChatMessages(allMessages.sortedBy { it.createdAt }, receiverId) + if (response.result.hasNext == true) { + fetchMessages(response.result.nextCursor) + } + } else { + Toast.makeText(this, "쪽지 목록을 불러오지 못했습니다.", Toast.LENGTH_SHORT).show() - } else { - Toast.makeText(this, "쪽지 목록을 불러오지 못했습니다.", Toast.LENGTH_SHORT).show() + } } - - } + fetchMessages(cursor) } // 메시지를 UI에 표시하는 함수 private fun displayChatMessages(messages: List, receiverId: Int) { + binding.chatContainer.removeAllViews() // 이전 메시지 뷰를 제거 + messages.forEach { message -> val dateFormat = formatDate(message.createdAt ?: "") val timeFormat = formatTime(message.createdAt ?: "") - // receiverId 와 sender가 같을 경우 상대방이 보낸 쪽지 - var isUserMessage = true - if (receiverId == message.sender ) { - isUserMessage = false - } - - + val isUserMessage = receiverId != message.sender message.content?.let { addChatBubble(it, dateFormat, timeFormat, isUserMessage) } - - - } - - // 모든 메시지를 추가한 후 스크롤을 맨 아래로 이동 binding.chatScrollView.post { binding.chatScrollView.fullScroll(View.FOCUS_DOWN) } } - - - - // 메시지 전송 api - - private fun sendMessage(accessToken: String?, threadId: Int, receiverId: Int) { - - val message = binding.messageInput.text.toString().trim() + private fun sendMessage(accessToken: String?, threadId: Int?, receiverId: Int) { + val message = binding.messageInput.text.toString().trim() if (message.isEmpty()) { Toast.makeText(this, "메시지를 입력하세요", Toast.LENGTH_SHORT).show() return } - - if (receiverId == -1) { Toast.makeText(this, "수신자 Id가 유효하지 않습니다.", Toast.LENGTH_SHORT).show() return } - - val images = emptyList() - - val messageRequest = if (threadId > 0 ) { - MessageRequest(threadId, message, images, receiverId) - } else { - MessageRequest(null, message, images, receiverId) - } - - - + val messageRequest = MessageRequest(threadId, message, images, receiverId) NoteRepository.sendMessage(accessToken!!, messageRequest) { response -> if (response != null) { - // 서버 응답 전체를 로그로 출력하여 문제를 확인 Log.d("NoteLiveChatActivity", "서버 응답: ${response.result}") - - // 채팅 시간 - val createdAt = response.result.createdAt val dateFormat = formatDate(createdAt) val timeFormat = formatTime(createdAt) @@ -250,8 +189,9 @@ class NoteLiveChatActivity : AppCompatActivity() { binding.chatScrollView.fullScroll(View.FOCUS_DOWN) } - //cursor 값 최신 값으로 - cursor = response.result.messageId!! + cursor = response.result.messageId + + @@ -262,63 +202,35 @@ class NoteLiveChatActivity : AppCompatActivity() { } } - - - - - // 시간 형식 변환 함수 private fun formatDate(createdAt: String): String { - return try { - val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) - - // 나노초를 제거한 후 파싱 val date = simpleDateFormat.parse(createdAt.substring(0, 19)) ?: Date() - - // "년 월 일 " 형식으로 변환 val outputFormat = SimpleDateFormat("yyyy년 M월 d일", Locale.KOREAN) outputFormat.format(date) - } catch (e: Exception) { e.printStackTrace() createdAt } } - - // 시간 형식 변환 함수 private fun formatTime(createdAt: String): String { - return try { val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) val date = simpleDateFormat.parse(createdAt.substring(0, 19)) ?: Date() - val timeFormat = SimpleDateFormat("a h:mm", Locale.KOREAN) timeFormat.format(date) } catch (e: Exception) { e.printStackTrace() createdAt - } } - - - - - - private fun addChatBubble(message: String, date: String, time: String, isUserMessage: Boolean) { - - // 처음 보낼 때만 시간 출력 + // 처음 보낼 때만 날짜 출력 if (lastDisplayedDate != date) { - lastDisplayedDate = date - - - // 날짜를 표시하는 TextView 생성 val dateTextView = TextView(this).apply { text = date gravity = Gravity.CENTER @@ -326,69 +238,48 @@ class NoteLiveChatActivity : AppCompatActivity() { setPadding(0, 20, 0, 10) setTextColor(resources.getColor(R.color.gray_scale7, null)) } - - binding.chatContainer.addView(dateTextView) } - // 연속으로 채팅 전송할 경우 -> 마지막 시간 표시를 제거 lastTimeTextView?.let { binding.chatContainer.removeView(it) } - - - - // 채팅 내용 출력 - val chatBubble = TextView(this).apply { text = message - setBackgroundResource(if (isUserMessage) R.drawable.chat_bubble_background else R.drawable.chat_bubble_background_receiver) // 사용자 메시지와 상대방 메시지의 배경 구분 + setBackgroundResource(if (isUserMessage) R.drawable.chat_bubble_background else R.drawable.chat_bubble_background_receiver) setPadding(34, 22, 34, 22) setTextColor(resources.getColor(R.color.gray_scale8, null)) - - // 최대 너비 설정 (약 50자 정도의 너비) maxWidth = resources.displayMetrics.densityDpi * 250 / 160 - - - // 레이아웃 파라미터 설정 val layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT - ) + ).apply { - // 메시지 방향에 따라 정렬 및 여백 조정 - if (isUserMessage) { - layoutParams.gravity = Gravity.END // 오른쪽 정렬 - layoutParams.setMargins(50, 30, 8, 8) // 오른쪽에 여백을 둠 - } else { - layoutParams.gravity = Gravity.START // 왼쪽 정렬 - layoutParams.setMargins(8, 8, 50, 8) // 왼쪽에 여백을 둠 + // 사용자의 메시지와 상대방의 메시지를 구분하여 배치 + if (isUserMessage) { + gravity = Gravity.END // 오른쪽 정렬 + setMargins(50, 30, 8, 8) // 오른쪽 여백 + } else { + gravity = Gravity.START // 왼쪽 정렬 + setMargins(8, 30, 50, 8) // 왼쪽 여백 + } } - this.layoutParams = layoutParams } - // 시각 출력 + // 시간 출력 val timeTextView = TextView(this).apply { - //text = time - //gravity = Gravity.END + text = time setPadding(8, 4, 8, 8) setTextAppearance(R.style.Caption1) setTextColor(resources.getColor(R.color.gray_scale7, null)) - val layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT ).apply { - // 시각의 위치를 설정 - if (isUserMessage) { - text = time - gravity = Gravity.END // 오른쪽 끝에 정렬 - } else { - text = time - gravity = Gravity.START // 왼쪽 끝에 정렬 - } + // 메시지 방향에 따라 시간의 위치를 설정 + gravity = if (isUserMessage) Gravity.END else Gravity.START } this.layoutParams = layoutParams } @@ -400,5 +291,4 @@ class NoteLiveChatActivity : AppCompatActivity() { // 마지막으로 추가된 시간 표시를 추적 lastTimeTextView = timeTextView } - -} \ No newline at end of file +}