Skip to content

Commit

Permalink
[추가] 로딩 구현 (#213)
Browse files Browse the repository at this point in the history
  • Loading branch information
wonniiii committed Aug 14, 2024
1 parent 22eee35 commit 8f26ed1
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 70 deletions.
40 changes: 21 additions & 19 deletions package-kuring/Sources/Caches/Dependency/Bots.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ import SwiftData
import Dependencies
import Models
import SwiftUI
import Foundation

public struct BotDataBase {
public var fetch: @Sendable (FetchDescriptor<ChatInfo>) throws -> [ChatInfo]
public var add: @Sendable (ChatInfo) throws -> Void
public var update: @Sendable (ChatInfo) throws -> Void
public var delete: @Sendable (ChatInfo) throws -> Void

public enum BotError: Error {
case add
case update
case delete
}
}

Expand All @@ -28,21 +27,24 @@ extension BotDataBase: DependencyKey {
return try botContext.fetch(descriptor)
},
add: { model in
@Dependency(\.swiftData.context) var modelContext
let botContext = try modelContext()
botContext.insert(model)
try botContext.save()
},
update: { model in
@Dependency(\.swiftData.context) var modelContext
let botContext = try modelContext()
let modelID = model.index
/// index 일치 확인
if let existing = try botContext.fetch(FetchDescriptor<ChatInfo>(predicate: #Predicate<ChatInfo> { $0.index == modelID })).first {
existing.text = model.text
do {
@Dependency(\.swiftData.context) var modelContext
let botContext = try modelContext()
botContext.insert(model)
try botContext.save()
} else {
throw BotError.update
} catch {
throw BotError.add
}
},
delete: { model in
do {
@Dependency(\.swiftData.context) var modelContext
let botContext = try modelContext()

let modelToBeDelete = model
botContext.delete(modelToBeDelete)
} catch {
throw BotError.delete
}
}
)
Expand All @@ -54,13 +56,13 @@ extension BotDataBase: TestDependencyKey {
public static let testValue = Self(
fetch: unimplemented("\(Self.self).fetchDescriptor"),
add: unimplemented("\(Self.self).add"),
update: unimplemented("\(Self.self).update")
delete: unimplemented("\(Self.self).delete")
)

public static let noop = Self(
fetch: { _ in [] },
add: { _ in },
update: { _ in }
delete: { _ in }
)
}

Expand Down
41 changes: 23 additions & 18 deletions package-kuring/Sources/Features/BotFeatures/BotFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// See the 'License.txt' file for licensing information.
//

import Foundation
import ComposableArchitecture
import Networks
import SwiftData
Expand All @@ -18,15 +17,18 @@ public struct BotFeature {
public var chatInfo: ChatInfo = .init()
public var chatHistory: [ChatInfo] = []
public var focus: Field? = .question
public var isLoading: Bool = false

public init(
chatInfo: ChatInfo = .init(),
chatHistory: [ChatInfo] = [],
focus: Field? = .question
focus: Field? = .question,
isLoading: Bool = false
){
self.chatInfo = chatInfo
self.chatHistory = chatHistory
self.focus = focus
self.isLoading = isLoading
}

var fetchDescriptor: FetchDescriptor<ChatInfo> {
Expand All @@ -46,10 +48,10 @@ public struct BotFeature {
}

public enum Action: BindableAction, Equatable {
case binding(BindingAction<State>)
case sendMessage
case addQuestion(String)
case messageResponse(Result<String, ChatError>)
case binding(BindingAction<State>)
case addQuestion(String)
case queryChanged([ChatInfo])
case onAppear

Expand All @@ -76,8 +78,7 @@ public struct BotFeature {

case .sendMessage:
state.focus = nil
state.chatInfo.chatStatus = .waiting

state.isLoading = true
return .run { [question = state.chatInfo.text] send in
do {
SSEClient.shared.sessionStart(question: question)
Expand Down Expand Up @@ -107,34 +108,38 @@ public struct BotFeature {
}

case let .messageResponse(.success(message)):
state.isLoading = false
if let lastMessage = state.chatHistory.last, lastMessage.type == .answer {
state.chatHistory[state.chatHistory.count - 1].text += message
} else {
let newResponse = ChatInfo(
index: state.chatHistory.count + 1,
text: message,
type: .answer,
chatStatus: .complete
)
state.chatHistory.append(newResponse)
do { try context.add(newResponse) } catch {}
state.chatHistory[state.chatHistory.count - 1].chatStatus = .complete
}
return .none

case let .messageResponse(.failure(error)):
state.chatInfo.chatStatus = .failure
print(error.localizedDescription)
return .none

case let .addQuestion(question):
let newQuestion = ChatInfo(
index: state.chatHistory.count + 1,
index: state.chatHistory.count,
text: question,
type: .question,
chatStatus: .complete
)
do { try context.add(newQuestion) } catch {}

state.chatHistory.append(newQuestion)
do { try context.add(newQuestion) } catch {}

let newResponse = ChatInfo(
index: state.chatHistory.count,
text: "",
type: .answer,
chatStatus: .waiting
)

state.chatHistory.append(newResponse)
do { try context.add(newResponse) } catch {}

return .none

case .queryChanged(let newMessage):
Expand Down
9 changes: 5 additions & 4 deletions package-kuring/Sources/UIKit/BotUI/BotView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import SwiftUI
import ComposableArchitecture
import ColorSet
import Networks
import Models
import SwiftData
import Caches
import BotFeatures

public struct BotView: View {
Expand All @@ -18,6 +16,7 @@ public struct BotView: View {
@State private var isPopoverVisible = false
@State private var isSendPopupVisible = false
@State private var tempInputText: String = ""
@State private var isLoading = false

public var body: some View {
ZStack {
Expand Down Expand Up @@ -118,7 +117,7 @@ public struct BotView: View {
sendButton
}
.padding(.horizontal, 20)
.disabled(store.chatHistory.count == 4)
// .disabled(store.chatHistory.count >= 4)
}

private var sendButton: some View {
Expand All @@ -132,7 +131,7 @@ public struct BotView: View {
.scaledToFit()
.frame(width: 40, height: 40)
}
.disabled(store.chatHistory.count == 4)
// .disabled(store.chatHistory.count >= 4)
}

private var infoText: some View {
Expand All @@ -144,9 +143,11 @@ public struct BotView: View {

private var sendPopup: some View {
SendPopup(isVisible: $isSendPopupVisible) {
isLoading = true
store.send(.addQuestion(tempInputText))
tempInputText = ""
store.send(.sendMessage)
isLoading = false
}
}

Expand Down
57 changes: 28 additions & 29 deletions package-kuring/Sources/UIKit/BotUI/ChatView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import ComposableArchitecture
import Lottie
import BotFeatures
import Models
import Caches
import SwiftData

struct ChatView: View {
Expand All @@ -20,51 +19,51 @@ struct ChatView: View {
ScrollView {
VStack(alignment: .center, spacing: 16) {
ForEach(store.chatHistory) { chat in
HStack(alignment: .top) {
if chat.type == .question {
Spacer()
messageBubble(for: chat)
userImage(for: chat.type)
} else {
userImage(for: chat.type)
chatStatusView(for: chat)
Spacer()
}
}
.padding(chat.type == .question ? .trailing : .leading, 16)
chatRow(for: chat)
if chat.type == .answer {
possibleCountText(for: 2 - (chat.index / 2))
/// 질문 가능 횟수
possibleCountText(for: 2 - (chat.index / 2) + 1)
}
}
Spacer()
}
.padding(.bottom, 5)

}
/// query 변경 감지
.onChange(of: self.chatQuery, initial: true) {_, newValue in
.onChange(of: self.chatQuery, initial: true) { _, newValue in
store.send(.queryChanged(newValue))
}
}

@ViewBuilder
private func chatStatusView(for message: ChatInfo) -> some View {
switch message.chatStatus {
case .waiting:
lottieView
case .complete:
messageBubble(for: message)
default:
private func chatRow(for chat: ChatInfo) -> some View {
HStack(alignment: .top) {
if chat.type == .question {
Spacer()
messageBubble(for: chat)
userImage(for: chat.type)
} else {
userImage(for: chat.type)
responseContentView(for: chat)
Spacer()
}
}
.padding(chat.type == .question ? .trailing : .leading, 16)
}

@ViewBuilder
private func responseContentView(for chat: ChatInfo) -> some View {
if chat.index == store.chatHistory.count - 1, store.state.isLoading {
lottieView
} else {
messageBubble(for: chat)
}
}

private var lottieView: some View {
LottieView(animation: .named("animation_loading.json", bundle: Bundle.bots))
.playing()
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
.frame(width: 70, alignment: .leading)
}

private func messageBubble(for message: ChatInfo) -> some View {
Expand Down Expand Up @@ -93,13 +92,13 @@ struct ChatView: View {
}

private func possibleCountText(for sendCount: Int) -> some View {
let currentDate = formattedCurrentDate()
let currentDate = formattedCurrentDate
return Text("질문 가능 횟수 \(sendCount)회 (\(currentDate) 기준)")
.font(.system(size: 12, weight: .medium))
.foregroundStyle(sendCount == 0 ? Color.Kuring.warning : Color.Kuring.caption1)
}

private func formattedCurrentDate() -> String {
private var formattedCurrentDate: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy.MM.dd"
return dateFormatter.string(from: Date())
Expand Down

0 comments on commit 8f26ed1

Please sign in to comment.