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

Refactor router state to more closely match tab bar behavior #901

Merged
merged 1 commit into from
Aug 28, 2024
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
47 changes: 6 additions & 41 deletions Meshtastic/Router/NavigationState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,7 @@ enum SettingsNavigationState: String {
case firmwareUpdates
}

enum NavigationState: Hashable {
case messages(MessagesNavigationState? = nil)
case bluetooth
case nodes(selectedNodeNum: Int64? = nil)
case map(MapNavigationState? = nil)
case settings(SettingsNavigationState? = nil)
}

// MARK: Tab Bar

extension NavigationState {
struct NavigationState: Hashable {
enum Tab: String, Hashable {
case messages
case bluetooth
Expand All @@ -74,34 +64,9 @@ extension NavigationState {
case settings
}

var tab: Tab {
get {
switch self {
case .messages:
.messages
case .bluetooth:
.bluetooth
case .nodes:
.nodes
case .map:
.map
case .settings:
.settings
}
}
set {
self = switch newValue {
case .messages:
.messages()
case .bluetooth:
.bluetooth
case .nodes:
.nodes()
case .map:
.map()
case .settings:
.settings()
}
}
}
var selectedTab: Tab = .bluetooth
var messages: MessagesNavigationState?
var nodeListSelectedNodeNum: Int64?
var map: MapNavigationState?
var settings: SettingsNavigationState?
}
30 changes: 17 additions & 13 deletions Meshtastic/Router/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class Router: ObservableObject {
private var cancellables: Set<AnyCancellable> = []

init(
navigationState: NavigationState = .bluetooth
navigationState: NavigationState = NavigationState(
selectedTab: .bluetooth
)
) {
self.navigationState = navigationState

Expand All @@ -21,10 +23,6 @@ class Router: ObservableObject {
}.store(in: &cancellables)
}

func route(to destination: NavigationState) {
navigationState = destination
}

func route(url: URL) {
guard url.scheme == "meshtastic" else {
Logger.services.error("🛣 Received routing URL \(url, privacy: .public) with invalid scheme. Ignoring route.")
Expand All @@ -38,7 +36,7 @@ class Router: ObservableObject {
if components.path == "/messages" {
routeMessages(components)
} else if components.path == "/bluetooth" {
route(to: .bluetooth)
navigationState.selectedTab = .bluetooth
} else if components.path == "/nodes" {
routeNodes(components)
} else if components.path == "/map" {
Expand Down Expand Up @@ -75,15 +73,18 @@ class Router: ObservableObject {
} else {
nil
}
route(to: .messages(state))
navigationState.selectedTab = .messages
navigationState.messages = state
}

private func routeNodes(_ components: URLComponents) {
let nodeId = components.queryItems?
.first(where: { $0.name == "nodenum" })?
.value
.flatMap(Int64.init)
route(to: .nodes(selectedNodeNum: nodeId))

navigationState.selectedTab = .nodes
navigationState.nodeListSelectedNodeNum = nodeId
}

private func routeMap(_ components: URLComponents) {
Expand All @@ -95,12 +96,14 @@ class Router: ObservableObject {
.first(where: { $0.name == "waypointId" })?
.value
.flatMap(Int64.init)
if let nodeId {
route(to: .map(.selectedNode(nodeId)))

navigationState.selectedTab = .map
navigationState.map = if let nodeId {
.selectedNode(nodeId)
} else if let waypointId {
route(to: .map(.waypoint(waypointId)))
.waypoint(waypointId)
} else {
route(to: .map())
nil
}
}

Expand All @@ -112,6 +115,7 @@ class Router: ObservableObject {
.flatMap(String.init)
.flatMap(SettingsNavigationState.init(rawValue:))

route(to: .settings(settingFromPath))
navigationState.selectedTab = .settings
navigationState.settings = settingFromPath
}
}
9 changes: 1 addition & 8 deletions Meshtastic/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,7 @@ struct ContentView: View {
var router: Router

var body: some View {
TabView(selection: Binding(
get: {
appState.router.navigationState.tab
},
set: { newValue in
appState.router.navigationState.tab = newValue
}
)) {
TabView(selection: $appState.router.navigationState.selectedTab) {
Messages(
router: appState.router,
unreadChannelMessages: $appState.unreadChannelMessages,
Expand Down
34 changes: 8 additions & 26 deletions Meshtastic/Views/Messages/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,6 @@ struct Messages: View {
@Binding
var unreadDirectMessages: Int

// Aliases the navigation state for the NavigationSplitView sidebar selection
private var messagesSelection: Binding<MessagesNavigationState?> {
Binding(
get: {
guard case .messages(let state) = router.navigationState else {
return nil
}
return state
},
set: { newValue in
router.navigationState = .messages(newValue)
}
)
}

@State var node: NodeInfoEntity?
@State private var userSelection: UserEntity? // Nothing selected by default.
@State private var channelSelection: ChannelEntity? // Nothing selected by default.
Expand All @@ -49,7 +34,7 @@ struct Messages: View {

var body: some View {
NavigationSplitView(columnVisibility: $columnVisibility) {
List(selection: messagesSelection) {
List(selection: $router.navigationState.messages) {
NavigationLink(value: MessagesNavigationState.channels()) {
Label {
Text("channels")
Expand Down Expand Up @@ -88,21 +73,22 @@ struct Messages: View {
.navigationBarTitleDisplayMode(.large)
.navigationBarItems(leading: MeshtasticLogo())
} content: {
if case .messages(.channels) = router.navigationState {
switch router.navigationState.messages {
case .channels(let channelId, let messageId):
ChannelList(node: $node, channelSelection: $channelSelection)
} else if case .messages(.directMessages) = router.navigationState {
case .directMessages(let userNum, let messageId):
UserList(node: $node, userSelection: $userSelection)
} else if case .messages(nil) = router.navigationState {
case nil:
Text("Select a conversation type")
}
} detail: {
if let myInfo = node?.myInfo, let channelSelection {
ChannelMessageList(myInfo: myInfo, channel: channelSelection)
} else if let userSelection {
UserMessageList(user: userSelection)
} else if case .messages(.channels) = router.navigationState {
} else if case .channels = router.navigationState.messages {
Text("Select a channel")
} else if case .messages(.directMessages) = router.navigationState {
} else if case .directMessages = router.navigationState.messages {
Text("Select a conversation")
}
}.onChange(of: router.navigationState) { _ in
Expand All @@ -116,11 +102,7 @@ struct Messages: View {
node = getNodeInfo(id: nodeId, context: context)
}

guard case .messages(let state) = router.navigationState else {
return
}

guard let state else {
guard let state = router.navigationState.messages else {
channelSelection = nil
userSelection = nil
return
Expand Down
2 changes: 1 addition & 1 deletion Meshtastic/Views/Nodes/MeshMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ struct MeshMap: View {
MapSettingsForm(traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMap: $isMeshMap)
}
.onChange(of: router.navigationState) {
guard case .map(let selectedNodeNum) = router.navigationState else { return }
guard case .map = router.navigationState.selectedTab else { return }
// TODO: handle deep link for waypoints
}
.onChange(of: selectedMapLayer) { newMapLayer in
Expand Down
7 changes: 2 additions & 5 deletions Meshtastic/Views/Nodes/NodeList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,8 @@ struct NodeList: View {
}
}
.onChange(of: router.navigationState) { _ in
// Handle deep link routing
if case .nodes(let selected) = router.navigationState {
self.selectedNode = selected.flatMap {
getNodeInfo(id: $0, context: context)
}
if let selected = router.navigationState.nodeListSelectedNodeNum {
self.selectedNode = getNodeInfo(id: selected, context: context)
} else {
self.selectedNode = nil
}
Expand Down
7 changes: 2 additions & 5 deletions Meshtastic/Views/Settings/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -299,13 +299,10 @@ struct Settings: View {
NavigationStack(
path: Binding<[SettingsNavigationState]>(
get: {
guard case .settings(let route) = router.navigationState, let setting = route else {
return []
}
return [setting]
[router.navigationState.settings].compactMap { $0 }
},
set: { newPath in
router.navigationState = .settings(newPath.first)
router.navigationState.settings = newPath.first
}
)
) {
Expand Down
Loading