Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions docmostly/Features/Notifications/NotificationListRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import SwiftUI

struct NotificationListRow: View {
@Environment(AppState.self) private var appState
@Environment(\.pageOpenPresentation) private var pageOpenPresentation

let notification: DocmostNotification
let isUnread: Bool
Expand All @@ -24,18 +25,24 @@ struct NotificationListRow: View {
.accessibilityIdentifier("PageOpenLink.\(target.slugId)")
#else
Button {
openPage(target)
if pageOpenPresentation == .stack {
openPage(target)
} else {
appState.openPage(target)
}
if isUnread {
markRead()
}
} label: {
HStack(spacing: 8) {
HStack {
NotificationRowView(notification: notification, isUnread: isUnread)

Image(systemName: "chevron.forward")
.imageScale(.small)
.foregroundStyle(.tertiary)
.accessibilityHidden(true)
if pageOpenPresentation == .stack {
Image(systemName: "chevron.forward")
.imageScale(.small)
.foregroundStyle(.tertiary)
.accessibilityHidden(true)
}
}
}
.buttonStyle(.plain)
Expand Down
14 changes: 14 additions & 0 deletions docmostly/Features/PageReader/PageOpenPresentation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import SwiftUI

enum PageOpenPresentation: Equatable {
case stack
case detailColumn

var shouldClearSelectedPageOnReaderDisappear: Bool {
self == .stack
}
}

extension EnvironmentValues {
@Entry var pageOpenPresentation = PageOpenPresentation.stack
}
19 changes: 16 additions & 3 deletions docmostly/Features/PageReader/PageOpenTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ nonisolated struct PageOpenTarget: Identifiable, Hashable, Sendable {

struct PageOpenLink<Label: View>: View {
@Environment(AppState.self) private var appState
@Environment(\.pageOpenPresentation) private var pageOpenPresentation

let target: PageOpenTarget
let label: () -> Label
Expand All @@ -104,10 +105,20 @@ struct PageOpenLink<Label: View>: View {
.buttonStyle(.plain)
.accessibilityIdentifier(accessibilityIdentifier)
#else
NavigationLink(value: target) {
label()
if pageOpenPresentation == .stack {
NavigationLink(value: target) {
label()
}
.accessibilityIdentifier(accessibilityIdentifier)
} else {
Button {
appState.openPage(target)
} label: {
label()
}
.buttonStyle(.plain)
.accessibilityIdentifier(accessibilityIdentifier)
}
.accessibilityIdentifier(accessibilityIdentifier)
#endif
}

Expand All @@ -118,6 +129,7 @@ struct PageOpenLink<Label: View>: View {

struct PageOpenDestinationView: View {
@Environment(AppState.self) private var appState
@Environment(\.pageOpenPresentation) private var pageOpenPresentation

let target: PageOpenTarget

Expand All @@ -127,6 +139,7 @@ struct PageOpenDestinationView: View {
appState.openPage(target)
}
.onDisappear {
guard pageOpenPresentation.shouldClearSelectedPageOnReaderDisappear else { return }
appState.clearSelectedPage(ifMatching: target.slugId)
}
}
Expand Down
2 changes: 2 additions & 0 deletions docmostly/Features/PageReader/PageReaderDestinationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import SwiftUI

struct PageReaderDestinationView: View {
@Environment(AppState.self) private var appState
@Environment(\.pageOpenPresentation) private var pageOpenPresentation

let pageID: String

Expand All @@ -11,6 +12,7 @@ struct PageReaderDestinationView: View {
appState.selectPage(id: pageID)
}
.onDisappear {
guard pageOpenPresentation.shouldClearSelectedPageOnReaderDisappear else { return }
appState.clearSelectedPage(ifMatching: pageID)
}
}
Expand Down
17 changes: 14 additions & 3 deletions docmostly/Features/PageTree/PageTreeNodeView.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import SwiftUI

struct PageTreeNodeView: View {
@Environment(\.pageOpenPresentation) private var pageOpenPresentation

let node: PageTreeNode
let depth: Int
let isExpanded: Bool
Expand Down Expand Up @@ -32,10 +34,19 @@ struct PageTreeNodeView: View {
.accessibilityLabel(node.title)
.accessibilityIdentifier(pageAccessibilityIdentifier)
#else
NavigationLink(value: node) {
PageTreeNodeLabel(node: node)
if pageOpenPresentation == .stack {
NavigationLink(value: node) {
PageTreeNodeLabel(node: node)
}
.accessibilityIdentifier(pageAccessibilityIdentifier)
} else {
Button(action: openNodeInDetailColumn) {
PageTreeNodeLabel(node: node)
}
.buttonStyle(.plain)
.accessibilityLabel(node.title)
.accessibilityIdentifier(pageAccessibilityIdentifier)
}
.accessibilityIdentifier(pageAccessibilityIdentifier)
#endif
}
.padding(.leading, CGFloat(depth) * PageTreeSidebarMetrics.depthIndent)
Expand Down
53 changes: 33 additions & 20 deletions docmostly/Features/Spaces/MainShellContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,47 @@ import SwiftUI

struct MainShellContentView: View {
@Environment(AppState.self) private var appState
@Environment(\.pageOpenPresentation) private var pageOpenPresentation
@State private var navigationPath = NavigationPath()

var body: some View {
NavigationStack(path: $navigationPath) {
Group {
switch appState.selectedSidebarDestination {
case .favorites:
FavoritesView()
case .notifications:
NotificationListView()
case .search:
SearchView()
case .settings:
SettingsView()
case .space(let spaceID):
if let space = appState.spaces.first(where: { $0.id == spaceID }) {
PageTreeView(space: space)
} else {
ContentUnavailableView("Space unavailable", systemImage: "square.stack.3d.up")
}
case nil:
ContentUnavailableView("Select a space", systemImage: "square.stack.3d.up")
}
}
MainShellContentRootView()
}
.onChange(of: appState.selectedSidebarDestination) {
navigationPath = NavigationPath()
}
.onChange(of: pageOpenPresentation) { _, newPresentation in
guard newPresentation == .detailColumn else { return }
navigationPath = NavigationPath()
Comment thread
Chefski marked this conversation as resolved.
}
.navigationSplitViewColumnWidth(min: 280, ideal: 340, max: 460)
}
}

private struct MainShellContentRootView: View {
@Environment(AppState.self) private var appState

var body: some View {
Group {
switch appState.selectedSidebarDestination {
case .favorites:
FavoritesView()
case .notifications:
NotificationListView()
case .search:
SearchView()
case .settings:
SettingsView()
case .space(let spaceID):
if let space = appState.spaces.first(where: { $0.id == spaceID }) {
PageTreeView(space: space)
} else {
ContentUnavailableView("Space unavailable", systemImage: "square.stack.3d.up")
}
case nil:
ContentUnavailableView("Select a space", systemImage: "square.stack.3d.up")
}
}
}
}
7 changes: 7 additions & 0 deletions docmostly/Features/Spaces/MainShellView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import SwiftUI
struct MainShellView: View {
@Environment(AppState.self) private var appState
@Environment(\.scenePhase) private var scenePhase
#if os(iOS)
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
#endif
#if os(macOS)
@Environment(MacDesktopCommandController.self) private var commandController
@State private var isShowingSpaceSettings = false
Expand Down Expand Up @@ -44,6 +47,10 @@ struct MainShellView: View {
MainShellDetailView()
}
.navigationSplitViewStyle(.balanced)
.environment(
\.pageOpenPresentation,
horizontalSizeClass == .compact ? .stack : .detailColumn
)
#endif
}
.environment(notificationStore)
Expand Down
13 changes: 13 additions & 0 deletions docmostlyTests/PageReader/PageOpenPresentationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Testing
@testable import docmostly

@MainActor
struct PageOpenPresentationTests {
@Test func stackReaderClearsSelectionWhenItDisappears() {
#expect(PageOpenPresentation.stack.shouldClearSelectedPageOnReaderDisappear)
}

@Test func detailColumnTransitionPreservesTheSelectedPage() {
#expect(!PageOpenPresentation.detailColumn.shouldClearSelectedPageOnReaderDisappear)
}
}
Loading