From 98d58bf5284f6fce850cb7f80aec16b9b5f701eb Mon Sep 17 00:00:00 2001 From: Navan Chauhan Date: Tue, 19 Dec 2023 16:11:26 -0700 Subject: fix and format --- .../AppIcon.appiconset/Contents.json | 13 +- .../AppIcon.appiconset/iGopherBrowserLogo-2 1.png | Bin 81570 -> 0 bytes .../AppIcon.appiconset/icon_128x128.png | Bin 0 -> 12235 bytes .../AppIcon.appiconset/icon_128x128@2x.png | Bin 0 -> 30154 bytes .../AppIcon.appiconset/icon_16x16.png | Bin 0 -> 646 bytes .../AppIcon.appiconset/icon_16x16@2x.png | Bin 0 -> 1795 bytes .../AppIcon.appiconset/icon_256x256.png | Bin 0 -> 30154 bytes .../AppIcon.appiconset/icon_256x256@2x.png | Bin 0 -> 64876 bytes .../AppIcon.appiconset/icon_32x32.png | Bin 0 -> 1795 bytes .../AppIcon.appiconset/icon_32x32@2x.png | Bin 0 -> 4774 bytes .../AppIcon.appiconset/icon_512x512.png | Bin 0 -> 64876 bytes .../AppIcon.appiconset/icon_512x512@2x.png | Bin 0 -> 156298 bytes iGopherBrowser/BrowserView.swift | 595 +++++++++++---------- iGopherBrowser/ContentView.swift | 82 ++- iGopherBrowser/FileView.swift | 222 ++++---- iGopherBrowser/Helpers.swift | 61 ++- iGopherBrowser/Info.plist | 6 +- iGopherBrowser/Item.swift | 18 - iGopherBrowser/SearchInputView.swift | 54 +- iGopherBrowser/SidebarView.swift | 26 +- iGopherBrowser/iGopherBrowser.entitlements | 10 - iGopherBrowser/iGopherBrowserApp.swift | 33 +- 22 files changed, 549 insertions(+), 571 deletions(-) delete mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/iGopherBrowserLogo-2 1.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512.png create mode 100644 iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png delete mode 100644 iGopherBrowser/Item.swift diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/Contents.json b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/Contents.json index 1110c85..adda6b3 100644 --- a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,58 +1,67 @@ { "images" : [ { - "filename" : "iGopherBrowserLogo-2 1.png", + "filename" : "iGopherBrowserLogo-2.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" }, { + "filename" : "icon_16x16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { + "filename" : "icon_16x16@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { + "filename" : "icon_32x32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { + "filename" : "icon_32x32@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { + "filename" : "icon_128x128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { + "filename" : "icon_128x128@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { + "filename" : "icon_256x256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { + "filename" : "icon_256x256@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { + "filename" : "icon_512x512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { - "filename" : "iGopherBrowserLogo-2.png", + "filename" : "icon_512x512@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/iGopherBrowserLogo-2 1.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/iGopherBrowserLogo-2 1.png deleted file mode 100644 index a3e0c2e..0000000 Binary files a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/iGopherBrowserLogo-2 1.png and /dev/null differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128.png new file mode 100644 index 0000000..8ffc07f Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png new file mode 100644 index 0000000..951a0ba Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16.png new file mode 100644 index 0000000..e6d6265 Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png new file mode 100644 index 0000000..70cfbf9 Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256.png new file mode 100644 index 0000000..951a0ba Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png new file mode 100644 index 0000000..78f8ce6 Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32.png new file mode 100644 index 0000000..70cfbf9 Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png new file mode 100644 index 0000000..bfb9ae7 Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512.png new file mode 100644 index 0000000..78f8ce6 Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512.png differ diff --git a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png new file mode 100644 index 0000000..0699d09 Binary files /dev/null and b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png differ diff --git a/iGopherBrowser/BrowserView.swift b/iGopherBrowser/BrowserView.swift index 663851c..9aa45cc 100644 --- a/iGopherBrowser/BrowserView.swift +++ b/iGopherBrowser/BrowserView.swift @@ -5,312 +5,331 @@ // Created by Navan Chauhan on 12/16/23. // +import GopherHelpers import SwiftUI import swiftGopherClient -import GopherHelpers struct BrowserView: View { - - @State var url: String = "" - @State private var gopherItems: [gopherItem] = [] - - @Binding public var hosts: [GopherNode] - @Binding var selectedNode: GopherNode? - - @State private var backwardStack: [GopherNode] = [] - @State private var forwardStack: [GopherNode] = [] - - @State private var searchText: String = "" - @State private var showSearchInput = false - @State var selectedSearchItem: Int? - - let client = GopherClient() - - var body: some View { - NavigationStack { - VStack(spacing: 0) { - if (gopherItems.count >= 1) { - List { - ForEach(Array(gopherItems.enumerated()), id: \.offset) { idx, item in - if item.parsedItemType == .info { - Text(item.message) - .font(.system(size: 12, design: .monospaced)) - .frame(height: 20) - .listRowInsets(EdgeInsets()) - .listRowSeparator(.hidden) - } else if item.parsedItemType == .directory { - HStack { - Text(Image(systemName: "folder")) - Text(item.message) - Spacer() - }.onTapGesture { - performGopherRequest(host: item.host, port: item.port, selector: item.selector) - } - } else if item.parsedItemType == .search { - HStack { - Text(Image(systemName: "magnifyingglass")) - Text(item.message) - Spacer() - }.onTapGesture { - self.selectedSearchItem = idx - self.showSearchInput = true - } - } else if item.parsedItemType == .text { - NavigationLink(destination: FileView(item: item)) { - HStack { - Text(Image(systemName: "doc.plaintext")) - Text(item.message) - Spacer() - } - } - } else if [.doc, .image, .gif, .movie, .sound, .bitmap].contains(item.parsedItemType) { - NavigationLink(destination: FileView(item: item)) { - HStack { - Text(Image(systemName: itemToImageType(item))) - Text(item.message) - Spacer() - } - } - } else { - Text(item.message) - .onTapGesture { - performGopherRequest(host: item.host, port: item.port, selector: item.selector) - } - - } - } - } - .background(Color.white) - .cornerRadius(10) - .sheet(isPresented: $showSearchInput) { - if let index = selectedSearchItem, gopherItems.indices.contains(index) { - let searchItem = gopherItems[index] - SearchInputView( - host: searchItem.host, - port: searchItem.port, - selector: searchItem.selector, - searchText: $searchText, - onSearch: { query in - performGopherRequest(host: searchItem.host, port: searchItem.port, selector: "\(searchItem.selector)\t\(query)") - showSearchInput = false - } - ) - } else { - - VStack { - Text("Weird bug. Please Dismiss -> Press Go -> Try Again") - Button("Dismiss") { - self.showSearchInput = false - } - } - - } - } - } else { + + @State var url: String = "" + @State private var gopherItems: [gopherItem] = [] + + @Binding public var hosts: [GopherNode] + @Binding var selectedNode: GopherNode? + + @State private var backwardStack: [GopherNode] = [] + @State private var forwardStack: [GopherNode] = [] + + @State private var searchText: String = "" + @State private var showSearchInput = false + @State var selectedSearchItem: Int? + + let client = GopherClient() + + var body: some View { + NavigationStack { + VStack(spacing: 0) { + if gopherItems.count >= 1 { + List { + ForEach(Array(gopherItems.enumerated()), id: \.offset) { idx, item in + if item.parsedItemType == .info { + Text(item.message) + .font(.system(size: 12, design: .monospaced)) + .frame(height: 20) + .listRowInsets(EdgeInsets()) + .listRowSeparator(.hidden) + } else if item.parsedItemType == .directory { + HStack { + Text(Image(systemName: "folder")) + Text(item.message) + Spacer() + }.onTapGesture { + performGopherRequest(host: item.host, port: item.port, selector: item.selector) + } + } else if item.parsedItemType == .search { + HStack { + Text(Image(systemName: "magnifyingglass")) + Text(item.message) + Spacer() + }.onTapGesture { + self.selectedSearchItem = idx + self.showSearchInput = true + } + } else if item.parsedItemType == .text { + NavigationLink(destination: FileView(item: item)) { + HStack { + Text(Image(systemName: "doc.plaintext")) + Text(item.message) Spacer() - Text("Welcome to iGopher Browser") + } + } + } else if [.doc, .image, .gif, .movie, .sound, .bitmap].contains(item.parsedItemType) + { + NavigationLink(destination: FileView(item: item)) { + HStack { + Text(Image(systemName: itemToImageType(item))) + Text(item.message) Spacer() + } } - #if os(iOS) - VStack { - HStack(spacing: 10) { - HStack { - Spacer() + } else { + Text(item.message) + .onTapGesture { + performGopherRequest(host: item.host, port: item.port, selector: item.selector) + } - - - TextField("Enter a URL", text: $url) - #if !os(OSX) - .keyboardType(.URL) - .autocapitalization(.none) - #endif - .padding(10) - Spacer() - } - //.background(Color.white) - .cornerRadius(30) - - Button("Go", action: { - performGopherRequest(clearForward: false) - }) - .keyboardShortcut(.defaultAction) - .onSubmit { - performGopherRequest() - } - Spacer() - } - HStack { - Spacer() - Button { - performGopherRequest(host:"gopher.navan.dev",port: 70,selector: "/") - } label: { - Label("Home", systemImage: "house") - .labelStyle(.iconOnly) - } - Spacer() - Button { - if let curNode = backwardStack.popLast() { - forwardStack.append(curNode) - if let prevNode = backwardStack.popLast() { - performGopherRequest(host: prevNode.host, port: prevNode.port, selector: prevNode.selector, clearForward: false) - } - } - } label: { - Label("Back", systemImage: "chevron.left") - .labelStyle(.iconOnly) - } - .disabled(backwardStack.count < 2) - Spacer() - Button { - if let nextNode = forwardStack.popLast() { - //backwardStack.append(nextNode) - performGopherRequest(host: nextNode.host, port: nextNode.port, selector: nextNode.selector, clearForward: false) - } - } label: { - Label("Forward", systemImage: "chevron.right") - .labelStyle(.iconOnly) - } - .disabled(forwardStack.isEmpty) - Spacer() - } + } + } + } + .background(Color.white) + .cornerRadius(10) + .sheet(isPresented: $showSearchInput) { + if let index = selectedSearchItem, gopherItems.indices.contains(index) { + let searchItem = gopherItems[index] + SearchInputView( + host: searchItem.host, + port: searchItem.port, + selector: searchItem.selector, + searchText: $searchText, + onSearch: { query in + performGopherRequest( + host: searchItem.host, port: searchItem.port, + selector: "\(searchItem.selector)\t\(query)") + showSearchInput = false } - #else - HStack(spacing: 10) { - HStack { - Spacer() - Button { - performGopherRequest(host:"gopher.navan.dev",port: 70,selector: "/") - } label: { - Label("Home", systemImage: "house") - .labelStyle(.iconOnly) - } - - Button { - if let curNode = backwardStack.popLast() { - forwardStack.append(curNode) - if let prevNode = backwardStack.popLast() { - performGopherRequest(host: prevNode.host, port: prevNode.port, selector: prevNode.selector, clearForward: false) - } - } - } label: { - Label("Back", systemImage: "chevron.left") - .labelStyle(.iconOnly) - } - .disabled(backwardStack.count < 2) - - Button { - if let nextNode = forwardStack.popLast() { - //backwardStack.append(nextNode) - performGopherRequest(host: nextNode.host, port: nextNode.port, selector: nextNode.selector, clearForward: false) - } - } label: { - Label("Forward", systemImage: "chevron.right") - .labelStyle(.iconOnly) - } - .disabled(forwardStack.isEmpty) - - - TextField("Enter a URL", text: $url) -#if !os(OSX) - .keyboardType(.URL) - .autocapitalization(.none) -#endif - .padding(10) - Spacer() - } - //.background(Color.white) - .cornerRadius(30) - - Button("Go", action: { - performGopherRequest(clearForward: false) - }) - .keyboardShortcut(.defaultAction) - .onSubmit { - performGopherRequest() - } - Spacer() + ) + } else { + + VStack { + Text("Weird bug. Please Dismiss -> Press Go -> Try Again") + Button("Dismiss") { + self.showSearchInput = false + } + } + + } + } + } else { + Spacer() + Text("Welcome to iGopher Browser") + Spacer() + } + #if os(iOS) + VStack { + HStack(spacing: 10) { + HStack { + Spacer() + + TextField("Enter a URL", text: $url) + #if !os(OSX) + .keyboardType(.URL) + .autocapitalization(.none) + #endif + .padding(10) + Spacer() + } + //.background(Color.white) + .cornerRadius(30) + + Button( + "Go", + action: { + performGopherRequest(clearForward: false) + } + ) + .keyboardShortcut(.defaultAction) + .onSubmit { + performGopherRequest() + } + Spacer() + } + HStack { + Spacer() + Button { + performGopherRequest(host: "gopher.navan.dev", port: 70, selector: "/") + } label: { + Label("Home", systemImage: "house") + .labelStyle(.iconOnly) + } + Spacer() + Button { + if let curNode = backwardStack.popLast() { + forwardStack.append(curNode) + if let prevNode = backwardStack.popLast() { + performGopherRequest( + host: prevNode.host, port: prevNode.port, selector: prevNode.selector, + clearForward: false) + } + } + } label: { + Label("Back", systemImage: "chevron.left") + .labelStyle(.iconOnly) + } + .disabled(backwardStack.count < 2) + Spacer() + Button { + if let nextNode = forwardStack.popLast() { + //backwardStack.append(nextNode) + performGopherRequest( + host: nextNode.host, port: nextNode.port, selector: nextNode.selector, + clearForward: false) + } + } label: { + Label("Forward", systemImage: "chevron.right") + .labelStyle(.iconOnly) + } + .disabled(forwardStack.isEmpty) + Spacer() + } + } + #else + HStack(spacing: 10) { + HStack { + Spacer() + Button { + performGopherRequest(host: "gopher.navan.dev", port: 70, selector: "/") + } label: { + Label("Home", systemImage: "house") + .labelStyle(.iconOnly) + } + + Button { + if let curNode = backwardStack.popLast() { + forwardStack.append(curNode) + if let prevNode = backwardStack.popLast() { + performGopherRequest( + host: prevNode.host, port: prevNode.port, selector: prevNode.selector, + clearForward: false) + } + } + } label: { + Label("Back", systemImage: "chevron.left") + .labelStyle(.iconOnly) + } + .disabled(backwardStack.count < 2) + + Button { + if let nextNode = forwardStack.popLast() { + //backwardStack.append(nextNode) + performGopherRequest( + host: nextNode.host, port: nextNode.port, selector: nextNode.selector, + clearForward: false) } + } label: { + Label("Forward", systemImage: "chevron.right") + .labelStyle(.iconOnly) + } + .disabled(forwardStack.isEmpty) + + TextField("Enter a URL", text: $url) + #if !os(OSX) + .keyboardType(.URL) + .autocapitalization(.none) #endif + .padding(10) + Spacer() } - } - .onChange(of: selectedNode) { - if let node = selectedNode { - performGopherRequest(host: node.host, port: node.port, selector: node.selector) + //.background(Color.white) + .cornerRadius(30) + + Button( + "Go", + action: { + performGopherRequest(clearForward: false) + } + ) + .keyboardShortcut(.defaultAction) + .onSubmit { + performGopherRequest() } - } + Spacer() + } + #endif + } + } + .onChange(of: selectedNode) { + if let node = selectedNode { + performGopherRequest(host: node.host, port: node.port, selector: node.selector) + } + } + } + + private func performGopherRequest( + host: String = "", port: Int = -1, selector: String = "", clearForward: Bool = true + ) { + // TODO: Remove getHostandPort call here, and call it before calling performGopherRequest + print("recieved ", host, port, selector) + var res = getHostAndPort(from: self.url) + + if host != "" { + res.host = host + if selector != "" { + res.selector = selector + } else { + res.selector = "" + } } - - private func performGopherRequest(host: String = "", port: Int = -1, selector: String = "", clearForward: Bool = true) { - // TODO: Remove getHostandPort call here, and call it before calling performGopherRequest - print("recieved ", host, port, selector) - var res = getHostAndPort(from: self.url) - - if host != "" { - res.host = host - if selector != "" { - res.selector = selector + + if port != -1 { + res.port = port + } + + self.url = "\(res.host):\(res.port)\(res.selector)" + + client.sendRequest(to: res.host, port: res.port, message: "\(res.selector)\r\n") { result in + switch result { + case .success(let resp): + //print(resp) + var newNode = GopherNode( + host: res.host, port: res.port, selector: selector, item: nil, + children: convertToHostNodes(resp)) + backwardStack.append(newNode) + if clearForward { + forwardStack.removeAll() + } + print(newNode.selector) + if let index = self.hosts.firstIndex(where: { $0.host == res.host && $0.port == res.port }) + { + // TODO: Handle case where first link visited is a subdirectory, should the sidebar auto fetch the rest? + print("parent already exists") + //hosts[index] = newNode + hosts[index].children = hosts[index].children?.map { child in + if child.selector == newNode.selector { + newNode.message = child.message + return newNode } else { - res.selector = "" + return child } + } + + } else { + newNode.selector = "/" + hosts.append(newNode) + print("created new") } - - if port != -1 { - res.port = port - } - - - - self.url = "\(res.host):\(res.port)\(res.selector)" - - client.sendRequest(to: res.host, port: res.port, message: "\(res.selector)\r\n") { result in - switch result { - case .success(let resp): - //print(resp) - var newNode = GopherNode(host: res.host, port: res.port, selector: selector, item: nil, children: convertToHostNodes(resp)) - backwardStack.append(newNode) - if clearForward { - forwardStack.removeAll() - } - print(newNode.selector) - if let index = self.hosts.firstIndex(where: { $0.host == res.host && $0.port == res.port }) { - // TODO: Handle case where first link visited is a subdirectory, should the sidebar auto fetch the rest? - print("parent already exists") - //hosts[index] = newNode - hosts[index].children = hosts[index].children?.map { child in - if child.selector == newNode.selector { - newNode.message = child.message - return newNode - } else { - return child - } - } - - } else { - newNode.selector = "/" - hosts.append(newNode) - print("created new") - } - self.gopherItems = resp - - - case .failure(let error): - print("Error \(error)") - var item = gopherItem(rawLine: "Error \(error)") - item.message = "Error \(error)" - self.gopherItems = [item] - } - } + self.gopherItems = resp + + case .failure(let error): + print("Error \(error)") + var item = gopherItem(rawLine: "Error \(error)") + item.message = "Error \(error)" + self.gopherItems = [item] + } } - - private func convertToHostNodes(_ responseItems: [gopherItem]) -> [GopherNode] { - var returnItems: [GopherNode] = [] - responseItems.forEach { item in - if item.parsedItemType != .info { - returnItems.append(GopherNode(host: item.host, port: item.port, selector: item.selector, message: item.message, item: item, children: nil)) - //print("found: \(item.message)") - } - } - return returnItems + } + + private func convertToHostNodes(_ responseItems: [gopherItem]) -> [GopherNode] { + var returnItems: [GopherNode] = [] + responseItems.forEach { item in + if item.parsedItemType != .info { + returnItems.append( + GopherNode( + host: item.host, port: item.port, selector: item.selector, message: item.message, + item: item, children: nil)) + //print("found: \(item.message)") + } } + return returnItems + } } diff --git a/iGopherBrowser/ContentView.swift b/iGopherBrowser/ContentView.swift index 26dd941..31c9688 100644 --- a/iGopherBrowser/ContentView.swift +++ b/iGopherBrowser/ContentView.swift @@ -5,61 +5,55 @@ // Created by Navan Chauhan on 12/12/23. // +import GopherHelpers import SwiftUI -import SwiftData - import swiftGopherClient -import GopherHelpers struct GopherNode: Identifiable, Equatable { - static func == (lhs: GopherNode, rhs: GopherNode) -> Bool { - return lhs.host == rhs.host && lhs.port == rhs.port && lhs.selector == rhs.selector - } - - let id = UUID() - var host: String - let port: Int - var selector: String - var message: String? - let item: gopherItem? - var children: [GopherNode]? + static func == (lhs: GopherNode, rhs: GopherNode) -> Bool { + return lhs.host == rhs.host && lhs.port == rhs.port && lhs.selector == rhs.selector + } + + let id = UUID() + var host: String + let port: Int + var selector: String + var message: String? + let item: gopherItem? + var children: [GopherNode]? } struct ContentView: View { - @Environment(\.modelContext) private var modelContext - @Query private var items: [Item] - - @State public var hosts: [GopherNode] = [] - @State private var selectedNode: GopherNode? - - @State private var columnVisibility = NavigationSplitViewVisibility.automatic - var body: some View { - - #if os(iOS) - BrowserView(hosts: $hosts, selectedNode: $selectedNode) - #else - - NavigationSplitView(columnVisibility: $columnVisibility) { - SidebarView(hosts: hosts, onSelect: { node in - selectedNode = node - - }) - .listStyle(.sidebar) - } detail: { - BrowserView(hosts: $hosts, selectedNode: $selectedNode) - } - #endif - } - - - + @State public var hosts: [GopherNode] = [] + @State private var selectedNode: GopherNode? -} + @State private var columnVisibility = NavigationSplitViewVisibility.automatic + var body: some View { + #if os(iOS) + BrowserView(hosts: $hosts, selectedNode: $selectedNode) + #else + + NavigationSplitView(columnVisibility: $columnVisibility) { + SidebarView( + hosts: hosts, + onSelect: { node in + selectedNode = node + + } + ) + .listStyle(.sidebar) + } detail: { + BrowserView(hosts: $hosts, selectedNode: $selectedNode) + } + #endif + } + +} #Preview { - ContentView() - //.modelContainer(for: Item.self, inMemory: true) + ContentView() + //.modelContainer(for: Item.self, inMemory: true) } diff --git a/iGopherBrowser/FileView.swift b/iGopherBrowser/FileView.swift index c4080e4..32b7068 100644 --- a/iGopherBrowser/FileView.swift +++ b/iGopherBrowser/FileView.swift @@ -5,131 +5,133 @@ // Created by Navan Chauhan on 12/16/23. // import Foundation -import SwiftUI -import swiftGopherClient import GopherHelpers import QuickLook - +import SwiftUI +import swiftGopherClient func determineFileType(data: Data) -> String? { - let signatures: [Data: String] = [ - Data([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]): "png", - Data([0xFF, 0xD8, 0xFF]): "jpeg", - Data("GIF87a".utf8): "gif", - Data("GIF89a".utf8): "gif", - Data("BM".utf8): "bmp", - Data("%PDF-".utf8): "pdf", - Data([0x50, 0x4B, 0x03, 0x04]): "docx", - Data([0x50, 0x4B, 0x05, 0x06]): "docx", - Data([0x50, 0x4B, 0x07, 0x08]): "docx", - Data([0x49, 0x44, 0x33]): "mp3", - Data([0x52, 0x49, 0x46, 0x46]): "wav", - Data([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70]): "mp4", - Data([0x6D, 0x6F, 0x6F, 0x76]): "mov", - // Add other file signatures as needed - ] + let signatures: [Data: String] = [ + Data([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]): "png", + Data([0xFF, 0xD8, 0xFF]): "jpeg", + Data("GIF87a".utf8): "gif", + Data("GIF89a".utf8): "gif", + Data("BM".utf8): "bmp", + Data("%PDF-".utf8): "pdf", + Data([0x50, 0x4B, 0x03, 0x04]): "docx", + Data([0x50, 0x4B, 0x05, 0x06]): "docx", + Data([0x50, 0x4B, 0x07, 0x08]): "docx", + Data([0x49, 0x44, 0x33]): "mp3", + Data([0x52, 0x49, 0x46, 0x46]): "wav", + Data([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70]): "mp4", + Data([0x6D, 0x6F, 0x6F, 0x76]): "mov", + // Add other file signatures as needed + ] - // Check for each signature - for (signature, fileType) in signatures { - if data.starts(with: signature) { - return fileType - } + // Check for each signature + for (signature, fileType) in signatures { + if data.starts(with: signature) { + return fileType } - - return nil -} + } + return nil +} struct FileView: View { - var item: gopherItem - let client = GopherClient() - @State private var fileContent: String = "Loading..." - @State private var fileURL: URL? - @State private var QLURL: URL? - - let fileSignatures: [String: [UInt8]] = [ - "jpg": [0xFF, 0xD8, 0xFF], - "png": [0x89, 0x50, 0x4E, 0x47], - "gif": [0x47, 0x49, 0x46], - "pdf": [0x25, 0x50, 0x44, 0x46], - ] + var item: gopherItem + let client = GopherClient() + @State private var fileContent: String = "Loading..." + @State private var fileURL: URL? + @State private var QLURL: URL? - var body: some View { - if item.parsedItemType == .text { - ScrollView { - Text(fileContent) - .onAppear { - readFile(item) - } - } - } else if [.doc, .image, .gif, .movie, .sound, .bitmap].contains(item.parsedItemType) { // Preview Document: .pdf, .docx, e.t.c - // Quicklook - if let url = fileURL { - Button("Preview Document") { - print(url) - QLURL = url - }.quickLookPreview($QLURL) - } else { - Text("Loading Document...") - .onAppear { - readFile(item) - } - } + let fileSignatures: [String: [UInt8]] = [ + "jpg": [0xFF, 0xD8, 0xFF], + "png": [0x89, 0x50, 0x4E, 0x47], + "gif": [0x47, 0x49, 0x46], + "pdf": [0x25, 0x50, 0x44, 0x46], + ] + + var body: some View { + if item.parsedItemType == .text { + GeometryReader { geometry in + ScrollView { + VStack { + Text(fileContent) + .onAppear { + readFile(item) + } + .frame(width: geometry.size.width, height: geometry.size.height) + Spacer() + } } + } + } else if [.doc, .image, .gif, .movie, .sound, .bitmap].contains(item.parsedItemType) { // Preview Document: .pdf, .docx, e.t.c + // Quicklook + if let url = fileURL { + Button("Preview Document") { + print(url) + QLURL = url + }.quickLookPreview($QLURL) + } else { + Text("Loading Document...") + .onAppear { + readFile(item) + } + } } + } - private func readFile(_ item: gopherItem) { - self.client.sendRequest(to: item.host, port: item.port, message: "\(item.selector)\r\n") { result in - // Dispatch the result handling back to the main thread - switch result { - case .success(let resp): - if item.parsedItemType == .text { - if let firstLine = resp.first?.rawLine { - self.fileContent = firstLine - } else { - self.fileContent = "File is empty or couldn't be read." - } - } else { - if var data = resp.first?.rawData { - let tempDirURL = FileManager.default.temporaryDirectory - - do { - var fileData = Data() - print("Readable byees", data.readableBytes) - while data.readableBytes > 0 { - if let bytes = data.readBytes(length: data.readableBytes) { - fileData.append(contentsOf: bytes) - } - } - - let fileURL = tempDirURL.appendingPathComponent(UUID().uuidString + ".\(determineFileType(data: fileData) ?? "unkown")") - print(fileURL) - try fileData.write(to: fileURL) - self.fileURL = fileURL - } catch { - print("Error writing file to temp directory: \(error)") - } - } - } - case .failure(_): - self.fileContent = "Unable to fetch file due to network error." - } - } - - - } - - private func getTempFileURL(_ data: [UInt8]) -> URL? { - let tempDirURL = FileManager.default.temporaryDirectory - let fileURL = tempDirURL.appendingPathComponent(UUID().uuidString + ".pdf") - print(fileURL) - do { - let fileData = Data(data) + private func readFile(_ item: gopherItem) { + self.client.sendRequest(to: item.host, port: item.port, message: "\(item.selector)\r\n") { + result in + // Dispatch the result handling back to the main thread + switch result { + case .success(let resp): + if var data = resp.first?.rawData { + let tempDirURL = FileManager.default.temporaryDirectory + + do { + var fileData = Data() + print("Readable byees", data.readableBytes) + while data.readableBytes > 0 { + if let bytes = data.readBytes(length: data.readableBytes) { + fileData.append(contentsOf: bytes) + } + } + if item.parsedItemType == .text { + if let string = String(data: fileData, encoding: .utf8) { + self.fileContent = string + } + return + } + let fileURL = tempDirURL.appendingPathComponent( + UUID().uuidString + ".\(determineFileType(data: fileData) ?? "unkown")") + print(fileURL) try fileData.write(to: fileURL) - return fileURL - } catch { + self.fileURL = fileURL + } catch { print("Error writing file to temp directory: \(error)") - return nil + } } + case .failure(_): + self.fileContent = "Unable to fetch file due to network error." + } + } + + } + + private func getTempFileURL(_ data: [UInt8]) -> URL? { + let tempDirURL = FileManager.default.temporaryDirectory + let fileURL = tempDirURL.appendingPathComponent(UUID().uuidString + ".pdf") + print(fileURL) + do { + let fileData = Data(data) + try fileData.write(to: fileURL) + return fileURL + } catch { + print("Error writing file to temp directory: \(error)") + return nil } + } } diff --git a/iGopherBrowser/Helpers.swift b/iGopherBrowser/Helpers.swift index ef42905..0235e2e 100644 --- a/iGopherBrowser/Helpers.swift +++ b/iGopherBrowser/Helpers.swift @@ -8,38 +8,37 @@ import Foundation import swiftGopherClient -public func getHostAndPort(from urlString: String, defaultPort: Int = 70, defaultHost: String = "gopher.navan.dev") -> (host: String, port: Int, selector: String) { - if let urlComponents = URLComponents(string: urlString), - let host = urlComponents.host { - let port = urlComponents.port ?? defaultPort - let selector = urlComponents.path - print("Mainmain, ", urlComponents, host, port, selector) - return (host, port, selector) - } else { - // Fallback for simpler formats like "localhost:8080" - let components = urlString.split(separator: ":") - let host = components.first.map(String.init) ?? defaultHost - - var port = (components.count > 1 ? Int(components[1]) : nil) ?? defaultPort - var selector = "/" - - if (components.count > 1) { - let portCompString = components[1] - let portCompComponents = portCompString.split(separator: "/", maxSplits: 1) - if portCompComponents.count > 1 { - port = Int(portCompComponents[0]) ?? defaultPort - selector = "/" + portCompComponents[1] - - } - } - - - print("Else Else",components, host, port, selector) - return (host, port, selector) - } -} - +public func getHostAndPort( + from urlString: String, defaultPort: Int = 70, defaultHost: String = "gopher.navan.dev" +) -> (host: String, port: Int, selector: String) { + if let urlComponents = URLComponents(string: urlString), + let host = urlComponents.host + { + let port = urlComponents.port ?? defaultPort + let selector = urlComponents.path + print("Mainmain, ", urlComponents, host, port, selector) + return (host, port, selector) + } else { + // Fallback for simpler formats like "localhost:8080" + let components = urlString.split(separator: ":") + let host = components.first.map(String.init) ?? defaultHost + var port = (components.count > 1 ? Int(components[1]) : nil) ?? defaultPort + var selector = "/" + if components.count > 1 { + let portCompString = components[1] + let portCompComponents = portCompString.split(separator: "/", maxSplits: 1) + if portCompComponents.count > 1 { + port = Int(portCompComponents[0]) ?? defaultPort + selector = "/" + portCompComponents[1] + } else if portCompComponents.count == 1 { + port = Int(portCompComponents[0]) ?? defaultPort + } + } + print("Else Else", components, host, port, selector) + return (host, port, selector) + } +} diff --git a/iGopherBrowser/Info.plist b/iGopherBrowser/Info.plist index ca9a074..8608c07 100644 --- a/iGopherBrowser/Info.plist +++ b/iGopherBrowser/Info.plist @@ -2,9 +2,7 @@ - UIBackgroundModes - - remote-notification - + LSApplicationCategoryType + public.app-category.productivity diff --git a/iGopherBrowser/Item.swift b/iGopherBrowser/Item.swift deleted file mode 100644 index b6c02d8..0000000 --- a/iGopherBrowser/Item.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Item.swift -// iGopherBrowser -// -// Created by Navan Chauhan on 12/12/23. -// - -import Foundation -import SwiftData - -@Model -final class Item { - var timestamp: Date - - init(timestamp: Date) { - self.timestamp = timestamp - } -} diff --git a/iGopherBrowser/SearchInputView.swift b/iGopherBrowser/SearchInputView.swift index 3780f95..54e36c5 100644 --- a/iGopherBrowser/SearchInputView.swift +++ b/iGopherBrowser/SearchInputView.swift @@ -8,34 +8,34 @@ import SwiftUI struct SearchInputView: View { - - var host: String - var port: Int - var selector: String - @Binding var searchText: String - var onSearch: (String) -> Void - - @Environment(\.presentationMode) var presentationMode - - var body: some View { - VStack { - Text("Enter your query") - TextField("Search", text: $searchText) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .padding() - HStack { - - Button("Cancel") { - presentationMode.wrappedValue.dismiss() - } - .padding() - - Button("Search") { - onSearch(searchText) - } - .padding() - } + + var host: String + var port: Int + var selector: String + @Binding var searchText: String + var onSearch: (String) -> Void + + @Environment(\.presentationMode) var presentationMode + + var body: some View { + VStack { + Text("Enter your query") + TextField("Search", text: $searchText) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding() + HStack { + + Button("Cancel") { + presentationMode.wrappedValue.dismiss() + } + .padding() + + Button("Search") { + onSearch(searchText) } .padding() + } } + .padding() + } } diff --git a/iGopherBrowser/SidebarView.swift b/iGopherBrowser/SidebarView.swift index 301f45e..f19670f 100644 --- a/iGopherBrowser/SidebarView.swift +++ b/iGopherBrowser/SidebarView.swift @@ -9,18 +9,18 @@ import Foundation import SwiftUI struct SidebarView: View { - let hosts: [GopherNode] - var onSelect: (GopherNode) -> Void - - var body: some View { - VStack { - List(hosts, children: \.children) { node in - Text(node.message ?? node.host) - .onTapGesture { - onSelect(node) - } - } - } - .navigationTitle("Your Gophertree") + let hosts: [GopherNode] + var onSelect: (GopherNode) -> Void + + var body: some View { + VStack { + List(hosts, children: \.children) { node in + Text(node.message ?? node.host) + .onTapGesture { + onSelect(node) + } + } } + .navigationTitle("Your Gophertree") + } } diff --git a/iGopherBrowser/iGopherBrowser.entitlements b/iGopherBrowser/iGopherBrowser.entitlements index 068b8e8..625af03 100644 --- a/iGopherBrowser/iGopherBrowser.entitlements +++ b/iGopherBrowser/iGopherBrowser.entitlements @@ -2,16 +2,6 @@ - aps-environment - development - com.apple.developer.aps-environment - development - com.apple.developer.icloud-container-identifiers - - com.apple.developer.icloud-services - - CloudKit - com.apple.security.app-sandbox com.apple.security.files.user-selected.read-only diff --git a/iGopherBrowser/iGopherBrowserApp.swift b/iGopherBrowser/iGopherBrowserApp.swift index 856ff5e..93c3b8a 100644 --- a/iGopherBrowser/iGopherBrowserApp.swift +++ b/iGopherBrowser/iGopherBrowserApp.swift @@ -6,32 +6,17 @@ // import SwiftUI -import SwiftData @main struct iGopherBrowserApp: App { - var sharedModelContainer: ModelContainer = { - let schema = Schema([ - Item.self, - ]) - let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) - - do { - return try ModelContainer(for: schema, configurations: [modelConfiguration]) - } catch { - fatalError("Could not create ModelContainer: \(error)") - } - }() - - var body: some Scene { - WindowGroup { - ContentView() - } - .modelContainer(sharedModelContainer) - .commands { - #if os(macOS) - SidebarCommands() - #endif - } + var body: some Scene { + WindowGroup { + ContentView() + } + .commands { + #if os(macOS) + SidebarCommands() + #endif } + } } -- cgit v1.2.3