Add homepage preference

This commit is contained in:
Shadowfacts 2021-06-15 23:21:22 -04:00
parent 8183e6986a
commit e5f520cf6f
9 changed files with 171 additions and 8 deletions

View File

@ -10,7 +10,7 @@ import UIKit
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
static let defaultHomepage = URL(string: "gemini://gemini.circumlunar.space/")!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
SymbolCache.load() SymbolCache.load()

View File

@ -304,7 +304,7 @@ class BrowserNavigationController: UIViewController {
private func showShareSheet(_ source: UIView) { private func showShareSheet(_ source: UIView) {
guard let doc = currentBrowserVC.document else { return } 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 vc.popoverPresentationController?.sourceView = source
present(vc, animated: true) present(vc, animated: true)
} }

View File

@ -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<URL?>, focus: Binding<Bool>) {
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<URL?>
init(binding: Binding<URL?>) {
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()
}
}

View File

@ -34,6 +34,10 @@ class Preferences: Codable, ObservableObject {
required init(from decoder: Decoder) throws { required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) 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) theme = try container.decode(UIUserInterfaceStyle.self, forKey: .theme)
if let stored = try container.decodeIfPresent(Bool.self, forKey: .showLinkIcons) { if let stored = try container.decodeIfPresent(Bool.self, forKey: .showLinkIcons) {
showLinkIcons = stored showLinkIcons = stored
@ -46,6 +50,8 @@ class Preferences: Codable, ObservableObject {
func encode(to encoder: Encoder) throws { func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(homepage, forKey: .homepage)
try container.encode(theme, forKey: .theme) try container.encode(theme, forKey: .theme)
try container.encode(showLinkIcons, forKey: .showLinkIcons) try container.encode(showLinkIcons, forKey: .showLinkIcons)
@ -53,6 +59,8 @@ class Preferences: Codable, ObservableObject {
try container.encode(useReaderMode, forKey: .useReaderMode) try container.encode(useReaderMode, forKey: .useReaderMode)
} }
@Published var homepage = AppDelegate.defaultHomepage
@Published var theme = UIUserInterfaceStyle.unspecified @Published var theme = UIUserInterfaceStyle.unspecified
@Published var showLinkIcons = true @Published var showLinkIcons = true
@ -60,6 +68,8 @@ class Preferences: Codable, ObservableObject {
@Published var useReaderMode = false @Published var useReaderMode = false
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case homepage
case theme case theme
case showLinkIcons case showLinkIcons

View File

@ -15,6 +15,8 @@ struct PreferencesView: View {
var body: some View { var body: some View {
NavigationView { NavigationView {
List { List {
untitledSection
appearanceSection appearanceSection
safariSection safariSection
@ -23,7 +25,7 @@ struct PreferencesView: View {
.insetOrGroupedListStyle() .insetOrGroupedListStyle()
.navigationBarItems(trailing: doneButton) .navigationBarItems(trailing: doneButton)
} }
.navigationViewStyle(StackNavigationViewStyle()) .navigationViewStyle(.stack)
.onDisappear { .onDisappear {
Preferences.save() Preferences.save()
} }
@ -38,6 +40,15 @@ struct PreferencesView: View {
.hoverEffect(.highlight) .hoverEffect(.highlight)
} }
private var untitledSection: some View {
Section {
NavigationLink(destination: HomepagePrefView()) {
Text("Homepage")
}
}
}
private var appearanceSection: some View { private var appearanceSection: some View {
Section(header: Text("Appearance")) { Section(header: Text("Appearance")) {
Picker(selection: $preferences.theme, label: Text("Theme")) { Picker(selection: $preferences.theme, label: Text("Theme")) {
@ -64,9 +75,9 @@ fileprivate extension View {
@ViewBuilder @ViewBuilder
func insetOrGroupedListStyle() -> some View { func insetOrGroupedListStyle() -> some View {
if #available(iOS 14.0, *) { if #available(iOS 14.0, *) {
self.listStyle(InsetGroupedListStyle()) self.listStyle(.insetGrouped)
} else { } else {
self.listStyle(GroupedListStyle()) self.listStyle(.grouped)
} }
} }
} }

View File

@ -32,7 +32,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
if ProcessInfo.processInfo.environment.keys.contains("DEFAULT_URL") { if ProcessInfo.processInfo.environment.keys.contains("DEFAULT_URL") {
initialURL = URL(string: ProcessInfo.processInfo.environment["DEFAULT_URL"]!)! initialURL = URL(string: ProcessInfo.processInfo.environment["DEFAULT_URL"]!)!
} else { } else {
initialURL = URL(string: "gemini://gemini.circumlunar.space/")! initialURL = Preferences.shared.homepage
} }
} }

View File

@ -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
}
}
}

View File

@ -37,6 +37,8 @@
D62664F024BC0D7700DF9B88 /* GeminiFormat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D62664A824BBF26A00DF9B88 /* GeminiFormat.framework */; }; D62664F024BC0D7700DF9B88 /* GeminiFormat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D62664A824BBF26A00DF9B88 /* GeminiFormat.framework */; };
D62664FA24BC12BC00DF9B88 /* DocumentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62664F924BC12BC00DF9B88 /* DocumentTests.swift */; }; D62664FA24BC12BC00DF9B88 /* DocumentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62664F924BC12BC00DF9B88 /* DocumentTests.swift */; };
D653F40B267996FF004E32B1 /* ActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D653F40A267996FF004E32B1 /* ActivityItemSource.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 */; }; D664673624BD07F700B0B741 /* RenderingBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673524BD07F700B0B741 /* RenderingBlock.swift */; };
D664673824BD086F00B0B741 /* RenderingBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673724BD086F00B0B741 /* RenderingBlockView.swift */; }; D664673824BD086F00B0B741 /* RenderingBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673724BD086F00B0B741 /* RenderingBlockView.swift */; };
D664673A24BD0B8E00B0B741 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673924BD0B8E00B0B741 /* Fonts.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 = "<group>"; }; D62664ED24BC0BCE00DF9B88 /* MaybeLazyVStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaybeLazyVStack.swift; sourceTree = "<group>"; };
D62664F924BC12BC00DF9B88 /* DocumentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTests.swift; sourceTree = "<group>"; }; D62664F924BC12BC00DF9B88 /* DocumentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTests.swift; sourceTree = "<group>"; };
D653F40A267996FF004E32B1 /* ActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityItemSource.swift; sourceTree = "<group>"; }; D653F40A267996FF004E32B1 /* ActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityItemSource.swift; sourceTree = "<group>"; };
D653F40C26799F2F004E32B1 /* HomepagePrefView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepagePrefView.swift; sourceTree = "<group>"; };
D653F40E2679A0AB004E32B1 /* SetHomepageActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetHomepageActivity.swift; sourceTree = "<group>"; };
D664673524BD07F700B0B741 /* RenderingBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlock.swift; sourceTree = "<group>"; }; D664673524BD07F700B0B741 /* RenderingBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlock.swift; sourceTree = "<group>"; };
D664673724BD086F00B0B741 /* RenderingBlockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlockView.swift; sourceTree = "<group>"; }; D664673724BD086F00B0B741 /* RenderingBlockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlockView.swift; sourceTree = "<group>"; };
D664673924BD0B8E00B0B741 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = "<group>"; }; D664673924BD0B8E00B0B741 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = "<group>"; };
@ -595,7 +599,9 @@
D6BC9AD6258FC8B3008652BC /* TableOfContentsView.swift */, D6BC9AD6258FC8B3008652BC /* TableOfContentsView.swift */,
D691A64D25217C6F00348C4B /* Preferences.swift */, D691A64D25217C6F00348C4B /* Preferences.swift */,
D691A66625217FD800348C4B /* PreferencesView.swift */, D691A66625217FD800348C4B /* PreferencesView.swift */,
D653F40C26799F2F004E32B1 /* HomepagePrefView.swift */,
D653F40A267996FF004E32B1 /* ActivityItemSource.swift */, D653F40A267996FF004E32B1 /* ActivityItemSource.swift */,
D653F40E2679A0AB004E32B1 /* SetHomepageActivity.swift */,
D688F618258AD231003A0A73 /* Resources */, D688F618258AD231003A0A73 /* Resources */,
D6E152AA24BFFDF600FDF9D3 /* Assets.xcassets */, D6E152AA24BFFDF600FDF9D3 /* Assets.xcassets */,
D6E152AF24BFFDF600FDF9D3 /* LaunchScreen.storyboard */, D6E152AF24BFFDF600FDF9D3 /* LaunchScreen.storyboard */,
@ -1117,9 +1123,11 @@
D653F40B267996FF004E32B1 /* ActivityItemSource.swift in Sources */, D653F40B267996FF004E32B1 /* ActivityItemSource.swift in Sources */,
D6BC9AB3258E8E13008652BC /* ToolbarView.swift in Sources */, D6BC9AB3258E8E13008652BC /* ToolbarView.swift in Sources */,
D688F64A258C17F3003A0A73 /* SymbolCache.swift in Sources */, D688F64A258C17F3003A0A73 /* SymbolCache.swift in Sources */,
D653F40F2679A0AB004E32B1 /* SetHomepageActivity.swift in Sources */,
D688F65A258C2256003A0A73 /* BrowserNavigationController.swift in Sources */, D688F65A258C2256003A0A73 /* BrowserNavigationController.swift in Sources */,
D6BC9AD7258FC8B3008652BC /* TableOfContentsView.swift in Sources */, D6BC9AD7258FC8B3008652BC /* TableOfContentsView.swift in Sources */,
D688F663258C2479003A0A73 /* UIViewController+Children.swift in Sources */, D688F663258C2479003A0A73 /* UIViewController+Children.swift in Sources */,
D653F40D26799F2F004E32B1 /* HomepagePrefView.swift in Sources */,
D691A64E25217C6F00348C4B /* Preferences.swift in Sources */, D691A64E25217C6F00348C4B /* Preferences.swift in Sources */,
D6BC9ABC258E9862008652BC /* NavigationBarView.swift in Sources */, D6BC9ABC258E9862008652BC /* NavigationBarView.swift in Sources */,
); );

View File

@ -7,7 +7,7 @@
<key>BrowserCore.xcscheme_^#shared#^_</key> <key>BrowserCore.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>5</integer> <integer>3</integer>
</dict> </dict>
<key>Gemini-iOS.xcscheme_^#shared#^_</key> <key>Gemini-iOS.xcscheme_^#shared#^_</key>
<dict> <dict>
@ -32,7 +32,7 @@
<key>GeminiRenderer.xcscheme_^#shared#^_</key> <key>GeminiRenderer.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>3</integer> <integer>5</integer>
</dict> </dict>
</dict> </dict>
<key>SuppressBuildableAutocreation</key> <key>SuppressBuildableAutocreation</key>