From e5f520cf6f0442911fa2f0478da55ed0f0260ce0 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Tue, 15 Jun 2021 23:21:22 -0400 Subject: [PATCH] Add homepage preference --- Gemini-iOS/AppDelegate.swift | 2 +- Gemini-iOS/BrowserNavigationController.swift | 2 +- Gemini-iOS/HomepagePrefView.swift | 96 +++++++++++++++++++ Gemini-iOS/Preferences.swift | 10 ++ Gemini-iOS/PreferencesView.swift | 17 +++- Gemini-iOS/SceneDelegate.swift | 2 +- Gemini-iOS/SetHomepageActivity.swift | 38 ++++++++ Gemini.xcodeproj/project.pbxproj | 8 ++ .../xcschemes/xcschememanagement.plist | 4 +- 9 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 Gemini-iOS/HomepagePrefView.swift create mode 100644 Gemini-iOS/SetHomepageActivity.swift diff --git a/Gemini-iOS/AppDelegate.swift b/Gemini-iOS/AppDelegate.swift index 93de494..8aa30c0 100644 --- a/Gemini-iOS/AppDelegate.swift +++ b/Gemini-iOS/AppDelegate.swift @@ -10,7 +10,7 @@ import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - + static let defaultHomepage = URL(string: "gemini://gemini.circumlunar.space/")! func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { SymbolCache.load() diff --git a/Gemini-iOS/BrowserNavigationController.swift b/Gemini-iOS/BrowserNavigationController.swift index f45bc07..b32565d 100644 --- a/Gemini-iOS/BrowserNavigationController.swift +++ b/Gemini-iOS/BrowserNavigationController.swift @@ -304,7 +304,7 @@ class BrowserNavigationController: UIViewController { private func showShareSheet(_ source: UIView) { guard let doc = currentBrowserVC.document else { return } - let vc = UIActivityViewController(activityItems: [ActivityItemSource(document: doc)], applicationActivities: nil) + let vc = UIActivityViewController(activityItems: [ActivityItemSource(document: doc)], applicationActivities: [SetHomepageActivity()]) vc.popoverPresentationController?.sourceView = source present(vc, animated: true) } diff --git a/Gemini-iOS/HomepagePrefView.swift b/Gemini-iOS/HomepagePrefView.swift new file mode 100644 index 0000000..e076308 --- /dev/null +++ b/Gemini-iOS/HomepagePrefView.swift @@ -0,0 +1,96 @@ +// +// HomepagePrefView.swift +// Gemini-iOS +// +// Created by Shadowfacts on 6/15/21. +// + +import SwiftUI + +struct HomepagePrefView: View { + @State private var url: URL? = Preferences.shared.homepage + @State private var focus = false + + var body: some View { + List { + // use a custom binding to allow the state variable to be nil temporarily, and update the pref when not + HomepagePrefTextField(value: Binding(get: { + url + }, set: { (newVal) in + self.url = newVal + if let newVal = newVal { + Preferences.shared.homepage = newVal + } + }), focus: $focus) + } + .navigationBarTitle("Homepage") + .onAppear { + focus = true + } + .onDisappear { + // if the text field was empty when we disappeared, reset to the default homepage + if url == nil { + Preferences.shared.homepage = AppDelegate.defaultHomepage + } + } } +} + +struct HomepagePrefTextField: UIViewRepresentable { + @Binding private var value: URL? + @Binding private var focus: Bool + + init(value: Binding, focus: Binding) { + self._value = value + self._focus = focus + } + + func makeUIView(context: Context) -> UITextField { + let field = UITextField() + field.addTarget(context.coordinator, action: #selector(Coordinator.textChanged), for: .editingChanged) + field.clearButtonMode = .whileEditing + field.placeholder = AppDelegate.defaultHomepage.absoluteString + // fix for text field expanding horizontally when text grows + field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + return field + } + + func updateUIView(_ uiView: UITextField, context: Context) { + uiView.text = value?.absoluteString + context.coordinator.binding = $value + if focus { + DispatchQueue.main.async { + uiView.becomeFirstResponder() + focus = false + } + } + } + + func makeCoordinator() -> Coordinator { + return Coordinator(binding: $value) + } + + class Coordinator: NSObject { + var binding: Binding + + init(binding: Binding) { + self.binding = binding + } + + @objc func textChanged(_ textField: UITextField) { + if let text = textField.text, + !text.isEmpty { + if let url = URL(string: text) { + binding.wrappedValue = url + } + } else { + binding.wrappedValue = nil + } + } + } +} + +struct HomepagePrefView_Previews: PreviewProvider { + static var previews: some View { + HomepagePrefView() + } +} diff --git a/Gemini-iOS/Preferences.swift b/Gemini-iOS/Preferences.swift index a1b5de1..3cec739 100644 --- a/Gemini-iOS/Preferences.swift +++ b/Gemini-iOS/Preferences.swift @@ -34,6 +34,10 @@ class Preferences: Codable, ObservableObject { required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) + if let stored = try container.decodeIfPresent(URL.self, forKey: .homepage) { + homepage = stored + } + theme = try container.decode(UIUserInterfaceStyle.self, forKey: .theme) if let stored = try container.decodeIfPresent(Bool.self, forKey: .showLinkIcons) { showLinkIcons = stored @@ -46,6 +50,8 @@ class Preferences: Codable, ObservableObject { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(homepage, forKey: .homepage) + try container.encode(theme, forKey: .theme) try container.encode(showLinkIcons, forKey: .showLinkIcons) @@ -53,6 +59,8 @@ class Preferences: Codable, ObservableObject { try container.encode(useReaderMode, forKey: .useReaderMode) } + @Published var homepage = AppDelegate.defaultHomepage + @Published var theme = UIUserInterfaceStyle.unspecified @Published var showLinkIcons = true @@ -60,6 +68,8 @@ class Preferences: Codable, ObservableObject { @Published var useReaderMode = false enum CodingKeys: String, CodingKey { + case homepage + case theme case showLinkIcons diff --git a/Gemini-iOS/PreferencesView.swift b/Gemini-iOS/PreferencesView.swift index c050282..b4b1442 100644 --- a/Gemini-iOS/PreferencesView.swift +++ b/Gemini-iOS/PreferencesView.swift @@ -15,6 +15,8 @@ struct PreferencesView: View { var body: some View { NavigationView { List { + untitledSection + appearanceSection safariSection @@ -23,7 +25,7 @@ struct PreferencesView: View { .insetOrGroupedListStyle() .navigationBarItems(trailing: doneButton) } - .navigationViewStyle(StackNavigationViewStyle()) + .navigationViewStyle(.stack) .onDisappear { Preferences.save() } @@ -38,6 +40,15 @@ struct PreferencesView: View { .hoverEffect(.highlight) } + private var untitledSection: some View { + Section { + NavigationLink(destination: HomepagePrefView()) { + Text("Homepage") + } + + } + } + private var appearanceSection: some View { Section(header: Text("Appearance")) { Picker(selection: $preferences.theme, label: Text("Theme")) { @@ -64,9 +75,9 @@ fileprivate extension View { @ViewBuilder func insetOrGroupedListStyle() -> some View { if #available(iOS 14.0, *) { - self.listStyle(InsetGroupedListStyle()) + self.listStyle(.insetGrouped) } else { - self.listStyle(GroupedListStyle()) + self.listStyle(.grouped) } } } diff --git a/Gemini-iOS/SceneDelegate.swift b/Gemini-iOS/SceneDelegate.swift index 95fb111..906919d 100644 --- a/Gemini-iOS/SceneDelegate.swift +++ b/Gemini-iOS/SceneDelegate.swift @@ -32,7 +32,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { if ProcessInfo.processInfo.environment.keys.contains("DEFAULT_URL") { initialURL = URL(string: ProcessInfo.processInfo.environment["DEFAULT_URL"]!)! } else { - initialURL = URL(string: "gemini://gemini.circumlunar.space/")! + initialURL = Preferences.shared.homepage } } diff --git a/Gemini-iOS/SetHomepageActivity.swift b/Gemini-iOS/SetHomepageActivity.swift new file mode 100644 index 0000000..d735ae7 --- /dev/null +++ b/Gemini-iOS/SetHomepageActivity.swift @@ -0,0 +1,38 @@ +// +// SetHomepageActivity.swift +// Gemini-iOS +// +// Created by Shadowfacts on 6/15/21. +// + +import UIKit + +class SetHomepageActivity: UIActivity { + + override class var activityCategory: UIActivity.Category { + return .action + } + + override var activityTitle: String? { + return "Set as Homepage" + } + + override var activityImage: UIImage? { + // large size more closely matches system activity images + return UIImage(systemName: "house", withConfiguration: UIImage.SymbolConfiguration(scale: .large)) + } + + override func canPerform(withActivityItems activityItems: [Any]) -> Bool { + for case let url as URL in activityItems { + return Preferences.shared.homepage != url + } + return false + } + + override func prepare(withActivityItems activityItems: [Any]) { + for case let url as URL in activityItems { + Preferences.shared.homepage = url + break + } + } +} diff --git a/Gemini.xcodeproj/project.pbxproj b/Gemini.xcodeproj/project.pbxproj index 9fc19f0..215390e 100644 --- a/Gemini.xcodeproj/project.pbxproj +++ b/Gemini.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ D62664F024BC0D7700DF9B88 /* GeminiFormat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D62664A824BBF26A00DF9B88 /* GeminiFormat.framework */; }; D62664FA24BC12BC00DF9B88 /* DocumentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62664F924BC12BC00DF9B88 /* DocumentTests.swift */; }; D653F40B267996FF004E32B1 /* ActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D653F40A267996FF004E32B1 /* ActivityItemSource.swift */; }; + D653F40D26799F2F004E32B1 /* HomepagePrefView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D653F40C26799F2F004E32B1 /* HomepagePrefView.swift */; }; + D653F40F2679A0AB004E32B1 /* SetHomepageActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D653F40E2679A0AB004E32B1 /* SetHomepageActivity.swift */; }; D664673624BD07F700B0B741 /* RenderingBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673524BD07F700B0B741 /* RenderingBlock.swift */; }; D664673824BD086F00B0B741 /* RenderingBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673724BD086F00B0B741 /* RenderingBlockView.swift */; }; D664673A24BD0B8E00B0B741 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673924BD0B8E00B0B741 /* Fonts.swift */; }; @@ -305,6 +307,8 @@ D62664ED24BC0BCE00DF9B88 /* MaybeLazyVStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaybeLazyVStack.swift; sourceTree = ""; }; D62664F924BC12BC00DF9B88 /* DocumentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTests.swift; sourceTree = ""; }; D653F40A267996FF004E32B1 /* ActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityItemSource.swift; sourceTree = ""; }; + D653F40C26799F2F004E32B1 /* HomepagePrefView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepagePrefView.swift; sourceTree = ""; }; + D653F40E2679A0AB004E32B1 /* SetHomepageActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetHomepageActivity.swift; sourceTree = ""; }; D664673524BD07F700B0B741 /* RenderingBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlock.swift; sourceTree = ""; }; D664673724BD086F00B0B741 /* RenderingBlockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlockView.swift; sourceTree = ""; }; D664673924BD0B8E00B0B741 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = ""; }; @@ -595,7 +599,9 @@ D6BC9AD6258FC8B3008652BC /* TableOfContentsView.swift */, D691A64D25217C6F00348C4B /* Preferences.swift */, D691A66625217FD800348C4B /* PreferencesView.swift */, + D653F40C26799F2F004E32B1 /* HomepagePrefView.swift */, D653F40A267996FF004E32B1 /* ActivityItemSource.swift */, + D653F40E2679A0AB004E32B1 /* SetHomepageActivity.swift */, D688F618258AD231003A0A73 /* Resources */, D6E152AA24BFFDF600FDF9D3 /* Assets.xcassets */, D6E152AF24BFFDF600FDF9D3 /* LaunchScreen.storyboard */, @@ -1117,9 +1123,11 @@ D653F40B267996FF004E32B1 /* ActivityItemSource.swift in Sources */, D6BC9AB3258E8E13008652BC /* ToolbarView.swift in Sources */, D688F64A258C17F3003A0A73 /* SymbolCache.swift in Sources */, + D653F40F2679A0AB004E32B1 /* SetHomepageActivity.swift in Sources */, D688F65A258C2256003A0A73 /* BrowserNavigationController.swift in Sources */, D6BC9AD7258FC8B3008652BC /* TableOfContentsView.swift in Sources */, D688F663258C2479003A0A73 /* UIViewController+Children.swift in Sources */, + D653F40D26799F2F004E32B1 /* HomepagePrefView.swift in Sources */, D691A64E25217C6F00348C4B /* Preferences.swift in Sources */, D6BC9ABC258E9862008652BC /* NavigationBarView.swift in Sources */, ); diff --git a/Gemini.xcodeproj/xcuserdata/shadowfacts.xcuserdatad/xcschemes/xcschememanagement.plist b/Gemini.xcodeproj/xcuserdata/shadowfacts.xcuserdatad/xcschemes/xcschememanagement.plist index 6f8d461..b8da17f 100644 --- a/Gemini.xcodeproj/xcuserdata/shadowfacts.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Gemini.xcodeproj/xcuserdata/shadowfacts.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ BrowserCore.xcscheme_^#shared#^_ orderHint - 5 + 3 Gemini-iOS.xcscheme_^#shared#^_ @@ -32,7 +32,7 @@ GeminiRenderer.xcscheme_^#shared#^_ orderHint - 3 + 5 SuppressBuildableAutocreation