aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Hotline.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved8
-rw-r--r--Hotline/Hotline/HotlineExtensions.swift69
-rw-r--r--Hotline/Models/FileInfo.swift1
-rw-r--r--Hotline/Shared/FileIconView.swift26
-rw-r--r--Hotline/macOS/FileDetailsView.swift2
-rw-r--r--Hotline/macOS/FilePreviewImageView.swift2
-rw-r--r--Hotline/macOS/FilePreviewTextView.swift2
-rw-r--r--Hotline/macOS/FilesView.swift196
-rw-r--r--Hotline/macOS/NewsView.swift4
-rw-r--r--Hotline/macOS/ServerView.swift4
10 files changed, 241 insertions, 73 deletions
diff --git a/Hotline.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Hotline.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index d76a9ac..13c2046 100644
--- a/Hotline.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Hotline.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -15,8 +15,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/stevengharris/SplitView",
"state" : {
- "revision" : "b37d559fd334cd5403433a29a5da6fe78f80714a",
- "version" : "3.5.2"
+ "revision" : "7737a80740919c2fa94a6e64f870d37ef77b9b5f",
+ "version" : "3.5.3"
}
},
{
@@ -24,8 +24,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/gonzalezreal/swift-markdown-ui",
"state" : {
- "revision" : "ae799d015a5374708f7b4c85f3294c05f2a564e2",
- "version" : "2.3.0"
+ "revision" : "9a8119b37e09a770367eeb26e05267c75d854053",
+ "version" : "2.3.1"
}
}
],
diff --git a/Hotline/Hotline/HotlineExtensions.swift b/Hotline/Hotline/HotlineExtensions.swift
index fd57971..81387bf 100644
--- a/Hotline/Hotline/HotlineExtensions.swift
+++ b/Hotline/Hotline/HotlineExtensions.swift
@@ -108,6 +108,73 @@ extension FileManager {
"xml": "R*ch".fourCharCode(),
]
+ static var HFSTypeToExtension: [String: String] = [
+ // Documents
+ "text": "txt",
+ "rtf ": "rtf",
+ "wdbn": "doc",
+ "w8bn": "docx",
+ "wprd": "prd",
+ "wp5 ": "wpd",
+ "pdf ": "pdf",
+ "xdoc": "qxd",
+ "indd": "ind",
+
+ // Spreadsheets
+ "xls ": "xls",
+ "xlsx": "xlsx",
+ "nmbr": "numbers",
+
+ // Presentations
+ "ppt3": "ppt",
+ "keyn": "key",
+
+ // Images
+ "jpeg": "jpg",
+ "pngf": "png",
+ "giff": "gif",
+ "tiff": "tiff",
+ "ico ": "ico",
+ "bmpf": "bmp",
+ "epsf": "eps",
+ "8bps": "psd",
+ "pict": "pict",
+ "tpic": "tga",
+ "swfl": "swf",
+
+ // Audio
+ "mp3 ": "mp3",
+ "m4a ": "m4a",
+ "caff": "aac",
+ "wave": "wav",
+ "aiff": "aiff",
+ "midi": "midi",
+ "snd ": "snd",
+
+ // Video
+ "m4v ": "mp4",
+ "mpg ": "mpeg",
+ "mpg2": "mpg2",
+ "moov": "mov",
+ "vfw ": "avi",
+ "wmv ": "wmv",
+
+ // Archives
+ "zip ": "zip",
+ "rar ": "rar",
+ "7zip": "7z",
+ "tar ": "tar",
+ "gzip": "gz",
+ "sitx": "sit",
+ "sit!": "sit",
+ "sit5": "sit",
+ "udif": "dmg",
+ "cdrw": "cdr",
+
+ // Fonts
+ "tfil": "ttf",
+ ]
+
static var extensionToHFSType: [String: UInt32] = [
// Documents
"txt": "TEXT".fourCharCode(),
@@ -115,7 +182,7 @@ extension FileManager {
"doc": "WDBN".fourCharCode(),
"docx": "W8BN".fourCharCode(),
"prd": "WPRD".fourCharCode(),
- "wpd": "WP5".fourCharCode(),
+ "wpd": "WP5 ".fourCharCode(),
"pdf": "PDF ".fourCharCode(),
"qxd": "XDOC".fourCharCode(),
"indd": "inDd".fourCharCode(),
diff --git a/Hotline/Models/FileInfo.swift b/Hotline/Models/FileInfo.swift
index 463f0be..2da7378 100644
--- a/Hotline/Models/FileInfo.swift
+++ b/Hotline/Models/FileInfo.swift
@@ -27,7 +27,6 @@ import UniformTypeIdentifiers
self.isDropboxFolder && (self.name.range(of: "admin", options: [.caseInsensitive]) != nil)
}
-
var expanded: Bool = false
var children: [FileInfo]? = nil
diff --git a/Hotline/Shared/FileIconView.swift b/Hotline/Shared/FileIconView.swift
index ffc9051..d0949fa 100644
--- a/Hotline/Shared/FileIconView.swift
+++ b/Hotline/Shared/FileIconView.swift
@@ -19,10 +19,11 @@ struct FolderIconView: View {
struct FileIconView: View {
let filename: String
+ let fileType: String?
#if os(iOS)
- private func fileIcon(filename: String) -> Image {
- let fileExtension = (filename as NSString).pathExtension
+ private func fileIcon() -> Image {
+ let fileExtension = (self.filename as NSString).pathExtension
if let fileType = UTType(filenameExtension: fileExtension) {
if fileType.isSubtype(of: .movie) {
return Image(systemName: "play.rectangle")
@@ -44,14 +45,29 @@ struct FileIconView: View {
return Image(systemName: "doc")
}
#elseif os(macOS)
- private func fileIcon(filename: String) -> Image {
- Image(nsImage: NSWorkspace.shared.icon(for: UTType(filenameExtension: (filename as NSString).pathExtension) ?? UTType.content))
+ private func fileIcon() -> Image {
+ let fileExtension = (self.filename as NSString).pathExtension
+
+ if !fileExtension.isEmpty,
+ let uttype = UTType(filenameExtension: fileExtension) {
+ return Image(nsImage: NSWorkspace.shared.icon(for: uttype))
+ }
+ else if let fileType = self.fileType,
+ let fileTypeExtension = FileManager.HFSTypeToExtension[fileType.lowercased()],
+ let uttype = UTType(filenameExtension: fileTypeExtension) {
+ return Image(nsImage: NSWorkspace.shared.icon(for: uttype))
+ }
+ else {
+ return Image(nsImage: NSWorkspace.shared.icon(for: UTType.data))
+ }
+
+// Image(nsImage: NSWorkspace.shared.icon(for: UTType(filenameExtension: (filename as NSString).pathExtension) ?? UTType.content))
}
#endif
var body: some View {
- fileIcon(filename: filename)
+ fileIcon()
.resizable()
.scaledToFit()
}
diff --git a/Hotline/macOS/FileDetailsView.swift b/Hotline/macOS/FileDetailsView.swift
index aa7ab09..34e083b 100644
--- a/Hotline/macOS/FileDetailsView.swift
+++ b/Hotline/macOS/FileDetailsView.swift
@@ -14,7 +14,7 @@ struct FileDetailsView: View {
VStack (alignment: .leading){
Form {
HStack(alignment: .center){
- FileIconView(filename: fd.name)
+ FileIconView(filename: fd.name, fileType: nil)
.frame(width: 32, height: 32)
TextField("", text: $filename)
.disabled(!self.canRename())
diff --git a/Hotline/macOS/FilePreviewImageView.swift b/Hotline/macOS/FilePreviewImageView.swift
index b8ad37b..9beeb80 100644
--- a/Hotline/macOS/FilePreviewImageView.swift
+++ b/Hotline/macOS/FilePreviewImageView.swift
@@ -69,7 +69,7 @@ struct FilePreviewImageView: View {
.background(.black)
.toolbar {
ToolbarItem(placement: .navigation) {
- FileIconView(filename: info?.name ?? "")
+ FileIconView(filename: info?.name ?? "", fileType: nil)
.frame(width: 16, height: 16)
.opacity(controlActiveState == .inactive ? 0.5 : 1.0)
}
diff --git a/Hotline/macOS/FilePreviewTextView.swift b/Hotline/macOS/FilePreviewTextView.swift
index 2cb192c..c286381 100644
--- a/Hotline/macOS/FilePreviewTextView.swift
+++ b/Hotline/macOS/FilePreviewTextView.swift
@@ -80,7 +80,7 @@ struct FilePreviewTextView: View {
.navigationTitle(info?.name ?? "File Preview")
.toolbar {
ToolbarItem(placement: .navigation) {
- FileIconView(filename: info?.name ?? "")
+ FileIconView(filename: info?.name ?? "", fileType: nil)
.frame(width: 16, height: 16)
.opacity(controlActiveState == .inactive ? 0.5 : 1.0)
}
diff --git a/Hotline/macOS/FilesView.swift b/Hotline/macOS/FilesView.swift
index e34c7b5..80d89f9 100644
--- a/Hotline/macOS/FilesView.swift
+++ b/Hotline/macOS/FilesView.swift
@@ -1,39 +1,51 @@
import SwiftUI
import UniformTypeIdentifiers
-struct FileView: View {
+struct FolderView: View {
@Environment(Hotline.self) private var model: Hotline
- @State var expanded = false
@State var loading = false
+ @State var dragOver = false
var file: FileInfo
let depth: Int
+ @MainActor private func uploadFile(file fileURL: URL) {
+ var filePath: [String] = [String](self.file.path)
+ if !self.file.isFolder {
+ filePath.removeLast()
+ }
+
+ print("UPLOADING TO PATH: ", filePath)
+
+ model.uploadFile(url: fileURL, path: filePath) { info in
+ Task {
+ // Refresh file listing to display newly uploaded file.
+ let _ = await model.getFileList(path: filePath)
+ }
+ }
+ }
+
var body: some View {
HStack(alignment: .center, spacing: 0) {
- if file.isFolder {
- Button {
- if file.isFolder {
- file.expanded.toggle()
- }
- } label: {
- Text(Image(systemName: file.expanded ? "chevron.down" : "chevron.right"))
- .bold()
- .font(.system(size: 10))
- .opacity(0.5)
+ Spacer()
+ .frame(width: CGFloat(depth * (12 + 2)))
+
+ Button {
+ if file.isFolder {
+ file.expanded.toggle()
}
- .buttonStyle(.plain)
- .frame(width: 10)
- .padding(.leading, 4)
- .padding(.trailing, 8)
- }
- else {
- Spacer()
- .frame(width: 10)
- .padding(.leading, 4)
- .padding(.trailing, 8)
+ } label: {
+ Text(Image(systemName: file.expanded ? "chevron.down" : "chevron.right"))
+ .bold()
+ .font(.system(size: 10))
+ .foregroundStyle(dragOver ? Color.white : Color.primary)
+ .opacity(0.5)
}
+ .buttonStyle(.plain)
+ .frame(width: 10)
+ .padding(.leading, 4)
+ .padding(.trailing, 8)
HStack(alignment: .center) {
if file.isUnavailable {
@@ -53,17 +65,11 @@ struct FileView: View {
.scaledToFit()
.frame(width: 16, height: 16)
}
- else if file.isFolder {
+ else {
Image("Folder")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
-// FolderIconView(dropbox: file.isUploadFolder)
-// .frame(width: 16, height: 16)
- }
- else {
- FileIconView(filename: file.name)
- .frame(width: 16, height: 16)
}
}
.frame(width: 16)
@@ -72,47 +78,123 @@ struct FileView: View {
Text(file.name)
.lineLimit(1)
.truncationMode(.tail)
+ .foregroundStyle(dragOver ? Color.white : Color.primary)
.opacity(file.isUnavailable ? 0.5 : 1.0)
- if file.isFolder && loading {
+ if loading {
ProgressView().controlSize(.small).padding([.leading, .trailing], 5)
}
Spacer()
if !file.isUnavailable {
- if file.isFolder {
- Text(file.fileSize == 0 ? "Empty" : "^[\(file.fileSize) \("file")](inflect: true)")
- .foregroundStyle(.secondary)
- .lineLimit(1)
- .padding(.trailing, 6)
- }
- else {
- Text(formattedFileSize(file.fileSize))
- .foregroundStyle(.secondary)
- .lineLimit(1)
- .padding(.trailing, 6)
- }
+ Text(file.fileSize == 0 ? "Empty" : "^[\(file.fileSize) \("file")](inflect: true)")
+ .foregroundStyle(dragOver ? Color.white.opacity(0.75) : Color.secondary)
+ .lineLimit(1)
+ .padding(.trailing, 6)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
- .padding(.leading, CGFloat(depth * (12 + 10)))
+ .background(
+ RoundedRectangle(cornerRadius: 4.0)
+ .fill(dragOver ? Color(nsColor: NSColor.selectedContentBackgroundColor) : Color.clear)
+ .padding(.horizontal, -6)
+ .padding(.vertical, -4)
+ )
.onChange(of: file.expanded) {
loading = false
- if file.isFolder && file.fileSize > 0 {
- if file.expanded {
- Task {
- loading = true
- let _ = await model.getFileList(path: file.path)
- loading = false
+ if file.expanded && file.fileSize > 0 {
+ Task {
+ loading = true
+ let _ = await model.getFileList(path: file.path)
+ loading = false
+ }
+ }
+ }
+ .onDrop(of: [.fileURL], isTargeted: $dragOver) { items in
+ guard let item = items.first,
+ let identifier = item.registeredTypeIdentifiers.first else {
+ return false
+ }
+
+ item.loadItem(forTypeIdentifier: identifier, options: nil) { (urlData, error) in
+ print("WHAT?", Thread.isMainThread)
+ DispatchQueue.main.async {
+ print("WHAT 2?", Thread.isMainThread)
+ if let urlData = urlData as? Data,
+ let fileURL = URL(dataRepresentation: urlData, relativeTo: nil, isAbsolute: true) {
+ // uploadFile(file: fileURL)
}
}
}
+
+ return true
}
if file.expanded {
ForEach(file.children!, id: \.self) { childFile in
- FileView(file: childFile, depth: self.depth + 1).tag(file.id)
-// .environment(self.selectedFile)
-// .frame(height: 34)
+ if childFile.isFolder {
+ FolderView(file: childFile, depth: self.depth + 1).tag(file.id)
+ }
+ else {
+ FileView(file: childFile, depth: self.depth + 1).tag(file.id)
+ }
+ }
+ }
+ }
+}
+
+struct FileView: View {
+ @Environment(Hotline.self) private var model: Hotline
+
+ var file: FileInfo
+ let depth: Int
+
+ var body: some View {
+ HStack(alignment: .center, spacing: 0) {
+ Spacer()
+ .frame(width: CGFloat(depth * (12 + 2)))
+
+ Spacer()
+ .frame(width: 10)
+ .padding(.leading, 4)
+ .padding(.trailing, 8)
+
+ HStack(alignment: .center) {
+ if file.isUnavailable {
+ Image(systemName: "questionmark.app.fill")
+ .frame(width: 16, height: 16)
+ .opacity(0.5)
+ }
+ else {
+ FileIconView(filename: file.name, fileType: file.type)
+ .frame(width: 16, height: 16)
+ }
+ }
+ .frame(width: 16)
+ .padding(.trailing, 6)
+
+ Text(file.name)
+ .lineLimit(1)
+ .truncationMode(.tail)
+ .opacity(file.isUnavailable ? 0.5 : 1.0)
+
+ Spacer()
+ if !file.isUnavailable {
+ Text(formattedFileSize(file.fileSize))
+ .foregroundStyle(.secondary)
+ .lineLimit(1)
+ .padding(.trailing, 6)
+ }
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+
+ if file.expanded {
+ ForEach(file.children!, id: \.self) { childFile in
+ if childFile.isFolder {
+ FolderView(file: childFile, depth: self.depth + 1).tag(file.id)
+ }
+ else {
+ FileView(file: childFile, depth: self.depth + 1).tag(file.id)
+ }
}
}
}
@@ -120,7 +202,6 @@ struct FileView: View {
static let byteFormatter = ByteCountFormatter()
private func formattedFileSize(_ fileSize: UInt) -> String {
- // let bcf = ByteCountFormatter()
FileView.byteFormatter.allowedUnits = [.useAll]
FileView.byteFormatter.countStyle = .file
return FileView.byteFormatter.string(fromByteCount: Int64(fileSize))
@@ -199,9 +280,14 @@ struct FilesView: View {
var body: some View {
NavigationStack {
List(model.files, id: \.self, selection: $selection) { file in
- FileView(file: file, depth: 0).tag(file.id)
+ if file.isFolder {
+ FolderView(file: file, depth: 0).tag(file.id)
+ }
+ else {
+ FileView(file: file, depth: 0).tag(file.id)
+ }
}
- .environment(\.defaultMinListRowHeight, 34)
+ .environment(\.defaultMinListRowHeight, 28)
.listStyle(.inset)
.alternatingRowBackgrounds(.enabled)
.task {
diff --git a/Hotline/macOS/NewsView.swift b/Hotline/macOS/NewsView.swift
index 45cb057..91bf2fe 100644
--- a/Hotline/macOS/NewsView.swift
+++ b/Hotline/macOS/NewsView.swift
@@ -147,7 +147,7 @@ struct NewsView: View {
NewsItemView(news: newsItem, depth: 0).tag(newsItem.id)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
- .environment(\.defaultMinListRowHeight, 34)
+ .environment(\.defaultMinListRowHeight, 28)
.listStyle(.inset)
.alternatingRowBackgrounds(.enabled)
.contextMenu(forSelectionType: NewsInfo.self) { items in
@@ -219,7 +219,7 @@ struct NewsView: View {
VStack {
HStack {
ProgressView {
- Text("Loading News")
+ Text("Loading Newsgroups")
}
.controlSize(.regular)
}
diff --git a/Hotline/macOS/ServerView.swift b/Hotline/macOS/ServerView.swift
index 979a233..e9f4a96 100644
--- a/Hotline/macOS/ServerView.swift
+++ b/Hotline/macOS/ServerView.swift
@@ -469,7 +469,7 @@ struct ServerView: View {
case .news:
NewsView()
.navigationTitle(model.serverTitle)
- .navigationSubtitle("News")
+ .navigationSubtitle("Newsgroups")
.navigationSplitViewColumnWidth(min: 250, ideal: 500)
case .board:
MessageBoardView()
@@ -609,7 +609,7 @@ struct TransferItemView: View {
HStack(alignment: .center, spacing: 5) {
HStack(spacing: 0) {
Spacer()
- FileIconView(filename: transfer.title)
+ FileIconView(filename: transfer.title, fileType: nil)
.frame(width: 16, height: 16)
.opacity(controlActiveState == .inactive ? 0.5 : 1.0)
// .padding(.leading, 2)