aboutsummaryrefslogtreecommitdiff
path: root/Hotline/macOS
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2024-05-02 18:53:56 -0700
committerDustin Mierau <dustin@mierau.me>2024-05-02 18:53:56 -0700
commit73208d70d21db7e526e371fa863acd0327caad9e (patch)
treea93bed8633077e77a576e65f14244c82a32868bb /Hotline/macOS
parent480500a3e7cfe1e2aa8ff261ca6f9e522c3aaf4c (diff)
Add link highlighting to agreements and message board. Add basic markdown styling and link highlighting to chat messages.
Diffstat (limited to 'Hotline/macOS')
-rw-r--r--Hotline/macOS/ChatView.swift173
-rw-r--r--Hotline/macOS/FilePreviewImageView.swift2
2 files changed, 98 insertions, 77 deletions
diff --git a/Hotline/macOS/ChatView.swift b/Hotline/macOS/ChatView.swift
index 57814e2..35ff28e 100644
--- a/Hotline/macOS/ChatView.swift
+++ b/Hotline/macOS/ChatView.swift
@@ -24,20 +24,19 @@ struct ChatView: View {
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 {
- VStack(alignment: .center, spacing: 16) {
- #if os(iOS)
+#if os(iOS)
if let bannerImage = self.model.bannerImage {
Image(uiImage: bannerImage)
.resizable()
@@ -45,35 +44,29 @@ struct ChatView: View {
.frame(maxWidth: 468.0)
.clipShape(RoundedRectangle(cornerRadius: 3))
}
- #endif
- HStack(spacing: 0) {
- Spacer()
-
- ScrollView(.vertical) {
- HStack {
- Text(LocalizedStringKey(msg.text.convertLinksToMarkdown()))
- .font(.system(size: 12))
- .fontDesign(.monospaced)
- .textSelection(.enabled)
- .tint(Color("Link Color"))
- Spacer()
- }
- .padding(16)
+#endif
+ ScrollView(.vertical) {
+ HStack {
+ Spacer()
+ Text(msg.text.convertToAttributedStringWithLinks(relaxed: false))
+ .font(.system(size: 12))
+ .fontDesign(.monospaced)
+ .textSelection(.enabled)
+ .tint(Color("Link Color"))
+ .frame(maxWidth: 400, alignment: .center)
+ .padding(16)
+ Spacer()
}
- .scrollBounceBehavior(.basedOnSize)
- .frame(maxWidth: 468, maxHeight: 375)
-
- Spacer()
}
- #if os(iOS)
+ .frame(maxWidth: .infinity, maxHeight: 375)
+ .scrollBounceBehavior(.basedOnSize)
+#if os(iOS)
.background(Color("Agreement Background"))
- #elseif os(macOS)
+#elseif os(macOS)
.background(VisualEffectView(material: .titlebar, blendingMode: .withinWindow))
- #endif
+#endif
.clipShape(RoundedRectangle(cornerRadius: 8))
- }
- .frame(maxWidth: .infinity)
- .padding()
+ .padding(.bottom, 16)
}
// MARK: Status
else if msg.type == .status {
@@ -86,16 +79,49 @@ struct ChatView: View {
.opacity(0.3)
Spacer()
}
- .padding()
+ .padding(EdgeInsets(top: 2, leading: 0, bottom: 2, trailing: 0))
}
else {
HStack(alignment: .firstTextBaseline) {
if let username = msg.username {
- Text(LocalizedStringKey("**\(username):** \(msg.text)".convertLinksToMarkdown()))
- .lineSpacing(4)
- .multilineTextAlignment(.leading)
- .textSelection(.enabled)
- .tint(Color("Link Color"))
+// if msg.text.isImageURL() {
+// HStack(alignment: .bottom) {
+// Text("**\(username):** ")
+//
+// let imageURL = URL(string: msg.text)!
+// AsyncImage(url: imageURL) { phase in
+// switch phase {
+// case .failure:
+// Text(LocalizedStringKey(msg.text.convertLinksToMarkdown()))
+// .lineSpacing(4)
+// .multilineTextAlignment(.leading)
+// .textSelection(.enabled)
+// .tint(Color("Link Color"))
+// case .success(let img):
+// Link(destination: imageURL) {
+// img
+// .resizable()
+// .scaledToFit()
+// .frame(maxWidth: 250, maxHeight: 150, alignment: .leading)
+// .onAppear {
+// reader.scrollTo(bottomID, anchor: .bottom)
+// }
+// }
+// default:
+// ProgressView().controlSize(.small)
+// }
+// }
+//
+// Spacer()
+// }
+// }
+// else {
+ Text(LocalizedStringKey("**\(username):** \(msg.text)".convertLinksToMarkdown()))
+ .lineSpacing(4)
+ .multilineTextAlignment(.leading)
+ .textSelection(.enabled)
+ .tint(Color("Link Color"))
+// }
}
else {
Text(LocalizedStringKey(msg.text.convertLinksToMarkdown()))
@@ -106,68 +132,63 @@ struct ChatView: View {
}
Spacer()
}
- .padding(EdgeInsets(top: 4, leading: 16, bottom: 4, trailing: 16))
+ .padding(EdgeInsets(top: 2, leading: 0, bottom: 2, trailing: 0))
}
}
EmptyView().id(bottomID)
}
- // .padding(.bottom, 12)
+ .padding()
}
- // .padding(.bottom, 60)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.defaultScrollAnchor(.bottom)
.onChange(of: model.chat.count) {
-// withAnimation {
reader.scrollTo(bottomID, anchor: .bottom)
-// }
}
.onAppear {
reader.scrollTo(bottomID, anchor: .bottom)
-// focusedField = .chatInput
}
.onChange(of: gm.size) {
reader.scrollTo(bottomID, anchor: .bottom)
}
}
- }
-
- // MARK: Input Divider
- Divider()
- .padding([.top, .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)
+
+ // MARK: Input Divider
+ Divider()
+
+ // 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 = ""
}
- self.input = ""
+ .frame(maxWidth: .infinity)
+ .padding()
+ }
+ .frame(maxWidth: .infinity, minHeight: 28)
+ .padding(EdgeInsets(top: 4, leading: 16, bottom: 4, 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
}
- .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
+ .onTapGesture {
+ focusedField = .chatInput
+ }
}
}
}
diff --git a/Hotline/macOS/FilePreviewImageView.swift b/Hotline/macOS/FilePreviewImageView.swift
index 461f5a4..ae9a825 100644
--- a/Hotline/macOS/FilePreviewImageView.swift
+++ b/Hotline/macOS/FilePreviewImageView.swift
@@ -31,7 +31,7 @@ struct FilePreviewImageView: View {
}
else {
if let image = preview?.image {
- AnimatedImageView(image: image)
+ FileImageView(image: image)
.frame(minWidth: 200, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)
}
else {