@granite-js/micro-frontend 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GraniteMicroFrontendRuntime.podspec +40 -0
- package/LICENSE +202 -0
- package/LICENSE.react-native-teleport +21 -0
- package/NOTICE +17 -0
- package/README.md +508 -0
- package/android/CMakeLists.txt +34 -0
- package/android/build.gradle +74 -0
- package/android/gradle.properties +4 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/cpp/BundleEvaluator.cpp +64 -0
- package/android/src/main/cpp/BundleEvaluator.h +20 -0
- package/android/src/main/cpp/FileReader.cpp +92 -0
- package/android/src/main/cpp/FileReader.h +27 -0
- package/android/src/main/cpp/onLoad.cpp +9 -0
- package/android/src/main/java/com/teleport/extensions/Float.kt +12 -0
- package/android/src/main/java/com/teleport/extensions/View.kt +10 -0
- package/android/src/main/java/com/teleport/global/PortalRegistry.kt +117 -0
- package/android/src/main/java/com/teleport/host/PortalHostView.kt +97 -0
- package/android/src/main/java/com/teleport/host/PortalHostViewManager.kt +41 -0
- package/android/src/main/java/com/teleport/host/PortalReactRootView.kt +116 -0
- package/android/src/main/java/com/teleport/managers/TeleportViewManager.kt +27 -0
- package/android/src/main/java/com/teleport/portal/PortalLayoutStateController.kt +107 -0
- package/android/src/main/java/com/teleport/portal/PortalView.kt +309 -0
- package/android/src/main/java/com/teleport/portal/PortalViewManager.kt +51 -0
- package/android/src/main/java/run/granite/microfrontend/ActivitySessionBinding.kt +87 -0
- package/android/src/main/java/run/granite/microfrontend/BundleEvaluator.kt +46 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendEvent.kt +62 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendRuntimeEventRouter.kt +186 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendRuntimeHost.kt +42 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendRuntimeModule.kt +56 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendRuntimePackage.kt +35 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendSessionStore.kt +116 -0
- package/android/src/main/jni/CMakeLists.txt +64 -0
- package/android/src/test/cpp/CMakeLists.txt +35 -0
- package/android/src/test/cpp/FileReaderTest.cpp +44 -0
- package/android/src/test/java/com/teleport/host/PortalReactRootViewContractTest.kt +76 -0
- package/android/src/test/java/run/granite/microfrontend/GraniteMicroFrontendRuntimeEventRouterTest.kt +216 -0
- package/android/src/test/java/run/granite/microfrontend/GraniteMicroFrontendSessionStoreTest.kt +190 -0
- package/android/test-cpp.sh +12 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/ComponentDescriptors.h +24 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/PortalShadowRegistry.cpp +31 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/PortalShadowRegistry.h +29 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewComponentDescriptor.h +22 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewShadowNode.cpp +7 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewShadowNode.h +43 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewState.h +21 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewComponentDescriptor.h +54 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewShadowNode.cpp +7 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewShadowNode.h +51 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewState.h +43 -0
- package/dist/module/createMicroFrontendRuntime.js +18 -0
- package/dist/module/createMicroFrontendRuntime.js.map +1 -0
- package/dist/module/createRoute.js +43 -0
- package/dist/module/createRoute.js.map +1 -0
- package/dist/module/host/PendingHostComponent.js +70 -0
- package/dist/module/host/PendingHostComponent.js.map +1 -0
- package/dist/module/host/index.js +6 -0
- package/dist/module/host/index.js.map +1 -0
- package/dist/module/host/pendingHostComponentStore.js +164 -0
- package/dist/module/host/pendingHostComponentStore.js.map +1 -0
- package/dist/module/host/resolveParams.js +60 -0
- package/dist/module/host/resolveParams.js.map +1 -0
- package/dist/module/host/routeMatcher.js +92 -0
- package/dist/module/host/routeMatcher.js.map +1 -0
- package/dist/module/host/types.js +4 -0
- package/dist/module/host/types.js.map +1 -0
- package/dist/module/index.js +12 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/plugin/index.js +4 -0
- package/dist/module/plugin/index.js.map +1 -0
- package/dist/module/plugin/intoShared.js +32 -0
- package/dist/module/plugin/intoShared.js.map +1 -0
- package/dist/module/plugin/microFrontendPlugin.js +58 -0
- package/dist/module/plugin/microFrontendPlugin.js.map +1 -0
- package/dist/module/plugin/prelude.js +23 -0
- package/dist/module/plugin/prelude.js.map +1 -0
- package/dist/module/plugin/resolver.js +25 -0
- package/dist/module/plugin/resolver.js.map +1 -0
- package/dist/module/plugin/types.js +4 -0
- package/dist/module/plugin/types.js.map +1 -0
- package/dist/module/runtime/createMicroFrontendRuntime.js +79 -0
- package/dist/module/runtime/createMicroFrontendRuntime.js.map +1 -0
- package/dist/module/runtime/errors.js +63 -0
- package/dist/module/runtime/errors.js.map +1 -0
- package/dist/module/runtime/parseAppRequest.js +16 -0
- package/dist/module/runtime/parseAppRequest.js.map +1 -0
- package/dist/module/runtime/parseNativeRuntimeEvent.js +54 -0
- package/dist/module/runtime/parseNativeRuntimeEvent.js.map +1 -0
- package/dist/module/runtime/registry.js +87 -0
- package/dist/module/runtime/registry.js.map +1 -0
- package/dist/module/session/MicroFrontendSessionContext.js +32 -0
- package/dist/module/session/MicroFrontendSessionContext.js.map +1 -0
- package/dist/module/session/useMicroFrontendSessions.js +47 -0
- package/dist/module/session/useMicroFrontendSessions.js.map +1 -0
- package/dist/module/specs/NativeGraniteMicroFrontendRuntime.js +21 -0
- package/dist/module/specs/NativeGraniteMicroFrontendRuntime.js.map +1 -0
- package/dist/module/specs/PortalHostViewNativeComponent.ts +9 -0
- package/dist/module/specs/PortalViewNativeComponent.ts +9 -0
- package/dist/module/types.js +2 -0
- package/dist/module/types.js.map +1 -0
- package/dist/typescript/createMicroFrontendRuntime.d.ts +6 -0
- package/dist/typescript/createRoute.d.ts +23 -0
- package/dist/typescript/host/PendingHostComponent.d.ts +14 -0
- package/dist/typescript/host/index.d.ts +5 -0
- package/dist/typescript/host/pendingHostComponentStore.d.ts +11 -0
- package/dist/typescript/host/resolveParams.d.ts +10 -0
- package/dist/typescript/host/routeMatcher.d.ts +7 -0
- package/dist/typescript/host/types.d.ts +32 -0
- package/dist/typescript/index.d.ts +16 -0
- package/dist/typescript/plugin/index.d.ts +2 -0
- package/dist/typescript/plugin/intoShared.d.ts +3 -0
- package/dist/typescript/plugin/microFrontendPlugin.d.ts +3 -0
- package/dist/typescript/plugin/prelude.d.ts +6 -0
- package/dist/typescript/plugin/resolver.d.ts +3 -0
- package/dist/typescript/plugin/types.d.ts +5 -0
- package/dist/typescript/runtime/createMicroFrontendRuntime.d.ts +31 -0
- package/dist/typescript/runtime/errors.d.ts +35 -0
- package/dist/typescript/runtime/parseAppRequest.d.ts +6 -0
- package/dist/typescript/runtime/parseNativeRuntimeEvent.d.ts +3 -0
- package/dist/typescript/runtime/registry.d.ts +35 -0
- package/dist/typescript/session/MicroFrontendSessionContext.d.ts +13 -0
- package/dist/typescript/session/useMicroFrontendSessions.d.ts +8 -0
- package/dist/typescript/specs/NativeGraniteMicroFrontendRuntime.d.ts +21 -0
- package/dist/typescript/specs/PortalHostViewNativeComponent.d.ts +6 -0
- package/dist/typescript/specs/PortalViewNativeComponent.d.ts +6 -0
- package/dist/typescript/types.d.ts +49 -0
- package/ios/GraniteMicroFrontendRuntimeHost+Internal.h +18 -0
- package/ios/GraniteMicroFrontendRuntimeHost.h +34 -0
- package/ios/GraniteMicroFrontendRuntimeHost.mm +485 -0
- package/ios/GraniteMicroFrontendRuntimeModule.h +6 -0
- package/ios/GraniteMicroFrontendRuntimeModule.mm +138 -0
- package/ios/PortalHostContainerView.h +56 -0
- package/ios/PortalHostContainerView.mm +133 -0
- package/ios/PortalHostView.h +39 -0
- package/ios/PortalHostView.mm +153 -0
- package/ios/PortalRegistry.h +38 -0
- package/ios/PortalRegistry.mm +228 -0
- package/ios/PortalView.h +32 -0
- package/ios/PortalView.mm +426 -0
- package/package.json +113 -0
- package/react-native.config.js +13 -0
- package/src/createMicroFrontendRuntime.ts +22 -0
- package/src/createRoute.ts +84 -0
- package/src/host/PendingHostComponent.tsx +92 -0
- package/src/host/index.ts +26 -0
- package/src/host/pendingHostComponentStore.ts +241 -0
- package/src/host/resolveParams.ts +70 -0
- package/src/host/routeMatcher.ts +117 -0
- package/src/host/types.ts +45 -0
- package/src/index.ts +57 -0
- package/src/plugin/index.ts +2 -0
- package/src/plugin/intoShared.ts +50 -0
- package/src/plugin/microFrontendPlugin.ts +57 -0
- package/src/plugin/prelude.ts +73 -0
- package/src/plugin/resolver.ts +73 -0
- package/src/plugin/types.ts +6 -0
- package/src/runtime/createMicroFrontendRuntime.ts +115 -0
- package/src/runtime/errors.ts +86 -0
- package/src/runtime/parseAppRequest.ts +22 -0
- package/src/runtime/parseNativeRuntimeEvent.ts +54 -0
- package/src/runtime/registry.ts +131 -0
- package/src/session/MicroFrontendSessionContext.tsx +45 -0
- package/src/session/useMicroFrontendSessions.ts +73 -0
- package/src/specs/NativeGraniteMicroFrontendRuntime.ts +44 -0
- package/src/specs/PortalHostViewNativeComponent.ts +9 -0
- package/src/specs/PortalViewNativeComponent.ts +9 -0
- package/src/types.ts +55 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
package run.granite.microfrontend
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertFalse
|
|
5
|
+
import org.junit.Assert.assertThrows
|
|
6
|
+
import org.junit.Assert.assertTrue
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
import java.util.Collections
|
|
9
|
+
import java.util.concurrent.CountDownLatch
|
|
10
|
+
import java.util.concurrent.TimeUnit
|
|
11
|
+
import kotlin.concurrent.thread
|
|
12
|
+
|
|
13
|
+
class GraniteMicroFrontendRuntimeEventRouterTest {
|
|
14
|
+
@Test
|
|
15
|
+
fun `another runtime starting event delivery does not replace the active runtime`() {
|
|
16
|
+
val router = GraniteMicroFrontendRuntimeEventRouter()
|
|
17
|
+
val activeRuntime = RecordingEventTarget()
|
|
18
|
+
val unrelatedRuntime = RecordingEventTarget()
|
|
19
|
+
val event = GraniteMicroFrontendEvent.PreloadApp("shopping")
|
|
20
|
+
router.attach(activeRuntime)
|
|
21
|
+
router.startEventDelivery(activeRuntime)
|
|
22
|
+
|
|
23
|
+
router.attach(unrelatedRuntime)
|
|
24
|
+
router.startEventDelivery(unrelatedRuntime)
|
|
25
|
+
router.emit(event)
|
|
26
|
+
|
|
27
|
+
assertEquals(listOf(event), activeRuntime.events)
|
|
28
|
+
assertEquals(emptyList<GraniteMicroFrontendEvent>(), unrelatedRuntime.events)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Test
|
|
32
|
+
fun `events queued before delivery starts are sent to the runtime that starts delivery`() {
|
|
33
|
+
val router = GraniteMicroFrontendRuntimeEventRouter()
|
|
34
|
+
val runtime = RecordingEventTarget()
|
|
35
|
+
val event = GraniteMicroFrontendEvent.PreloadApp("shopping")
|
|
36
|
+
router.attach(runtime)
|
|
37
|
+
router.emit(event)
|
|
38
|
+
|
|
39
|
+
router.startEventDelivery(runtime)
|
|
40
|
+
|
|
41
|
+
assertEquals(listOf(event), runtime.events)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
fun `new events cannot overtake pending events while delivery starts`() {
|
|
46
|
+
val events = Collections.synchronizedList(mutableListOf<GraniteMicroFrontendEvent>())
|
|
47
|
+
val pendingDeliveryStarted = CountDownLatch(1)
|
|
48
|
+
val continuePendingDelivery = CountDownLatch(1)
|
|
49
|
+
val router = GraniteMicroFrontendRuntimeEventRouter()
|
|
50
|
+
val target = object : GraniteMicroFrontendRuntimeEventTarget {
|
|
51
|
+
override fun emit(event: GraniteMicroFrontendEvent, onDelivered: () -> Unit): Boolean {
|
|
52
|
+
if (event is GraniteMicroFrontendEvent.OpenApp) {
|
|
53
|
+
pendingDeliveryStarted.countDown()
|
|
54
|
+
assertTrue(continuePendingDelivery.await(5, TimeUnit.SECONDS))
|
|
55
|
+
}
|
|
56
|
+
events.add(event)
|
|
57
|
+
onDelivered()
|
|
58
|
+
return true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
val open = GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping")
|
|
62
|
+
val close = GraniteMicroFrontendEvent.CloseApp("session-1")
|
|
63
|
+
|
|
64
|
+
router.emit(open)
|
|
65
|
+
router.attach(target)
|
|
66
|
+
val startThread = thread { router.startEventDelivery(target) }
|
|
67
|
+
assertTrue(pendingDeliveryStarted.await(5, TimeUnit.SECONDS))
|
|
68
|
+
val emitThread = thread { router.emit(close) }
|
|
69
|
+
emitThread.join(5_000)
|
|
70
|
+
|
|
71
|
+
assertFalse(emitThread.isAlive)
|
|
72
|
+
assertTrue(events.isEmpty())
|
|
73
|
+
continuePendingDelivery.countDown()
|
|
74
|
+
startThread.join(5_000)
|
|
75
|
+
|
|
76
|
+
assertFalse(startThread.isAlive)
|
|
77
|
+
assertEquals(listOf(open, close), events)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Test
|
|
81
|
+
fun `failed event delivery is retried before newer events`() {
|
|
82
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
83
|
+
val router = GraniteMicroFrontendRuntimeEventRouter()
|
|
84
|
+
val open = GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping")
|
|
85
|
+
val close = GraniteMicroFrontendEvent.CloseApp("session-1")
|
|
86
|
+
var shouldFail = true
|
|
87
|
+
val target = object : GraniteMicroFrontendRuntimeEventTarget {
|
|
88
|
+
override fun emit(event: GraniteMicroFrontendEvent, onDelivered: () -> Unit): Boolean {
|
|
89
|
+
if (shouldFail) {
|
|
90
|
+
shouldFail = false
|
|
91
|
+
throw IllegalStateException("delivery failed")
|
|
92
|
+
}
|
|
93
|
+
events.add(event)
|
|
94
|
+
onDelivered()
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
router.attach(target)
|
|
99
|
+
router.startEventDelivery(target)
|
|
100
|
+
|
|
101
|
+
assertThrows(IllegalStateException::class.java) { router.emit(open) }
|
|
102
|
+
router.emit(close)
|
|
103
|
+
|
|
104
|
+
assertEquals(listOf(open, close), events)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@Test
|
|
108
|
+
fun `replacement runtime resumes a drain owned by a detached runtime`() {
|
|
109
|
+
val firstEvents = mutableListOf<GraniteMicroFrontendEvent>()
|
|
110
|
+
val secondEvents = mutableListOf<GraniteMicroFrontendEvent>()
|
|
111
|
+
val firstDeliveryStarted = CountDownLatch(1)
|
|
112
|
+
val continueFirstDelivery = CountDownLatch(1)
|
|
113
|
+
val router = GraniteMicroFrontendRuntimeEventRouter()
|
|
114
|
+
val firstTarget = object : GraniteMicroFrontendRuntimeEventTarget {
|
|
115
|
+
override fun emit(event: GraniteMicroFrontendEvent, onDelivered: () -> Unit): Boolean {
|
|
116
|
+
firstDeliveryStarted.countDown()
|
|
117
|
+
assertTrue(continueFirstDelivery.await(5, TimeUnit.SECONDS))
|
|
118
|
+
firstEvents.add(event)
|
|
119
|
+
onDelivered()
|
|
120
|
+
return true
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
val secondTarget = object : GraniteMicroFrontendRuntimeEventTarget {
|
|
124
|
+
override fun emit(event: GraniteMicroFrontendEvent, onDelivered: () -> Unit): Boolean {
|
|
125
|
+
secondEvents.add(event)
|
|
126
|
+
onDelivered()
|
|
127
|
+
return true
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
val open = GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping")
|
|
131
|
+
val close = GraniteMicroFrontendEvent.CloseApp("session-1")
|
|
132
|
+
router.attach(firstTarget)
|
|
133
|
+
router.startEventDelivery(firstTarget)
|
|
134
|
+
|
|
135
|
+
val firstEmitThread = thread { router.emit(open) }
|
|
136
|
+
assertTrue(firstDeliveryStarted.await(5, TimeUnit.SECONDS))
|
|
137
|
+
router.detach(firstTarget)
|
|
138
|
+
router.attach(secondTarget)
|
|
139
|
+
router.startEventDelivery(secondTarget)
|
|
140
|
+
router.emit(close)
|
|
141
|
+
continueFirstDelivery.countDown()
|
|
142
|
+
firstEmitThread.join(5_000)
|
|
143
|
+
|
|
144
|
+
assertFalse(firstEmitThread.isAlive)
|
|
145
|
+
assertEquals(listOf(open), firstEvents)
|
|
146
|
+
assertEquals(listOf(close), secondEvents)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@Test
|
|
150
|
+
fun `event rejected by an unavailable runtime is delivered to its replacement`() {
|
|
151
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
152
|
+
val router = GraniteMicroFrontendRuntimeEventRouter()
|
|
153
|
+
val unavailableRuntime = object : GraniteMicroFrontendRuntimeEventTarget {
|
|
154
|
+
override fun emit(event: GraniteMicroFrontendEvent, onDelivered: () -> Unit): Boolean = false
|
|
155
|
+
}
|
|
156
|
+
val replacementRuntime = RecordingEventTarget()
|
|
157
|
+
val open = GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping")
|
|
158
|
+
router.attach(unavailableRuntime)
|
|
159
|
+
router.startEventDelivery(unavailableRuntime)
|
|
160
|
+
|
|
161
|
+
router.emit(open)
|
|
162
|
+
router.detach(unavailableRuntime)
|
|
163
|
+
router.attach(replacementRuntime)
|
|
164
|
+
router.startEventDelivery(replacementRuntime)
|
|
165
|
+
|
|
166
|
+
assertEquals(listOf(open), replacementRuntime.events)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@Test
|
|
170
|
+
fun `event accepted but never acknowledged is redelivered to the replacement runtime`() {
|
|
171
|
+
val acceptedEvents = mutableListOf<GraniteMicroFrontendEvent>()
|
|
172
|
+
val router = GraniteMicroFrontendRuntimeEventRouter()
|
|
173
|
+
val destroyedRuntime = object : GraniteMicroFrontendRuntimeEventTarget {
|
|
174
|
+
override fun emit(event: GraniteMicroFrontendEvent, onDelivered: () -> Unit): Boolean {
|
|
175
|
+
acceptedEvents.add(event)
|
|
176
|
+
return true
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
val replacementRuntime = RecordingEventTarget()
|
|
180
|
+
val open = GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping")
|
|
181
|
+
router.attach(destroyedRuntime)
|
|
182
|
+
router.startEventDelivery(destroyedRuntime)
|
|
183
|
+
|
|
184
|
+
router.emit(open)
|
|
185
|
+
router.detach(destroyedRuntime)
|
|
186
|
+
router.attach(replacementRuntime)
|
|
187
|
+
router.startEventDelivery(replacementRuntime)
|
|
188
|
+
|
|
189
|
+
assertEquals(listOf(open), acceptedEvents)
|
|
190
|
+
assertEquals(listOf(open), replacementRuntime.events)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@Test
|
|
194
|
+
fun `events beyond the pending limit are dropped while no runtime consumes them`() {
|
|
195
|
+
val router = GraniteMicroFrontendRuntimeEventRouter()
|
|
196
|
+
val runtime = RecordingEventTarget()
|
|
197
|
+
val limit = GraniteMicroFrontendRuntimeEventRouter.MAX_PENDING_EVENTS
|
|
198
|
+
val events = (1..limit + 8).map { GraniteMicroFrontendEvent.PreloadApp("app-$it") }
|
|
199
|
+
|
|
200
|
+
events.forEach(router::emit)
|
|
201
|
+
router.attach(runtime)
|
|
202
|
+
router.startEventDelivery(runtime)
|
|
203
|
+
|
|
204
|
+
assertEquals(events.take(limit), runtime.events)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private class RecordingEventTarget : GraniteMicroFrontendRuntimeEventTarget {
|
|
209
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
210
|
+
|
|
211
|
+
override fun emit(event: GraniteMicroFrontendEvent, onDelivered: () -> Unit): Boolean {
|
|
212
|
+
events.add(event)
|
|
213
|
+
onDelivered()
|
|
214
|
+
return true
|
|
215
|
+
}
|
|
216
|
+
}
|
package/android/src/test/java/run/granite/microfrontend/GraniteMicroFrontendSessionStoreTest.kt
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
package run.granite.microfrontend
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertFalse
|
|
5
|
+
import org.junit.Assert.assertTrue
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
import java.util.Collections
|
|
8
|
+
import java.util.concurrent.CountDownLatch
|
|
9
|
+
import java.util.concurrent.TimeUnit
|
|
10
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
11
|
+
import kotlin.concurrent.thread
|
|
12
|
+
|
|
13
|
+
class GraniteMicroFrontendSessionStoreTest {
|
|
14
|
+
@Test
|
|
15
|
+
fun `session emits open visible close in native lifecycle order`() {
|
|
16
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
17
|
+
val store = GraniteMicroFrontendSessionStore(events::add)
|
|
18
|
+
val registration = store.registerSession("session-1")
|
|
19
|
+
|
|
20
|
+
registration.openApp("shopping", "granite://shopping")
|
|
21
|
+
assertTrue(registration.setVisible(true))
|
|
22
|
+
assertTrue(registration.closeApp())
|
|
23
|
+
registration.close()
|
|
24
|
+
|
|
25
|
+
assertEquals(
|
|
26
|
+
listOf(
|
|
27
|
+
GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping"),
|
|
28
|
+
GraniteMicroFrontendEvent.SessionVisibilityChanged("session-1", true),
|
|
29
|
+
GraniteMicroFrontendEvent.CloseApp("session-1"),
|
|
30
|
+
),
|
|
31
|
+
events,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Test
|
|
36
|
+
fun `open app is emitted only once per session`() {
|
|
37
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
38
|
+
val store = GraniteMicroFrontendSessionStore(events::add)
|
|
39
|
+
val registration = store.registerSession("session-1")
|
|
40
|
+
|
|
41
|
+
assertTrue(registration.openApp("shopping", "granite://shopping"))
|
|
42
|
+
assertFalse(registration.openApp("shopping", "granite://shopping"))
|
|
43
|
+
|
|
44
|
+
assertEquals(
|
|
45
|
+
listOf(GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping")),
|
|
46
|
+
events,
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Test
|
|
51
|
+
fun `session visibility updates are idempotent per session`() {
|
|
52
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
53
|
+
val store = GraniteMicroFrontendSessionStore(events::add)
|
|
54
|
+
val registration = store.registerSession("session-1")
|
|
55
|
+
|
|
56
|
+
assertTrue(registration.openApp("shopping", "granite://shopping"))
|
|
57
|
+
assertTrue(registration.setVisible(true))
|
|
58
|
+
assertFalse(registration.setVisible(true))
|
|
59
|
+
assertTrue(registration.setVisible(false))
|
|
60
|
+
assertFalse(registration.setVisible(false))
|
|
61
|
+
|
|
62
|
+
assertEquals(
|
|
63
|
+
listOf(
|
|
64
|
+
GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping"),
|
|
65
|
+
GraniteMicroFrontendEvent.SessionVisibilityChanged("session-1", true),
|
|
66
|
+
GraniteMicroFrontendEvent.SessionVisibilityChanged("session-1", false),
|
|
67
|
+
),
|
|
68
|
+
events,
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Test
|
|
73
|
+
fun `visibility deduplication is isolated per session`() {
|
|
74
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
75
|
+
val store = GraniteMicroFrontendSessionStore(events::add)
|
|
76
|
+
val first = store.registerSession("session-1")
|
|
77
|
+
val second = store.registerSession("session-2")
|
|
78
|
+
|
|
79
|
+
assertTrue(first.openApp("shopping", "granite://shopping"))
|
|
80
|
+
assertTrue(second.openApp("payment", "granite://payment"))
|
|
81
|
+
assertTrue(first.setVisible(true))
|
|
82
|
+
assertTrue(second.setVisible(true))
|
|
83
|
+
assertFalse(first.setVisible(true))
|
|
84
|
+
assertFalse(second.setVisible(true))
|
|
85
|
+
|
|
86
|
+
assertEquals(
|
|
87
|
+
listOf(
|
|
88
|
+
GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping"),
|
|
89
|
+
GraniteMicroFrontendEvent.OpenApp("session-2", "payment", "granite://payment"),
|
|
90
|
+
GraniteMicroFrontendEvent.SessionVisibilityChanged("session-1", true),
|
|
91
|
+
GraniteMicroFrontendEvent.SessionVisibilityChanged("session-2", true),
|
|
92
|
+
),
|
|
93
|
+
events,
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Test
|
|
98
|
+
fun `concurrent close cannot overtake open event delivery`() {
|
|
99
|
+
val events = Collections.synchronizedList(mutableListOf<GraniteMicroFrontendEvent>())
|
|
100
|
+
val openDeliveryStarted = CountDownLatch(1)
|
|
101
|
+
val continueOpenDelivery = CountDownLatch(1)
|
|
102
|
+
val store = GraniteMicroFrontendSessionStore { event ->
|
|
103
|
+
if (event is GraniteMicroFrontendEvent.OpenApp) {
|
|
104
|
+
openDeliveryStarted.countDown()
|
|
105
|
+
assertTrue(continueOpenDelivery.await(5, TimeUnit.SECONDS))
|
|
106
|
+
}
|
|
107
|
+
events.add(event)
|
|
108
|
+
}
|
|
109
|
+
val registration = store.registerSession("session-1")
|
|
110
|
+
val openResult = AtomicBoolean()
|
|
111
|
+
val closeResult = AtomicBoolean()
|
|
112
|
+
val closeStarted = CountDownLatch(1)
|
|
113
|
+
val closeCompleted = CountDownLatch(1)
|
|
114
|
+
|
|
115
|
+
val openThread = thread {
|
|
116
|
+
openResult.set(registration.openApp("shopping", "granite://shopping"))
|
|
117
|
+
}
|
|
118
|
+
assertTrue(openDeliveryStarted.await(5, TimeUnit.SECONDS))
|
|
119
|
+
val closeThread = thread {
|
|
120
|
+
closeStarted.countDown()
|
|
121
|
+
closeResult.set(registration.closeApp())
|
|
122
|
+
closeCompleted.countDown()
|
|
123
|
+
}
|
|
124
|
+
assertTrue(closeStarted.await(5, TimeUnit.SECONDS))
|
|
125
|
+
val blockedDeadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5)
|
|
126
|
+
while (closeThread.isAlive && closeThread.state != Thread.State.BLOCKED && System.nanoTime() < blockedDeadline) {
|
|
127
|
+
Thread.yield()
|
|
128
|
+
}
|
|
129
|
+
assertEquals(Thread.State.BLOCKED, closeThread.state)
|
|
130
|
+
|
|
131
|
+
continueOpenDelivery.countDown()
|
|
132
|
+
openThread.join(5_000)
|
|
133
|
+
closeThread.join(5_000)
|
|
134
|
+
|
|
135
|
+
assertFalse(openThread.isAlive)
|
|
136
|
+
assertFalse(closeThread.isAlive)
|
|
137
|
+
assertTrue(openResult.get())
|
|
138
|
+
assertTrue(closeResult.get())
|
|
139
|
+
assertEquals(
|
|
140
|
+
listOf(
|
|
141
|
+
GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping"),
|
|
142
|
+
GraniteMicroFrontendEvent.CloseApp("session-1"),
|
|
143
|
+
),
|
|
144
|
+
events,
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@Test
|
|
149
|
+
fun `visibility before open and after close is ignored`() {
|
|
150
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
151
|
+
val store = GraniteMicroFrontendSessionStore(events::add)
|
|
152
|
+
val registration = store.registerSession("session-1")
|
|
153
|
+
|
|
154
|
+
assertFalse(registration.setVisible(true))
|
|
155
|
+
assertTrue(registration.openApp("shopping", "granite://shopping"))
|
|
156
|
+
assertTrue(registration.setVisible(true))
|
|
157
|
+
assertTrue(registration.closeApp())
|
|
158
|
+
assertFalse(registration.setVisible(false))
|
|
159
|
+
|
|
160
|
+
assertEquals(
|
|
161
|
+
listOf(
|
|
162
|
+
GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping"),
|
|
163
|
+
GraniteMicroFrontendEvent.SessionVisibilityChanged("session-1", true),
|
|
164
|
+
GraniteMicroFrontendEvent.CloseApp("session-1"),
|
|
165
|
+
),
|
|
166
|
+
events,
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@Test
|
|
171
|
+
fun `close app is emitted only once after open for actual teardown`() {
|
|
172
|
+
val events = mutableListOf<GraniteMicroFrontendEvent>()
|
|
173
|
+
val store = GraniteMicroFrontendSessionStore(events::add)
|
|
174
|
+
val registration = store.registerSession("session-1")
|
|
175
|
+
|
|
176
|
+
assertFalse(registration.closeApp())
|
|
177
|
+
assertTrue(registration.openApp("shopping", "granite://shopping"))
|
|
178
|
+
assertTrue(registration.closeApp())
|
|
179
|
+
assertFalse(registration.closeApp())
|
|
180
|
+
registration.close()
|
|
181
|
+
|
|
182
|
+
assertEquals(
|
|
183
|
+
listOf(
|
|
184
|
+
GraniteMicroFrontendEvent.OpenApp("session-1", "shopping", "granite://shopping"),
|
|
185
|
+
GraniteMicroFrontendEvent.CloseApp("session-1"),
|
|
186
|
+
),
|
|
187
|
+
events,
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
+
SOURCE_DIR="${SCRIPT_DIR}/src/test/cpp"
|
|
7
|
+
BUILD_DIR="/tmp/granite-micro-frontend-test-build"
|
|
8
|
+
CMAKE_BIN="${CMAKE_BIN:-cmake}"
|
|
9
|
+
|
|
10
|
+
"${CMAKE_BIN}" -S "${SOURCE_DIR}" -B "${BUILD_DIR}" -DCMAKE_BUILD_TYPE=Release
|
|
11
|
+
"${CMAKE_BIN}" --build "${BUILD_DIR}" --parallel 2
|
|
12
|
+
"${CMAKE_BIN}" --build "${BUILD_DIR}" --target test
|
package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/ComponentDescriptors.h
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// This header intentionally shares its path with the codegen-generated
|
|
4
|
+
// ComponentDescriptors.h and shadows it: common/cpp comes before the generated jni directory on
|
|
5
|
+
// the include path. React Native's generated autolinking.cpp includes this path and then names
|
|
6
|
+
// the descriptors listed in react-native.config.js, so the two custom descriptors below have to
|
|
7
|
+
// be reachable through it. Renaming or removing this file breaks the consuming app's build with
|
|
8
|
+
// "use of undeclared identifier 'PortalViewComponentDescriptor'".
|
|
9
|
+
|
|
10
|
+
#include <react/renderer/components/GraniteMicroFrontendRuntimeSpec/ShadowNodes.h>
|
|
11
|
+
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
12
|
+
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
|
13
|
+
|
|
14
|
+
#include <react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewComponentDescriptor.h>
|
|
15
|
+
#include <react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewComponentDescriptor.h>
|
|
16
|
+
|
|
17
|
+
#include <memory>
|
|
18
|
+
|
|
19
|
+
namespace facebook::react {
|
|
20
|
+
|
|
21
|
+
void GraniteMicroFrontendRuntimeSpec_registerComponentDescriptorsFromCodegen(
|
|
22
|
+
std::shared_ptr<const ComponentDescriptorProviderRegistry> registry);
|
|
23
|
+
|
|
24
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#include "PortalShadowRegistry.h"
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
namespace facebook::react {
|
|
6
|
+
|
|
7
|
+
PortalShadowRegistry &PortalShadowRegistry::getInstance() {
|
|
8
|
+
static PortalShadowRegistry instance;
|
|
9
|
+
return instance;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
void PortalShadowRegistry::unregisterHost(const std::string &name) {
|
|
13
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
14
|
+
hostSizes_.erase(name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
void PortalShadowRegistry::updateHostSize(const std::string &name, HostSize size) {
|
|
18
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
19
|
+
hostSizes_[name] = size;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
HostSize PortalShadowRegistry::getHostSize(const std::string &name) const {
|
|
23
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
24
|
+
auto it = hostSizes_.find(name);
|
|
25
|
+
if (it != hostSizes_.end()) {
|
|
26
|
+
return it->second;
|
|
27
|
+
}
|
|
28
|
+
return HostSize{0, 0};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
} // namespace facebook::react
|
package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/PortalShadowRegistry.h
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <unordered_map>
|
|
5
|
+
#include <mutex>
|
|
6
|
+
#include <memory>
|
|
7
|
+
|
|
8
|
+
namespace facebook::react {
|
|
9
|
+
|
|
10
|
+
struct HostSize {
|
|
11
|
+
float width;
|
|
12
|
+
float height;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
class PortalShadowRegistry {
|
|
16
|
+
public:
|
|
17
|
+
static PortalShadowRegistry &getInstance();
|
|
18
|
+
|
|
19
|
+
void unregisterHost(const std::string &name);
|
|
20
|
+
void updateHostSize(const std::string &name, HostSize size);
|
|
21
|
+
HostSize getHostSize(const std::string &name) const;
|
|
22
|
+
|
|
23
|
+
private:
|
|
24
|
+
PortalShadowRegistry() = default;
|
|
25
|
+
mutable std::mutex mutex_;
|
|
26
|
+
std::unordered_map<std::string, HostSize> hostSizes_;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "RNTPortalHostViewShadowNode.h"
|
|
4
|
+
#include "PortalShadowRegistry.h"
|
|
5
|
+
|
|
6
|
+
#include <react/debug/react_native_assert.h>
|
|
7
|
+
#include <react/renderer/components/GraniteMicroFrontendRuntimeSpec/Props.h>
|
|
8
|
+
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
9
|
+
|
|
10
|
+
namespace facebook::react {
|
|
11
|
+
|
|
12
|
+
class PortalHostViewComponentDescriptor final
|
|
13
|
+
: public ConcreteComponentDescriptor<PortalHostViewShadowNode> {
|
|
14
|
+
public:
|
|
15
|
+
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
16
|
+
|
|
17
|
+
void adopt(ShadowNode &shadowNode) const override {
|
|
18
|
+
ConcreteComponentDescriptor::adopt(shadowNode);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "RNTPortalHostViewState.h"
|
|
4
|
+
#include "PortalShadowRegistry.h"
|
|
5
|
+
|
|
6
|
+
#include <react/renderer/components/GraniteMicroFrontendRuntimeSpec/EventEmitters.h>
|
|
7
|
+
#include <react/renderer/components/GraniteMicroFrontendRuntimeSpec/Props.h>
|
|
8
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
9
|
+
#include <react/renderer/core/LayoutContext.h>
|
|
10
|
+
#include <jsi/jsi.h>
|
|
11
|
+
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
JSI_EXPORT extern const char PortalHostViewComponentName[];
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* `ShadowNode` for <PortalHostView> component.
|
|
18
|
+
*/
|
|
19
|
+
class PortalHostViewShadowNode : public ConcreteViewShadowNode<
|
|
20
|
+
PortalHostViewComponentName,
|
|
21
|
+
PortalHostViewProps,
|
|
22
|
+
PortalHostViewEventEmitter,
|
|
23
|
+
PortalHostViewState> {
|
|
24
|
+
public:
|
|
25
|
+
using ConcreteViewShadowNode::ConcreteViewShadowNode;
|
|
26
|
+
|
|
27
|
+
void layout(LayoutContext layoutContext) override {
|
|
28
|
+
ConcreteViewShadowNode::layout(layoutContext);
|
|
29
|
+
|
|
30
|
+
// After layout is complete, update the size in the registry
|
|
31
|
+
auto &props = static_cast<const PortalHostViewProps &>(*getProps());
|
|
32
|
+
if (!props.name.empty()) {
|
|
33
|
+
auto layoutMetrics = getLayoutMetrics();
|
|
34
|
+
PortalShadowRegistry::getInstance().updateHostSize(
|
|
35
|
+
props.name,
|
|
36
|
+
HostSize{
|
|
37
|
+
static_cast<float>(layoutMetrics.frame.size.width),
|
|
38
|
+
static_cast<float>(layoutMetrics.frame.size.height)});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef ANDROID
|
|
4
|
+
#include <folly/dynamic.h>
|
|
5
|
+
#endif
|
|
6
|
+
|
|
7
|
+
namespace facebook::react {
|
|
8
|
+
|
|
9
|
+
class PortalHostViewState {
|
|
10
|
+
public:
|
|
11
|
+
PortalHostViewState() = default;
|
|
12
|
+
|
|
13
|
+
#ifdef ANDROID
|
|
14
|
+
PortalHostViewState(PortalHostViewState const &previousState, folly::dynamic data) {}
|
|
15
|
+
folly::dynamic getDynamic() const {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
#endif
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "RNTPortalViewShadowNode.h"
|
|
4
|
+
#include "PortalShadowRegistry.h"
|
|
5
|
+
|
|
6
|
+
#include <react/debug/react_native_assert.h>
|
|
7
|
+
#include <react/renderer/components/GraniteMicroFrontendRuntimeSpec/Props.h>
|
|
8
|
+
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
9
|
+
#include <react/renderer/components/view/YogaLayoutableShadowNode.h>
|
|
10
|
+
|
|
11
|
+
namespace facebook::react {
|
|
12
|
+
|
|
13
|
+
class PortalViewComponentDescriptor final
|
|
14
|
+
: public ConcreteComponentDescriptor<PortalViewShadowNode> {
|
|
15
|
+
public:
|
|
16
|
+
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
17
|
+
|
|
18
|
+
void adopt(ShadowNode &shadowNode) const override {
|
|
19
|
+
react_native_assert(dynamic_cast<PortalViewShadowNode *>(&shadowNode));
|
|
20
|
+
|
|
21
|
+
auto &portalViewShadowNode = static_cast<PortalViewShadowNode &>(shadowNode);
|
|
22
|
+
auto &props = static_cast<const PortalViewProps &>(*portalViewShadowNode.getProps());
|
|
23
|
+
|
|
24
|
+
// If this portal is teleported to a host, apply the host's layout constraints
|
|
25
|
+
if (!props.hostName.empty()) {
|
|
26
|
+
auto &yogaPortal = static_cast<YogaLayoutableShadowNode &>(portalViewShadowNode);
|
|
27
|
+
|
|
28
|
+
// Set position to absolute because:
|
|
29
|
+
// - when the view is teleported, we need to "free" its original space
|
|
30
|
+
// - we need to stretch the view beyond the parent layout constraints
|
|
31
|
+
yogaPortal.setPositionType(YGPositionTypeAbsolute);
|
|
32
|
+
|
|
33
|
+
const auto &stateData =
|
|
34
|
+
static_cast<const PortalViewShadowNode::ConcreteState &>(
|
|
35
|
+
*portalViewShadowNode.getState())
|
|
36
|
+
.getData();
|
|
37
|
+
|
|
38
|
+
// Native state tracks the destination host after it re-layouts (for
|
|
39
|
+
// example, on rotation). The registry is the initial-layout fallback.
|
|
40
|
+
HostSize hostSize{
|
|
41
|
+
static_cast<float>(stateData.hostWidth),
|
|
42
|
+
static_cast<float>(stateData.hostHeight)};
|
|
43
|
+
if (hostSize.width == 0 || hostSize.height == 0) {
|
|
44
|
+
hostSize = PortalShadowRegistry::getInstance().getHostSize(props.hostName);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
portalViewShadowNode.setDimensionsFromHost(hostSize);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
ConcreteComponentDescriptor::adopt(shadowNode);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
} // namespace facebook::react
|