aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/Contents.json13
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/iGopherBrowserLogo-2 1.pngbin81570 -> 0 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128.pngbin0 -> 12235 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.pngbin0 -> 30154 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16.pngbin0 -> 646 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.pngbin0 -> 1795 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256.pngbin0 -> 30154 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.pngbin0 -> 64876 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32.pngbin0 -> 1795 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.pngbin0 -> 4774 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512.pngbin0 -> 64876 bytes
-rw-r--r--iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.pngbin0 -> 156298 bytes
-rw-r--r--iGopherBrowser/BrowserView.swift595
-rw-r--r--iGopherBrowser/ContentView.swift82
-rw-r--r--iGopherBrowser/FileView.swift222
-rw-r--r--iGopherBrowser/Helpers.swift61
-rw-r--r--iGopherBrowser/Info.plist6
-rw-r--r--iGopherBrowser/Item.swift18
-rw-r--r--iGopherBrowser/SearchInputView.swift54
-rw-r--r--iGopherBrowser/SidebarView.swift26
-rw-r--r--iGopherBrowser/iGopherBrowser.entitlements10
-rw-r--r--iGopherBrowser/iGopherBrowserApp.swift33
22 files changed, 549 insertions, 571 deletions
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
--- a/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/iGopherBrowserLogo-2 1.png
+++ /dev/null
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
Binary files 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
--- /dev/null
+++ b/iGopherBrowser/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
Binary files 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 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>UIBackgroundModes</key>
- <array>
- <string>remote-notification</string>
- </array>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.productivity</string>
</dict>
</plist>
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 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>aps-environment</key>
- <string>development</string>
- <key>com.apple.developer.aps-environment</key>
- <string>development</string>
- <key>com.apple.developer.icloud-container-identifiers</key>
- <array/>
- <key>com.apple.developer.icloud-services</key>
- <array>
- <string>CloudKit</string>
- </array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
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
}
+ }
}