From 767eb58da13d83aa321fff12c97fe433cd8fa734 Mon Sep 17 00:00:00 2001 From: Dustin Mierau Date: Thu, 7 Dec 2023 18:03:44 -0800 Subject: A semi working file browser. --- Hotline/Views/ChatView.swift | 4 +- Hotline/Views/FilesView.swift | 256 ++++++++++++++++++++++++----------- Hotline/Views/MessageBoardView.swift | 4 +- Hotline/Views/NewsView.swift | 4 +- Hotline/Views/UserListView.swift | 5 +- 5 files changed, 186 insertions(+), 87 deletions(-) (limited to 'Hotline/Views') diff --git a/Hotline/Views/ChatView.swift b/Hotline/Views/ChatView.swift index 2283cdd..28e548d 100644 --- a/Hotline/Views/ChatView.swift +++ b/Hotline/Views/ChatView.swift @@ -106,11 +106,11 @@ struct ChatView: View { Button { hotline.disconnect() } label: { - Image(systemName: "xmark.circle.fill") + Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) + .font(.title2) .foregroundColor(.secondary) } - } } diff --git a/Hotline/Views/FilesView.swift b/Hotline/Views/FilesView.swift index 52963e0..091d718 100644 --- a/Hotline/Views/FilesView.swift +++ b/Hotline/Views/FilesView.swift @@ -1,21 +1,23 @@ import SwiftUI import UniformTypeIdentifiers -struct FileListView: View { +struct FileView: View { @Environment(HotlineClient.self) private var hotline - @State private var fetched = false + @State var file: HotlineFile + @State var loaded = false @State var fileList: [HotlineFile] = [] - var path: [String] = [] + let depth: Int + let path: [String] static let byteFormatter = ByteCountFormatter() private func formattedFileSize(_ fileSize: UInt32) -> String { // let bcf = ByteCountFormatter() - FileListView.byteFormatter.allowedUnits = [.useAll] - FileListView.byteFormatter.countStyle = .file - return FileListView.byteFormatter.string(fromByteCount: Int64(fileSize)) + FileView.byteFormatter.allowedUnits = [.useAll] + FileView.byteFormatter.countStyle = .file + return FileView.byteFormatter.string(fromByteCount: Int64(fileSize)) } private func fileIcon(name: String) -> UIImage { @@ -45,103 +47,199 @@ struct FileListView: View { } var body: some View { - List { - ForEach(fileList, id: \.self) { file in - if file.isFolder { - DisclosureGroup { - if !fetched { - ProgressView() - } - else { - FileListView(path: []) - } - } label: { - HStack { - HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) { - Image(systemName: "folder.fill") + if file.isFolder { + DisclosureGroup { + if !loaded { + Text("Loading...") + .task { + if !loaded { + hotline.sendGetFileList(path: path) { + print("FETCHED!") + } reply: { newFiles in + print("GOT FILES REPLY?", newFiles) + + fileList = newFiles + loaded = true + } } - .frame(minWidth: 25) - Text(file.name).fontWeight(.medium) - Spacer() - Text("\(file.fileSize)").foregroundStyle(.secondary) } - } } else { + FileListView(fileList: fileList, depth: depth + 1, path: path + [file.name]) + } + } label: { + VStack { HStack { HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) { - Image(uiImage: fileIcon(name: file.name)) - .renderingMode(.template) - .foregroundColor(.accentColor) + Image(systemName: "folder.fill") } .frame(minWidth: 25) - Text(file.name) + Text(file.name).fontWeight(.medium) Spacer() - Text(formattedFileSize(file.fileSize)).foregroundStyle(.gray) + Text("\(file.fileSize)").foregroundStyle(.secondary) } } + .frame(height: 44) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + + // Divider() + // .padding(EdgeInsets(top: 0, leading: 16 + 25 + 8, bottom: 0, trailing: 0)) } + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: depth == 0 ? 16 : 0)) + .background(Color(uiColor: UIColor.systemBackground)) } - .listStyle(.plain) - .task { - if !fetched { - hotline.sendGetFileList() { - print("FETCHED!") - fetched = true - } reply: { - print("GOT FILES REPLY?") - fileList = hotline.fileList + else { + VStack { + HStack { + HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) { + Image(uiImage: fileIcon(name: file.name)) + .renderingMode(.template) + .foregroundColor(.accentColor) + } + .frame(minWidth: 25) + Text(file.name) + Spacer() + Text(formattedFileSize(file.fileSize)).foregroundStyle(.gray) } + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 0)) } + .frame(height: 44) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: depth == 0 ? 16 : 0)) + .background(Color(uiColor: UIColor.systemBackground)) + + Divider() + .padding(EdgeInsets(top: 0, leading: 16 + 25 + 8, bottom: 0, trailing: 0)) } } } -struct FilesView: View { +struct FileListView: View { @Environment(HotlineClient.self) private var hotline @State private var fetched = false + @State private var loaded = false + @State var fileList: [HotlineFile] = [] + @State var expanded = false -// let fileList: [HotlineFile] - -// static let byteFormatter = ByteCountFormatter() -// -// private func formattedFileSize(_ fileSize: UInt32) -> String { -// // let bcf = ByteCountFormatter() -// FilesView.byteFormatter.allowedUnits = [.useAll] -// FilesView.byteFormatter.countStyle = .file -// return FilesView.byteFormatter.string(fromByteCount: Int64(fileSize)) -// } -// -// private func fileIcon(name: String) -> UIImage { -// // func utTypeForFilename(_ filename: String) -> UTType? { -// let fileExtension = (name as NSString).pathExtension -// if let fileType = UTType(filenameExtension: fileExtension) { -// print("\(name) \(fileExtension) = \(fileType)") -// -// if fileType.isSubtype(of: .movie) { -// return UIImage(systemName: "play.rectangle")! -// } -// else if fileType.isSubtype(of: .image) { -// return UIImage(systemName: "photo")! -// } -// else if fileType.isSubtype(of: .archive) { -// return UIImage(systemName: "doc.zipper")! -// } -// else if fileType.isSubtype(of: .text) { -// return UIImage(systemName: "doc.text")! -// } -// else { -// return UIImage(systemName: "doc")! -// } -// } -// -// return UIImage(systemName: "doc")! -// } + var depth = 0 + + var path: [String] = [] + + var body: some View { + LazyVStack(alignment: .leading, spacing: 0) { + Section { + ForEach(fileList, id: \.self) { file in + FileView(file: file, depth: depth, path: path + [file.name]) + } + } + Spacer() + } + .listStyle(.plain) + .animation(.default, value: fileList) + } +} + +struct BestFileView: View { + @Environment(HotlineClient.self) private var hotline + + @State var expanded = false + + var file: HotlineFile + + var body: some View { + if file.isFolder { + DisclosureGroup(isExpanded: $expanded) { + ForEach(file.files!) { childFile in + BestFileView(file: childFile) + .frame(height: 44) + } + } label: { + HStack { + HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) { + Image(systemName: "folder.fill") + } + .frame(minWidth: 25) + Text(file.name).fontWeight(.medium) + Spacer() + Text("\(file.fileSize)").foregroundStyle(.secondary) + } + } + .onChange(of: expanded) { + print("EXPANDED CHANGED") + + hotline.sendGetFileList(path: file.path) + } + } + else { + HStack { + HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) { + Image(uiImage: fileIcon(name: file.name)) + } + .frame(minWidth: 25) + Text(file.name).fontWeight(.medium) + Spacer() + Text(formattedFileSize(file.fileSize)).foregroundStyle(.secondary) + } + } + } + + static let byteFormatter = ByteCountFormatter() + + private func formattedFileSize(_ fileSize: UInt32) -> String { + // let bcf = ByteCountFormatter() + BestFileView.byteFormatter.allowedUnits = [.useAll] + BestFileView.byteFormatter.countStyle = .file + return BestFileView.byteFormatter.string(fromByteCount: Int64(fileSize)) + } + + private func fileIcon(name: String) -> UIImage { + // func utTypeForFilename(_ filename: String) -> UTType? { + let fileExtension = (name as NSString).pathExtension + if let fileType = UTType(filenameExtension: fileExtension) { + print("\(name) \(fileExtension) = \(fileType)") + + if fileType.isSubtype(of: .movie) { + return UIImage(systemName: "play.rectangle")! + } + else if fileType.isSubtype(of: .image) { + return UIImage(systemName: "photo")! + } + else if fileType.isSubtype(of: .archive) { + return UIImage(systemName: "doc.zipper")! + } + else if fileType.isSubtype(of: .text) { + return UIImage(systemName: "doc.text")! + } + else { + return UIImage(systemName: "doc")! + } + } + + return UIImage(systemName: "doc")! + } +} + +struct FilesView: View { + @Environment(HotlineClient.self) private var hotline + + @State var initialLoad = false var body: some View { NavigationStack { - FileListView(path: []) + List(hotline.fileList) { file in +// OutlineGroup(hotline.fileList, children: \.files, expanded: $expandedFolders) { file in + BestFileView(file: file) + .frame(height: 44) +// } + } + .task { + if !initialLoad { + hotline.sendGetFileList(path: []) { + initialLoad = true + } + } + } + .listStyle(.plain) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .principal) { @@ -152,11 +250,11 @@ struct FilesView: View { Button { hotline.disconnect() } label: { - Image(systemName: "xmark.circle.fill") + Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) + .font(.title2) .foregroundColor(.secondary) } - } } } diff --git a/Hotline/Views/MessageBoardView.swift b/Hotline/Views/MessageBoardView.swift index 35b4282..04f51df 100644 --- a/Hotline/Views/MessageBoardView.swift +++ b/Hotline/Views/MessageBoardView.swift @@ -41,11 +41,11 @@ struct MessageBoardView: View { Button { hotline.disconnect() } label: { - Image(systemName: "xmark.circle.fill") + Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) + .font(.title2) .foregroundColor(.secondary) } - } ToolbarItem(placement: .navigationBarTrailing) { Button { diff --git a/Hotline/Views/NewsView.swift b/Hotline/Views/NewsView.swift index 78282d6..539ead8 100644 --- a/Hotline/Views/NewsView.swift +++ b/Hotline/Views/NewsView.swift @@ -105,11 +105,11 @@ struct NewsView: View { Button { hotline.disconnect() } label: { - Image(systemName: "xmark.circle.fill") + Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) + .font(.title2) .foregroundColor(.secondary) } - } ToolbarItem(placement: .navigationBarTrailing) { Button { diff --git a/Hotline/Views/UserListView.swift b/Hotline/Views/UserListView.swift index e0fa7fb..8ea186e 100644 --- a/Hotline/Views/UserListView.swift +++ b/Hotline/Views/UserListView.swift @@ -6,7 +6,7 @@ struct UserListView: View { var body: some View { NavigationStack { List(hotline.userList) { u in - Text(u.name) + Text("🤖 \(u.name)") .fontWeight(.medium) .lineLimit(1) .truncationMode(.tail) @@ -24,8 +24,9 @@ struct UserListView: View { Button { hotline.disconnect() } label: { - Image(systemName: "xmark.circle.fill") + Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) + .font(.title2) .foregroundColor(.secondary) } } -- cgit