Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use enum navigation path #609

Merged
merged 12 commits into from
Sep 15, 2023
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
20 changes: 20 additions & 0 deletions Mlem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,13 @@
CDF842682A49FB9000723DA0 /* Inbox View Logic.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF842672A49FB9000723DA0 /* Inbox View Logic.swift */; };
CDF8426B2A4A2AB600723DA0 /* Inbox Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF8426A2A4A2AB600723DA0 /* Inbox Item.swift */; };
CDF8426F2A4A385A00723DA0 /* Inbox Item Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF8426E2A4A385A00723DA0 /* Inbox Item Type.swift */; };
E40E018C2AABF85500410B2C /* NavigationRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018B2AABF85500410B2C /* NavigationRoutes.swift */; };
E40E018E2AABFBDE00410B2C /* NavigationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018D2AABFBDE00410B2C /* NavigationRouter.swift */; };
E40E01902AABFC9300410B2C /* AnyNavigationPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018F2AABFC9300410B2C /* AnyNavigationPath.swift */; };
E453477E2A9DE37300D1B46F /* Array+SafeIndexing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E453477D2A9DE37300D1B46F /* Array+SafeIndexing.swift */; };
E453A1D02A81C2140004BB8A /* QuickLookPreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E453A1CF2A81C2140004BB8A /* QuickLookPreviewController.swift */; };
E47478132AAC350E001CB1AC /* NavigationLink+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47478122AAC350E001CB1AC /* NavigationLink+Helpers.swift */; };
E47478152AAC3C19001CB1AC /* NavigationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47478142AAC3C19001CB1AC /* NavigationContext.swift */; };
E47B2B762A902DE200629AF7 /* SettingsRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47B2B752A902DE200629AF7 /* SettingsRoutes.swift */; };
E4902BAB2A9024BF0054FB36 /* SettingsRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4902BAA2A9024BF0054FB36 /* SettingsRouter.swift */; };
E49F0E762A90395400BC4EE3 /* NavigationPath+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E49F0E752A90395400BC4EE3 /* NavigationPath+Helpers.swift */; };
Expand Down Expand Up @@ -852,8 +857,13 @@
CDF842672A49FB9000723DA0 /* Inbox View Logic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Inbox View Logic.swift"; sourceTree = "<group>"; };
CDF8426A2A4A2AB600723DA0 /* Inbox Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Inbox Item.swift"; sourceTree = "<group>"; };
CDF8426E2A4A385A00723DA0 /* Inbox Item Type.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Inbox Item Type.swift"; sourceTree = "<group>"; };
E40E018B2AABF85500410B2C /* NavigationRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRoutes.swift; sourceTree = "<group>"; };
E40E018D2AABFBDE00410B2C /* NavigationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = "<group>"; };
E40E018F2AABFC9300410B2C /* AnyNavigationPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyNavigationPath.swift; sourceTree = "<group>"; };
E453477D2A9DE37300D1B46F /* Array+SafeIndexing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+SafeIndexing.swift"; sourceTree = "<group>"; };
E453A1CF2A81C2140004BB8A /* QuickLookPreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickLookPreviewController.swift; sourceTree = "<group>"; };
E47478122AAC350E001CB1AC /* NavigationLink+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NavigationLink+Helpers.swift"; sourceTree = "<group>"; };
E47478142AAC3C19001CB1AC /* NavigationContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationContext.swift; sourceTree = "<group>"; };
E47B2B752A902DE200629AF7 /* SettingsRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRoutes.swift; sourceTree = "<group>"; };
E4902BAA2A9024BF0054FB36 /* SettingsRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRouter.swift; sourceTree = "<group>"; };
E49F0E752A90395400BC4EE3 /* NavigationPath+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NavigationPath+Helpers.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2214,6 +2224,7 @@
E47B2B742A902DB400629AF7 /* Route */ = {
isa = PBXGroup;
children = (
E40E018B2AABF85500410B2C /* NavigationRoutes.swift */,
E47B2B752A902DE200629AF7 /* SettingsRoutes.swift */,
);
path = Route;
Expand All @@ -2222,6 +2233,7 @@
E47B2B772A902E3C00629AF7 /* Router */ = {
isa = PBXGroup;
children = (
E40E018D2AABFBDE00410B2C /* NavigationRouter.swift */,
E4902BAA2A9024BF0054FB36 /* SettingsRouter.swift */,
);
path = Router;
Expand All @@ -2230,6 +2242,9 @@
E4902BA92A90245E0054FB36 /* Navigation */ = {
isa = PBXGroup;
children = (
E40E018F2AABFC9300410B2C /* AnyNavigationPath.swift */,
E47478142AAC3C19001CB1AC /* NavigationContext.swift */,
E47478122AAC350E001CB1AC /* NavigationLink+Helpers.swift */,
E49F0E752A90395400BC4EE3 /* NavigationPath+Helpers.swift */,
E47B2B772A902E3C00629AF7 /* Router */,
E47B2B742A902DB400629AF7 /* Route */,
Expand Down Expand Up @@ -2433,8 +2448,10 @@
637218472A3A2AAD008C4816 /* APICommentView.swift in Sources */,
CDDCF6492A6641F0003DA3AC /* FancyTabItemLabelBuilder.swift in Sources */,
63344C542A07D193001BC616 /* FiltersSettingsView.swift in Sources */,
E40E01902AABFC9300410B2C /* AnyNavigationPath.swift in Sources */,
6372185C2A3A2AAD008C4816 /* APICommunity.swift in Sources */,
6372185D2A3A2AAD008C4816 /* APICommunityAggregates.swift in Sources */,
E47478152AAC3C19001CB1AC /* NavigationContext.swift in Sources */,
CD04D5DB2A36154F008EF95B /* SaveButtonView.swift in Sources */,
CDE6A80D2A45EAB30062D161 /* Embedded Post.swift in Sources */,
B1955A212A6145C00056CF99 /* Environment - EasterFlagSetter.swift in Sources */,
Expand All @@ -2450,6 +2467,7 @@
034C724F2A82B61200B8A4B8 /* LayoutWidgetTracker.swift in Sources */,
637218682A3A2AAD008C4816 /* CreateComment.swift in Sources */,
637218722A3A2AAD008C4816 /* HideCommunity.swift in Sources */,
E47478132AAC350E001CB1AC /* NavigationLink+Helpers.swift in Sources */,
63344C6E2A097FA1001BC616 /* View - Hide View.swift in Sources */,
5064D0432A6E645D00B22EE3 /* Notifiable.swift in Sources */,
6318EDCB27EE4E2200BFCAE8 /* User.swift in Sources */,
Expand Down Expand Up @@ -2496,6 +2514,7 @@
CD1446272A5B36DA00610EF1 /* EULA.swift in Sources */,
500C168E2A66FAAB006F243B /* HapticManager+Dependency.swift in Sources */,
038A16E52A7A97380087987E /* LayoutWidgetView.swift in Sources */,
E40E018E2AABFBDE00410B2C /* NavigationRouter.swift in Sources */,
CD1446252A5B357900610EF1 /* Document.swift in Sources */,
CDEBC32C2A9A582500518D9D /* Votes Model.swift in Sources */,
CDEBC3282A9A57F200518D9D /* Content Model Identifier.swift in Sources */,
Expand Down Expand Up @@ -2785,6 +2804,7 @@
50BC1ABB2A8D6A5A00E3C48B /* ScoringOperation.swift in Sources */,
CD18DC692A51ECB6002C56BC /* InteractionSwipeAndMenuHelpers.swift in Sources */,
6386E0362A042C59006B3C1D /* Contributor.swift in Sources */,
E40E018C2AABF85500410B2C /* NavigationRoutes.swift in Sources */,
CD18DC6F2A5209C3002C56BC /* MarkPrivateMessageAsReadRequest.swift in Sources */,
CD82A2552A716C7C00111034 /* APIPersonUnreadCounts.swift in Sources */,
CD04D5E72A3636FB008EF95B /* Headline Post.swift in Sources */,
Expand Down
15 changes: 15 additions & 0 deletions Mlem/Extensions/Navigation getter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import Foundation
import SwiftUI

// MARK: - SwiftUI.NavigationPath

private struct NavigationPathGetter: EnvironmentKey {
static let defaultValue: Binding<NavigationPath> = .constant(NavigationPath())
}
Expand All @@ -18,3 +20,16 @@ extension EnvironmentValues {
set { self[NavigationPathGetter.self] = newValue }
}
}

// MARK: - Mlem NavigationRoute

private struct NavigationPathWithRoutes: EnvironmentKey {
static let defaultValue: Binding<[NavigationRoute]> = .constant([])
}

extension EnvironmentValues {
var navigationPathWithRoutes: Binding<[NavigationRoute]> {
get { self[NavigationPathWithRoutes.self] }
set { self[NavigationPathWithRoutes.self] = newValue }
}
}
102 changes: 56 additions & 46 deletions Mlem/Extensions/View - Handle Lemmy Links.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,63 +15,73 @@ struct HandleLemmyLinksDisplay: ViewModifier {

@AppStorage("internetSpeed") var internetSpeed: InternetSpeed = .fast
@AppStorage("defaultPostSorting") var defaultPostSorting: PostSortType = .hot

@AppStorage("upvoteOnSave") var upvoteOnSave = false

// swiftlint:disable function_body_length
func body(content: Content) -> some View {
content
.navigationDestination(for: APICommunityView.self) { context in
FeedView(community: context.community, feedType: .all, sortType: defaultPostSorting)
.environmentObject(appState)
.environmentObject(filtersTracker)
.environmentObject(CommunitySearchResultsTracker())
}
.navigationDestination(for: APICommunity.self) { community in
FeedView(community: community, feedType: .all, sortType: defaultPostSorting)
.environmentObject(appState)
.environmentObject(filtersTracker)
.environmentObject(CommunitySearchResultsTracker())
}
.navigationDestination(for: CommunityLinkWithContext.self) { context in
FeedView(community: context.community, feedType: context.feedType, sortType: defaultPostSorting)
.environmentObject(appState)
.navigationDestination(for: NavigationRoute.self) { route in
switch route {
case .apiCommunity(let community):
FeedView(community: community, feedType: .all, sortType: defaultPostSorting)
.environmentObject(appState)
.environmentObject(filtersTracker)
.environmentObject(CommunitySearchResultsTracker())
case .apiCommunityView(let context):
FeedView(community: context.community, feedType: .all, sortType: defaultPostSorting)
.environmentObject(appState)
.environmentObject(filtersTracker)
.environmentObject(CommunitySearchResultsTracker())
case .communityLinkWithContext(let context):
FeedView(community: context.community, feedType: context.feedType, sortType: defaultPostSorting)
.environmentObject(appState)
.environmentObject(filtersTracker)
.environmentObject(CommunitySearchResultsTracker())
case .communitySidebarLinkWithContext(let context):
CommunitySidebarView(
community: context.community,
communityDetails: context.communityDetails
)
.environmentObject(filtersTracker)
.environmentObject(CommunitySearchResultsTracker())
}
.navigationDestination(for: CommunitySidebarLinkWithContext.self) { context in
CommunitySidebarView(
community: context.community,
communityDetails: context.communityDetails
)
.environmentObject(filtersTracker)
.environmentObject(CommunitySearchResultsTracker())
}
.navigationDestination(for: APIPost.self) { post in
LazyLoadExpandedPost(post: post)
}
.navigationDestination(for: PostLinkWithContext.self) { post in
ExpandedPost(post: post.post, scrollTarget: post.scrollTarget)
.environmentObject(post.postTracker)
.environmentObject(appState)
}
.navigationDestination(for: LazyLoadPostLinkWithContext.self) { post in
LazyLoadExpandedPost(post: post.post, scrollTarget: post.scrollTarget)
}
.navigationDestination(for: APIPerson.self) { user in
UserView(userID: user.id)
.environmentObject(appState)
}
.navigationDestination(for: UserModeratorLink.self) { user in
UserModeratorView(userDetails: user.user, moderatedCommunities: user.moderatedCommunities)
.environmentObject(appState)
case .apiPostView(let post):
let postModel = PostModel(from: post)
ExpandedPost(post: postModel)
.environmentObject(
PostTracker(
shouldPerformMergeSorting: false,
internetSpeed: internetSpeed,
initialItems: [postModel],
upvoteOnSave: upvoteOnSave
)
)
.environmentObject(appState)
case .apiPost(let post):
LazyLoadExpandedPost(post: post)
case .apiPerson(let user):
UserView(userID: user.id)
.environmentObject(appState)
case .postLinkWithContext(let post):
ExpandedPost(post: post.post, scrollTarget: post.scrollTarget)
.environmentObject(post.postTracker)
.environmentObject(appState)
case .lazyLoadPostLinkWithContext(let post):
LazyLoadExpandedPost(post: post.post, scrollTarget: post.scrollTarget)
case .userModeratorLink(let user):
UserModeratorView(userDetails: user.user, moderatedCommunities: user.moderatedCommunities)
.environmentObject(appState)
}
}
}
// swiftlint:enable function_body_length
}

struct HandleLemmyLinkResolution: ViewModifier {
struct HandleLemmyLinkResolution<Path: AnyNavigationPath>: ViewModifier {
@Dependency(\.apiClient) var apiClient
@Dependency(\.errorHandler) var errorHandler
@Dependency(\.notifier) var notifier

let navigationPath: Binding<NavigationPath>
let navigationPath: Binding<Path>

func body(content: Content) -> some View {
content
Expand Down Expand Up @@ -165,7 +175,7 @@ extension View {
modifier(HandleLemmyLinksDisplay())
}

func handleLemmyLinkResolution(navigationPath: Binding<NavigationPath>) -> some View {
func handleLemmyLinkResolution<P: AnyNavigationPath>(navigationPath: Binding<P>) -> some View {
modifier(HandleLemmyLinkResolution(navigationPath: navigationPath))
}
}
28 changes: 28 additions & 0 deletions Mlem/Navigation/AnyNavigationPath.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// AnyNavigationPath.swift
// Mlem
//
// Created by Bosco Ho on 2023-09-08.
//

import Foundation
import SwiftUI

protocol AnyNavigationPath {

/// The number of elements in this path.
var count: Int { get }

/// A Boolean that indicates whether this path is empty.
var isEmpty: Bool { get }

/// Appends a new value to the end of this path.
mutating func append<V>(_ value: V) where V: Hashable

// swiftlint:disable identifier_name
/// Removes values from the end of this path.
mutating func removeLast(_ k: Int)
// swiftlint:enable identifier_name
}

extension NavigationPath: AnyNavigationPath {}
16 changes: 16 additions & 0 deletions Mlem/Navigation/NavigationContext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// NavigationContext.swift
// Mlem
//
// Created by Bosco Ho on 2023-09-08.
//

import Foundation

/// You may wish to vary how to construct a `NavigationLink` or any other SwiftUI Navigation construct for a variety of reasons.
enum NavigationContext {
/// When presented inside a `NavigationSplitView` sidebar.
case sidebar
/// When presented in any view.
case view
}
16 changes: 16 additions & 0 deletions Mlem/Navigation/NavigationLink+Helpers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// NavigationLink+Helpers.swift
// Mlem
//
// Created by Bosco Ho on 2023-09-08.
//

import SwiftUI

extension NavigationLink where Destination == Never {

/// Convenience initializer.
init(_ route: NavigationRoute, @ViewBuilder label: () -> Label) {
self = .init(value: route, label: label)
}
}
28 changes: 28 additions & 0 deletions Mlem/Navigation/Route/NavigationRoutes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// NavigationRoutes.swift
// Mlem
//
// Created by Bosco Ho on 2023-09-08.
//

import Foundation

/// Possible routes for navigation links in `Mlem.app`.
///
/// See `SettingsRoutes` for settings-related routes.
enum NavigationRoute: Hashable {
case apiCommunityView(APICommunityView)
case apiCommunity(APICommunity)

case communityLinkWithContext(CommunityLinkWithContext)
case communitySidebarLinkWithContext(CommunitySidebarLinkWithContext)

case apiPostView(APIPostView)
case apiPost(APIPost)

case apiPerson(APIPerson)

case postLinkWithContext(PostLinkWithContext)
case lazyLoadPostLinkWithContext(LazyLoadPostLinkWithContext)
case userModeratorLink(UserModeratorLink)
}
8 changes: 7 additions & 1 deletion Mlem/Navigation/Route/SettingsRoutes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@

import Foundation

enum SettingsRoute: Hashable, Codable {
enum SettingsRoute: Hashable {
case accountsPage
case general
case accessibility
case appearance
case contentFilters
case about
case advanced

case aboutPage(AboutSettingsRoute)
case appearancePage(AppearanceSettingsRoute)
case commentPage(CommentSettingsRoute)
case postPage(PostSettingsRoute)
case licensesPage(LicensesSettingsRoute)
}

enum AppearanceSettingsRoute: Hashable, Codable {
Expand Down
36 changes: 36 additions & 0 deletions Mlem/Navigation/Router/NavigationRouter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// NavigationRouter.swift
// Mlem
//
// Created by Bosco Ho on 2023-09-08.
//

import Foundation

final class NavigationRouter<Route: Hashable>: ObservableObject {
@Published var path: [Route] = []
}

extension NavigationRouter: AnyNavigationPath {
var count: Int {
path.count
}

var isEmpty: Bool {
path.isEmpty
}

func append<V>(_ value: V) where V: Hashable {
assert(value is Route)
guard let route = value as? Route else {
return
}
path.append(route)
}

// swiftlint:disable identifier_name
func removeLast(_ k: Int = 1) {
path.removeLast(k)
}
// swiftlint:enable identifier_name
}
Loading
Loading