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 3 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
28 changes: 16 additions & 12 deletions Mlem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@
503422582AAB798600EFE88D /* AppFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503422572AAB798600EFE88D /* AppFlow.swift */; };
503BA26F2A2C94540052516C /* URL+Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503BA26E2A2C94540052516C /* URL+Identifiable.swift */; };
504106CD2A744D7F000AAEF8 /* CommentRepository+Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504106CC2A744D7F000AAEF8 /* CommentRepository+Dependency.swift */; };
504ECBAE2AB45B2A006C0B96 /* LemmyURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */; };
504ECBB12AB4B101006C0B96 /* LemmyURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */; };
504ECBAA2AB27C73006C0B96 /* LandingPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBA92AB27C73006C0B96 /* LandingPage.swift */; };
504ECBAC2AB27CB1006C0B96 /* OnboardingRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBAB2AB27CB1006C0B96 /* OnboardingRoute.swift */; };
504ECBAE2AB45B2A006C0B96 /* LemmyURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */; };
504ECBB12AB4B101006C0B96 /* LemmyURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */; };
505240E32A86916500EA4558 /* FavoriteCommunitiesTracker+Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505240E22A86916500EA4558 /* FavoriteCommunitiesTracker+Dependency.swift */; };
505240E52A86E32700EA4558 /* CommunityListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505240E42A86E32700EA4558 /* CommunityListModel.swift */; };
505240E72A88D36D00EA4558 /* SectionIndexTitles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505240E62A88D36D00EA4558 /* SectionIndexTitles.swift */; };
Expand Down Expand Up @@ -429,6 +429,7 @@
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 */; };
Expand Down Expand Up @@ -489,10 +490,10 @@
503422572AAB798600EFE88D /* AppFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFlow.swift; sourceTree = "<group>"; };
503BA26E2A2C94540052516C /* URL+Identifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Identifiable.swift"; sourceTree = "<group>"; };
504106CC2A744D7F000AAEF8 /* CommentRepository+Dependency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CommentRepository+Dependency.swift"; sourceTree = "<group>"; };
504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LemmyURL.swift; sourceTree = "<group>"; };
504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LemmyURLTests.swift; sourceTree = "<group>"; };
504ECBA92AB27C73006C0B96 /* LandingPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandingPage.swift; sourceTree = "<group>"; };
504ECBAB2AB27CB1006C0B96 /* OnboardingRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingRoute.swift; sourceTree = "<group>"; };
504ECBAD2AB45B2A006C0B96 /* LemmyURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LemmyURL.swift; sourceTree = "<group>"; };
504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LemmyURLTests.swift; sourceTree = "<group>"; };
505240E22A86916500EA4558 /* FavoriteCommunitiesTracker+Dependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FavoriteCommunitiesTracker+Dependency.swift"; sourceTree = "<group>"; };
505240E42A86E32700EA4558 /* CommunityListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommunityListModel.swift; sourceTree = "<group>"; };
505240E62A88D36D00EA4558 /* SectionIndexTitles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionIndexTitles.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -874,6 +875,7 @@
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>"; };
Expand Down Expand Up @@ -1074,14 +1076,6 @@
path = TabBar;
sourceTree = "<group>";
};
504ECBAF2AB4B0DF006C0B96 /* Model */ = {
isa = PBXGroup;
children = (
504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */,
);
path = Model;
sourceTree = "<group>";
};
504ECBA82AB27C4C006C0B96 /* Onboarding */ = {
isa = PBXGroup;
children = (
Expand All @@ -1092,6 +1086,14 @@
path = Onboarding;
sourceTree = "<group>";
};
504ECBAF2AB4B0DF006C0B96 /* Model */ = {
isa = PBXGroup;
children = (
504ECBB02AB4B101006C0B96 /* LemmyURLTests.swift */,
);
path = Model;
sourceTree = "<group>";
};
5064D03B2A6DE05000B22EE3 /* Notifications */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2274,6 +2276,7 @@
E47478142AAC3C19001CB1AC /* NavigationContext.swift */,
E47478122AAC350E001CB1AC /* NavigationLink+Helpers.swift */,
E49F0E752A90395400BC4EE3 /* NavigationPath+Helpers.swift */,
E49E01F32ABD99D300E42BB3 /* Routable.swift */,
E47B2B772A902E3C00629AF7 /* Router */,
E47B2B742A902DB400629AF7 /* Route */,
);
Expand Down Expand Up @@ -2851,6 +2854,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
24 changes: 18 additions & 6 deletions Mlem/Extensions/View - Handle Lemmy Links.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,26 @@ struct HandleLemmyLinkResolution<Path: AnyNavigationPath>: ViewModifier {
return await MainActor.run {
switch resolution {
case let .post(object):
navigationPath.wrappedValue.append(object)
return true
if let route = Path.makeRoute(object) {
navigationPath.wrappedValue.append(route)
return true
} else {
return false
}
case let .person(object):
navigationPath.wrappedValue.append(object.person)
return true
if let route = Path.makeRoute(object.person) {
navigationPath.wrappedValue.append(route)
return true
} else {
return false
}
case let .community(object):
navigationPath.wrappedValue.append(object)
return true
if let route = Path.makeRoute(object) {
navigationPath.wrappedValue.append(route)
return true
} else {
return false
}
case .comment:
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) -> 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 {}
37 changes: 37 additions & 0 deletions Mlem/Navigation/Routable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// 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.
/// - Returns: `nil` if data value cannot be mapped to a navigation route.
static func makeRoute<V>(_ value: V) -> Self? where V: Hashable

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

extension Routable {

/// Default implementation.
static func makeRoute<V>(_ value: V) -> Self? where V: Hashable {
switch value {
case let value as Self:
return value
default:
print(Self.makeRouteErrorString)
return nil
}
}

static var makeRouteErrorString: String {
"`makeRoute(...) implementation must return a valid route for all valid data values."
}
}
32 changes: 31 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,34 @@ enum NavigationRoute: Hashable {
case postLinkWithContext(PostLinkWithContext)
case lazyLoadPostLinkWithContext(LazyLoadPostLinkWithContext)
case userModeratorLink(UserModeratorLink)

// swiftlint:disable cyclomatic_complexity
static func makeRoute<V>(_ value: V) -> 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)
default:
print(Self.makeRouteErrorString)
return nil
}
}
// swiftlint:enable cyclomatic_complexity
}
77 changes: 71 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,51 @@ enum SettingsRoute: Hashable {
case commentPage(CommentSettingsRoute)
case postPage(PostSettingsRoute)
case licensesPage(LicensesSettingsRoute)

// swiftlint:disable cyclomatic_complexity
static func makeRoute<V>(_ value: V) -> SettingsRoute? where V: Hashable {
switch value {
case let value as Self:
return value
case let value as AboutSettingsRoute:
if let route = AboutSettingsRoute.makeRoute(value) {
return .aboutPage(route)
} else {
return nil
}
case let value as AppearanceSettingsRoute:
if let route = AppearanceSettingsRoute.makeRoute(value) {
return .appearancePage(route)
} else {
return nil
}
case let value as CommentSettingsRoute:
if let route = CommentSettingsRoute.makeRoute(value) {
return .commentPage(route)
} else {
return nil
}
case let value as PostSettingsRoute:
if let route = PostSettingsRoute.makeRoute(value) {
return .postPage(route)
} else {
return nil
}
case let value as LicensesSettingsRoute:
if let route = LicensesSettingsRoute.makeRoute(value) {
return .licensesPage(route)
} else {
return nil
}
default:
print(Self.makeRouteErrorString)
return nil
}
}
// swiftlint:enable cyclomatic_complexity
}

enum AppearanceSettingsRoute: Hashable, Codable {
enum AppearanceSettingsRoute: Routable, Codable {
case theme
case appIcon
case posts
Expand All @@ -33,21 +75,44 @@ 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) -> AboutSettingsRoute? where V: Hashable {
switch value {
case let value as Self:
return value
case let value as Document:
// return .privacyPolicy(value)
return .eula(value)
mormaer marked this conversation as resolved.
Show resolved Hide resolved
default:
print(Self.makeRouteErrorString)
return nil
}
}
}

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

static func makeRoute<V>(_ value: V) -> LicensesSettingsRoute? where V: Hashable {
switch value {
case let value as Document:
return .licenseDocument(value)
default:
print(Self.makeRouteErrorString)
boscojwho marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
}
}
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) -> Route? where V: Hashable {
RouteValue.makeRoute(value) ?? nil
}

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