aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Hotline.xcodeproj/project.pbxproj4
-rw-r--r--Hotline/Application.swift12
-rw-r--r--Hotline/Utility/TextDocument.swift29
-rw-r--r--Hotline/macOS/ChatView.swift85
-rw-r--r--Hotline/macOS/ServerView.swift42
5 files changed, 115 insertions, 57 deletions
diff --git a/Hotline.xcodeproj/project.pbxproj b/Hotline.xcodeproj/project.pbxproj
index fb6cc35..f0e6116 100644
--- a/Hotline.xcodeproj/project.pbxproj
+++ b/Hotline.xcodeproj/project.pbxproj
@@ -19,6 +19,7 @@
DA4F2C012B1A558E00D8ADDC /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4F2C002B1A558E00D8ADDC /* ChatView.swift */; platformFilter = ios; };
DA5753682B33E88A00FAC277 /* HotlineFileClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5753672B33E88A00FAC277 /* HotlineFileClient.swift */; };
DA57536A2B34A60D00FAC277 /* FileImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5753692B34A60D00FAC277 /* FileImageView.swift */; };
+ DA57536C2B36BA1D00FAC277 /* TextDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA57536B2B36BA1D00FAC277 /* TextDocument.swift */; };
DA6300972B24036B0034CBFD /* HotlineClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA6300962B24036B0034CBFD /* HotlineClient.swift */; };
DA77253F2B21176D006C5ABB /* NewsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA77253E2B21176D006C5ABB /* NewsView.swift */; platformFilter = ios; };
DA7725412B21435B006C5ABB /* ObservableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA7725402B21435B006C5ABB /* ObservableScrollView.swift */; };
@@ -57,6 +58,7 @@
DA4F2C002B1A558E00D8ADDC /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
DA5753672B33E88A00FAC277 /* HotlineFileClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineFileClient.swift; sourceTree = "<group>"; };
DA5753692B34A60D00FAC277 /* FileImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileImageView.swift; sourceTree = "<group>"; };
+ DA57536B2B36BA1D00FAC277 /* TextDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextDocument.swift; sourceTree = "<group>"; };
DA6300962B24036B0034CBFD /* HotlineClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineClient.swift; sourceTree = "<group>"; };
DA77253E2B21176D006C5ABB /* NewsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsView.swift; sourceTree = "<group>"; };
DA7725402B21435B006C5ABB /* ObservableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableScrollView.swift; sourceTree = "<group>"; };
@@ -166,6 +168,7 @@
DA7725402B21435B006C5ABB /* ObservableScrollView.swift */,
DAE735062B3251B3000C56F6 /* Utilities.swift */,
DAE735082B329810000C56F6 /* VisualEffectView.swift */,
+ DA57536B2B36BA1D00FAC277 /* TextDocument.swift */,
);
path = Utility;
sourceTree = "<group>";
@@ -291,6 +294,7 @@
DAE735072B3251B3000C56F6 /* Utilities.swift in Sources */,
DAE735012B2E71F2000C56F6 /* FilesView.swift in Sources */,
DABFCC292B1530DC009F40D2 /* FoundationExtensions.swift in Sources */,
+ DA57536C2B36BA1D00FAC277 /* TextDocument.swift in Sources */,
DA77253F2B21176D006C5ABB /* NewsView.swift in Sources */,
DA4F2BF82B16A17200D8ADDC /* HotlineProtocol.swift in Sources */,
DA5753682B33E88A00FAC277 /* HotlineFileClient.swift in Sources */,
diff --git a/Hotline/Application.swift b/Hotline/Application.swift
index 2e35e3c..65833f1 100644
--- a/Hotline/Application.swift
+++ b/Hotline/Application.swift
@@ -36,6 +36,18 @@ struct Application: App {
ServerView(server: s)
.frame(minWidth: 400, minHeight: 300)
.environment(Hotline(trackerClient: HotlineTrackerClient(), client: HotlineClient()))
+ .toolbar {
+ ToolbarItem(placement: .navigation) {
+ Image(systemName: "globe.americas.fill")
+ .resizable()
+ .scaledToFit()
+// .foregroundColor(.secondary)
+ .frame(width: 22)
+// .fontWeight(.light)
+// Text("􀵲")
+// .font(.system(size: 22, weight: .ultraLight))
+ }
+ }
}
}
.defaultSize(width: 700, height: 800)
diff --git a/Hotline/Utility/TextDocument.swift b/Hotline/Utility/TextDocument.swift
new file mode 100644
index 0000000..3904a21
--- /dev/null
+++ b/Hotline/Utility/TextDocument.swift
@@ -0,0 +1,29 @@
+import Foundation
+import UniformTypeIdentifiers
+import SwiftUI
+
+struct TextFile: FileDocument {
+ // tell the system we support only plain text
+ static var readableContentTypes = [UTType.plainText, UTType.utf8PlainText]
+
+ // by default our document is empty
+ var text = ""
+
+ // a simple initializer that creates new, empty documents
+ init(initialText: String = "") {
+ text = initialText
+ }
+
+ // this initializer loads data that has been saved previously
+ init(configuration: ReadConfiguration) throws {
+ if let data = configuration.file.regularFileContents {
+ text = String(decoding: data, as: UTF8.self)
+ }
+ }
+
+ // this will be called when the system wants to write our data to disk
+ func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
+ let data = Data(text.utf8)
+ return FileWrapper(regularFileWithContents: data)
+ }
+}
diff --git a/Hotline/macOS/ChatView.swift b/Hotline/macOS/ChatView.swift
index 2b6d10b..618fd12 100644
--- a/Hotline/macOS/ChatView.swift
+++ b/Hotline/macOS/ChatView.swift
@@ -16,6 +16,10 @@ struct ChatView: View {
@FocusState private var focusedField: FocusedField?
@Namespace var bottomID
+
+ @State private var showingExporter: Bool = false
+
+ @State private var chatDocument: TextFile = TextFile()
var body: some View {
NavigationStack {
@@ -32,13 +36,13 @@ struct ChatView: View {
// MARK: Agreement
if msg.type == .agreement {
- VStack(alignment: .center) {
+ VStack(alignment: .center, spacing: 16) {
FileImageView()
.environment(self.model)
.frame(maxWidth: 468.0)
- .clipShape(RoundedRectangle(cornerRadius: 8))
-
+ .clipShape(RoundedRectangle(cornerRadius: 3))
+
VStack(spacing: 0) {
ScrollView(.vertical) {
HStack {
@@ -52,28 +56,10 @@ struct ChatView: View {
}
.scrollBounceBehavior(.basedOnSize)
.frame(maxHeight: 375)
-
-// Divider()
-//
-// HStack {
-// Button("Disagree") {
-// dismiss()
-// }
-// .controlSize(.large)
-//
-// Spacer()
-//
-// Button("Agree") {
-// model.sendAgree()
-// }
-// .controlSize(.large)
-// .keyboardShortcut(.defaultAction)
-// }
-// .padding(16)
}
.background(Color(white: colorScheme == .light ? 0.0 : 1.0).opacity(0.05))
.frame(maxWidth: 468.0)
- .clipShape(RoundedRectangle(cornerRadius: 8))
+ .clipShape(RoundedRectangle(cornerRadius: 3))
}
.frame(maxWidth: .infinity)
.padding()
@@ -177,6 +163,61 @@ struct ChatView: View {
.navigationTitle(self.model.serverTitle)
.navigationSubtitle(self.model.users.count > 0 ? "^[\(self.model.users.count) user](inflect: true) online" : "")
.background(Color(nsColor: .textBackgroundColor))
+ .toolbar {
+ ToolbarItem(placement: .primaryAction) {
+ Button {
+ if prepareChatDocument() {
+ showingExporter = true
+ }
+ } label: {
+ Image(systemName: "square.and.arrow.up")
+ }.help("Save Chat")
+ }
+ }
+ .fileExporter(isPresented: $showingExporter, document: chatDocument, contentType: .utf8PlainText, defaultFilename: "\(model.serverTitle) Chat") { result in
+// switch result {
+// case .success(let url):
+// print("Saved to \(url)")
+//
+// case .failure(let error):
+// print(error.localizedDescription)
+// }
+ self.chatDocument.text = ""
+ }
+ }
+
+ private func prepareChatDocument() -> Bool {
+ var text: String = String()
+
+ self.chatDocument.text = ""
+
+ for msg in model.chat {
+ if msg.type == .agreement {
+ text.append(msg.text)
+ text.append("\n\n")
+ }
+ else if msg.type == .message {
+ if let username = msg.username {
+ text.append("\(username): \(msg.text)")
+ }
+ else {
+ text.append(msg.text)
+ }
+ text.append("\n")
+ }
+ else if msg.type == .status {
+ text.append(msg.text)
+ text.append("\n")
+ }
+ }
+
+ if text.isEmpty {
+ return false
+ }
+
+ self.chatDocument.text = text
+
+ return true
}
}
diff --git a/Hotline/macOS/ServerView.swift b/Hotline/macOS/ServerView.swift
index 7476b3c..7ad344e 100644
--- a/Hotline/macOS/ServerView.swift
+++ b/Hotline/macOS/ServerView.swift
@@ -131,32 +131,6 @@ struct ServerView: View {
var body: some View {
NavigationSplitView {
List(selection: $selection) {
-
-// VStack(spacing: 0) {
-// bannerImage
-// .resizable()
-// .aspectRatio(contentMode: .fit)
-// .frame(maxWidth: .infinity, minHeight: 60, alignment: .topLeading)
-// .clipped()
-// }
-// .frame(maxWidth: .infinity)
-// }
-// else {
-// HStack {
-// Text(server.name ?? "")
-// .fontWeight(.medium)
-// .lineLimit(2)
-// .font(.title3)
-// .multilineTextAlignment(.center)
-// .padding()
-// }
-// .selectionDisabled()
-// .frame(maxWidth: .infinity, minHeight: 60)
-// .background(VisualEffectView(material: .titlebar, blendingMode: .withinWindow).cornerRadius(16))
-// .cornerRadius(10)
-// .tag(MenuItem(name: "title", image: "", type: .banner))
-// .padding(.bottom, 16)
-// }
if model.status != .loggedIn {
HStack {
@@ -169,19 +143,17 @@ struct ServerView: View {
}
if model.status == .loggedIn {
- Section(model.serverTitle) {
- ForEach(ServerView.menuItems) { menuItem in
- if let minServerVersion = menuItem.serverVersion {
- if let v = model.serverVersion, v >= minServerVersion {
- ListItemView(icon: menuItem.image, title: menuItem.name)
- .tag(menuItem)
- }
- }
- else {
+ ForEach(ServerView.menuItems) { menuItem in
+ if let minServerVersion = menuItem.serverVersion {
+ if let v = model.serverVersion, v >= minServerVersion {
ListItemView(icon: menuItem.image, title: menuItem.name)
.tag(menuItem)
}
}
+ else {
+ ListItemView(icon: menuItem.image, title: menuItem.name)
+ .tag(menuItem)
+ }
}
if model.users.count > 0 {