Merge branch 'develop' of https://codeberg.org/readeck/readeck-ios into develop
This commit is contained in:
commit
f3147a6cc6
3
.gitignore
vendored
3
.gitignore
vendored
@ -66,3 +66,6 @@ fastlane/AuthKey_JZJCQWW9N3.p8
|
|||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
documentation/
|
documentation/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
**/.DS_Store
|
||||||
@ -14,6 +14,8 @@ struct Settings {
|
|||||||
var theme: Theme? = nil
|
var theme: Theme? = nil
|
||||||
var cardLayoutStyle: CardLayoutStyle? = nil
|
var cardLayoutStyle: CardLayoutStyle? = nil
|
||||||
|
|
||||||
|
var urlOpener: UrlOpener? = nil
|
||||||
|
|
||||||
var isLoggedIn: Bool {
|
var isLoggedIn: Bool {
|
||||||
token != nil && !token!.isEmpty
|
token != nil && !token!.isEmpty
|
||||||
}
|
}
|
||||||
@ -91,6 +93,10 @@ class SettingsRepository: PSettingsRepository {
|
|||||||
existingSettings.theme = theme.rawValue
|
existingSettings.theme = theme.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let urlOpener = settings.urlOpener {
|
||||||
|
existingSettings.urlOpener = urlOpener.rawValue
|
||||||
|
}
|
||||||
|
|
||||||
if let cardLayoutStyle = settings.cardLayoutStyle {
|
if let cardLayoutStyle = settings.cardLayoutStyle {
|
||||||
existingSettings.cardLayoutStyle = cardLayoutStyle.rawValue
|
existingSettings.cardLayoutStyle = cardLayoutStyle.rawValue
|
||||||
}
|
}
|
||||||
@ -132,7 +138,8 @@ class SettingsRepository: PSettingsRepository {
|
|||||||
fontSize: FontSize(rawValue: settingEntity?.fontSize ?? FontSize.medium.rawValue),
|
fontSize: FontSize(rawValue: settingEntity?.fontSize ?? FontSize.medium.rawValue),
|
||||||
enableTTS: settingEntity?.enableTTS,
|
enableTTS: settingEntity?.enableTTS,
|
||||||
theme: Theme(rawValue: settingEntity?.theme ?? Theme.system.rawValue),
|
theme: Theme(rawValue: settingEntity?.theme ?? Theme.system.rawValue),
|
||||||
cardLayoutStyle: CardLayoutStyle(rawValue: settingEntity?.cardLayoutStyle ?? CardLayoutStyle.magazine.rawValue)
|
cardLayoutStyle: CardLayoutStyle(rawValue: settingEntity?.cardLayoutStyle ?? CardLayoutStyle.magazine.rawValue),
|
||||||
|
urlOpener: UrlOpener(rawValue: settingEntity?.urlOpener ?? UrlOpener.inAppBrowser.rawValue)
|
||||||
)
|
)
|
||||||
continuation.resume(returning: settings)
|
continuation.resume(returning: settings)
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
11
readeck/Domain/Model/UrlOpener.swift
Normal file
11
readeck/Domain/Model/UrlOpener.swift
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
enum UrlOpener: String, CaseIterable {
|
||||||
|
case inAppBrowser = "inAppBrowser"
|
||||||
|
case defaultBrowser = "defaultBrowser"
|
||||||
|
|
||||||
|
var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .inAppBrowser: return "In App Browser"
|
||||||
|
case .defaultBrowser: return "Default Browser"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ protocol PSaveSettingsUseCase {
|
|||||||
func execute(selectedFontFamily: FontFamily, selectedFontSize: FontSize) async throws
|
func execute(selectedFontFamily: FontFamily, selectedFontSize: FontSize) async throws
|
||||||
func execute(enableTTS: Bool) async throws
|
func execute(enableTTS: Bool) async throws
|
||||||
func execute(theme: Theme) async throws
|
func execute(theme: Theme) async throws
|
||||||
|
func execute(urlOpener: UrlOpener) async throws
|
||||||
}
|
}
|
||||||
|
|
||||||
class SaveSettingsUseCase: PSaveSettingsUseCase {
|
class SaveSettingsUseCase: PSaveSettingsUseCase {
|
||||||
@ -33,4 +34,10 @@ class SaveSettingsUseCase: PSaveSettingsUseCase {
|
|||||||
.init(theme: theme)
|
.init(theme: theme)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func execute(urlOpener: UrlOpener) async throws {
|
||||||
|
try await settingsRepository.saveSettings(
|
||||||
|
.init(urlOpener: urlOpener)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,9 @@
|
|||||||
"General Settings" = "Allgemeine Einstellungen";
|
"General Settings" = "Allgemeine Einstellungen";
|
||||||
"Server Settings" = "Server-Einstellungen";
|
"Server Settings" = "Server-Einstellungen";
|
||||||
"Server Connection" = "Server-Verbindung";
|
"Server Connection" = "Server-Verbindung";
|
||||||
|
"Open external links in" = "Öffne externe Links in";
|
||||||
|
"In App Browser" = "In App Browser";
|
||||||
|
"Default Browser" = "Standard Browser";
|
||||||
|
|
||||||
"Add" = "Hinzufügen";
|
"Add" = "Hinzufügen";
|
||||||
"Add new tag:" = "Neues Label hinzufügen:";
|
"Add new tag:" = "Neues Label hinzufügen:";
|
||||||
|
|||||||
@ -72,7 +72,7 @@ struct BookmarkDetailView: View {
|
|||||||
.padding()
|
.padding()
|
||||||
} else {
|
} else {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
SafariUtil.openInSafari(url: viewModel.bookmarkDetail.url)
|
URLUtil.open(url: viewModel.bookmarkDetail.url, urlOpener: appSettings.urlOpener)
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "safari")
|
Image(systemName: "safari")
|
||||||
@ -263,7 +263,7 @@ struct BookmarkDetailView: View {
|
|||||||
.padding()
|
.padding()
|
||||||
} else {
|
} else {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
SafariUtil.openInSafari(url: viewModel.bookmarkDetail.url)
|
URLUtil.open(url: viewModel.bookmarkDetail.url, urlOpener: appSettings.urlOpener)
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "safari")
|
Image(systemName: "safari")
|
||||||
@ -319,7 +319,7 @@ struct BookmarkDetailView: View {
|
|||||||
|
|
||||||
metaRow(icon: "safari") {
|
metaRow(icon: "safari") {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
SafariUtil.openInSafari(url: viewModel.bookmarkDetail.url)
|
URLUtil.open(url: viewModel.bookmarkDetail.url, urlOpener: appSettings.urlOpener)
|
||||||
}) {
|
}) {
|
||||||
Text((URLUtil.extractDomain(from: viewModel.bookmarkDetail.url) ?? "Open original page") + " open")
|
Text((URLUtil.extractDomain(from: viewModel.bookmarkDetail.url) ?? "Open original page") + " open")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
|
|||||||
@ -14,6 +14,7 @@ extension View {
|
|||||||
|
|
||||||
struct BookmarkCardView: View {
|
struct BookmarkCardView: View {
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@EnvironmentObject var appSettings: AppSettings
|
||||||
|
|
||||||
let bookmark: Bookmark
|
let bookmark: Bookmark
|
||||||
let currentState: BookmarkState
|
let currentState: BookmarkState
|
||||||
@ -255,7 +256,7 @@ struct BookmarkCardView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
Label((URLUtil.extractDomain(from: bookmark.url) ?? "Original Site") + " open", systemImage: "safari")
|
Label((URLUtil.extractDomain(from: bookmark.url) ?? "Original Site") + " open", systemImage: "safari")
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
SafariUtil.openInSafari(url: bookmark.url)
|
URLUtil.open(url: bookmark.url, urlOpener: appSettings.urlOpener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,7 +337,7 @@ struct BookmarkCardView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
Label((URLUtil.extractDomain(from: bookmark.url) ?? "Original Site") + " open", systemImage: "safari")
|
Label((URLUtil.extractDomain(from: bookmark.url) ?? "Original Site") + " open", systemImage: "safari")
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
SafariUtil.openInSafari(url: bookmark.url)
|
URLUtil.open(url: bookmark.url, urlOpener: appSettings.urlOpener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -150,6 +150,7 @@ class MockSaveSettingsUseCase: PSaveSettingsUseCase {
|
|||||||
func execute(selectedFontFamily: FontFamily, selectedFontSize: FontSize) async throws {}
|
func execute(selectedFontFamily: FontFamily, selectedFontSize: FontSize) async throws {}
|
||||||
func execute(enableTTS: Bool) async throws {}
|
func execute(enableTTS: Bool) async throws {}
|
||||||
func execute(theme: Theme) async throws {}
|
func execute(theme: Theme) async throws {}
|
||||||
|
func execute(urlOpener: UrlOpener) async throws {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockGetBookmarkUseCase: PGetBookmarkUseCase {
|
class MockGetBookmarkUseCase: PGetBookmarkUseCase {
|
||||||
|
|||||||
@ -27,6 +27,10 @@ class AppSettings: ObservableObject {
|
|||||||
settings?.theme ?? .system
|
settings?.theme ?? .system
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var urlOpener: UrlOpener {
|
||||||
|
settings?.urlOpener ?? .inAppBrowser
|
||||||
|
}
|
||||||
|
|
||||||
init(settings: Settings? = nil) {
|
init(settings: Settings? = nil) {
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,23 @@ struct SettingsGeneralView: View {
|
|||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reading Settings
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
Text("Open external links in".localized)
|
||||||
|
.font(.headline)
|
||||||
|
Picker("urlOpener", selection: $viewModel.urlOpener) {
|
||||||
|
ForEach(UrlOpener.allCases, id: \.self) { urlOpener in
|
||||||
|
Text(urlOpener.displayName.localized).tag(urlOpener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(.segmented)
|
||||||
|
.onChange(of: viewModel.urlOpener) {
|
||||||
|
Task {
|
||||||
|
await viewModel.saveGeneralSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Sync Settings
|
// Sync Settings
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
@ -55,8 +72,6 @@ struct SettingsGeneralView: View {
|
|||||||
.font(.headline)
|
.font(.headline)
|
||||||
Toggle("Safari Reader Mode", isOn: $viewModel.enableReaderMode)
|
Toggle("Safari Reader Mode", isOn: $viewModel.enableReaderMode)
|
||||||
.toggleStyle(SwitchToggleStyle())
|
.toggleStyle(SwitchToggleStyle())
|
||||||
Toggle("Open external links in in-app Safari", isOn: $viewModel.openExternalLinksInApp)
|
|
||||||
.toggleStyle(SwitchToggleStyle())
|
|
||||||
Toggle("Automatically mark articles as read", isOn: $viewModel.autoMarkAsRead)
|
Toggle("Automatically mark articles as read", isOn: $viewModel.autoMarkAsRead)
|
||||||
.toggleStyle(SwitchToggleStyle())
|
.toggleStyle(SwitchToggleStyle())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,8 @@ class SettingsGeneralViewModel {
|
|||||||
// MARK: - Reading Settings
|
// MARK: - Reading Settings
|
||||||
var enableReaderMode: Bool = false
|
var enableReaderMode: Bool = false
|
||||||
var enableTTS: Bool = false
|
var enableTTS: Bool = false
|
||||||
var openExternalLinksInApp: Bool = true
|
|
||||||
var autoMarkAsRead: Bool = false
|
var autoMarkAsRead: Bool = false
|
||||||
|
var urlOpener: UrlOpener = .inAppBrowser
|
||||||
|
|
||||||
// MARK: - Messages
|
// MARK: - Messages
|
||||||
|
|
||||||
@ -36,6 +36,7 @@ class SettingsGeneralViewModel {
|
|||||||
if let settings = try await loadSettingsUseCase.execute() {
|
if let settings = try await loadSettingsUseCase.execute() {
|
||||||
enableTTS = settings.enableTTS ?? false
|
enableTTS = settings.enableTTS ?? false
|
||||||
selectedTheme = settings.theme ?? .system
|
selectedTheme = settings.theme ?? .system
|
||||||
|
urlOpener = settings.urlOpener ?? .inAppBrowser
|
||||||
autoSyncEnabled = false
|
autoSyncEnabled = false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@ -48,6 +49,7 @@ class SettingsGeneralViewModel {
|
|||||||
do {
|
do {
|
||||||
try await saveSettingsUseCase.execute(enableTTS: enableTTS)
|
try await saveSettingsUseCase.execute(enableTTS: enableTTS)
|
||||||
try await saveSettingsUseCase.execute(theme: selectedTheme)
|
try await saveSettingsUseCase.execute(theme: selectedTheme)
|
||||||
|
try await saveSettingsUseCase.execute(urlOpener: urlOpener)
|
||||||
|
|
||||||
successMessage = "Settings saved"
|
successMessage = "Settings saved"
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,25 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import SafariServices
|
import SafariServices
|
||||||
|
|
||||||
class SafariUtil {
|
struct URLUtil {
|
||||||
static func openInSafari(url: String) {
|
|
||||||
|
static func open(url: String, urlOpener: UrlOpener = .inAppBrowser) {
|
||||||
|
// Could be extended to open in other browsers like Firefox, Brave etc. if somebody has a multi browser setup
|
||||||
|
// and wants readeck links to always opened in a specific browser
|
||||||
|
switch urlOpener {
|
||||||
|
case .defaultBrowser:
|
||||||
|
openUrlInDefaultBrowser(url: url)
|
||||||
|
default:
|
||||||
|
openUrlInInAppBrowser(url: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func openUrlInDefaultBrowser(url: String) {
|
||||||
|
guard let url = URL(string: url) else { return }
|
||||||
|
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func openUrlInInAppBrowser(url: String) {
|
||||||
guard let url = URL(string: url) else { return }
|
guard let url = URL(string: url) else { return }
|
||||||
|
|
||||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||||
@ -22,9 +39,7 @@ class SafariUtil {
|
|||||||
presentingViewController.present(safariViewController, animated: true)
|
presentingViewController.present(safariViewController, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct URLUtil {
|
|
||||||
static func extractDomain(from urlString: String) -> String? {
|
static func extractDomain(from urlString: String) -> String? {
|
||||||
guard let url = URL(string: urlString), let host = url.host else { return nil }
|
guard let url = URL(string: urlString), let host = url.host else { return nil }
|
||||||
return host.replacingOccurrences(of: "www.", with: "")
|
return host.replacingOccurrences(of: "www.", with: "")
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23788.4" systemVersion="24G84" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="24299" systemVersion="25A354" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||||
<entity name="ArticleURLEntity" representedClassName="ArticleURLEntity" syncable="YES" codeGenerationType="class">
|
<entity name="ArticleURLEntity" representedClassName="ArticleURLEntity" syncable="YES" codeGenerationType="class">
|
||||||
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
<attribute name="tags" optional="YES" attributeType="String"/>
|
<attribute name="tags" optional="YES" attributeType="String"/>
|
||||||
@ -57,6 +57,7 @@
|
|||||||
<attribute name="fontSize" optional="YES" attributeType="String"/>
|
<attribute name="fontSize" optional="YES" attributeType="String"/>
|
||||||
<attribute name="theme" optional="YES" attributeType="String"/>
|
<attribute name="theme" optional="YES" attributeType="String"/>
|
||||||
<attribute name="token" optional="YES" attributeType="String"/>
|
<attribute name="token" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="urlOpener" optional="YES" attributeType="String"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="TagEntity" representedClassName="TagEntity" syncable="YES" codeGenerationType="class">
|
<entity name="TagEntity" representedClassName="TagEntity" syncable="YES" codeGenerationType="class">
|
||||||
<attribute name="name" optional="YES" attributeType="String"/>
|
<attribute name="name" optional="YES" attributeType="String"/>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user