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

Fix router lemmy link resolution #644

Merged
merged 7 commits into from
Sep 27, 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
16 changes: 16 additions & 0 deletions Mlem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,13 @@
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 */; };
E49E01F42ABD99D300E42BB3 /* Routable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E49E01F32ABD99D300E42BB3 /* Routable.swift */; };
E49F0E762A90395400BC4EE3 /* NavigationPath+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E49F0E752A90395400BC4EE3 /* NavigationPath+Helpers.swift */; };
E4D4DBA02A7C7B9D00C4F3DE /* Comments.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D4DB9F2A7C7B9D00C4F3DE /* Comments.swift */; };
E4D4DBA22A7F233200C4F3DE /* FancyTabNavigationSelectionHashValueEnvironmentKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D4DBA12A7F233200C4F3DE /* FancyTabNavigationSelectionHashValueEnvironmentKey.swift */; };
E4DDB4322A81819300B3A7E0 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DDB4312A81819300B3A7E0 /* Double.swift */; };
E4DDB4342A819C8000B3A7E0 /* QuickLookView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DDB4332A819C8000B3A7E0 /* QuickLookView.swift */; };
E4F0B5722AC2581800BC3E4A /* RoutableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F0B5712AC2581800BC3E4A /* RoutableTests.swift */; };
E4F0B56F2ABD00A000BC3E4A /* PresentationBackgroundInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F0B56E2ABD00A000BC3E4A /* PresentationBackgroundInteraction.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -877,11 +879,13 @@
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>"; };
E49E01F32ABD99D300E42BB3 /* Routable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Routable.swift; sourceTree = "<group>"; };
E49F0E752A90395400BC4EE3 /* NavigationPath+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NavigationPath+Helpers.swift"; sourceTree = "<group>"; };
E4D4DB9F2A7C7B9D00C4F3DE /* Comments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comments.swift; sourceTree = "<group>"; };
E4D4DBA12A7F233200C4F3DE /* FancyTabNavigationSelectionHashValueEnvironmentKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FancyTabNavigationSelectionHashValueEnvironmentKey.swift; sourceTree = "<group>"; };
E4DDB4312A81819300B3A7E0 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = "<group>"; };
E4DDB4332A819C8000B3A7E0 /* QuickLookView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickLookView.swift; sourceTree = "<group>"; };
E4F0B5712AC2581800BC3E4A /* RoutableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutableTests.swift; sourceTree = "<group>"; };
E4F0B56E2ABD00A000BC3E4A /* PresentationBackgroundInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationBackgroundInteraction.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -1511,6 +1515,7 @@
isa = PBXGroup;
children = (
504ECBAF2AB4B0DF006C0B96 /* Model */,
E4F0B5702AC257FD00BC3E4A /* Navigation */,
50CC4A802AA0D5F90074C845 /* Parsers */,
50CC4A7B2A9CFF840074C845 /* Supporting Files */,
50BC1AB72A89741000E3C48B /* Community List */,
Expand Down Expand Up @@ -2280,6 +2285,7 @@
E47478142AAC3C19001CB1AC /* NavigationContext.swift */,
E47478122AAC350E001CB1AC /* NavigationLink+Helpers.swift */,
E49F0E752A90395400BC4EE3 /* NavigationPath+Helpers.swift */,
E49E01F32ABD99D300E42BB3 /* Routable.swift */,
E47B2B772A902E3C00629AF7 /* Router */,
E47B2B742A902DB400629AF7 /* Route */,
);
Expand All @@ -2294,6 +2300,14 @@
path = Animations;
sourceTree = "<group>";
};
E4F0B5702AC257FD00BC3E4A /* Navigation */ = {
isa = PBXGroup;
children = (
E4F0B5712AC2581800BC3E4A /* RoutableTests.swift */,
);
path = Navigation;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -2859,6 +2873,7 @@
CDE6A8182A490AF20062D161 /* Inbox Mention View.swift in Sources */,
CD3FBCDD2A4A6F0600B2063F /* GetReplies.swift in Sources */,
6332FDCF27EFDD2E0009A98A /* Accounts Page.swift in Sources */,
E49E01F42ABD99D300E42BB3 /* Routable.swift in Sources */,
CDF842682A49FB9000723DA0 /* Inbox View Logic.swift in Sources */,
6D15D74C2A44DC240061B5CB /* Date.swift in Sources */,
CDF1EF122A6B672C003594B6 /* Feed View.swift in Sources */,
Expand Down Expand Up @@ -2886,6 +2901,7 @@
031BF9552AB25AFB00F4517F /* SiteVersionTests.swift in Sources */,
50CC4A782A9CBDF70074C845 /* TimestampedValueTests.swift in Sources */,
50CC4A822AA0D61F0074C845 /* InstanceMetadataParserTests.swift in Sources */,
E4F0B5722AC2581800BC3E4A /* RoutableTests.swift in Sources */,
50C86ABC2A7E50E200277519 /* PersistenceRepositoryTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
27 changes: 16 additions & 11 deletions Mlem/Extensions/View - Handle Lemmy Links.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,22 @@ struct HandleLemmyLinkResolution<Path: AnyNavigationPath>: ViewModifier {
}

return await MainActor.run {
switch resolution {
case let .post(object):
navigationPath.wrappedValue.append(object)
return true
case let .person(object):
navigationPath.wrappedValue.append(object.person)
return true
case let .community(object):
navigationPath.wrappedValue.append(object)
return true
case .comment:
do {
switch resolution {
case let .post(object):
navigationPath.wrappedValue.append(try Path.makeRoute(object))
return true
case let .person(object):
navigationPath.wrappedValue.append(try Path.makeRoute(object.person))
return true
case let .community(object):
navigationPath.wrappedValue.append(try Path.makeRoute(object))
return true
case .comment:
return false
}
} catch {
errorHandler.handle(error)
return false
}
}
Expand Down
9 changes: 6 additions & 3 deletions Mlem/Navigation/AnyNavigationPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,22 @@ import SwiftUI

protocol AnyNavigationPath {

associatedtype Route: Routable

/// Implementation should make a route that makes sense for the passed-in data value and can be appended to the navigation path.
static func makeRoute<V>(_ value: V) throws -> Route where V: Hashable

/// 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
mutating func append<V>(_ value: V) where V: Routable

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

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

import Foundation

/// Conforming types can be added to a `NavigationRouter`'s path.
protocol Routable: Hashable {

/// - Parameter value: A data type for a given navigation destination. This value could be (but not limited to) some raw data, a view model, or an enum case (representing a route on a navigation path).
/// - Returns: `nil` if data value cannot be mapped to a navigation route.
static func makeRoute<V>(_ value: V) throws -> Self where V: Hashable

/// Generic error string
static var makeRouteErrorString: String { get }
}

enum RoutableError<V: Hashable>: LocalizedError {
case routeNotConfigured(value: V)
}

extension Routable {

/// Default implementation.
static func makeRoute<V>(_ value: V) throws -> Self where V: Hashable {
switch value {
case let value as Self:
return value
default:
throw RoutableError.routeNotConfigured(value: value)
}
}

static var makeRouteErrorString: String {
"`makeRoute(...) implementation must return a valid route for all valid data values."
}
}
34 changes: 33 additions & 1 deletion Mlem/Navigation/Route/NavigationRoutes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
/// Possible routes for navigation links in `Mlem.app`.
///
/// See `SettingsRoutes` for settings-related routes.
enum NavigationRoute: Hashable {
enum NavigationRoute: Routable {
case apiCommunityView(APICommunityView)
case apiCommunity(APICommunity)

Expand All @@ -25,4 +25,36 @@ enum NavigationRoute: Hashable {
case postLinkWithContext(PostLinkWithContext)
case lazyLoadPostLinkWithContext(LazyLoadPostLinkWithContext)
case userModeratorLink(UserModeratorLink)

// swiftlint:disable cyclomatic_complexity
static func makeRoute<V>(_ value: V) throws -> NavigationRoute where V: Hashable {
switch value {
case let value as APICommunityView:
return .apiCommunityView(value)
case let value as APICommunity:
return .apiCommunity(value)
case let value as CommunityLinkWithContext:
return .communityLinkWithContext(value)
case let value as CommunitySidebarLinkWithContext:
return .communitySidebarLinkWithContext(value)
case let value as APIPostView:
return .apiPostView(value)
case let value as APIPost:
return .apiPost(value)
case let value as APIPerson:
return .apiPerson(value)
case let value as PostLinkWithContext:
return .postLinkWithContext(value)
case let value as LazyLoadPostLinkWithContext:
return .lazyLoadPostLinkWithContext(value)
case let value as UserModeratorLink:
return .userModeratorLink(value)
case let value as Self:
/// Value is an enum case of type `Self` with either no associated value or pre-populated associated value.
return value
default:
throw RoutableError.routeNotConfigured(value: value)
}
}
// swiftlint:enable cyclomatic_complexity
}
54 changes: 48 additions & 6 deletions Mlem/Navigation/Route/SettingsRoutes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

enum SettingsRoute: Hashable {
enum SettingsRoute: Routable {
case accountsPage
case general
case accessibility
Expand All @@ -21,9 +21,29 @@ enum SettingsRoute: Hashable {
case commentPage(CommentSettingsRoute)
case postPage(PostSettingsRoute)
case licensesPage(LicensesSettingsRoute)

static func makeRoute<V>(_ value: V) throws -> SettingsRoute where V: Hashable {
switch value {
case let value as AboutSettingsRoute:
return try .aboutPage(AboutSettingsRoute.makeRoute(value))
case let value as AppearanceSettingsRoute:
return try .appearancePage(AppearanceSettingsRoute.makeRoute(value))
case let value as CommentSettingsRoute:
return try .commentPage(CommentSettingsRoute.makeRoute(value))
case let value as PostSettingsRoute:
return try .postPage(PostSettingsRoute.makeRoute(value))
case let value as LicensesSettingsRoute:
return try .licensesPage(LicensesSettingsRoute.makeRoute(value))
case let value as Self:
/// Value is an enum case of type `Self` with either no associated value or pre-populated associated value.
return value
default:
throw RoutableError.routeNotConfigured(value: value)
}
}
}

enum AppearanceSettingsRoute: Hashable, Codable {
enum AppearanceSettingsRoute: Routable, Codable {
case theme
case appIcon
case posts
Expand All @@ -33,21 +53,43 @@ enum AppearanceSettingsRoute: Hashable, Codable {
case tabBar
}

enum CommentSettingsRoute: Hashable, Codable {
enum CommentSettingsRoute: Routable, Codable {
case layoutWidget
}

enum PostSettingsRoute: Hashable, Codable {
enum PostSettingsRoute: Routable, Codable {
case customizeWidgets
}

enum AboutSettingsRoute: Hashable {
enum AboutSettingsRoute: Routable {
case contributors
case privacyPolicy(Document)
case eula(Document)
case licenses

static func makeRoute<V>(_ value: V) throws -> AboutSettingsRoute where V: Hashable {
switch value {
case let value as Document:
// return .privacyPolicy(value)
return .eula(value)
mormaer marked this conversation as resolved.
Show resolved Hide resolved
case let value as Self:
/// Value is an enum case of type `Self` with either no associated value or pre-populated associated value.
return value
default:
throw RoutableError.routeNotConfigured(value: value)
}
}
}

enum LicensesSettingsRoute: Hashable {
enum LicensesSettingsRoute: Routable {
case licenseDocument(Document)

static func makeRoute<V>(_ value: V) throws -> LicensesSettingsRoute where V: Hashable {
switch value {
case let value as Document:
return .licenseDocument(value)
default:
throw RoutableError.routeNotConfigured(value: value)
}
}
}
20 changes: 15 additions & 5 deletions Mlem/Navigation/Router/NavigationRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,21 @@

import Foundation

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

/// - Avoid directly manipulating this value, if alternate methods are provided.
@Published var path: [RouteValue] = []

}

extension NavigationRouter: AnyNavigationPath {

typealias Route = RouteValue

static func makeRoute<V>(_ value: V) throws -> Route where V: Hashable {
try RouteValue.makeRoute(value)
}

var count: Int {
path.count
}
Expand All @@ -20,9 +30,9 @@ extension NavigationRouter: AnyNavigationPath {
path.isEmpty
}

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