Merge pull request #3 from pawelorzech/codex/github-mention-default-browser-suggested-improvement

Add preferred browser setting for opening links
This commit is contained in:
Paweł Orzech 2026-02-04 13:42:32 +01:00 committed by GitHub
commit 9b8eaed844
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 102 additions and 21 deletions

View file

@ -32,6 +32,7 @@
AAA00023 /* CreditsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10024 /* CreditsView.swift */; }; AAA00023 /* CreditsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10024 /* CreditsView.swift */; };
AAA00024 /* TransparencyEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10025 /* TransparencyEnvironment.swift */; }; AAA00024 /* TransparencyEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10025 /* TransparencyEnvironment.swift */; };
AAA00025 /* FeedbackPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10026 /* FeedbackPromptView.swift */; }; AAA00025 /* FeedbackPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10026 /* FeedbackPromptView.swift */; };
AAA00026 /* BrowserManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10027 /* BrowserManager.swift */; };
/* Unit Tests */ /* Unit Tests */
BBB00001 /* MockNetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB10001 /* MockNetworkSession.swift */; }; BBB00001 /* MockNetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB10001 /* MockNetworkSession.swift */; };
BBB00002 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB10002 /* TestHelpers.swift */; }; BBB00002 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB10002 /* TestHelpers.swift */; };
@ -94,6 +95,7 @@
AAA10024 /* CreditsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsView.swift; sourceTree = "<group>"; }; AAA10024 /* CreditsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsView.swift; sourceTree = "<group>"; };
AAA10025 /* TransparencyEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransparencyEnvironment.swift; sourceTree = "<group>"; }; AAA10025 /* TransparencyEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransparencyEnvironment.swift; sourceTree = "<group>"; };
AAA10026 /* FeedbackPromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPromptView.swift; sourceTree = "<group>"; }; AAA10026 /* FeedbackPromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPromptView.swift; sourceTree = "<group>"; };
AAA10027 /* BrowserManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserManager.swift; sourceTree = "<group>"; };
AAA10000 /* MacTorn.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MacTorn.app; sourceTree = BUILT_PRODUCTS_DIR; }; AAA10000 /* MacTorn.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MacTorn.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* Unit Test Files */ /* Unit Test Files */
BBB10001 /* MockNetworkSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNetworkSession.swift; sourceTree = "<group>"; }; BBB10001 /* MockNetworkSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNetworkSession.swift; sourceTree = "<group>"; };
@ -226,6 +228,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
AAA10009 /* NotificationManager.swift */, AAA10009 /* NotificationManager.swift */,
AAA10027 /* BrowserManager.swift */,
AAA10011 /* LaunchAtLoginManager.swift */, AAA10011 /* LaunchAtLoginManager.swift */,
AAA10012 /* ShortcutsManager.swift */, AAA10012 /* ShortcutsManager.swift */,
AAA10016 /* SoundManager.swift */, AAA10016 /* SoundManager.swift */,
@ -464,6 +467,7 @@
AAA00023 /* CreditsView.swift in Sources */, AAA00023 /* CreditsView.swift in Sources */,
AAA00024 /* TransparencyEnvironment.swift in Sources */, AAA00024 /* TransparencyEnvironment.swift in Sources */,
AAA00025 /* FeedbackPromptView.swift in Sources */, AAA00025 /* FeedbackPromptView.swift in Sources */,
AAA00026 /* BrowserManager.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View file

@ -0,0 +1,62 @@
import AppKit
enum PreferredBrowser: String, CaseIterable, Identifiable {
case system = "System Default"
case safari = "Safari"
case chrome = "Google Chrome"
case firefox = "Firefox"
case edge = "Microsoft Edge"
case brave = "Brave"
var id: String { rawValue }
var bundleIdentifier: String? {
switch self {
case .system:
return nil
case .safari:
return "com.apple.Safari"
case .chrome:
return "com.google.Chrome"
case .firefox:
return "org.mozilla.firefox"
case .edge:
return "com.microsoft.edgemac"
case .brave:
return "com.brave.Browser"
}
}
init(storedValue: String?) {
guard let storedValue,
let value = PreferredBrowser(rawValue: storedValue) else {
self = .system
return
}
self = value
}
}
final class BrowserManager {
static let shared = BrowserManager()
private init() {}
func open(_ url: URL) {
guard let scheme = url.scheme,
["http", "https"].contains(scheme) else {
NSWorkspace.shared.open(url)
return
}
let preference = PreferredBrowser(storedValue: UserDefaults.standard.string(forKey: "preferredBrowser"))
guard let bundleIdentifier = preference.bundleIdentifier,
let appURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleIdentifier) else {
NSWorkspace.shared.open(url)
return
}
let configuration = NSWorkspace.OpenConfiguration()
NSWorkspace.shared.open([url], withApplicationAt: appURL, configuration: configuration, completionHandler: nil)
}
}

View file

@ -127,7 +127,7 @@ class NotificationManager: NSObject, UNUserNotificationCenterDelegate {
) { ) {
let categoryIdentifier = response.notification.request.content.categoryIdentifier let categoryIdentifier = response.notification.request.content.categoryIdentifier
if let type = NotificationType(rawValue: categoryIdentifier) { if let type = NotificationType(rawValue: categoryIdentifier) {
NSWorkspace.shared.open(type.url) BrowserManager.shared.open(type.url)
} }
completionHandler() completionHandler()
} }

View file

@ -41,6 +41,6 @@ class ShortcutsManager: ObservableObject {
func openURL(_ urlString: String) { func openURL(_ urlString: String) {
guard let url = URL(string: urlString) else { return } guard let url = URL(string: urlString) else { return }
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }

View file

@ -770,7 +770,7 @@ class AppState: ObservableObject {
showFeedbackPrompt = false showFeedbackPrompt = false
saveFeedbackState() saveFeedbackState()
if let url = URL(string: "https://www.torn.com/forums.php#/p=threads&f=67&t=16532308") { if let url = URL(string: "https://www.torn.com/forums.php#/p=threads&f=67&t=16532308") {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
@ -779,7 +779,7 @@ class AppState: ObservableObject {
showFeedbackPrompt = false showFeedbackPrompt = false
saveFeedbackState() saveFeedbackState()
if let url = URL(string: "mailto:pawel@orzech.lol?subject=MacTorn%20Feedback") { if let url = URL(string: "mailto:pawel@orzech.lol?subject=MacTorn%20Feedback") {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }

View file

@ -58,7 +58,7 @@ struct AttacksView: View {
Button { Button {
if let opponentId = attack.opponentId(forUserId: userId), if let opponentId = attack.opponentId(forUserId: userId),
let url = URL(string: "https://www.torn.com/profiles.php?XID=\(opponentId)") { let url = URL(string: "https://www.torn.com/profiles.php?XID=\(opponentId)") {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} label: { } label: {
HStack(spacing: 6) { HStack(spacing: 6) {
@ -128,7 +128,7 @@ struct AttacksView: View {
private func openURL(_ urlString: String) { private func openURL(_ urlString: String) {
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
} }

View file

@ -238,21 +238,21 @@ struct CreditsView: View {
private func openTornProfile(_ tornID: Int) { private func openTornProfile(_ tornID: Int) {
let urlString = "https://www.torn.com/profiles.php?XID=\(tornID)" let urlString = "https://www.torn.com/profiles.php?XID=\(tornID)"
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
private func openFaction(_ factionID: Int) { private func openFaction(_ factionID: Int) {
let urlString = "https://www.torn.com/factions.php?step=profile&ID=\(factionID)" let urlString = "https://www.torn.com/factions.php?step=profile&ID=\(factionID)"
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
private func openCompany(_ ownerID: Int) { private func openCompany(_ ownerID: Int) {
let urlString = "https://www.torn.com/joblist.php#/p=corpinfo&userID=\(ownerID)" let urlString = "https://www.torn.com/joblist.php#/p=corpinfo&userID=\(ownerID)"
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
} }

View file

@ -132,7 +132,7 @@ struct FactionView: View {
private func openURL(_ urlString: String) { private func openURL(_ urlString: String) {
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
} }

View file

@ -106,7 +106,7 @@ struct MoneyView: View {
private func openURL(_ urlString: String) { private func openURL(_ urlString: String) {
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
} }

View file

@ -52,7 +52,7 @@ struct PropertiesView: View {
private func openURL(_ urlString: String) { private func openURL(_ urlString: String) {
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
} }

View file

@ -4,6 +4,7 @@ struct SettingsView: View {
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@AppStorage("appearanceMode") private var appearanceMode: String = AppearanceMode.system.rawValue @AppStorage("appearanceMode") private var appearanceMode: String = AppearanceMode.system.rawValue
@AppStorage("reduceTransparency") private var reduceTransparency: Bool = false @AppStorage("reduceTransparency") private var reduceTransparency: Bool = false
@AppStorage("preferredBrowser") private var preferredBrowser: String = PreferredBrowser.system.rawValue
@State private var inputKey: String = "" @State private var inputKey: String = ""
@State private var showCredits: Bool = false @State private var showCredits: Bool = false
@ -106,6 +107,20 @@ struct SettingsView: View {
.labelsHidden() .labelsHidden()
} }
// Preferred Browser
HStack {
Image(systemName: "globe")
.foregroundColor(.secondary)
.frame(width: 20)
Picker("Preferred Browser", selection: $preferredBrowser) {
ForEach(PreferredBrowser.allCases) { browser in
Text(browser.rawValue).tag(browser.rawValue)
}
}
.pickerStyle(.menu)
}
// Reduce Transparency (Accessibility) // Reduce Transparency (Accessibility)
HStack { HStack {
Image(systemName: "eye") Image(systemName: "eye")
@ -164,7 +179,7 @@ struct SettingsView: View {
Button("Download Update") { Button("Download Update") {
if let url = URL(string: update.htmlUrl) { if let url = URL(string: update.htmlUrl) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
.buttonStyle(.borderedProminent) .buttonStyle(.borderedProminent)
@ -268,7 +283,7 @@ struct SettingsView: View {
private func openTornProfile() { private func openTornProfile() {
let url = "https://www.torn.com/profiles.php?XID=\(developerID)" let url = "https://www.torn.com/profiles.php?XID=\(developerID)"
if let url = URL(string: url) { if let url = URL(string: url) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
} }

View file

@ -99,7 +99,7 @@ struct StatusView: View {
private var messagesBadge: some View { private var messagesBadge: some View {
Button { Button {
if let url = URL(string: "https://www.torn.com/messages.php") { if let url = URL(string: "https://www.torn.com/messages.php") {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} label: { } label: {
HStack { HStack {

View file

@ -149,7 +149,7 @@ struct TravelView: View {
Button { Button {
if let url = URL(string: "https://www.torn.com/travelagency.php") { if let url = URL(string: "https://www.torn.com/travelagency.php") {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} label: { } label: {
HStack { HStack {
@ -204,7 +204,7 @@ struct TravelView: View {
// Show only return button when abroad // Show only return button when abroad
Button { Button {
if let url = URL(string: "https://www.torn.com/travelagency.php") { if let url = URL(string: "https://www.torn.com/travelagency.php") {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} label: { } label: {
HStack { HStack {
@ -234,7 +234,7 @@ struct TravelView: View {
private func destinationButton(_ destination: TornDestination) -> some View { private func destinationButton(_ destination: TornDestination) -> some View {
Button { Button {
NSWorkspace.shared.open(destination.travelAgencyURL) BrowserManager.shared.open(destination.travelAgencyURL)
} label: { } label: {
VStack(spacing: 4) { VStack(spacing: 4) {
HStack(spacing: 4) { HStack(spacing: 4) {
@ -304,7 +304,7 @@ struct TravelView: View {
HStack(spacing: 8) { HStack(spacing: 8) {
Button { Button {
if let url = URL(string: "https://www.torn.com/travelagency.php") { if let url = URL(string: "https://www.torn.com/travelagency.php") {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} label: { } label: {
HStack { HStack {
@ -321,7 +321,7 @@ struct TravelView: View {
Button { Button {
if let url = URL(string: "https://www.torn.com/page.php?sid=ItemMarket") { if let url = URL(string: "https://www.torn.com/page.php?sid=ItemMarket") {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} label: { } label: {
HStack { HStack {

View file

@ -126,7 +126,7 @@ struct WatchlistView: View {
private func openURL(_ urlString: String) { private func openURL(_ urlString: String) {
if let url = URL(string: urlString) { if let url = URL(string: urlString) {
NSWorkspace.shared.open(url) BrowserManager.shared.open(url)
} }
} }
} }