gemenon

[ACTIVE] The Safari of the Gemini ecosystem
git clone git://git.figbert.com/gemenon.git
Log | Files | Refs

ContentView.swift (6218B)


      1 //
      2 //  ContentView.swift
      3 //  Shared
      4 //
      5 //  Created by figbert on 8/30/22.
      6 //
      7 
      8 import SwiftUI
      9 
     10 struct ContentView: View {
     11     @EnvironmentObject var data: BrowserData
     12 
     13     @State private var columnVisibility = NavigationSplitViewVisibility.detailOnly
     14 
     15     var body: some View {
     16         NavigationSplitView(columnVisibility: $columnVisibility) {
     17             List(data.views, id: \.self, selection: $data.currentView) { view in
     18                 NavigationLink {
     19                     switch view {
     20                     case .Capsule:
     21                         CapsuleView()
     22                     case .History:
     23                         HistoryView()
     24                     case .Bookmarks:
     25                         BookmarksView()
     26                     }
     27                 } label: {
     28                     switch view {
     29                     case .Capsule:
     30                         if data.tab.home {
     31                             Label("Start Page", systemImage: "laptopcomputer")
     32                         } else {
     33                             Label("X Tabs", systemImage: "laptopcomputer")
     34                         }
     35                     case .History:
     36                         Label("History", systemImage: "clock")
     37                     case .Bookmarks:
     38                         Label("Bookmarks", systemImage: "star")
     39                     }
     40                 }
     41             }
     42         } detail: {
     43             switch data.currentView {
     44             case .Capsule:
     45                 CapsuleView()
     46             case .History:
     47                 HistoryView()
     48             case .Bookmarks:
     49                 BookmarksView()
     50             }
     51         }
     52         .toolbar {
     53             ToolbarItem(placement: .principal) {
     54                 TextField("Search or enter website name", text: $data.tab.urlBar)
     55                     .frame(minWidth: 500)
     56                     .textFieldStyle(.roundedBorder)
     57                     .autocorrectionDisabled(true)
     58                     .onSubmit { Task { await submitURLBar() } }
     59                     .overlay(HStack {
     60                         bookmarksButton
     61                         reloadButton
     62                         loading
     63                     }, alignment: .trailing)
     64             }
     65             ToolbarItem(placement: .navigation) {
     66                 Button(action: { data.goToStartPage() }) {
     67                     Label("Go to Start Page", systemImage: "house")
     68                         .labelStyle(.iconOnly)
     69                 }
     70             }
     71             ToolbarItem(placement: .navigation) {
     72                 Button(action: { Task { await data.goBack() } }) {
     73                     Label("Back", systemImage: "chevron.backward")
     74                         .labelStyle(.iconOnly)
     75                 }
     76                 .disabled(data.tab.prev == nil)
     77             }
     78             ToolbarItem(placement: .navigation) {
     79                 Button(action: { Task { await data.goForward() } }) {
     80                     Label("Forward", systemImage: "chevron.forward")
     81                         .labelStyle(.iconOnly)
     82                 }
     83                 .disabled(data.tab.next == nil)
     84             }
     85             ToolbarItem(placement: .secondaryAction) {
     86                 if data.tab.url != nil {
     87                     ShareLink(item: data.tab.url!)
     88                 } else {
     89                     ShareLink(item: "").disabled(true)
     90                 }
     91             }
     92         }
     93         .handlesExternalEvents(preferring: ["gemini://*"], allowing: ["*"])
     94         .onOpenURL(perform: { url in
     95             Task { await data.openURL(url) }
     96         })
     97     }
     98 
     99     @ViewBuilder private var bookmarksButton: some View {
    100         if data.tab.response != nil {
    101             if data.hasURLInBookmarks(data.tab.url!) {
    102                 Button(action: { data.dropURLFromBookmarks(data.tab.url!) }) {
    103                     Label("Drop from bookmarks", systemImage: "star.fill")
    104                         .labelStyle(.iconOnly)
    105                 }
    106                 .buttonStyle(.borderless)
    107                 .padding(.trailing, 2)
    108             } else {
    109                 Button(action: {
    110                     data.addURLToBookmarks(
    111                         data.tab.url!,
    112                         label: data.tab.url?.host() ?? data.tab.url!.absoluteString,
    113                         timestamp: Date.now
    114                     )
    115                 }) {
    116                     Label("Add to bookmarks", systemImage: "star")
    117                         .labelStyle(.iconOnly)
    118                 }
    119                 .buttonStyle(.borderless)
    120                 .padding(.trailing, 2)
    121             }
    122         }
    123     }
    124     @ViewBuilder private var reloadButton: some View {
    125         if data.tab.response != nil {
    126             Button(action: { Task { await data.reload() } }) {
    127                 Label("Reload page", systemImage: "arrow.clockwise")
    128                     .labelStyle(.iconOnly)
    129             }
    130             .buttonStyle(.borderless)
    131             .padding(.trailing, data.loading ? 2 : 8)
    132         }
    133     }
    134     @ViewBuilder private var loading: some View {
    135         if data.loading {
    136             ProgressView()
    137                 .scaleEffect(0.5)
    138                 .padding(.trailing, 4)
    139         }
    140     }
    141 
    142     private func submitURLBar() async {
    143         if let url = URL(string: data.tab.urlBar) {
    144             if var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
    145                 if components.scheme?.isEmpty ?? true {
    146                     if components.host?.isEmpty ?? true {
    147                         let index = components.path.firstIndex(of: "/") ?? components.path.endIndex
    148                         components.host = String(components.path[..<index])
    149                         components.path = String(components.path[index...])
    150                     }
    151                     components.scheme = "gemini"
    152                 }
    153                 if let url = components.url {
    154                     await data.openURL(url)
    155                 }
    156             }
    157         } else {
    158             var components = URLComponents(string: "gemini://geminispace.info/search")
    159             components?.query = data.tab.urlBar
    160             if let url = components?.url {
    161                 await data.openURL(url)
    162             }
    163         }
    164     }
    165 }
    166 
    167 struct ContentView_Previews: PreviewProvider {
    168     static var previews: some View {
    169         ContentView()
    170     }
    171 }