diff --git a/KuringApp/KuringApp/ContentView.swift b/KuringApp/KuringApp/ContentView.swift index 20a01161..9ed75273 100644 --- a/KuringApp/KuringApp/ContentView.swift +++ b/KuringApp/KuringApp/ContentView.swift @@ -83,11 +83,6 @@ struct ContentView: View { } } .tint(Color.Kuring.gray600) - - /// 팝업 추가 - if PopupView.checkShowPopup() { - PopupView() - } } } } diff --git a/package-kuring/Package.swift b/package-kuring/Package.swift index 47043d82..90e320e1 100644 --- a/package-kuring/Package.swift +++ b/package-kuring/Package.swift @@ -12,6 +12,7 @@ let package = Package( /// ```swift /// import NoticeUI /// ``` + "BotUI", "NoticeUI", "SubscriptionUI", "DepartmentUI", @@ -47,6 +48,15 @@ let package = Package( ], targets: [ // MARK: App Library Dependencies + .target( + name: "BotUI", + dependencies: [ + "ColorSet", "BotFeatures", + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + ], + path: "Sources/UIKit/BotUI", + resources: [.process("Resources")] + ), .target( name: "NoticeUI", dependencies: [ @@ -138,6 +148,14 @@ let package = Package( ), // MARK: Features + .target( + name: "BotFeatures", + dependencies: [ + "Networks", + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + ], + path: "Sources/Features/BotFeatures" + ), .target( name: "NoticeFeatures", dependencies: [ diff --git a/package-kuring/Sources/Features/BotFeatures/BotFeature.swift b/package-kuring/Sources/Features/BotFeatures/BotFeature.swift new file mode 100644 index 00000000..8cc7f2cc --- /dev/null +++ b/package-kuring/Sources/Features/BotFeatures/BotFeature.swift @@ -0,0 +1,129 @@ +// +// File.swift +// +// +// Created by 최효원 on 8/5/24. +// + +import Foundation +import ComposableArchitecture + +@Reducer +public struct BotFeature { + @ObservableState + public struct State: Equatable { + public var chatInfo: ChatInfo = .init() + public var chatHistory: [ChatInfo] = [] + public var focus: Field? = .question + + public struct ChatInfo: Equatable { + public var limit: Int = 2 + public var text: String = "" + public var messageType: MessageType = .question + public var chatStatus: ChatStatus = .before + + public enum MessageType: String, Equatable { + case question + case answer + } + + public enum ChatStatus { + /// 질문 보내기 전 + case before + /// 질문 보낸 후 + case waiting + /// 답변 완료 + case complete + /// 답변 실패 + case failure + } + + public init( + limit: Int = 2, + text: String = "", + messageType: MessageType = .question, + chatStatus: ChatStatus = .before + ) { + self.limit = limit + self.text = text + self.messageType = messageType + self.chatStatus = chatStatus + } + } + + public enum Field { + case question + } + + public init( + chatInfo: ChatInfo = .init(), + chatHistory: [ChatInfo] = [], + focus: Field? = .question + ) { + self.chatInfo = chatInfo + self.chatHistory = chatHistory + self.focus = focus + } + } + + public enum Action: BindableAction, Equatable { + case sendMessage + case messageResponse(Result) + case updateQuestion(String) + case binding(BindingAction) + + public enum ChatError: Error, Equatable { + case serverError(Error) + + public static func == (lhs: ChatError, rhs: ChatError) -> Bool { + switch (lhs, rhs) { + case let (.serverError(lhsError), .serverError(rhsError)): + return lhsError.localizedDescription == rhsError.localizedDescription + } + } + } + } + + public var body: some ReducerOf { + BindingReducer() + + Reduce { state, action in + switch action { + case .binding: + return .none + + case .sendMessage: + guard !state.chatInfo.text.isEmpty else { return .none } + state.focus = nil + state.chatInfo.chatStatus = .waiting + return .run { [question = state.chatInfo.text] send in + do { + // let answer = try await chatService.sendQuestion(question) + /// 임시 응답 + await send(.messageResponse(.success(question))) + } catch { + await send(.messageResponse(.failure(.serverError(error)))) + } + } + + case let .messageResponse(.success(answer)): + state.chatInfo.text = answer + state.chatInfo.chatStatus = .complete + state.chatHistory.append(state.chatInfo) + state.chatInfo = .init() + return .none + + case let .messageResponse(.failure(error)): + state.chatInfo.chatStatus = .failure + print(error.localizedDescription) + return .none + + case let .updateQuestion(question): + state.chatInfo.text = question + return .none + } + } + } + + public init() { } +} diff --git a/package-kuring/Sources/UIKit/BotUI/BotView.swift b/package-kuring/Sources/UIKit/BotUI/BotView.swift new file mode 100644 index 00000000..fc2a205a --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/BotView.swift @@ -0,0 +1,177 @@ +// +// SwiftUIView.swift +// +// +// Created by 최효원 on 8/5/24. +// + +import SwiftUI +import ComposableArchitecture +import ColorSet +import BotFeatures + +public struct BotView: View { + @Bindable var store: StoreOf + @FocusState private var isInputFocused: Bool + @State private var isPopoverVisible = false + @State private var isSendPopupVisible = false + @State private var messageCountRemaining = 2 + @State private var chatMessages: [Message] = [] + + public var body: some View { + ZStack { + Color.Kuring.bg + .ignoresSafeArea() + .onTapGesture { + isInputFocused = false + } + + VStack(alignment: .center) { + headerView + chatView + inputView + infoText + } + .padding(.bottom, 16) + + if isSendPopupVisible { + sendPopup + .transition(.opacity) + .zIndex(1) + } + } + } + + private var headerView: some View { + HStack { + backButton + Spacer() + titleText + Spacer() + infoButton + } + .padding(.horizontal, 18) + } + + private var backButton: some View { + Button { + // 뒤로 가기 버튼 동작 구현 + } label: { + Image(systemName: "chevron.backward") + .padding() + .frame(width: 20, height: 11) + .foregroundStyle(Color.black) + } + } + + private var titleText: some View { + Text("쿠링봇") + .padding() + .font(.system(size: 18, weight: .semibold)) + } + + private var infoButton: some View { + Button { + isPopoverVisible.toggle() + } label: { + Image("icon_info_circle", bundle: Bundle.bots) + } + .popover(isPresented: $isPopoverVisible, arrowEdge: .top) { + popoverContent + } + } + + private var popoverContent: some View { + VStack(spacing: 10) { + Text("• 쿠링봇은 2024년 6월 이후의 공지\n 사항 내용을 기준으로 답변할 수 있\n 어요.") + Text("• 테스트 기간인 관계로 한 달에 2회\n 까지만 질문 가능해요.") + } + .lineSpacing(5) + .font(.system(size: 15, weight: .medium)) + .padding(20) + .presentationBackground(Color.Kuring.gray100) + .presentationCompactAdaptation(.popover) + } + + private var chatView: some View { + if !chatMessages.isEmpty { + AnyView(ChatView(messages: chatMessages)) + } else { + AnyView(ChatEmptyView()) + } + } + + private var inputView: some View { + HStack(alignment: .bottom, spacing: 12) { + TextField("질문을 입력해주세요", text: $store.chatInfo.question.limit(to: 300), axis: .vertical) + .lineLimit(5) + .focused($isInputFocused) + .padding(.horizontal) + .padding(.vertical, 12) + .overlay(RoundedRectangle(cornerRadius: 20).strokeBorder(Color.Kuring.gray200, style: StrokeStyle(lineWidth: 1.0))) + .disabled(messageCountRemaining == 0) + + sendButton + } + .padding(.horizontal, 20) + .disabled(messageCountRemaining == 0) + } + + private var sendButton: some View { + Button { + isInputFocused = false + isSendPopupVisible = true + } label: { + Image(systemName: "arrow.up.circle.fill") + .resizable() + .foregroundStyle(Color.Kuring.gray400) + .scaledToFit() + .frame(width: 40, height: 40) + } + } + + private var infoText: some View { + Text("쿠링봇은 실수를 할 수 있습니다. 중요한 정보를 확인하세요.") + .foregroundStyle(Color.Kuring.caption2) + .font(.system(size: 12, weight: .medium)) + .padding(.top, 8) + } + + private var sendPopup: some View { + SendPopup(isVisible: $isSendPopupVisible) { + if messageCountRemaining > 0 { + messageCountRemaining -= 1 + let userMessage = Message(text: store.chatInfo.question, type: .question, sendCount: messageCountRemaining) + let botResponse = Message(text: "자동 응답입니다.", type: .answer, sendCount: messageCountRemaining) + chatMessages.append(contentsOf: [userMessage, botResponse]) + store.chatInfo.question = "" + } + } + } + + public init(store: StoreOf) { + self.store = store + } +} + +/// 글자 수 max 판단 +extension Binding where Value == String { + func limit(to maxLength: Int) -> Self { + if self.wrappedValue.count > maxLength { + DispatchQueue.main.async { + self.wrappedValue = String(self.wrappedValue.prefix(maxLength)) + } + } + return self + } +} + + +#Preview { + BotView( + store: Store( + initialState: BotFeature.State(), + reducer: { BotFeature() } + ) + ) +} diff --git a/package-kuring/Sources/UIKit/BotUI/Bundle.swift b/package-kuring/Sources/UIKit/BotUI/Bundle.swift new file mode 100644 index 00000000..7c7f3df2 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Bundle.swift @@ -0,0 +1,12 @@ +// +// File.swift +// +// +// Created by 최효원 on 8/5/24. +// + +import Foundation + +extension Bundle { + public static var bots: Bundle { .module } +} diff --git a/package-kuring/Sources/UIKit/BotUI/ChatView.swift b/package-kuring/Sources/UIKit/BotUI/ChatView.swift new file mode 100644 index 00000000..a058a552 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/ChatView.swift @@ -0,0 +1,123 @@ +// +// SwiftUIView.swift +// +// +// Created by 최효원 on 8/6/24. +// + +import SwiftUI +import ColorSet +import ComposableArchitecture + +enum MessageType: Hashable { + case question + case answer + + var backgroundColor: Color { + switch self { + case .question: return Color.Kuring.gray100 + case .answer: return Color.Kuring.primarySelected + } + } + + var image: Image { + switch self { + case .question: + return Image(systemName: "person.circle.fill") + case .answer: + return Image("kuring_app_circle", bundle: Bundle.bots) + } + } +} + +struct ChatView: View { + var messages: [Message] + + var body: some View { + VStack(alignment: .center, spacing: 16) { + ForEach(messages, id: \.self) { message in + ChatRowView(message: message) + if message.type == .answer { + possibleCountText(for: message.sendCount) + } + } + Spacer() + } + .padding(.bottom, 5) + } + + private func possibleCountText(for sendCount: Int) -> some View { + 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 { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy.MM.dd" + return dateFormatter.string(from: Date()) + } +} + +struct ChatRowView: View { + var message: Message + + var body: some View { + HStack(alignment: .top) { + if message.type == .question { + Spacer() + messageBubble + userImage + } else { + userImage + messageBubble + Spacer() + } + } + .padding(message.type == .question ? .trailing : .leading, 16) + } + + private var messageBubble: some View { + let maxWidth = UIScreen.main.bounds.width * 0.7 + + return Text(message.text) + .padding() + .background(message.type.backgroundColor) + .cornerRadius(16) + .frame(maxWidth: maxWidth, alignment: message.type == .question ? .trailing : .leading) + .fixedSize(horizontal: false, vertical: true) + } + + private var userImage: some View { + message.type.image + .resizable() + .scaledToFill() + .frame(width: 36, height: 36) + .clipShape(Circle()) + .foregroundStyle(Color.Kuring.gray300, Color.Kuring.gray100) + .overlay(Circle().stroke(Color.Kuring.gray300, lineWidth: 0.1)) + } +} + +struct ChatEmptyView: View { + var body: some View { + VStack { + Spacer() + Image("kuring_app_gray", bundle: Bundle.bots) + Spacer().frame(height: 20) + Text("궁금한 건국대학교의\n공지 내용을 질문해보세요") + .foregroundStyle(Color.Kuring.caption1) + .font(.system(size: 15, weight: .medium)) + .multilineTextAlignment(.center) + .lineSpacing(5) + Spacer() + } + } +} + +struct Message: Hashable { + var text: String + var type: MessageType + var sendCount: Int +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/Contents.json b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/Contents.json b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/Contents.json b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/Contents.json new file mode 100644 index 00000000..89d044de --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "kuring.app.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "kuring.app2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "kuring.app3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app.png new file mode 100644 index 00000000..b5493826 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app2.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app2.png new file mode 100644 index 00000000..5a6283ae Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app2.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app3.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app3.png new file mode 100644 index 00000000..cc1e6e8d Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_circle.imageset/kuring.app3.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 1.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 1.png new file mode 100644 index 00000000..83147e86 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 1.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 2.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 2.png new file mode 100644 index 00000000..1798203f Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 2.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 3.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 3.png new file mode 100644 index 00000000..95d6d243 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Component 3.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Contents.json b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Contents.json new file mode 100644 index 00000000..7169c0a2 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/appIcon/kuring_app_gray.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Component 1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/Contents.json b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/Contents.json b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/Contents.json new file mode 100644 index 00000000..e4b02db6 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icon-info-circle-mono.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon-info-circle-mono2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon-info-circle-mono3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono.png new file mode 100644 index 00000000..d7b28333 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono2.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono2.png new file mode 100644 index 00000000..141ea95c Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono2.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono3.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono3.png new file mode 100644 index 00000000..e52373a9 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_info_circle.imageset/icon-info-circle-mono3.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Contents.json b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Contents.json new file mode 100644 index 00000000..e1477fb2 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Send.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Send2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Send3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send.png new file mode 100644 index 00000000..3c8973e6 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send2.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send2.png new file mode 100644 index 00000000..19201463 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send2.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send3.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send3.png new file mode 100644 index 00000000..b7af112a Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_send_white.imageset/Send3.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Contents.json b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Contents.json new file mode 100644 index 00000000..630995b7 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Frame 336.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 336-2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 336-3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336-2.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336-2.png new file mode 100644 index 00000000..44e81af7 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336-2.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336-3.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336-3.png new file mode 100644 index 00000000..527fcd41 Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336-3.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336.png b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336.png new file mode 100644 index 00000000..afd5ea9f Binary files /dev/null and b/package-kuring/Sources/UIKit/BotUI/Resources/Resources.xcassets/icon/icon_user.imageset/Frame 336.png differ diff --git a/package-kuring/Sources/UIKit/BotUI/SendPopup.swift b/package-kuring/Sources/UIKit/BotUI/SendPopup.swift new file mode 100644 index 00000000..bae4d190 --- /dev/null +++ b/package-kuring/Sources/UIKit/BotUI/SendPopup.swift @@ -0,0 +1,71 @@ +// +// Copyright (c) 2024 쿠링 +// See the 'License.txt' file for licensing information. +// + +import SwiftUI +import ComposableArchitecture +import ColorSet + +struct SendPopup: View { + @Binding var isVisible: Bool + var onSendAction: () -> Void + + var body: some View { + ZStack { + if isVisible { + Color.black.opacity(0.3) + .edgesIgnoringSafeArea(.all) + } + + VStack(spacing: 0) { + confirmationMessage + actionButtons + } + .background(Color.Kuring.bg) + .cornerRadius(15) + .padding(.horizontal, 45) + .frame(maxHeight: .infinity) + .font(.system(size: 16, weight: .medium)) + } + } + + private var confirmationMessage: some View { + Text("전송하시면 횟수 차감이 인정돼요.\n전송할까요?") + .foregroundStyle(Color.Kuring.body) + .multilineTextAlignment(.center) + .padding(40) + .padding(.bottom, 0) + } + + private var actionButtons: some View { + VStack(spacing: 0) { + Divider() + HStack(alignment: .center) { + cancelButton + Divider().padding(.horizontal, 40) + sendButton + } + .frame(height: 55) + } + } + + private var cancelButton: some View { + Button { + isVisible = false + } label: { + Text("취소하기") + .foregroundStyle(Color.Kuring.body) + } + } + + private var sendButton: some View { + Button { + isVisible = false + onSendAction() + } label: { + Text("전송하기") + .foregroundStyle(Color.Kuring.primary) + } + } +} diff --git a/package-kuring/Sources/UIKit/PopupUI/PopupView.swift b/package-kuring/Sources/UIKit/PopupUI/PopupView.swift deleted file mode 100644 index 861e15c1..00000000 --- a/package-kuring/Sources/UIKit/PopupUI/PopupView.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// SwiftUIView.swift -// -// -// Created by 최효원 on 8/1/24. -// - -import SwiftUI - -struct SwiftUIView: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) - } -} - -#Preview { - SwiftUIView() -}