203 lines
6.9 KiB
Swift
203 lines
6.9 KiB
Swift
import SwiftUI
|
|
import UIKit
|
|
|
|
struct UserCreatorChatRoomView: View {
|
|
@StateObject private var viewModel = UserCreatorChatRoomViewModel()
|
|
@State private var messageText = ""
|
|
|
|
private let roomId: Int?
|
|
private let creatorId: Int?
|
|
|
|
init(roomId: Int) {
|
|
self.roomId = roomId
|
|
self.creatorId = nil
|
|
}
|
|
|
|
init(creatorId: Int) {
|
|
self.roomId = nil
|
|
self.creatorId = creatorId
|
|
}
|
|
|
|
var body: some View {
|
|
BaseView(isLoading: $viewModel.isLoading) {
|
|
VStack(spacing: 0) {
|
|
headerView
|
|
|
|
messageListView
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
|
inputBarView
|
|
}
|
|
.background(Color.black.ignoresSafeArea())
|
|
}
|
|
.onAppear(perform: enterRoom)
|
|
.onDisappear { viewModel.leaveAndClose() }
|
|
.sodaToast(
|
|
isPresented: $viewModel.isShowPopup,
|
|
message: viewModel.errorMessage,
|
|
autohideIn: 2
|
|
)
|
|
}
|
|
|
|
private var headerView: some View {
|
|
HStack(spacing: SodaSpacing.s12) {
|
|
Image("ic_back")
|
|
.resizable()
|
|
.frame(width: 24, height: 24)
|
|
.onTapGesture { AppState.shared.back() }
|
|
|
|
DownsampledKFImage(
|
|
url: URL(string: viewModel.opponentProfileImageUrl ?? ""),
|
|
size: CGSize(width: 36, height: 36)
|
|
)
|
|
.clipShape(Circle())
|
|
|
|
Text(viewModel.opponentNickname)
|
|
.appFont(size: 14, weight: .bold)
|
|
.foregroundColor(.white)
|
|
.lineLimit(1)
|
|
.truncationMode(.tail)
|
|
|
|
Spacer(minLength: 0)
|
|
}
|
|
.padding(.horizontal, SodaSpacing.s16)
|
|
.padding(.vertical, SodaSpacing.s8)
|
|
.frame(width: screenSize().width, height: 60)
|
|
.background(Color.black)
|
|
}
|
|
|
|
private var messageListView: some View {
|
|
GeometryReader { geometry in
|
|
ScrollViewReader { proxy in
|
|
ScrollView(.vertical, showsIndicators: false) {
|
|
LazyVStack(spacing: SodaSpacing.s16) {
|
|
if viewModel.isLoadingNextPage {
|
|
ProgressView()
|
|
.progressViewStyle(.circular)
|
|
.tint(Color.soda400)
|
|
.padding(.vertical, SodaSpacing.s12)
|
|
}
|
|
|
|
ForEach(textMessages) { message in
|
|
UserCreatorChatTextMessageItemView(message: message)
|
|
.id(message.id)
|
|
.onAppear {
|
|
if message.id == textMessages.first?.id {
|
|
viewModel.loadMore()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, SodaSpacing.s24)
|
|
.padding(.vertical, SodaSpacing.s12)
|
|
.background(
|
|
GeometryReader { contentGeometry in
|
|
Color.clear.preference(
|
|
key: UserCreatorChatContentHeightPreferenceKey.self,
|
|
value: contentGeometry.size.height
|
|
)
|
|
}
|
|
)
|
|
.frame(minHeight: geometry.size.height, alignment: .bottom)
|
|
}
|
|
.onPreferenceChange(UserCreatorChatContentHeightPreferenceKey.self) { contentHeight in
|
|
guard contentHeight <= geometry.size.height else { return }
|
|
viewModel.loadMore()
|
|
}
|
|
.onChange(of: textMessages.last?.id) { _ in
|
|
scrollToBottom(proxy)
|
|
}
|
|
.onChange(of: viewModel.socketState) { state in
|
|
guard state == .joined else { return }
|
|
scrollToBottom(proxy)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var inputBarView: some View {
|
|
HStack(spacing: SodaSpacing.s8) {
|
|
ZStack(alignment: .leading) {
|
|
if messageText.isEmpty {
|
|
Text(I18n.UserCreatorChat.messagePlaceholder)
|
|
.appFont(size: 14, weight: .regular)
|
|
.foregroundColor(Color.gray500)
|
|
}
|
|
|
|
TextField("", text: $messageText)
|
|
.appFont(size: 14, weight: .regular)
|
|
.foregroundColor(.white)
|
|
.accentColor(Color.soda400)
|
|
.disabled(isInputEnabled == false)
|
|
.onSubmit { sendText() }
|
|
}
|
|
.padding(.horizontal, SodaSpacing.s16)
|
|
.padding(.vertical, SodaSpacing.s12)
|
|
.background(Color.gray900)
|
|
.cornerRadius(999)
|
|
|
|
Button(action: sendText) {
|
|
Image("ic_message_send")
|
|
.resizable()
|
|
.frame(width: 24, height: 24)
|
|
.opacity(isSendEnabled ? 1 : 0.4)
|
|
}
|
|
.buttonStyle(.plain)
|
|
.disabled(isSendEnabled == false)
|
|
}
|
|
.padding(.horizontal, SodaSpacing.s12)
|
|
.padding(.vertical, SodaSpacing.s12)
|
|
.frame(width: screenSize().width)
|
|
.background(Color.black)
|
|
}
|
|
|
|
private var textMessages: [UserCreatorChatDisplayMessage] {
|
|
viewModel.messages.filter { $0.messageType == "TEXT" }
|
|
}
|
|
|
|
private var isInputEnabled: Bool {
|
|
viewModel.socketState == .joined
|
|
}
|
|
|
|
private var isSendEnabled: Bool {
|
|
isInputEnabled && messageText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false
|
|
}
|
|
|
|
private func enterRoom() {
|
|
if let roomId {
|
|
viewModel.enter(roomId: roomId)
|
|
} else if let creatorId {
|
|
viewModel.enter(creatorId: creatorId)
|
|
}
|
|
}
|
|
|
|
private func sendText() {
|
|
let trimmedText = messageText.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard isInputEnabled, trimmedText.isEmpty == false else { return }
|
|
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
|
viewModel.sendText(trimmedText)
|
|
messageText = ""
|
|
}
|
|
|
|
private func scrollToBottom(_ proxy: ScrollViewProxy) {
|
|
guard let lastMessage = textMessages.last else { return }
|
|
withAnimation(.easeOut(duration: 0.3)) {
|
|
proxy.scrollTo(lastMessage.id, anchor: .bottom)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct UserCreatorChatContentHeightPreferenceKey: PreferenceKey {
|
|
static var defaultValue: CGFloat = 0
|
|
|
|
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
value = nextValue()
|
|
}
|
|
}
|
|
|
|
struct UserCreatorChatRoomView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
UserCreatorChatRoomView(roomId: 1)
|
|
}
|
|
}
|