Add homepage preference
This commit is contained in:
parent
8183e6986a
commit
e5f520cf6f
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 */,
|
||||||
);
|
);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue