diff options
| author | Dustin Mierau <mierau@users.noreply.github.com> | 2024-05-01 19:04:19 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-01 19:04:19 -0700 |
| commit | a5c1e0b475d14b176479f662bf29163f2220180b (patch) | |
| tree | 2a458fb29ade4e1c21fa662121bfa88a45c797de | |
| parent | 44dcb81e80e167374e2d3217b81cf0148fa7d66a (diff) | |
| parent | c55cae05aeeb629fc9d2939c5f5e935ebc3ac92b (diff) | |
Merge pull request #14 from jhalter/get-set-file-info
Add Get/Set file info
| -rw-r--r-- | Hotline.xcodeproj/project.pbxproj | 8 | ||||
| -rw-r--r-- | Hotline/Hotline/HotlineClient.swift | 46 | ||||
| -rw-r--r-- | Hotline/Hotline/HotlineProtocol.swift | 8 | ||||
| -rw-r--r-- | Hotline/Models/FileDetails.swift | 13 | ||||
| -rw-r--r-- | Hotline/Models/Hotline.swift | 11 | ||||
| -rw-r--r-- | Hotline/macOS/FileDetailsView.swift | 147 | ||||
| -rw-r--r-- | Hotline/macOS/FilesView.swift | 11 |
7 files changed, 242 insertions, 2 deletions
diff --git a/Hotline.xcodeproj/project.pbxproj b/Hotline.xcodeproj/project.pbxproj index a954b35..38c6cad 100644 --- a/Hotline.xcodeproj/project.pbxproj +++ b/Hotline.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 11A726082BE0672A000C1DA7 /* FileDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11A726072BE0672A000C1DA7 /* FileDetails.swift */; }; + 11A7260A2BE0675A000C1DA7 /* FileDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11A726092BE06759000C1DA7 /* FileDetailsView.swift */; }; DA0D698D2B1E7CF700C71DF5 /* UsersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0D698C2B1E7CF700C71DF5 /* UsersView.swift */; platformFilter = ios; }; DA0D698F2B1E841600C71DF5 /* MessageBoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0D698E2B1E841600C71DF5 /* MessageBoardView.swift */; platformFilter = ios; }; DA0D69912B1E894800C71DF5 /* FilesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0D69902B1E894800C71DF5 /* FilesView.swift */; platformFilter = ios; }; @@ -70,6 +72,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 11A726072BE0672A000C1DA7 /* FileDetails.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileDetails.swift; sourceTree = "<group>"; }; + 11A726092BE06759000C1DA7 /* FileDetailsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileDetailsView.swift; sourceTree = "<group>"; }; DA0D698C2B1E7CF700C71DF5 /* UsersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersView.swift; sourceTree = "<group>"; }; DA0D698E2B1E841600C71DF5 /* MessageBoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBoardView.swift; sourceTree = "<group>"; }; DA0D69902B1E894800C71DF5 /* FilesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesView.swift; sourceTree = "<group>"; }; @@ -258,6 +262,7 @@ DADDB2892B22B2C60024040D /* Models */ = { isa = PBXGroup; children = ( + 11A726072BE0672A000C1DA7 /* FileDetails.swift */, DADDB28A2B22B31F0024040D /* Tracker.swift */, DABE8C052B57A06100884D28 /* Bookmarks.swift */, DA2863D92B37BF6E00A7D050 /* Preferences.swift */, @@ -278,6 +283,7 @@ DAE734F72B2E4126000C56F6 /* macOS */ = { isa = PBXGroup; children = ( + 11A726092BE06759000C1DA7 /* FileDetailsView.swift */, DAE734FA2B2E41F9000C56F6 /* TrackerView.swift */, DAE734F82B2E4185000C56F6 /* ServerView.swift */, DAE734FE2B2E6750000C56F6 /* ChatView.swift */, @@ -396,6 +402,7 @@ DABE8C042B57940B00884D28 /* DAKeychain.swift in Sources */, DA72A0E22B4DAA4000A0F48A /* NewsArticle.swift in Sources */, DAE734FB2B2E41F9000C56F6 /* TrackerView.swift in Sources */, + 11A726082BE0672A000C1DA7 /* FileDetails.swift in Sources */, DA9CAFBD2B126D5700CDA197 /* TrackerView.swift in Sources */, DAB4D87B2B4B78310048A05C /* AnimatedImageView.swift in Sources */, DAE735032B30C0BB000C56F6 /* MessageView.swift in Sources */, @@ -409,6 +416,7 @@ DA0D698F2B1E841600C71DF5 /* MessageBoardView.swift in Sources */, DAE735072B3251B3000C56F6 /* SoundEffects.swift in Sources */, DAE735012B2E71F2000C56F6 /* FilesView.swift in Sources */, + 11A7260A2BE0675A000C1DA7 /* FileDetailsView.swift in Sources */, DA72A0DD2B4CD0BF00A0F48A /* NewsEditorView.swift in Sources */, DABFCC292B1530DC009F40D2 /* FoundationExtensions.swift in Sources */, DA57536C2B36BA1D00FAC277 /* TextDocument.swift in Sources */, diff --git a/Hotline/Hotline/HotlineClient.swift b/Hotline/Hotline/HotlineClient.swift index c511555..5ed71a3 100644 --- a/Hotline/Hotline/HotlineClient.swift +++ b/Hotline/Hotline/HotlineClient.swift @@ -616,6 +616,52 @@ class HotlineClient: NetSocketDelegate { } } + @MainActor func sendGetFileInfo(name fileName: String, path filePath: [String], callback: ((FileDetails?) -> Void)? = nil) { + var t = HotlineTransaction(type: .getFileInfo) + t.setFieldString(type: .fileName, val: fileName) + t.setFieldPath(type: .filePath, val: filePath) + + self.sendPacket(t) { reply, err in + guard err == nil, + let fileName = reply.getField(type: .fileName)?.getString(), + let fileCreator = reply.getField(type: .fileCreatorString)?.getString(), + let fileType = reply.getField(type: .fileTypeString)?.getString(), + let fileTypeString = reply.getField(type: .fileTypeString)?.getString(), + let fileCreateDate = reply.getField(type: .fileCreateDate)?.data.readDate(at: 0), + let fileModifyDate = reply.getField(type: .fileModifyDate)?.data.readDate(at: 0) + else { + callback?(nil) + return + } + + + // Size field is not included in server reply for folders + let fileSize = reply.getField(type: .fileSize)?.getInteger() ?? 0 + + // Comment field is not included for if no comment present + let fileComment = reply.getField(type: .fileComment)?.getString() ?? "" + + callback?(FileDetails(name: fileName, path: filePath, size: fileSize, comment: fileComment, type: fileType, creator: fileCreator, + created: fileCreateDate, modified: fileModifyDate)) + } + } + + @MainActor func sendSetFileInfo(fileName: String, path filePath: [String], fileNewName: String?, comment: String?, encoding: String.Encoding = .utf8) { + var t = HotlineTransaction(type: .setFileInfo) + t.setFieldString(type: .fileName, val: fileName, encoding: encoding) + t.setFieldPath(type: .filePath, val: filePath) + + if fileNewName != nil { + t.setFieldString(type: .fileNewName, val: fileNewName!, encoding: encoding) + } + + if comment != nil { + t.setFieldString(type: .fileComment, val: comment!, encoding: encoding) + } + + self.sendPacket(t) + } + @MainActor func sendDownloadFile(name fileName: String, path filePath: [String], preview: Bool = false, callback: ((Bool, UInt32?, Int?, Int?, Int?) -> Void)? = nil) { var t = HotlineTransaction(type: .downloadFile) t.setFieldString(type: .fileName, val: fileName) diff --git a/Hotline/Hotline/HotlineProtocol.swift b/Hotline/Hotline/HotlineProtocol.swift index f98e07b..1f9ff3e 100644 --- a/Hotline/Hotline/HotlineProtocol.swift +++ b/Hotline/Hotline/HotlineProtocol.swift @@ -752,8 +752,14 @@ enum HotlineTransactionFieldType: UInt16 { case fileName = 201 // String case filePath = 202 // Path case fileTransferOptions = 204 // Integer + case fileTypeString = 205 // String + case fileCreatorString = 206 // String case fileSize = 207 // Integer - // TODO: Add file field types + case fileCreateDate = 208 + case fileModifyDate = 209 + case fileComment = 210 // Integer + case fileNewName = 211 // String + case fileType = 213 // Integer case quotingMessage = 214 // String? case automaticResponse = 215 // String case folderItemCount = 220 // Integer diff --git a/Hotline/Models/FileDetails.swift b/Hotline/Models/FileDetails.swift new file mode 100644 index 0000000..20b1ee2 --- /dev/null +++ b/Hotline/Models/FileDetails.swift @@ -0,0 +1,13 @@ +import UniformTypeIdentifiers + +struct FileDetails:Identifiable { + let id = UUID() + var name: String + var path: [String] + var size: Int + var comment: String + var type: String + var creator: String + var created: Date + var modified: Date +} diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift index baf87eb..cccb4d4 100644 --- a/Hotline/Models/Hotline.swift +++ b/Hotline/Models/Hotline.swift @@ -440,6 +440,17 @@ final class Hotline: HotlineClientDelegate, HotlineFileClientDelegate { } } } + + @MainActor func fileDetails(_ fileName: String, path: [String], complete callback: ((FileDetails?) -> Void)? = nil) { + var fullPath: [String] = [] + if path.count > 1 { + fullPath = Array(path[0..<path.count-1]) + } + + self.client.sendGetFileInfo(name: fileName, path: fullPath) { info in + callback?(info) + } + } @MainActor func previewFile(_ fileName: String, path: [String], complete callback: ((PreviewFileInfo?) -> Void)? = nil) { var fullPath: [String] = [] diff --git a/Hotline/macOS/FileDetailsView.swift b/Hotline/macOS/FileDetailsView.swift new file mode 100644 index 0000000..d3c10e8 --- /dev/null +++ b/Hotline/macOS/FileDetailsView.swift @@ -0,0 +1,147 @@ +import Foundation +import SwiftUI + +struct FileDetailsView: View { + @Environment(Hotline.self) private var model: Hotline + @Environment(\.presentationMode) var presentationMode + + var fd: FileDetails + + @State private var comment: String = "" + @State private var filename: String = "" + + var body: some View { + VStack (alignment: .leading){ + Form { + HStack(alignment: .center){ + FileIconView(filename: fd.name) + .frame(width: 16, height: 16) + TextField("", text: $filename) + .disabled(!self.canRename()) + } + HStack(alignment: .center){ + Text("Type:").bold().padding(.leading, 43) + Text(fd.type) + } + HStack(alignment: .center){ + Text("Creator:").bold().padding(.leading, 26) + Text(fd.creator) + } + HStack(alignment: .center){ + Text("Size:").bold().bold().padding(.leading, 48) + Text(self.formattedSize(byteCount: fd.size)) + } + HStack(alignment: .center){ + Text("Created:").bold().padding(.leading, 24) + Text("\(FileDetailsView.dateFormatter.string(from: fd.created))") + } + HStack(alignment: .center){ + Text("Modified:").bold().padding(.leading, 19) + Text("\(FileDetailsView.dateFormatter.string(from: fd.modified))") + } + HStack(alignment: .center){ + Text("Comments:").bold().padding(.top, 8) + .padding(.leading, 5) + } + + VStack(alignment: .trailing){ + TextEditor(text: $comment) + .padding(.leading, 2) + .padding(.top, 1) + .font(.system(size: 13)) + .background(Color(nsColor: .textBackgroundColor)) + .border(Color.secondary, width: 1) + .clipShape(RoundedRectangle(cornerRadius: 2, style: .continuous)) + .disabled(!self.canSetComment()) + } + } + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { + presentationMode.wrappedValue.dismiss() + } + } + + ToolbarItem(placement: .primaryAction) { + Button{ + var editedFilename: String? + if filename != fd.name { + editedFilename = filename + } + + var editedComment: String? + if comment != fd.comment { + editedComment = comment + } + + model.client.sendSetFileInfo(fileName: fd.name, path: fd.path, fileNewName: editedFilename, comment: editedComment) + presentationMode.wrappedValue.dismiss() + + // TODO: Update the file list if the filename was changed + } label: { + Text("Save") + }.disabled(!isEdited()) + } + } + + .onAppear { + self.filename = fd.name + self.comment = fd.comment + } + .padding(EdgeInsets(top: 16, leading: 24, bottom: 16, trailing: 24)) + } + .frame(minWidth: 400, minHeight: 400) + } + + + static var dateFormatter: DateFormatter = { + var dateFormatter = DateFormatter() + dateFormatter.dateStyle = .long + dateFormatter.timeStyle = .short + + // Original format: Fri, Aug 20, 2021, 5:14:07 PM + return dateFormatter + }() + + static var byteCountSizeFormatter: NumberFormatter = { + let numberFormatter = NumberFormatter() + numberFormatter.numberStyle = .decimal + return numberFormatter + }() + + static let byteFormatter = ByteCountFormatter() + + private func formattedFileSize(_ fileSize: UInt) -> String { + FileView.byteFormatter.allowedUnits = [.useAll] + FileView.byteFormatter.countStyle = .file + return FileView.byteFormatter.string(fromByteCount: Int64(fileSize)) + } + + // Format byte count Int into string like: 23.4M (24,601,664 bytes) + private func formattedSize(byteCount: Int) -> String { + let formattedByteCount = FileDetailsView.byteCountSizeFormatter.string(from: NSNumber(value:byteCount)) ?? "0" + return "\(FileView.byteFormatter.string(fromByteCount: Int64(byteCount))) (\(formattedByteCount) bytes)" + } + + private func isEdited() -> Bool { + return self.filename != fd.name || self.comment != fd.comment + } + + private func canRename() -> Bool { + if self.fd.type == "fldr" { + return model.access?.contains(.canRenameFolders) == true + } + return model.access?.contains(.canRenameFiles) == true + } + + private func canSetComment() -> Bool { + if self.fd.type == "fldr" { + return model.access?.contains(.canSetFolderComment) == true + } + return model.access?.contains(.canSetFileComment) == true + } +} + +//#Preview { +// FileDetailsView(fd: FileDetails(name: "AppleWorks 6.sit", path: [""], size: 24601664, comment: "test comment", type: "SITD", creator: "SIT!", created: Date.now, modified: Date.now )) +//} diff --git a/Hotline/macOS/FilesView.swift b/Hotline/macOS/FilesView.swift index 397feb3..f698b5d 100644 --- a/Hotline/macOS/FilesView.swift +++ b/Hotline/macOS/FilesView.swift @@ -102,7 +102,8 @@ struct FilesView: View { @Environment(\.openWindow) private var openWindow @State private var selection: FileInfo? - + @State private var fileDetails: FileDetails? + private func openPreviewWindow(_ previewInfo: PreviewFileInfo) { switch previewInfo.previewType { case .image: @@ -206,6 +207,11 @@ struct FilesView: View { ToolbarItem(placement: .primaryAction) { Button { + if let s = selection { + model.fileDetails(s.name, path: s.path) { info in + fileDetails = info + } + } } label: { Label("Get File Info", systemImage: "info.circle") } @@ -224,6 +230,9 @@ struct FilesView: View { } } } + .sheet(item: $fileDetails ) { item in + FileDetailsView(fd: item) + } } } |