Skip to content

Commit 1698962

Browse files
committed
Fix live_patch handling
1 parent 132c5c9 commit 1698962

File tree

5 files changed

+71
-24
lines changed

5 files changed

+71
-24
lines changed

Sources/LiveViewNative/Coordinators/LiveNavigationEntry.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,20 @@ import SwiftUI
1111
public struct LiveNavigationEntry<R: RootRegistry>: Hashable {
1212
public let url: URL
1313
public let coordinator: LiveViewCoordinator<R>
14+
15+
let mode: LiveRedirect.Mode
16+
1417
let navigationTransition: Any?
1518
let pendingView: (any View)?
1619

1720
public static func == (lhs: Self, rhs: Self) -> Bool {
1821
lhs.url == rhs.url && lhs.coordinator === rhs.coordinator
22+
&& lhs.mode == rhs.mode
1923
}
2024

2125
public func hash(into hasher: inout Hasher) {
2226
hasher.combine(url)
2327
hasher.combine(ObjectIdentifier(coordinator))
28+
hasher.combine(mode)
2429
}
2530
}

Sources/LiveViewNative/Coordinators/LiveSessionCoordinator.swift

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
100100
try? LiveViewNativeCore.storeSessionCookie("\(cookie.name)=\(cookie.value)", self.url.absoluteString)
101101
}
102102

103-
self.navigationPath = [.init(url: url, coordinator: .init(session: self, url: self.url), navigationTransition: nil, pendingView: nil)]
103+
self.navigationPath = [.init(url: url, coordinator: .init(session: self, url: self.url), mode: .replaceTop, navigationTransition: nil, pendingView: nil)]
104104

