diff --git a/iOS/RollTheDice/.DS_Store b/iOS/RollTheDice/.DS_Store index 0ed7ed28..3e52fcdb 100644 Binary files a/iOS/RollTheDice/.DS_Store and b/iOS/RollTheDice/.DS_Store differ diff --git a/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj b/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj index 64081947..9d4f2cfb 100644 --- a/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj +++ b/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj @@ -8,6 +8,9 @@ /* Begin PBXBuildFile section */ 3509091A2C1C1248007D76A1 /* TokenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 350909192C1C1248007D76A1 /* TokenManager.swift */; }; + 3544D7402C228EFC007DBD18 /* CreateDebateRoomService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3544D73F2C228EFC007DBD18 /* CreateDebateRoomService.swift */; }; + 3544D7432C228F54007DBD18 /* CreateDebateRoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3544D7422C228F54007DBD18 /* CreateDebateRoomViewModel.swift */; }; + 3544D7462C229436007DBD18 /* ChatService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3544D7452C229436007DBD18 /* ChatService.swift */; }; 357666102BBD4BF6002C226A /* ReportListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3576660F2BBD4BF6002C226A /* ReportListView.swift */; }; 357666132BBD54AA002C226A /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 357666122BBD54AA002C226A /* SplashView.swift */; }; 3576993A2C09C1EB00AD2DA4 /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 357699392C09C1EB00AD2DA4 /* KakaoSDK */; }; @@ -103,6 +106,9 @@ /* Begin PBXFileReference section */ 350909192C1C1248007D76A1 /* TokenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenManager.swift; sourceTree = ""; }; + 3544D73F2C228EFC007DBD18 /* CreateDebateRoomService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateDebateRoomService.swift; sourceTree = ""; }; + 3544D7422C228F54007DBD18 /* CreateDebateRoomViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateDebateRoomViewModel.swift; sourceTree = ""; }; + 3544D7452C229436007DBD18 /* ChatService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatService.swift; sourceTree = ""; }; 3576660F2BBD4BF6002C226A /* ReportListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportListView.swift; sourceTree = ""; }; 357666122BBD54AA002C226A /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = ""; }; 357699432C09C7B900AD2DA4 /* LoginService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginService.swift; sourceTree = ""; }; @@ -210,6 +216,30 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3544D73E2C228EDB007DBD18 /* Service */ = { + isa = PBXGroup; + children = ( + 3544D73F2C228EFC007DBD18 /* CreateDebateRoomService.swift */, + ); + path = Service; + sourceTree = ""; + }; + 3544D7412C228F39007DBD18 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 3544D7422C228F54007DBD18 /* CreateDebateRoomViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + 3544D7442C229428007DBD18 /* Service */ = { + isa = PBXGroup; + children = ( + 3544D7452C229436007DBD18 /* ChatService.swift */, + ); + path = Service; + sourceTree = ""; + }; 357666112BBD5494002C226A /* Splah */ = { isa = PBXGroup; children = ( @@ -249,6 +279,8 @@ 6C41B8D62BE1048500274FA4 /* ChatList */ = { isa = PBXGroup; children = ( + 3544D7412C228F39007DBD18 /* ViewModel */, + 3544D73E2C228EDB007DBD18 /* Service */, 6C41B8D92BE104A800274FA4 /* RecentNewsCardView.swift */, 6C41B8DB2BE1095800274FA4 /* ChatListView.swift */, ); @@ -510,6 +542,7 @@ 6CDB29F72BAA06FB0081037B /* ChatGPT */ = { isa = PBXGroup; children = ( + 3544D7442C229428007DBD18 /* Service */, 6CDB29F82BAA07350081037B /* GPTChat.swift */, 6CDB29FA2BAA07B10081037B /* GPTChatViewModel.swift */, 6CDB29FC2BAA07FD0081037B /* GPTChatView.swift */, @@ -738,6 +771,7 @@ 6C454A882B9DB6C2006FD9D0 /* CustomNavigationBar.swift in Sources */, 6CF130BF2BAB783300A437B6 /* APIConstants.swift in Sources */, 357699442C09C7B900AD2DA4 /* LoginService.swift in Sources */, + 3544D7462C229436007DBD18 /* ChatService.swift in Sources */, 6C76513C2BF37ED300196536 /* Extension+Log.swift in Sources */, 6CE103102BD56A5B00498AA4 /* TypeReportViewModel.swift in Sources */, 6C5B0C8A2C1C308A00A0D5F4 /* ScoopAPIBookmarks.swift in Sources */, @@ -750,6 +784,7 @@ 6C7651492BF5FDB900196536 /* WebView.swift in Sources */, 6CDB29FF2BAA08280081037B /* GPTChatListViewModel.swift in Sources */, 6CF130AD2BAB0C4400A437B6 /* AuthenticationViewModel.swift in Sources */, + 3544D7402C228EFC007DBD18 /* CreateDebateRoomService.swift in Sources */, 6CDB29F92BAA07350081037B /* GPTChat.swift in Sources */, 6C41B8DC2BE1095800274FA4 /* ChatListView.swift in Sources */, 6CDB29FD2BAA07FD0081037B /* GPTChatView.swift in Sources */, @@ -763,6 +798,7 @@ 6C76513E2BF37F1E00196536 /* Extension+OSLog.swift in Sources */, 6CC6737C2C21BA5B009FB30E /* Auth.swift in Sources */, 357FC6EA2BCE866B00AD8915 /* DetailCardNews.swift in Sources */, + 3544D7432C228F54007DBD18 /* CreateDebateRoomViewModel.swift in Sources */, 6CC4DDC92B5574670080E7E8 /* ContentView.swift in Sources */, 6C3237A52B7C37D100B699AB /* BookmarkView.swift in Sources */, 6C3237AE2B7C382E00B699AB /* DetailNewsViewModel.swift in Sources */, @@ -932,10 +968,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"RollTheDice/Preview Content\""; - DEVELOPMENT_TEAM = 4YH4UGRTMH; + DEVELOPMENT_TEAM = NTXM48C3F8; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = RollTheDice/Resources/Support/Info.plist; @@ -951,6 +988,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = jinjihan.RollTheDice; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -962,10 +1000,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"RollTheDice/Preview Content\""; - DEVELOPMENT_TEAM = 4YH4UGRTMH; + DEVELOPMENT_TEAM = NTXM48C3F8; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = RollTheDice/Resources/Support/Info.plist; @@ -981,6 +1020,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = jinjihan.RollTheDice; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift b/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift index 17bfa543..3ea1b27f 100644 --- a/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift +++ b/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift @@ -66,6 +66,8 @@ struct RollTheDiceApp: App { DebateSummaryView() case .webView(let url): WebView(urlToLoad: url) + case .createdebateroom: + GPTChatView() } }) } diff --git a/iOS/RollTheDice/RollTheDice/Source/Model/Path/PathType.swift b/iOS/RollTheDice/RollTheDice/Source/Model/Path/PathType.swift index 30f880a2..806b0279 100644 --- a/iOS/RollTheDice/RollTheDice/Source/Model/Path/PathType.swift +++ b/iOS/RollTheDice/RollTheDice/Source/Model/Path/PathType.swift @@ -20,4 +20,6 @@ enum PathType: Hashable { case mypageView // 마이페이지뷰 case webView(url: String) + + case createdebateroom //토론방 생성 } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/GPTChatListViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/GPTChatListViewModel.swift index db009df2..7d94565e 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/GPTChatListViewModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/GPTChatListViewModel.swift @@ -7,20 +7,29 @@ import Foundation import OpenAI +import Combine +import Moya class GPTChatListViewModel: ObservableObject { @Published var chatList: [GPTChat] + private var cancellables = Set() + private let provider = MoyaProvider() - // TODO: Token Hidden - /// token 추가해야 함 - let openAI = OpenAI(apiToken: "") + // OpenAI API 토큰 관리 + private let openAI: OpenAI + +// // TODO: Token Hidden +// /// token 추가해야 함 +// let openAI = OpenAI(apiToken: "") init( chatList: [GPTChat] = [ - .init(title: "111111111", messages: []), - ] + .init(title: "토론제목", messages: []), + ], + apiToken: String = "" // TokenManager를 통해 API 토큰을 관리 ) { self.chatList = chatList + self.openAI = OpenAI(apiToken: apiToken) } } @@ -38,12 +47,40 @@ extension GPTChatListViewModel { func getBotReply(index: Int) { print("call getBotReply func") + let messages = self.chatList[index].messages.map { + Chat(role: $0.isUser ? .user : .system, content: $0.content) + } + + // openAI.chats( + // query: .init( + // model: .gpt3_5Turbo, + // messages: self.chatList[index].messages.map( + // {Chat(role: .user, content: $0.content)})) + // ) { result in + // switch result { + // case .success(let success): + // guard let choice = success.choices.first else { + // return + // } + // let message = choice.message.content + // + // DispatchQueue.main.async { + // self.chatList[index].messages.append(.init(content: message ?? "Error", isUser: false)) + // print("gpt msg: \(self.chatList[index].messages[1])") + // } + // + // case .failure(let failure): + // print(failure) + // } + // } openAI.chats( query: .init( model: .gpt3_5Turbo, - messages: self.chatList[index].messages.map( - {Chat(role: .user, content: $0.content)})) - ) { result in + messages: messages + ) + ) { [weak self] result in + guard let self = self else { return } + switch result { case .success(let success): guard let choice = success.choices.first else { @@ -53,7 +90,7 @@ extension GPTChatListViewModel { DispatchQueue.main.async { self.chatList[index].messages.append(.init(content: message ?? "Error", isUser: false)) - print("gpt msg: \(self.chatList[index].messages[1])") + print("gpt msg: \(self.chatList[index].messages.last?.content ?? "Error")") } case .failure(let failure): diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/GPTChatView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/GPTChatView.swift index 4fd938b0..b2ecf127 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/GPTChatView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/GPTChatView.swift @@ -11,7 +11,7 @@ struct GPTChatView: View { @EnvironmentObject var pathModel: PathModel @StateObject var chatListViewModel = GPTChatListViewModel() - @State var selectedChat: GPTChat? = .init(title: "hi", messages: [.init(content: "안녕하세요", isUser: false)]) + @State var selectedChat: GPTChat? = .init(title: "토론제목입니다", messages: [.init(content: "ChatGPT와의 토론!", isUser: false)]) var body: some View { ZStack { diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/Service/ChatService.swift b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/Service/ChatService.swift new file mode 100644 index 00000000..2b14f250 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatGPT/Service/ChatService.swift @@ -0,0 +1,45 @@ +// +// ChatService.swift +// RollTheDice +// +// Created by 신예진 on 6/19/24. +// + +import Foundation +import Moya + +enum ChatService { + case sendMessageToAI(roomId: Int, message: String) + case sendMessageToHuman(roomId: Int, message: String) +} + +extension ChatService: TargetType { + var baseURL: URL { + return URL(string: "http://roll-the-dice.store:8080")! + } + + var path: String { + switch self { + case .sendMessageToAI(let roomId, _): + return "/debates/\(roomId)/ai" + case .sendMessageToHuman(let roomId, _): + return "/debates/\(roomId)/human" + } + } + + var method: Moya.Method { + return .post + } + + var task: Task { + switch self { + case .sendMessageToAI(_, let message), .sendMessageToHuman(_, let message): + return .requestParameters(parameters: ["message": message], encoding: JSONEncoding.default) + } + } + + var headers: [String: String]? { + guard let token = TokenManager.shared.accessToken else { return nil } + return ["Authorization": "Bearer \(token)"] + } +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/Model/News.swift b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/Model/News.swift new file mode 100644 index 00000000..aa9f64f6 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/Model/News.swift @@ -0,0 +1,8 @@ +// +// News.swift +// RollTheDice +// +// Created by 신예진 on 6/19/24. +// + +import Foundation diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/RecentNewsCardView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/RecentNewsCardView.swift index 9fa0b186..8d5ff093 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/RecentNewsCardView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/RecentNewsCardView.swift @@ -8,6 +8,10 @@ import SwiftUI struct RecentNewsCardView: View { + @EnvironmentObject var pathModel: PathModel + @StateObject private var viewModel = CreateDebateRoomViewModel() + @State private var topic: String = "" + var body: some View { HStack { titleView @@ -36,7 +40,9 @@ struct RecentNewsCardView: View { } .shadow(color: .basicBlack.opacity(0.1), radius: 2) Button { - + print("버튼 클릭됨 - 주제: \(topic)") + viewModel.createDebate(topic: topic) + pathModel.paths.append(.createdebateroom) } label: { Text("토론 시작하기") .foregroundStyle(.basicWhite) @@ -47,6 +53,15 @@ struct RecentNewsCardView: View { .clipShape(RoundedRectangle(cornerRadius: 16)) } } + + if let debateID = viewModel.debateID { + Text("토론방 ID: \(debateID)") + } + + if let errorMessage = viewModel.errorMessage { + Text("Error: \(errorMessage)") + .foregroundColor(.red) + } } .padding(.horizontal, 20) .padding(.top, 24) diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/Service/CreateDebateRoomService.swift b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/Service/CreateDebateRoomService.swift new file mode 100644 index 00000000..bd80ce2f --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/Service/CreateDebateRoomService.swift @@ -0,0 +1,50 @@ +// +// CreateDebateRoomService.swift +// RollTheDice +// +// Created by 신예진 on 6/19/24. +// + +import Foundation +import Moya + +enum CreateDebateRoomService { + case createDebate(topic: String) +} + +extension CreateDebateRoomService: TargetType { + var baseURL: URL { + return URL(string: "http://roll-the-dice.store:8080")! + } + + var path: String { + switch self { + case .createDebate: + return "/debates" + } + } + + var method: Moya.Method { + switch self { + case .createDebate: + return .post + } + } + + var task: Task { + switch self { + case .createDebate(let topic): + let parameters: [String: Any] = ["topic": topic] + return .requestParameters(parameters: parameters, encoding: JSONEncoding.default) + } + } + + var headers: [String: String]? { + guard let token = TokenManager.shared.accessToken else { return nil } + return ["Authorization": "Bearer \(token)"] + } + + var sampleData: Data { + return Data() + } +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/ViewModel/CreateDebateRoomViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/ViewModel/CreateDebateRoomViewModel.swift new file mode 100644 index 00000000..3b6cfc9b --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/Debate/ChatList/ViewModel/CreateDebateRoomViewModel.swift @@ -0,0 +1,45 @@ +// +// CreateDebateRoomViewModel.swift +// RollTheDice +// +// Created by 신예진 on 6/19/24. +// + +import Foundation +import Moya +import Combine + +class CreateDebateRoomViewModel: ObservableObject { + private let provider = MoyaProvider() + @Published var debateID: Int? + @Published var errorMessage: String? + + func createDebate(topic: String) { + print("API 호출 시작 - 토론 주제: \(topic)") + provider.request(.createDebate(topic: topic)) { result in + switch result { + case .success(let response): + print("API 호출 성공 - 응답 코드: \(response.statusCode)") + do { + let json = try JSONSerialization.jsonObject(with: response.data, options: []) as? [String: Any] + if let id = json?["id"] as? Int { + DispatchQueue.main.async { + self.debateID = id + print("토론방 생성 성공 - ID: \(id)") + } + } + } catch { + DispatchQueue.main.async { + self.errorMessage = "Failed to parse response" + print("응답 파싱 중 에러: \(error.localizedDescription)") + } + } + case .failure(let error): + DispatchQueue.main.async { + self.errorMessage = error.localizedDescription + print("API 호출 실패 - 에러: \(error.localizedDescription)") + } + } + } + } +}