diff --git a/ADAbot/ADAbot.xcodeproj/project.pbxproj b/ADAbot/ADAbot.xcodeproj/project.pbxproj index f82e371a78b9256465b5f62965f09ca90895c82a..595bcb48686b1503d251d03fc9bc23389bd0e976 100644 --- a/ADAbot/ADAbot.xcodeproj/project.pbxproj +++ b/ADAbot/ADAbot.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 9449FE4924042D6500025F70 /* FeatureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9449FE4824042D6500025F70 /* FeatureView.swift */; }; 9449FE4B24042E8800025F70 /* Argument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9449FE4A24042E8800025F70 /* Argument.swift */; }; 9449FE4D2404561400025F70 /* ArgumentQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9449FE4C2404561400025F70 /* ArgumentQuery.swift */; }; + 9449FE4F240533FD00025F70 /* QueryOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9449FE4E240533FD00025F70 /* QueryOptionView.swift */; }; + 9449FE5124053DA500025F70 /* ChatManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9449FE5024053DA500025F70 /* ChatManager.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -52,6 +54,8 @@ 9449FE4824042D6500025F70 /* FeatureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureView.swift; sourceTree = "<group>"; }; 9449FE4A24042E8800025F70 /* Argument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Argument.swift; sourceTree = "<group>"; }; 9449FE4C2404561400025F70 /* ArgumentQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArgumentQuery.swift; sourceTree = "<group>"; }; + 9449FE4E240533FD00025F70 /* QueryOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryOptionView.swift; sourceTree = "<group>"; }; + 9449FE5024053DA500025F70 /* ChatManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatManager.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -109,16 +113,18 @@ 9449FE3E2403EDB400025F70 /* Chat */ = { isa = PBXGroup; children = ( + 9449FE5024053DA500025F70 /* ChatManager.swift */, 9449FE442403F0A600025F70 /* Sender.swift */, 9449FE382403E9A400025F70 /* Message.swift */, 9449FE3A2403EC3800025F70 /* ADAMessage.swift */, 9449FE4A24042E8800025F70 /* Argument.swift */, 9449FE4C2404561400025F70 /* ArgumentQuery.swift */, 9449FE3C2403ED0200025F70 /* UserMessage.swift */, + 9449FE422403F02500025F70 /* MessageBubble.swift */, 9449FE302402C90800025F70 /* ChatView.swift */, 9449FE402403EF5A00025F70 /* MessageView.swift */, - 9449FE422403F02500025F70 /* MessageBubble.swift */, 9449FE4824042D6500025F70 /* FeatureView.swift */, + 9449FE4E240533FD00025F70 /* QueryOptionView.swift */, ); name = Chat; path = ADAbot/Chat; @@ -209,6 +215,7 @@ 9449FE3B2403EC3800025F70 /* ADAMessage.swift in Sources */, 9449FE3D2403ED0200025F70 /* UserMessage.swift in Sources */, 9449FE2F2402C8C500025F70 /* ProductView.swift in Sources */, + 9449FE4F240533FD00025F70 /* QueryOptionView.swift in Sources */, 9449FE4B24042E8800025F70 /* Argument.swift in Sources */, 9449FE372402D29E00025F70 /* Product.swift in Sources */, 9449FE432403F02500025F70 /* MessageBubble.swift in Sources */, @@ -218,6 +225,7 @@ 9449FE4D2404561400025F70 /* ArgumentQuery.swift in Sources */, 9449FE4924042D6500025F70 /* FeatureView.swift in Sources */, 9449FE312402C90800025F70 /* ChatView.swift in Sources */, + 9449FE5124053DA500025F70 /* ChatManager.swift in Sources */, 9449FE352402CCDA00025F70 /* ConnectionManager.swift in Sources */, 9449FE1D2402C84F00025F70 /* SceneDelegate.swift in Sources */, 9449FE1F2402C84F00025F70 /* ContentView.swift in Sources */, diff --git a/ADAbot/ADAbot.xcodeproj/project.xcworkspace/xcuserdata/joeloksanen.xcuserdatad/UserInterfaceState.xcuserstate b/ADAbot/ADAbot.xcodeproj/project.xcworkspace/xcuserdata/joeloksanen.xcuserdatad/UserInterfaceState.xcuserstate index da7af78889fe5c2a08258edb7e01a0ab8616562c..340987542d5d28546f80ba13fdd7337f16270485 100644 Binary files a/ADAbot/ADAbot.xcodeproj/project.xcworkspace/xcuserdata/joeloksanen.xcuserdatad/UserInterfaceState.xcuserstate and b/ADAbot/ADAbot.xcodeproj/project.xcworkspace/xcuserdata/joeloksanen.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ADAbot/ADAbot.xcodeproj/xcuserdata/joeloksanen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/ADAbot/ADAbot.xcodeproj/xcuserdata/joeloksanen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index e00cd34891b2aa8910bfdbb27d275d68feb7503f..5e4eda707bea3471235dcc7a6cbbcec45f8cf534 100644 --- a/ADAbot/ADAbot.xcodeproj/xcuserdata/joeloksanen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/ADAbot/ADAbot.xcodeproj/xcuserdata/joeloksanen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,22 +3,4 @@ uuid = "91D35F8C-89D0-4137-A5A5-FAFC5B6FE3BA" type = "1" version = "2.0"> - <Breakpoints> - <BreakpointProxy - BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> - <BreakpointContent - uuid = "01922009-364B-4C8F-87E5-58886FB47FAA" - shouldBeEnabled = "No" - ignoreCount = "0" - continueAfterRunningActions = "No" - filePath = "ADAbot/Chat/MessageView.swift" - startingColumnNumber = "9223372036854775807" - endingColumnNumber = "9223372036854775807" - startingLineNumber = "34" - endingLineNumber = "34" - landmarkName = "body" - landmarkType = "24"> - </BreakpointContent> - </BreakpointProxy> - </Breakpoints> </Bucket> diff --git a/ADAbot/ADAbot/Chat/ADAMessage.swift b/ADAbot/ADAbot/Chat/ADAMessage.swift index 191bbea223143eb0fce6d1c8e458895743fb4287..5d899ce4a7ed8bd6ead4bfc5a739ec180e525d38 100644 --- a/ADAbot/ADAbot/Chat/ADAMessage.swift +++ b/ADAbot/ADAbot/Chat/ADAMessage.swift @@ -8,11 +8,15 @@ import UIKit -struct ADAMessage: Message, Decodable { +struct ADAMessage: Message, Decodable, Equatable { let id = UUID() let sender = Sender.ADA let text: String let arguments: [Argument] + static func == (lhs: ADAMessage, rhs: ADAMessage) -> Bool { + lhs.id == rhs.id + } + } diff --git a/ADAbot/ADAbot/Chat/Argument.swift b/ADAbot/ADAbot/Chat/Argument.swift index f9b6e45a536c1043424e0e22288074cfae666cdc..1c857c6ef3c071b8b174bf062d570b9dac5650a6 100644 --- a/ADAbot/ADAbot/Chat/Argument.swift +++ b/ADAbot/ADAbot/Chat/Argument.swift @@ -8,10 +8,15 @@ import UIKit -struct Argument: Codable { +struct Argument: Codable, Equatable { let id = UUID() let name: String - let queries = [ArgumentQuery]() + let queries = [ArgumentQuery(argumentID: 1, queryID: 1, text: "Why was the lens considered to be good?"), + ArgumentQuery(argumentID: 1, queryID: 2, text: "What did users say about the lens being good?")] + + static func == (lhs: Argument, rhs: Argument) -> Bool { + lhs.id == rhs.id + } } diff --git a/ADAbot/ADAbot/Chat/ArgumentQuery.swift b/ADAbot/ADAbot/Chat/ArgumentQuery.swift index 3d34e740d7bf50ba1024a4510425e08940ca81d8..e9b036a37c4f3a4d02d1980558962e4ddb046ceb 100644 --- a/ADAbot/ADAbot/Chat/ArgumentQuery.swift +++ b/ADAbot/ADAbot/Chat/ArgumentQuery.swift @@ -10,8 +10,14 @@ import UIKit struct ArgumentQuery: Codable { + let id = UUID() let argumentID: Int let queryID: Int let text: String + var message: UserMessage { + get { + return UserMessage(query: self) + } + } } diff --git a/ADAbot/ADAbot/Chat/ChatManager.swift b/ADAbot/ADAbot/Chat/ChatManager.swift new file mode 100644 index 0000000000000000000000000000000000000000..83cf0d700b1d56a4bd1f0e3975cd92b9a495a03a --- /dev/null +++ b/ADAbot/ADAbot/Chat/ChatManager.swift @@ -0,0 +1,39 @@ +// +// ChatManager.swift +// ADAbot +// +// Created by Joel Oksanen on 25.2.2020. +// Copyright © 2020 Joel Oksanen. All rights reserved. +// + +import SwiftUI + +class ChatManager: ObservableObject { + @Published var sent = false + @Published var showingOptionsForMessage: ADAMessage? + + let connectionManager: ConnectionManager + + init(connectionManager: ConnectionManager) { + self.connectionManager = connectionManager + } + + func sendQuery(query: ArgumentQuery) { + sent = true + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + self.sent = false + self.connectionManager.addMessage(message: query.message) + self.showingOptionsForMessage = nil + } + } + + func showOptionsFor(_ message: ADAMessage) { + showingOptionsForMessage = message + } + + func showingOptionsFor(_ message: Message) -> Bool { + return message is ADAMessage && (message as! ADAMessage) == showingOptionsForMessage + } + +} + diff --git a/ADAbot/ADAbot/Chat/ChatView.swift b/ADAbot/ADAbot/Chat/ChatView.swift index 85ca0df83c878ecbf403f966f60acdfec037e67d..a10b3393dc48ac4eb943724f2b0595205868b3e7 100644 --- a/ADAbot/ADAbot/Chat/ChatView.swift +++ b/ADAbot/ADAbot/Chat/ChatView.swift @@ -9,7 +9,13 @@ import SwiftUI struct ChatView: View { - @ObservedObject var connectionManager = ConnectionManager() + @ObservedObject var connectionManager: ConnectionManager + @ObservedObject var chatManager: ChatManager + + init(connectionManager: ConnectionManager) { + self.connectionManager = connectionManager + self.chatManager = ChatManager(connectionManager: connectionManager) + } var body: some View { ZStack { @@ -19,11 +25,8 @@ struct ChatView: View { ScrollView { VStack(spacing: 0) { ForEach(connectionManager.messages, id: \.id) { message in - MessageView(message: message) - .padding(EdgeInsets(top: 0, leading: 20, bottom: 15, trailing: 20)) + MessageView(chatManager: self.chatManager, message: message) } - FeatureView(arguments: connectionManager.messages.last!.arguments) - .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0)) } .padding(EdgeInsets(top: 30, leading: 0, bottom: 30, trailing: 0)) } diff --git a/ADAbot/ADAbot/Chat/FeatureView.swift b/ADAbot/ADAbot/Chat/FeatureView.swift index 4042e7c6666e6093740f0c8439250e53dafa27e4..6351f7cc1379a07396b2b623e68072d1717ec01e 100644 --- a/ADAbot/ADAbot/Chat/FeatureView.swift +++ b/ADAbot/ADAbot/Chat/FeatureView.swift @@ -9,44 +9,70 @@ import SwiftUI struct FeatureView: View { + @ObservedObject var chatManager: ChatManager + static let bubbleColor = Color(red: 75/255, green: 90/255, blue: 116/255) let arguments: [Argument] - let bubbleColor = Color(red: 75/255, green: 90/255, blue: 116/255) + @State var showingQueryOptionsForArgument: Argument? var body: some View { - HStack(spacing: 0) { - Spacer() - - Text("ASK ABOUT") - .foregroundColor(self.bubbleColor) - .font(Font.custom("Gill Sans", size: 11)) - .fixedSize(horizontal: true, vertical: false) - .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10)) - - Capsule() - .foregroundColor(self.bubbleColor) - .frame(width: 1, height: 38) - - ScrollView(.horizontal, showsIndicators: false) { + VStack(spacing: 0) { + if (!chatManager.sent) { HStack(spacing: 0) { Spacer() - .frame(width: 10) - ForEach(self.arguments, id: \.id) { argument in - Text(argument.name) - .foregroundColor(Color.white) - .font(Font.custom("Helvetica Neue", size: 12)) - .fixedSize(horizontal: true, vertical: true) - .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) - .background(Capsule().foregroundColor(self.bubbleColor)) - .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10)) + + Text("ASK ABOUT") + .foregroundColor(FeatureView.bubbleColor) + .font(Font.custom("Gill Sans", size: 11)) + .fixedSize(horizontal: true, vertical: false) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10)) + + Capsule() + .foregroundColor(FeatureView.bubbleColor) + .frame(width: 1, height: 38) + + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 0) { + Spacer() + .frame(width: 10) + ForEach(self.arguments, id: \.id) { argument in + FeatureBubble(argument: argument, + showingQueryOptionsForArgument: self.$showingQueryOptionsForArgument) + .onTapGesture { self.showingQueryOptionsForArgument = argument } + } + Spacer() + .frame(width: 10) + } } - Spacer() - .frame(width: 10) + .frame(maxWidth: 280) + .fixedSize(horizontal: true, vertical: false) + .zIndex(-1) } + .padding(EdgeInsets(top: 0, leading: 0, bottom: 15, trailing: 0)) + } + + if showingQueryOptionsForArgument != nil { + QueryOptionView(chatManager: chatManager, queries: showingQueryOptionsForArgument!.queries) } - .frame(maxWidth: 280) - .fixedSize(horizontal: true, vertical: false) - .zIndex(-1) } + .frame(width: UIScreen.main.bounds.width) + } +} + +struct FeatureBubble: View { + let argument: Argument + @Binding var showingQueryOptionsForArgument: Argument? + + var body: some View { + Text(argument.name) + .foregroundColor(self.showingQueryOptionsForArgument == argument ? Color.white : FeatureView.bubbleColor) + .font(Font.custom("Helvetica Neue", size: 12)) + .fixedSize(horizontal: true, vertical: true) + .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) + .background(self.showingQueryOptionsForArgument == argument ? + AnyView(Capsule().foregroundColor(FeatureView.bubbleColor)) : + AnyView(Capsule().strokeBorder(FeatureView.bubbleColor)) + ) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10)) } } diff --git a/ADAbot/ADAbot/Chat/MessageView.swift b/ADAbot/ADAbot/Chat/MessageView.swift index 2d08815718cf271432d197ae7eb9375dfe2cea3c..aec1e134c0e991673821e4f2714c36eeeb3ce878 100644 --- a/ADAbot/ADAbot/Chat/MessageView.swift +++ b/ADAbot/ADAbot/Chat/MessageView.swift @@ -9,6 +9,8 @@ import SwiftUI struct MessageView: View { + @ObservedObject var chatManager: ChatManager + let bubbleColors = [ Sender.ADA: Color(red: 242/255, green: 159/255, blue: 31/255), Sender.USER: Color(red: 35/255, green: 45/255, blue: 62/255) @@ -16,23 +18,60 @@ struct MessageView: View { let maxWidth: CGFloat = 300 let message: Message + let query: ArgumentQuery? + @State var sent: Bool + + init(chatManager: ChatManager, message: Message) { + self.chatManager = chatManager + self.message = message + self.query = nil + _sent = State(initialValue: true) + } + + init(chatManager: ChatManager, query: ArgumentQuery) { + self.chatManager = chatManager + self.message = query.message + self.query = query + _sent = State(initialValue: false) + } var body: some View { - HStack(spacing: 0) { - if message.sender == .USER { - Spacer() + VStack(spacing: 0) { + if !chatManager.sent || self.sent { + HStack(spacing: 0) { + if message.sender == .USER { + Spacer() + } + + Text(message.text) + .foregroundColor(Color.white) + .font(Font.custom("Helvetica Neue", size: 14)) + .fixedSize(horizontal: false, vertical: true) + .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) + .background(MessageBubble(sender: message.sender).foregroundColor(sent ? bubbleColors[message.sender] : FeatureView.bubbleColor)) + .frame(minWidth: 0, maxWidth: maxWidth, alignment: message.sender == .ADA ? .leading : .trailing) + .onTapGesture { + if self.message.sender == .ADA { + self.chatManager.showOptionsFor(self.message as! ADAMessage) + } else if !self.sent { + withAnimation(Animation.easeInOut(duration: 0.3)) { + self.chatManager.sendQuery(query: self.query!) + self.sent = true + } + } + } + + if message.sender == .ADA { + Spacer() + } + } + .padding(EdgeInsets(top: 0, leading: 20, bottom: 15, trailing: 20)) } - Text(message.text) - .foregroundColor(Color.white) - .font(Font.custom("Helvetica Neue", size: 14)) - .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) - .background(MessageBubble(sender: message.sender).foregroundColor(bubbleColors[message.sender])) - .frame(minWidth: 0, maxWidth: maxWidth, alignment: message.sender == .ADA ? .leading : .trailing) - - if message.sender == .ADA { - Spacer() + if chatManager.showingOptionsFor(message) { + FeatureView(chatManager: chatManager, arguments: message.arguments) } } + .frame(width: UIScreen.main.bounds.width) } } diff --git a/ADAbot/ADAbot/Chat/QueryOptionView.swift b/ADAbot/ADAbot/Chat/QueryOptionView.swift new file mode 100644 index 0000000000000000000000000000000000000000..d15f4c64f05b663908381c311bd8cb32d41900c1 --- /dev/null +++ b/ADAbot/ADAbot/Chat/QueryOptionView.swift @@ -0,0 +1,23 @@ +// +// QueryOptionView.swift +// ADAbot +// +// Created by Joel Oksanen on 25.2.2020. +// Copyright © 2020 Joel Oksanen. All rights reserved. +// + +import SwiftUI + +struct QueryOptionView: View { + @ObservedObject var chatManager: ChatManager + let queries: [ArgumentQuery] + + var body: some View { + VStack(spacing: 0) { + ForEach(queries, id: \.id) { query in + MessageView(chatManager: self.chatManager, query: query) + } + } + } +} + diff --git a/ADAbot/ADAbot/Chat/UserMessage.swift b/ADAbot/ADAbot/Chat/UserMessage.swift index cccac3de5a8f8e7af7ba85fe5fa5dc3e098d68bd..b9c7b014314dfacc85caebbd15123b08c47efda8 100644 --- a/ADAbot/ADAbot/Chat/UserMessage.swift +++ b/ADAbot/ADAbot/Chat/UserMessage.swift @@ -8,11 +8,26 @@ import UIKit -struct UserMessage: Message { +struct UserMessage: Message, Equatable { let id = UUID() let sender = Sender.USER let text: String let arguments = [Argument]() + let sent: Bool + + init(text: String) { + self.text = text + self.sent = true + } + + init(query: ArgumentQuery) { + self.text = query.text + self.sent = false + } + + static func == (lhs: UserMessage, rhs: UserMessage) -> Bool { + lhs.id == rhs.id + } } diff --git a/ADAbot/ADAbot/ConnectionManager.swift b/ADAbot/ADAbot/ConnectionManager.swift index 69d052ae325563231c35d8627da6525377ace576..7b60563b4c285b9fa19d11d696d28437b506e3a4 100644 --- a/ADAbot/ADAbot/ConnectionManager.swift +++ b/ADAbot/ADAbot/ConnectionManager.swift @@ -51,7 +51,10 @@ class ConnectionManager: ObservableObject { } } task.resume() - + } + + func addMessage(message: Message) { + messages.append(message) } } diff --git a/ADAbot/ADAbot/ContentView.swift b/ADAbot/ADAbot/ContentView.swift index 2ad81b509264fb00a916768603db3ba72005c690..0599447c34e30dec69b6033a992ce2a2c9ff1cc4 100644 --- a/ADAbot/ADAbot/ContentView.swift +++ b/ADAbot/ADAbot/ContentView.swift @@ -9,11 +9,13 @@ import SwiftUI struct ContentView: View { + @ObservedObject var connectionManager = ConnectionManager() + var body: some View { VStack(spacing: 0) { - ProductView() + ProductView(connectionManager: connectionManager) .zIndex(10) - ChatView() + ChatView(connectionManager: connectionManager) .zIndex(0) } .edgesIgnoringSafeArea(.all) diff --git a/ADAbot/ADAbot/Product/ProductView.swift b/ADAbot/ADAbot/Product/ProductView.swift index 4b0a0b5ff1d4aa29a699ad862fe62441e15ff588..09eff768a7e5db3db8a698f7d7fa4f99a9d7b4e9 100644 --- a/ADAbot/ADAbot/Product/ProductView.swift +++ b/ADAbot/ADAbot/Product/ProductView.swift @@ -9,7 +9,7 @@ import SwiftUI struct ProductView: View { - @ObservedObject var connectionManager = ConnectionManager() + @ObservedObject var connectionManager: ConnectionManager let height: CGFloat = 200 var body: some View {