Skip to content

Commit

Permalink
fix: Remember selected sidebar section across app launches (#676)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbmorley authored Jun 20, 2023
1 parent e3cc0b5 commit 2c573c6
Show file tree
Hide file tree
Showing 21 changed files with 130 additions and 87 deletions.
5 changes: 3 additions & 2 deletions core/Sources/BookmarksCore/Commands/ApplicationCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import SwiftUI

public struct ApplicationCommands: Commands {

@FocusedObject var sceneModel: SceneModel?
@Environment(\.openWindow) var openWindow

@FocusedBinding(\.sceneState) var sceneState

public init() {

}
Expand All @@ -35,7 +36,7 @@ public struct ApplicationCommands: Commands {
#if os(macOS)
openWindow(id: TagsWindow.identifier)
#else
sceneModel?.showTags()
sceneState?.showTags()
#endif
} label: {
Text("Tags...")
Expand Down
3 changes: 1 addition & 2 deletions core/Sources/BookmarksCore/Commands/BookmarkCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import SwiftUI

public struct BookmarkCommands: Commands {

@FocusedObject var sceneModel: SceneModel?
@FocusedObject var sectionViewModel: SectionViewModel?

public init() {
Expand All @@ -40,7 +39,7 @@ public struct BookmarkCommands: Commands {
.trailingDivider()
BookmarkShareCommands(sectionViewModel: sectionViewModel ?? SectionViewModel())
.trailingDivider()
BookmarkTagCommands(sceneModel: sceneModel, sectionViewModel: sectionViewModel ?? SectionViewModel())
BookmarkTagCommands(sectionViewModel: sectionViewModel ?? SectionViewModel())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import SwiftUI

struct BookmarkOpenCommands: View {

@EnvironmentObject var settings: Settings

@ObservedObject var sectionViewModel: SectionViewModel

var body: some View {

Button(LocalizedString("BOOKMARK_MENU_TITLE_OPEN")) {
sectionViewModel.open(.selection)
sectionViewModel.open(.selection, browser: settings.browser)
}
.keyboardShortcut(.return, modifiers: [.command])
.disabled(sectionViewModel.selection.isEmpty)
Expand All @@ -43,7 +45,7 @@ struct BookmarkOpenCommands: View {
Divider()

Button(LocalizedString("BOOKMARK_MENU_TITLE_VIEW_ON_INTERNET_ARCHIVE")) {
sectionViewModel.open(.selection, location: .internetArchive)
sectionViewModel.open(.selection, location: .internetArchive, browser: settings.browser)
}
.keyboardShortcut(.return, modifiers: [.command, .shift])
.disabled(sectionViewModel.selection.isEmpty)
Expand Down
6 changes: 3 additions & 3 deletions core/Sources/BookmarksCore/Commands/BookmarkTagCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import SwiftUI

struct BookmarkTagCommands: View {

var sceneModel: SceneModel?
@FocusedBinding(\.sceneState) var sceneState

@ObservedObject var sectionViewModel: SectionViewModel

Expand All @@ -31,9 +31,9 @@ struct BookmarkTagCommands: View {

ForEach(Array(sectionViewModel.selectionTags).sorted()) { tag in
Button(tag) {
sceneModel?.revealTag(tag)
sceneState?.revealTag(tag)
}
.disabled(sceneModel == nil)
.disabled(sceneState == nil)
}

}
Expand Down
6 changes: 3 additions & 3 deletions core/Sources/BookmarksCore/Commands/SectionCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import SwiftUI

public struct SectionCommands: Commands {

@FocusedObject var sceneModel: SceneModel?
@FocusedBinding(\.sceneState) var sceneState: SceneState?

static func keyEquivalent(_ value: Int) -> KeyEquivalent {
return KeyEquivalent(String(value).first!)
Expand All @@ -36,10 +36,10 @@ public struct SectionCommands: Commands {
CommandMenu("Go") {
ForEach(Array(BookmarksSection.defaultSections.enumerated()), id: \.element.id) { index, section in
Button(section.navigationTitle) {
sceneModel?.section = section
sceneState?.section = section
}
.keyboardShortcut(Self.keyEquivalent(index + 1), modifiers: .command)
.disabled(sceneModel == nil)
.disabled(sceneState == nil)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/Sources/BookmarksCore/Common/BookmarksSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public protocol Sectionable {

}

public enum BookmarksSection: Equatable {
public enum BookmarksSection: Equatable, Codable {

public static let defaultSections: [Self] = [
.all,
Expand Down
8 changes: 8 additions & 0 deletions core/Sources/BookmarksCore/Common/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ final public class Settings: ObservableObject {
}
}

var browser: BrowserPreference {
if useInAppBrowser {
return .app
} else {
return .system
}
}

@Published public var maximumConcurrentThumbnailDownloads: Int {
didSet {
defaults.set(maximumConcurrentThumbnailDownloads, forKey: .maximumConcurrentThumbnailDownloads)
Expand Down
1 change: 0 additions & 1 deletion core/Sources/BookmarksCore/Model/BrowserPreferene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ import Foundation
enum BrowserPreference {
case app
case system
case user
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Combine
import SwiftUI

import Interact

public class SceneModel: ObservableObject {
struct SceneState: Codable, RawRepresentable {

public enum SheetType: Identifiable {
public enum SheetType: Identifiable, Codable {

public var id: String {
switch self {
Expand All @@ -43,51 +42,79 @@ public class SceneModel: ObservableObject {
case edit(Bookmark.ID)
}

var settings: Settings
enum CodingKeys: String, CodingKey {
case section
case sheet
case previewURL
}

var section: BookmarksSection? = .all
var sheet: SheetType? = nil
var previewURL: URL? = nil

init() {

}

@Published public var section: BookmarksSection? = .all
@Published public var sheet: SheetType? = nil
@Published public var previewURL: URL? = nil
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
section = try container.decodeIfPresent(BookmarksSection.self, forKey: .section)
sheet = try container.decodeIfPresent(SheetType.self, forKey: .sheet)
previewURL = try container.decodeIfPresent(URL.self, forKey: .previewURL)
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(section, forKey: .section)
try container.encodeIfPresent(sheet, forKey: .sheet)
try container.encodeIfPresent(previewURL, forKey: .previewURL)
}

init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Self.self, from: data)
else {
return nil
}
self = result
}

public init(settings: Settings) {
self.settings = settings
var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let string = String(data: data, encoding: .utf8)
else {
return "{}"
}
return string
}

@MainActor public func showTags() {
mutating func showTags() {
sheet = .tags
}

@MainActor public func showSettings() {
mutating func showSettings() {
sheet = .settings
}

@MainActor public func edit(_ bookmark: Bookmark) {
mutating func edit(_ bookmark: Bookmark) {
sheet = .edit(bookmark.id)
}

@MainActor func showURL(_ url: URL, browser: BrowserPreference = .user) {
let useInAppBrowser: Bool
mutating func showURL(_ url: URL, browser: BrowserPreference) {
switch browser {
case .app:
useInAppBrowser = true
case .system:
useInAppBrowser = false
case .user:
useInAppBrowser = settings.useInAppBrowser
}
if useInAppBrowser {
previewURL = url
} else {
case .system:
Application.open(url)
}
}

@MainActor public func revealTag(_ tag: String) {
mutating func revealTag(_ tag: String) {
sheet = nil
section = .tag(tag)
}

@MainActor public func handleURL(_ url: URL) {
mutating func handleURL(_ url: URL) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return
}
Expand All @@ -108,3 +135,16 @@ public class SceneModel: ObservableObject {
}

}

struct FocusedSceneStateValueKey: FocusedValueKey {
typealias Value = Binding<SceneState>
}

extension FocusedValues {

var sceneState: FocusedSceneStateValueKey.Value? {
get { self[FocusedSceneStateValueKey.self] }
set { self[FocusedSceneStateValueKey.self] = newValue }
}

}
20 changes: 10 additions & 10 deletions core/Sources/BookmarksCore/Model/SectionViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class SectionViewModel: ObservableObject, Runnable {
@Published private var bookmarksLookup: [Bookmark.ID: Bookmark] = [:]

private let applicationModel: ApplicationModel?
private let sceneModel: SceneModel?
@Binding private var sceneState: SceneState
private let section: BookmarksSection
private let openWindow: OpenWindowAction?
private var cancellables: Set<AnyCancellable> = []
Expand All @@ -59,12 +59,12 @@ public class SectionViewModel: ObservableObject, Runnable {
return applicationModel == nil
}

public init(applicationModel: ApplicationModel? = nil,
sceneModel: SceneModel? = nil,
section: BookmarksSection = .all,
openWindow: OpenWindowAction? = nil) {
init(applicationModel: ApplicationModel? = nil,
sceneState: Binding<SceneState> = Binding.constant(SceneState()), // TODO: This is ugly.
section: BookmarksSection = .all,
openWindow: OpenWindowAction? = nil) {
self.applicationModel = applicationModel
self.sceneModel = sceneModel
_sceneState = sceneState
self.section = section
self.openWindow = openWindow
self.query = section.query
Expand Down Expand Up @@ -228,7 +228,7 @@ public class SectionViewModel: ObservableObject, Runnable {
@MainActor public func getInfo(_ scope: SelectionScope<Bookmark.ID>) {
for bookmark in bookmarks(scope) {
#if os(iOS)
sceneModel?.edit(bookmark)
sceneState.edit(bookmark)
#else
openWindow?(value: bookmark.id)
#endif
Expand All @@ -237,12 +237,12 @@ public class SectionViewModel: ObservableObject, Runnable {

@MainActor func open(_ scope: SelectionScope<Bookmark.ID>,
location: Bookmark.Location = .web,
browser: BrowserPreference = .user) {
browser: BrowserPreference) {
for bookmark in bookmarks(scope) {
guard let url = try? bookmark.url(location) else {
continue
}
sceneModel?.showURL(url, browser: browser)
sceneState.showURL(url, browser: browser)
}
}

Expand Down Expand Up @@ -348,7 +348,7 @@ public class SectionViewModel: ObservableObject, Runnable {
Divider()
#endif
MenuItem(LocalizedString("BOOKMARK_MENU_TITLE_VIEW_ON_INTERNET_ARCHIVE"), systemImage: "clock") {
self.open(.items(selection), location: .internetArchive)
self.open(.items(selection), location: .internetArchive, browser: self.applicationModel?.settings.browser ?? .system)
}
Divider()
MenuItem(containsUnreadBookmark ? "Mark as Read" : "Mark as Unread",
Expand Down
8 changes: 4 additions & 4 deletions core/Sources/BookmarksCore/Modifiers/SceneActionHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,22 @@ import SwiftUI

struct SceneActionHandler: ViewModifier {

@EnvironmentObject var sceneModel: SceneModel
@Binding var sceneState: SceneState

func body(content: Content) -> some View {
content
.handlesExternalEvents(preferring: ["/open"], allowing: [])
.onOpenURL { url in
sceneModel.handleURL(url)
sceneState.handleURL(url)
}
}

}

extension View {

public func handlesSceneActions() -> some View {
return modifier(SceneActionHandler())
func handlesSceneActions(_ sceneState: Binding<SceneState>) -> some View {
return modifier(SceneActionHandler(sceneState: sceneState))
}

}
4 changes: 3 additions & 1 deletion core/Sources/BookmarksCore/Toolbars/SelectionToolbar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public struct SelectionToolbar: CustomizableToolbarContent {

struct Content: CustomizableToolbarContent {

@EnvironmentObject var settings: Settings

@ObservedObject var sectionViewModel: SectionViewModel

var body: some CustomizableToolbarContent {
Expand All @@ -41,7 +43,7 @@ public struct SelectionToolbar: CustomizableToolbarContent {

ToolbarItem(id: "open") {
Button {
sectionViewModel.open(.selection)
sectionViewModel.open(.selection, browser: settings.browser)
} label: {
Label("Open", systemImage: "safari")
}
Expand Down
Loading

0 comments on commit 2c573c6

Please sign in to comment.