aboutsummaryrefslogtreecommitdiff
path: root/iGopherBrowser/BrowserView.swift
diff options
context:
space:
mode:
Diffstat (limited to 'iGopherBrowser/BrowserView.swift')
-rw-r--r--iGopherBrowser/BrowserView.swift1001
1 files changed, 522 insertions, 479 deletions
diff --git a/iGopherBrowser/BrowserView.swift b/iGopherBrowser/BrowserView.swift
index 880bccd..cd983a5 100644
--- a/iGopherBrowser/BrowserView.swift
+++ b/iGopherBrowser/BrowserView.swift
@@ -6,515 +6,558 @@
//
import GopherHelpers
+import SwiftGopherClient
import SwiftUI
import TelemetryClient
-import SwiftGopherClient
func openURL(url: URL) {
- #if os(OSX)
- NSWorkspace.shared.open(url)
- #else
- UIApplication.shared.open(url)
- #endif
+ #if os(OSX)
+ NSWorkspace.shared.open(url)
+ #else
+ UIApplication.shared.open(url)
+ #endif
}
#if canImport(UIKit)
- extension View {
- func hideKeyboard() {
- UIApplication.shared.sendAction(
- #selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
+ extension View {
+ func hideKeyboard() {
+ UIApplication.shared.sendAction(
+ #selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
+ }
}
- }
#endif
struct BrowserView: View {
- @AppStorage("homeURL") var homeURL: URL = URL(string: "gopher://gopher.navan.dev:70/")!
- @AppStorage("accentColour", store: .standard) var accentColour: Color = Color(.blue)
- @AppStorage("linkColour", store: .standard) var linkColour: Color = Color(.white)
- @AppStorage("shareThroughProxy", store: .standard) var shareThroughProxy: Bool = true
-
- @State var homeURLString = "gopher://gopher.navan.dev:70/"
-
- @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?
-
- @State private var showPreferences = false
- @State private var showBookmarks = false
-
- @Namespace var topID
- @State private var scrollToTop: Bool = false
-
- @State var currentTask: Task<Void, Never>?
-
- let client = GopherClient()
-
- var body: some View {
- NavigationStack {
- VStack(spacing: 0) {
- if gopherItems.count >= 1 {
- ScrollViewReader { proxy in
-
- 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)
- .listRowSeparator(.hidden)
- .padding(.vertical, -8)
- .id(idx)
- } else if item.parsedItemType == .directory {
- Button(action: {
- performGopherRequest(host: item.host, port: item.port, selector: item.selector)
- #if canImport(UIKit)
- hideKeyboard()
- #endif
- }) {
- HStack {
- Text(Image(systemName: "folder"))
- Text(item.message)
- Spacer()
- }.foregroundStyle(linkColour)
- }.buttonStyle(PlainButtonStyle())
- .id(idx)
- } else if item.parsedItemType == .search {
- Button(action: {
- #if canImport(UIKit)
- hideKeyboard()
- #endif
- self.selectedSearchItem = idx
- self.showSearchInput = true
- }) {
- HStack {
- Text(Image(systemName: "magnifyingglass"))
- Text(item.message)
- Spacer()
- }.foregroundStyle(linkColour)
- }.buttonStyle(PlainButtonStyle())
- .id(idx)
- } else if item.parsedItemType == .text {
- NavigationLink(destination: FileView(item: item)) {
- HStack {
- Text(Image(systemName: "doc.plaintext"))
- Text(item.message)
- Spacer()
- }.foregroundStyle(linkColour)
- }
- .id(idx)
- } else if item.selector.hasPrefix("URL:") {
- if let url = URL(string: item.selector.replacingOccurrences(of: "URL:", with: ""))
- {
- //UIApplication.shared.canOpenURL(url) {
- Button(action: {
- openURL(url: url)
- }) {
- HStack {
- Image(systemName: "link")
- Text(item.message)
- Spacer()
- }.foregroundStyle(linkColour)
- }.buttonStyle(PlainButtonStyle())
- .id(idx)
- }
- } 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()
- }.foregroundStyle(linkColour)
- .id(idx)
- }
+ @AppStorage("homeURL") var homeURL: URL = URL(string: "gopher://gopher.navan.dev:70/")!
+ @AppStorage("accentColour", store: .standard) var accentColour: Color = Color(.blue)
+ @AppStorage("linkColour", store: .standard) var linkColour: Color = Color(.white)
+ @AppStorage("shareThroughProxy", store: .standard) var shareThroughProxy: Bool = true
+
+ @State var homeURLString = "gopher://gopher.navan.dev:70/"
+
+ @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?
+
+ @State private var showPreferences = false
+ @State private var showBookmarks = false
+
+ @Namespace var topID
+ @State private var scrollToTop: Bool = false
+
+ @State var currentTask: Task<Void, Never>?
+
+ let client = GopherClient()
+
+ var body: some View {
+ NavigationStack {
+ VStack(spacing: 0) {
+ if gopherItems.count >= 1 {
+ ScrollViewReader { proxy in
+
+ 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)
+ .listRowSeparator(.hidden)
+ .padding(.vertical, -8)
+ .id(idx)
+ } else if item.parsedItemType == .directory {
+ Button(action: {
+ performGopherRequest(
+ host: item.host, port: item.port,
+ selector: item.selector)
+ #if canImport(UIKit)
+ hideKeyboard()
+ #endif
+ }) {
+ HStack {
+ Text(Image(systemName: "folder"))
+ Text(item.message)
+ Spacer()
+ }.foregroundStyle(linkColour)
+ }.buttonStyle(PlainButtonStyle())
+ .id(idx)
+ } else if item.parsedItemType == .search {
+ Button(action: {
+ #if canImport(UIKit)
+ hideKeyboard()
+ #endif
+ self.selectedSearchItem = idx
+ self.showSearchInput = true
+ }) {
+ HStack {
+ Text(Image(systemName: "magnifyingglass"))
+ Text(item.message)
+ Spacer()
+ }.foregroundStyle(linkColour)
+ }.buttonStyle(PlainButtonStyle())
+ .id(idx)
+ } else if item.parsedItemType == .text {
+ NavigationLink(destination: FileView(item: item)) {
+ HStack {
+ Text(Image(systemName: "doc.plaintext"))
+ Text(item.message)
+ Spacer()
+ }.foregroundStyle(linkColour)
+ }
+ .id(idx)
+ } else if item.selector.hasPrefix("URL:") {
+ if let url = URL(
+ string: item.selector.replacingOccurrences(
+ of: "URL:", with: ""))
+ {
+ //UIApplication.shared.canOpenURL(url) {
+ Button(action: {
+ openURL(url: url)
+ }) {
+ HStack {
+ Image(systemName: "link")
+ Text(item.message)
+ Spacer()
+ }.foregroundStyle(linkColour)
+ }.buttonStyle(PlainButtonStyle())
+ .id(idx)
+ }
+ } 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()
+ }.foregroundStyle(linkColour)
+ .id(idx)
+ }
+ } else {
+ Button(action: {
+ TelemetryManager.send(
+ "applicationBrowsedUnknown",
+ with: [
+ "gopherURL":
+ "\(item.host):\(item.port)\(item.selector)"
+ ])
+ performGopherRequest(
+ host: item.host, port: item.port,
+ selector: item.selector)
+ }) {
+ HStack {
+ Text(Image(systemName: "questionmark.app.dashed"))
+ Text(item.message)
+ Spacer()
+ }.foregroundStyle(linkColour)
+ }.buttonStyle(PlainButtonStyle())
+ .id(idx)
+ }
+
+ }
+ }
+ //.background(Color.white)
+ .cornerRadius(10)
+ .onChange(of: scrollToTop) {
+ // TODO: Cleanup
+ withAnimation {
+ proxy.scrollTo(0, anchor: .top)
+
+ }
+ }
+ }
+ .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
+ }.onAppear {
+ TelemetryManager.send(
+ "applicationSearchError", with: ["gopherURL": "\(self.url)"]
+ )
+ }
+ }
+
+ }
+ }
} else {
- Button(action: {
- TelemetryManager.send(
- "applicationBrowsedUnknown",
- with: ["gopherURL": "\(item.host):\(item.port)\(item.selector)"])
- performGopherRequest(host: item.host, port: item.port, selector: item.selector)
- }) {
- HStack {
- Text(Image(systemName: "questionmark.app.dashed"))
- Text(item.message)
- Spacer()
- }.foregroundStyle(linkColour)
- }.buttonStyle(PlainButtonStyle())
- .id(idx)
+ Spacer()
+ Text("Welcome to iGopher Browser")
+ Spacer()
}
+ #if os(iOS)
+ VStack {
+ HStack(spacing: 10) {
+ HStack {
+ Spacer()
+
+ TextField("Enter a URL", text: $url)
+ .keyboardType(.URL)
+ .autocapitalization(.none)
+ .padding(10)
+ .background(Color.gray.opacity(0.2))
+ .cornerRadius(10)
+ Spacer()
+ }
+ //.background(Color.white)
+ .cornerRadius(30)
+
+ Button(
+ "Go",
+ action: {
+ TelemetryManager.send(
+ "applicationClickedGo", with: ["gopherURL": "\(self.url)"])
+ performGopherRequest(clearForward: false)
+
+ }
+ )
+ .keyboardShortcut(.defaultAction)
+ .onSubmit {
+ performGopherRequest()
+ }
+ Spacer()
+ }
+ .padding(.bottom, 10)
+ .padding(.top, 5)
+ HStack {
+ Spacer()
+ Button {
+ print(homeURL, "home")
+ TelemetryManager.send(
+ "applicationClickedHome", with: ["gopherURL": "\(self.url)"])
+ performGopherRequest(
+ host: homeURL.host ?? "gopher.navan.dev",
+ port: homeURL.port ?? 70,
+ selector: homeURL.path)
+ } label: {
+ Label("Home", systemImage: "house")
+ .labelStyle(.iconOnly)
+ }
+ Spacer()
+ Button {
+ if let curNode = backwardStack.popLast() {
+ forwardStack.append(curNode)
+ if let prevNode = backwardStack.popLast() {
+ TelemetryManager.send(
+ "applicationClickedBack",
+ with: [
+ "gopherURL":
+ "\(prevNode.host):\(prevNode.port)\(prevNode.selector)"
+ ])
+ 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() {
+ TelemetryManager.send(
+ "applicationClickedForward",
+ with: [
+ "gopherURL":
+ "\(nextNode.host):\(nextNode.port)\(nextNode.selector)"
+ ])
+ performGopherRequest(
+ host: nextNode.host, port: nextNode.port,
+ selector: nextNode.selector,
+ clearForward: false)
+ }
+ } label: {
+ Label("Forward", systemImage: "chevron.right")
+ .labelStyle(.iconOnly)
+ }
+ .disabled(forwardStack.isEmpty)
+ Spacer()
+ if shareThroughProxy {
+ ShareLink(item: URL(string: "https://gopher.navan.dev/\(url)")!) {
+ Label("Share", systemImage: "square.and.arrow.up").labelStyle(
+ .iconOnly)
+ }
+ } else {
+ ShareLink(item: URL(string: "gopher://\(url)")!) {
+ Label("Share", systemImage: "square.and.arrow.up").labelStyle(
+ .iconOnly)
+ }
+ }
+ Spacer()
+ // Button {
+ // showBookmarks = true
+ // } label: {
+ // Label("Bookmarks", systemImage: "book")
+ // .labelStyle(.iconOnly)
+ // }.sheet(isPresented: $showBookmarks) {
+ // BookmarksView()
+ // .presentationDetents([.height(400), .medium, .large])
+ // .presentationDragIndicator(.automatic)
+ // }
+ // Spacer()
+ Button {
+ self.showPreferences = true
+ } label: {
+ Label("Settings", systemImage: "gear")
+ .labelStyle(.iconOnly)
+ }
+ Spacer()
+ }
+ }
+ #else
+ HStack(spacing: 10) {
+ HStack {
+ Spacer()
+ Button {
+ TelemetryManager.send(
+ "applicationClickedHome", with: ["gopherURL": "\(self.url)"])
+ performGopherRequest(
+ host: homeURL.host ?? "gopher.navan.dev",
+ port: homeURL.port ?? 70,
+ selector: homeURL.path)
+ } label: {
+ Label("Home", systemImage: "house")
+ .labelStyle(.iconOnly)
+ }
- }
+ #if os(visionOS)
+ Button {
+ self.showPreferences = true
+ } label: {
+ Label("Settings", systemImage: "gear")
+ .labelStyle(.iconOnly)
+ }
+ #endif
+
+ Button {
+ if let curNode = backwardStack.popLast() {
+ forwardStack.append(curNode)
+ if let prevNode = backwardStack.popLast() {
+ TelemetryManager.send(
+ "applicationClickedBack",
+ with: [
+ "gopherURL":
+ "\(prevNode.host):\(prevNode.port)\(prevNode.selector)"
+ ])
+ 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() {
+ TelemetryManager.send(
+ "applicationClickedForward",
+ with: [
+ "gopherURL":
+ "\(nextNode.host):\(nextNode.port)\(nextNode.selector)"
+ ])
+ 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)
+ }
+ //.background(Color.white)
+ .cornerRadius(30)
+ if shareThroughProxy {
+ ShareLink(item: URL(string: "https://gopher.navan.dev/\(url)")!) {
+ Label("Share", systemImage: "square.and.arrow.up").labelStyle(
+ .iconOnly)
+ }
+ } else {
+ ShareLink(item: URL(string: "gopher://\(url)")!) {
+ Label("Share", systemImage: "square.and.arrow.up").labelStyle(
+ .iconOnly)
+ }
+ }
+ Button(
+ "Go",
+ action: {
+ TelemetryManager.send(
+ "applicationClickedGo", with: ["gopherURL": "\(self.url)"])
+ performGopherRequest(clearForward: false)
+ }
+ )
+ .keyboardShortcut(.defaultAction)
+ .onSubmit {
+ performGopherRequest()
+ }
+ Spacer()
+ }
+ #endif
}
- //.background(Color.white)
- .cornerRadius(10)
- .onChange(of: scrollToTop) {
- // TODO: Cleanup
- withAnimation {
- proxy.scrollTo(0, anchor: .top)
-
- }
+ }
+ .onChange(of: selectedNode) {
+ if let node = selectedNode {
+ performGopherRequest(host: node.host, port: node.port, selector: node.selector)
}
- }
- .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
+ }
+ .onOpenURL { gopherURL in
+ self.url = gopherURL.absoluteString
+ performGopherRequest()
+ }
+ .sheet(
+ isPresented: $showPreferences,
+ onDismiss: {
+ print("badm", homeURL, homeURLString)
+ if let url = URL(string: homeURLString) {
+ self.homeURL = url
}
- )
- } else {
+ }
+ ) {
+ #if os(macOS)
+ SettingsView()
+ #else
+ SettingsView(homeURL: $homeURL, homeURLString: $homeURLString)
+ #endif
+ }
+ .accentColor(accentColour)
+ }
- VStack {
- Text("Weird bug. Please Dismiss -> Press Go -> Try Again")
- Button("Dismiss") {
- self.showSearchInput = false
- }.onAppear {
- TelemetryManager.send(
- "applicationSearchError", with: ["gopherURL": "\(self.url)"])
- }
- }
+ 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 = ""
}
- }
- } else {
- Spacer()
- Text("Welcome to iGopher Browser")
- Spacer()
}
- #if os(iOS)
- VStack {
- HStack(spacing: 10) {
- HStack {
- Spacer()
-
- TextField("Enter a URL", text: $url)
- .keyboardType(.URL)
- .autocapitalization(.none)
- .padding(10)
- .background(Color.gray.opacity(0.2))
- .cornerRadius(10)
- Spacer()
- }
- //.background(Color.white)
- .cornerRadius(30)
-
- Button(
- "Go",
- action: {
- TelemetryManager.send("applicationClickedGo", with: ["gopherURL": "\(self.url)"])
- performGopherRequest(clearForward: false)
+ if port != -1 {
+ res.port = port
+ }
+
+ self.url = "\(res.host):\(res.port)\(res.selector)"
+
+ currentTask?.cancel()
+
+ let myHost = res.host
+ let myPort = res.port
+ let mySelector = res.selector
+
+ currentTask = Task {
+ do {
+ try Task.checkCancellation()
+ let resp = try await client.sendRequest(
+ to: myHost, port: myPort, message: "\(mySelector)\r\n")
+
+ var newNode = GopherNode(
+ host: myHost, port: myPort, selector: mySelector, item: nil,
+ children: convertToHostNodes(resp))
+
+ backwardStack.append(newNode)
+ if clearForward {
+ forwardStack.removeAll()
}
- )
- .keyboardShortcut(.defaultAction)
- .onSubmit {
- performGopherRequest()
- }
- Spacer()
- }
- .padding(.bottom, 10)
- .padding(.top, 5)
- HStack {
- Spacer()
- Button {
- print(homeURL, "home")
- TelemetryManager.send("applicationClickedHome", with: ["gopherURL": "\(self.url)"])
- performGopherRequest(
- host: homeURL.host ?? "gopher.navan.dev", port: homeURL.port ?? 70,
- selector: homeURL.path)
- } label: {
- Label("Home", systemImage: "house")
- .labelStyle(.iconOnly)
- }
- Spacer()
- Button {
- if let curNode = backwardStack.popLast() {
- forwardStack.append(curNode)
- if let prevNode = backwardStack.popLast() {
- TelemetryManager.send(
- "applicationClickedBack",
- with: ["gopherURL": "\(prevNode.host):\(prevNode.port)\(prevNode.selector)"])
- 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() {
- TelemetryManager.send(
- "applicationClickedForward",
- with: ["gopherURL": "\(nextNode.host):\(nextNode.port)\(nextNode.selector)"])
- performGopherRequest(
- host: nextNode.host, port: nextNode.port, selector: nextNode.selector,
- clearForward: false)
- }
- } label: {
- Label("Forward", systemImage: "chevron.right")
- .labelStyle(.iconOnly)
- }
- .disabled(forwardStack.isEmpty)
- Spacer()
- if shareThroughProxy {
- ShareLink(item: URL(string: "https://gopher.navan.dev/\(url)")!) {
- Label("Share", systemImage: "square.and.arrow.up").labelStyle(.iconOnly)
- }
- } else {
- ShareLink(item: URL(string: "gopher://\(url)")!) {
- Label("Share", systemImage: "square.and.arrow.up").labelStyle(.iconOnly)
- }
- }
- Spacer()
- // Button {
- // showBookmarks = true
- // } label: {
- // Label("Bookmarks", systemImage: "book")
- // .labelStyle(.iconOnly)
- // }.sheet(isPresented: $showBookmarks) {
- // BookmarksView()
- // .presentationDetents([.height(400), .medium, .large])
- // .presentationDragIndicator(.automatic)
- // }
- // Spacer()
- Button {
- self.showPreferences = true
- } label: {
- Label("Settings", systemImage: "gear")
- .labelStyle(.iconOnly)
- }
- Spacer()
- }
- }
- #else
- HStack(spacing: 10) {
- HStack {
- Spacer()
- Button {
- TelemetryManager.send("applicationClickedHome", with: ["gopherURL": "\(self.url)"])
- performGopherRequest(
- host: homeURL.host ?? "gopher.navan.dev", port: homeURL.port ?? 70,
- selector: homeURL.path)
- } label: {
- Label("Home", systemImage: "house")
- .labelStyle(.iconOnly)
- }
-
- #if os(visionOS)
- Button {
- self.showPreferences = true
- } label: {
- Label("Settings", systemImage: "gear")
- .labelStyle(.iconOnly)
+
+ if let index = self.hosts.firstIndex(where: {
+ $0.host == myHost && $0.port == myPort
+ }) {
+ // TODO: Handle case where first link visited is a subdirectory, should the sidebar auto fetch the rest?
+ 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)
}
- #endif
-
- Button {
- if let curNode = backwardStack.popLast() {
- forwardStack.append(curNode)
- if let prevNode = backwardStack.popLast() {
- TelemetryManager.send(
- "applicationClickedBack",
- with: ["gopherURL": "\(prevNode.host):\(prevNode.port)\(prevNode.selector)"])
- performGopherRequest(
- host: prevNode.host, port: prevNode.port, selector: prevNode.selector,
- clearForward: false)
- }
+ //TODO: Fix this stupid bodge
+ if self.url != "\(myHost):\(myPort)\(mySelector)" {
+ print("Different URL being processed right now... Cancelling")
+ } else {
+ self.gopherItems = resp
}
- } label: {
- Label("Back", systemImage: "chevron.left")
- .labelStyle(.iconOnly)
- }
- .disabled(backwardStack.count < 2)
-
- Button {
- if let nextNode = forwardStack.popLast() {
- TelemetryManager.send(
- "applicationClickedForward",
- with: ["gopherURL": "\(nextNode.host):\(nextNode.port)\(nextNode.selector)"])
- performGopherRequest(
- host: nextNode.host, port: nextNode.port, selector: nextNode.selector,
- clearForward: false)
+
+ } catch is CancellationError {
+ print("Request was cancelled")
+ } catch {
+ TelemetryManager.send(
+ "applicationRequestError",
+ with: ["gopherURL": "\(self.url)", "errorMessage": "\(error)"])
+ print("Error \(error)")
+ var item = gopherItem(rawLine: "Error \(error)")
+ item.message = "Error \(error)"
+ if self.url != "\(myHost):\(myPort)\(mySelector)" {
+ print("Different URL being processed right now... Cancelling")
+ } else {
+ self.gopherItems = [item]
}
- } 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)
}
- //.background(Color.white)
- .cornerRadius(30)
- if shareThroughProxy {
- ShareLink(item: URL(string: "https://gopher.navan.dev/\(url)")!) {
- Label("Share", systemImage: "square.and.arrow.up").labelStyle(.iconOnly)
- }
- } else {
- ShareLink(item: URL(string: "gopher://\(url)")!) {
- Label("Share", systemImage: "square.and.arrow.up").labelStyle(.iconOnly)
- }
- }
- Button(
- "Go",
- action: {
- TelemetryManager.send("applicationClickedGo", with: ["gopherURL": "\(self.url)"])
- 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)
- }
- }
- .onOpenURL { gopherURL in
- self.url = gopherURL.absoluteString
- performGopherRequest()
- }
- .sheet(
- isPresented: $showPreferences,
- onDismiss: {
- print("badm", homeURL, homeURLString)
- if let url = URL(string: homeURLString) {
- self.homeURL = url
}
- }
- ) {
- #if os(macOS)
- SettingsView()
- #else
- SettingsView(homeURL: $homeURL, homeURLString: $homeURLString)
- #endif
- }
- .accentColor(accentColour)
- }
-
- 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 = ""
- }
}
- if port != -1 {
- res.port = port
- }
-
- self.url = "\(res.host):\(res.port)\(res.selector)"
-
- currentTask?.cancel()
-
- let myHost = res.host
- let myPort = res.port
- let mySelector = res.selector
-
- currentTask = Task {
- do {
- try Task.checkCancellation()
- let resp = try await client.sendRequest(to: myHost, port: myPort, message: "\(mySelector)\r\n")
-
- var newNode = GopherNode(host: myHost, port: myPort, selector: mySelector, item: nil, children: convertToHostNodes(resp))
-
- backwardStack.append(newNode)
- if clearForward {
- forwardStack.removeAll()
- }
-
- if let index = self.hosts.firstIndex(where: { $0.host == myHost && $0.port == myPort }) {
- // TODO: Handle case where first link visited is a subdirectory, should the sidebar auto fetch the rest?
- 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)
- }
- //TODO: Fix this stupid bodge
- if self.url != "\(myHost):\(myPort)\(mySelector)" {
- print("Different URL being processed right now... Cancelling")
- } else {
- self.gopherItems = resp
- }
-
- } catch is CancellationError {
- print("Request was cancelled")
- } catch {
- TelemetryManager.send(
- "applicationRequestError", with: ["gopherURL": "\(self.url)", "errorMessage": "\(error)"])
- print("Error \(error)")
- var item = gopherItem(rawLine: "Error \(error)")
- item.message = "Error \(error)"
- if self.url != "\(myHost):\(myPort)\(mySelector)" {
- print("Different URL being processed right now... Cancelling")
- } else {
- 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)")
- }
+ 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
}
- return returnItems
- }
}