105105
self.mergedEventSubjects = self.navigationPath.first!.coordinator.eventSubject.compactMap({ [weak self] value in
106106
self.map({ ($0.navigationPath.first!.coordinator, value) })
@@ -112,28 +112,64 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
112112
$navigationPath.scan(([LiveNavigationEntry<R>](), [LiveNavigationEntry<R>]()), { ($0.1, $1) }).sink { [weak self] prev, next in
113113
guard let self else { return }
114114
Task {
115-
try await prev.last?.coordinator.disconnect()
116115
if prev.count > next.count {
117-
let targetEntry = self.liveSocket!.getEntries()[next.count - 1]
118-
next.last?.coordinator.join(
119-
try await self.liveSocket!.traverseTo(targetEntry.id,
120-
.some([
121-
"_format": .str(string: LiveSessionParameters.platform),
122-
"_interface": .object(object: LiveSessionParameters.platformParams)
123-
]),
124-
nil)
125-
)
116+
// backward navigation
117+
118+
// if the coordinator is connected, the mode was a `patch`, and the new entry has the same coordinator
119+
// send a `live_patch` event and keep the same coordinator.
120+
switch prev.last!.mode {
121+
case .patch:
122+
if case .connected = prev.last?.coordinator.state,
123+
next.last?.coordinator === prev.last?.coordinator
124+
{
125+
_ = try await prev.last?.coordinator.doPushEvent(
126+
"live_patch",
127+
payload: .jsonPayload(json: .object(object: [
128+
"url": .str(string: next.last!.url.absoluteString)
129+
]))
130+
)
131+
next.last!.coordinator.url = next.last!.url
132+
next.last!.coordinator.objectWillChange.send()
133+
if next.count <= 1 { // if we navigated back to the root page, trigger an update on the session too
134+
self.objectWillChange.send()
135+
}
136+
return
137+
}
138+
case .replaceTop:
139+
try await prev.last?.coordinator.disconnect()
140+
let targetEntry = self.liveSocket!.getEntries()[next.count - 1]
141+
next.last?.coordinator.join(
142+
try await self.liveSocket!.traverseTo(
143+
targetEntry.id,
144+
.some([
145+
"_format": .str(string: LiveSessionParameters.platform),
146+
"_interface": .object(object: LiveSessionParameters.platformParams)
147+
]),
148+
nil
149+
)
150+
)
151+
}
126152
} else if next.count > prev.count && prev.count > 0 {
127153
// forward navigation (from `redirect` or `<NavigationLink>`)
128-
next.last?.coordinator.join(
129-
try await self.liveSocket!.navigate(next.last!.url.absoluteString,
130-
.some([
131-
"_format": .str(string: LiveSessionParameters.platform),
132-
"_interface": .object(object: LiveSessionParameters.platformParams)
133-
]),
134-
NavOptions(action: .push))
135-
)
154+
155+
// if the coordinator instance is the same and its connected, we don't need to handle a connection.
156+
switch next.last!.mode {
157+
case .patch:
158+
next.last?.coordinator.url = next.last!.url
159+
return
160+
case .replaceTop:
161+
try await prev.last?.coordinator.disconnect()
162+
next.last?.coordinator.join(
163+
try await self.liveSocket!.navigate(next.last!.url.absoluteString,
164+
.some([
165+
"_format": .str(string: LiveSessionParameters.platform),
166+
"_interface": .object(object: LiveSessionParameters.platformParams)
167+
]),
168+
NavOptions(action: .push))
169+
)
170+
}
136171
} else if next.count == prev.count {
172+
try await prev.last?.coordinator.disconnect()
137173
guard let liveChannel =
138174
try await self.liveSocket?.navigate(next.last!.url.absoluteString,
139175
.some([
@@ -318,7 +354,7 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
318354
if case .user(user: "assets_change") = event.event {
319355
Task { @MainActor in
320356
await self.disconnect()
321-
self.navigationPath = [.init(url: self.url, coordinator: .init(session: self, url: self.url), navigationTransition: nil, pendingView: nil)]
357+
self.navigationPath = [.init(url: self.url, coordinator: .init(session: self, url: self.url), mode: .replaceTop, navigationTransition: nil, pendingView: nil)]
322358
await self.connect()
323359
self.lastReloadTime = Date()
324360
}
@@ -374,7 +410,7 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
374410
await self.disconnect()
375411
if let url {
376412
self.url = url
377-
self.navigationPath = [.init(url: self.url, coordinator: self.navigationPath.first!.coordinator, navigationTransition: nil, pendingView: nil)]
413+
self.navigationPath = [.init(url: self.url, coordinator: self.navigationPath.first!.coordinator, mode: .replaceTop, navigationTransition: nil, pendingView: nil)]
378414
}
379415
await self.connect(httpMethod: httpMethod, httpBody: httpBody, additionalHeaders: headers)
380416
// do {
@@ -440,7 +476,7 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
440476
switch redirect.mode {
441477
case .replaceTop:
442478
let coordinator = LiveViewCoordinator(session: self, url: redirect.to)
443-
let entry = LiveNavigationEntry(url: redirect.to, coordinator: coordinator, navigationTransition: navigationTransition, pendingView: pendingView)
479+
let entry = LiveNavigationEntry(url: redirect.to, coordinator: coordinator, mode: redirect.mode, navigationTransition: navigationTransition, pendingView: pendingView)
444480
switch redirect.kind {
445481
case .push:
446482
navigationPath.append(entry)
@@ -458,7 +494,7 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
458494
// patch is like `replaceTop`, but it does not disconnect.
459495
let coordinator = navigationPath.last!.coordinator
460496
coordinator.url = redirect.to
461-
let entry = LiveNavigationEntry(url: redirect.to, coordinator: coordinator, navigationTransition: navigationTransition, pendingView: pendingView)
497+
let entry = LiveNavigationEntry(url: redirect.to, coordinator: coordinator, mode: redirect.mode, navigationTransition: navigationTransition, pendingView: pendingView)
462498
switch redirect.kind {
463499
case .push:
464500
navigationPath.append(entry)

Sources/LiveViewNative/Live/LiveView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ struct PhxMain<R: RootRegistry>: View {
288288
@EnvironmentObject private var session: LiveSessionCoordinator<R>
289289

290290
var body: some View {
291-
NavStackEntryView(.init(url: context.coordinator.url, coordinator: context.coordinator, navigationTransition: nil, pendingView: nil))
291+
NavStackEntryView(.init(url: context.coordinator.url, coordinator: context.coordinator, mode: .replaceTop, navigationTransition: nil, pendingView: nil))
292292
}
293293
}
294294

Sources/LiveViewNative/NavStackEntryView.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ struct NavStackEntryView<R: RootRegistry>: View {
7070
.transition(coordinator.session.configuration.transition ?? .identity)
7171
}
7272
}
73+
} else {
74+
SwiftUI.ZStack {
75+
SwiftUI.Rectangle().fill(.red)
76+
SwiftUI.Text("\(coordinator.url) != \(entry.url)")
77+
}
7378
}
7479
}
7580
.animation(coordinator.session.configuration.transition.map({ _ in .default }), value: coordinator.state.isConnected)

Sources/LiveViewNative/Views/Layout Containers/Presentation Containers/NavigationLink.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ struct NavigationLink<Root: RootRegistry>: View {
123123
value: LiveNavigationEntry(
124124
url: url,
125125
coordinator: LiveViewCoordinator(session: $liveElement.context.coordinator.session, url: url),
126+
mode: .replaceTop,
126127
navigationTransition: nil, // FIXME: navigationTransition
127128
pendingView: pendingView
128129
)

0 commit comments

Comments
 (0)