aboutsummaryrefslogtreecommitdiff
path: root/Hotline/macOS/ChatView.swift
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2023-12-19 20:38:13 -0800
committerDustin Mierau <dustin@mierau.me>2023-12-19 20:38:13 -0800
commit4fd69c02a3e7b581bb9229865336c315153f3b18 (patch)
treee6e11d872424196f2b4033077902126ad5556e40 /Hotline/macOS/ChatView.swift
parent5e87b5927cd931d46fb5f72fb035480a95969a9f (diff)
Beginnings of a UI for macOS as well as some visual changes to iOS client. :)
Diffstat (limited to 'Hotline/macOS/ChatView.swift')
-rw-r--r--Hotline/macOS/ChatView.swift191
1 files changed, 191 insertions, 0 deletions
diff --git a/Hotline/macOS/ChatView.swift b/Hotline/macOS/ChatView.swift
new file mode 100644
index 0000000..5e6faaf
--- /dev/null
+++ b/Hotline/macOS/ChatView.swift
@@ -0,0 +1,191 @@
+import SwiftUI
+
+enum FocusedField: Int, Hashable {
+ case chatInput
+}
+
+struct ChatView: View {
+ @Environment(Hotline.self) private var model: Hotline
+ @Environment(\.colorScheme) var colorScheme
+
+ @State var input: String = ""
+ @State private var scrollPos: Int?
+ @State private var contentHeight: CGFloat = 0
+
+ @FocusState private var focusedField: FocusedField?
+
+ @Namespace var bottomID
+
+ var body: some View {
+ NavigationStack {
+ ScrollViewReader { reader in
+
+ VStack(alignment: .leading, spacing: 0) {
+
+ // MARK: Scroll View
+ GeometryReader { gm in
+ ScrollView(.vertical) {
+ LazyVStack(alignment: .leading) {
+ ForEach(model.chat) { msg in
+
+ // MARK: Agreement
+ if msg.type == .agreement {
+ if !msg.text.isEmpty {
+ HStack {
+ VStack(alignment: .leading) {
+ Text(msg.text)
+ .textSelection(.enabled)
+ .padding()
+ .opacity(0.75)
+ }
+ .frame(minWidth: 40, maxWidth: 400, alignment: .center)
+ .background(VisualEffectView(material: .titlebar, blendingMode: .withinWindow).cornerRadius(24))
+ .padding()
+ }
+ .frame(maxWidth: .infinity)
+ .padding()
+ }
+ }
+ // MARK: Status
+ else if msg.type == .status {
+ HStack {
+ Spacer()
+ Text(msg.text)
+ .lineLimit(1)
+ .truncationMode(.middle)
+ .textSelection(.disabled)
+ .opacity(0.3)
+ Spacer()
+ }
+ .padding()
+ }
+ else {
+ HStack(alignment: .firstTextBaseline) {
+ if let username = msg.username {
+ Text("**\(username):** \(msg.text)")
+ .textSelection(.enabled)
+ }
+ else {
+ Text(msg.text)
+ .textSelection(.enabled)
+ }
+ Spacer()
+ }
+ .padding(EdgeInsets(top: 4, leading: 16, bottom: 4, trailing: 16))
+ }
+ }
+ EmptyView().id(bottomID)
+ }
+ // .padding(.bottom, 12)
+ }
+ // .padding(.bottom, 60)
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .defaultScrollAnchor(.bottom)
+ .onChange(of: model.chat.count) {
+ withAnimation {
+ reader.scrollTo(bottomID, anchor: .bottom)
+ }
+ print("SCROLLED TO BOTTOM")
+ }
+ .onAppear {
+ print("SCROLLED TO BOTTOM ON APPEAR")
+ reader.scrollTo(bottomID, anchor: .bottom)
+// focusedField = .chatInput
+ }
+ .onChange(of: gm.size) {
+ reader.scrollTo(bottomID, anchor: .bottom)
+ }
+ }
+ }
+
+ // MARK: Input Divider
+ Divider()
+ .padding(.bottom, 0)
+
+ // MARK: Input Bar
+ HStack(alignment: .lastTextBaseline, spacing: 0) {
+ TextField("", text: $input, axis: .vertical)
+ .focused($focusedField, equals: .chatInput)
+ .textFieldStyle(.plain)
+ .lineLimit(1...5)
+ .multilineTextAlignment(.leading)
+// .frame(maxWidth: .infinity)
+ .onSubmit {
+ if !self.input.isEmpty {
+ model.sendChat(self.input)
+ }
+ self.input = ""
+ }
+ .frame(maxWidth: .infinity)
+ .padding()
+ }
+ .frame(maxWidth: .infinity, minHeight: 28)
+ .padding(EdgeInsets(top: 0, leading: 16, bottom: 8, trailing: 16))
+ .overlay(alignment: .leadingLastTextBaseline) {
+ Image(systemName: "chevron.right").opacity(0.4).offset(x: 16)
+ }
+ .onContinuousHover { phase in
+ switch phase {
+ case .active(_):
+ NSCursor.iBeam.set()
+ case .ended:
+ NSCursor.arrow.set()
+ break
+ }
+ }
+ .onTapGesture {
+ focusedField = .chatInput
+ }
+
+
+// .overlay(alignment: .bottom) {
+// // MARK: Input Bar
+// VStack(alignment: .leading, spacing: 0) {
+//
+// }
+// }
+ // }
+
+ // VStack(alignment: .center) {
+ // Spacer()
+
+
+ // .onKeyPress(keys: [.return]) { event in
+ // print(event)
+ // guard
+ // event.phase == .down,
+ // event.key == .return else {
+ // return .ignored
+ // }
+ //
+ // if event.modifiers.contains(.shift) {
+ // print("TRYING TO ADD NEW LINE")
+ // self.input += "\n"
+ // return .handled
+ // }
+ //
+ // if !self.input.isEmpty {
+ // model.sendChat(self.input)
+ // }
+ // self.input = ""
+ //
+ // return .handled
+ // }
+ // .onSubmit {
+ // if !self.input.isEmpty {
+ // model.sendChat(self.input)
+ // }
+ // self.input = ""
+ // }
+
+ }
+ }
+ .background(Color(nsColor: .textBackgroundColor))
+ // }
+ }
+}
+
+#Preview {
+ ChatView()
+ .environment(Hotline(trackerClient: HotlineTrackerClient(), client: HotlineClient()))
+}