@use-voltra/ios-client 2.2.0 → 2.3.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/Voltra.podspec +1 -0
- package/build/commonjs/events.js +23 -0
- package/build/commonjs/events.js.map +1 -1
- package/build/commonjs/index.js +59 -0
- package/build/commonjs/index.js.map +1 -1
- package/build/commonjs/live-activity/api.js +3 -1
- package/build/commonjs/live-activity/api.js.map +1 -1
- package/build/commonjs/live-activity/dynamic-api.js +173 -0
- package/build/commonjs/live-activity/dynamic-api.js.map +1 -0
- package/build/commonjs/native/NativeVoltra.js.map +1 -1
- package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js +47 -0
- package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
- package/build/commonjs/utils/enableWidgetHotReload.js +8 -11
- package/build/commonjs/utils/enableWidgetHotReload.js.map +1 -1
- package/build/commonjs/utils/useUpdateOnHMR.js +3 -7
- package/build/commonjs/utils/useUpdateOnHMR.js.map +1 -1
- package/build/commonjs/widgets/server-credentials.js +9 -0
- package/build/commonjs/widgets/server-credentials.js.map +1 -1
- package/build/commonjs/widgets/server-update.js +78 -0
- package/build/commonjs/widgets/server-update.js.map +1 -0
- package/build/module/events.js +23 -0
- package/build/module/events.js.map +1 -1
- package/build/module/index.js +3 -0
- package/build/module/index.js.map +1 -1
- package/build/module/live-activity/api.js +2 -2
- package/build/module/live-activity/api.js.map +1 -1
- package/build/module/live-activity/dynamic-api.js +167 -0
- package/build/module/live-activity/dynamic-api.js.map +1 -0
- package/build/module/native/NativeVoltra.js.map +1 -1
- package/build/module/utils/enableDynamicLiveActivityHotReload.js +43 -0
- package/build/module/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
- package/build/module/utils/enableWidgetHotReload.js +9 -11
- package/build/module/utils/enableWidgetHotReload.js.map +1 -1
- package/build/module/utils/useUpdateOnHMR.js +3 -7
- package/build/module/utils/useUpdateOnHMR.js.map +1 -1
- package/build/module/widgets/server-credentials.js +9 -0
- package/build/module/widgets/server-credentials.js.map +1 -1
- package/build/module/widgets/server-update.js +72 -0
- package/build/module/widgets/server-update.js.map +1 -0
- package/build/typescript/commonjs/events.d.ts +7 -0
- package/build/typescript/commonjs/events.d.ts.map +1 -1
- package/build/typescript/commonjs/index.d.ts +3 -0
- package/build/typescript/commonjs/index.d.ts.map +1 -1
- package/build/typescript/commonjs/live-activity/api.d.ts +7 -0
- package/build/typescript/commonjs/live-activity/api.d.ts.map +1 -1
- package/build/typescript/commonjs/live-activity/dynamic-api.d.ts +54 -0
- package/build/typescript/commonjs/live-activity/dynamic-api.d.ts.map +1 -0
- package/build/typescript/commonjs/native/NativeVoltra.d.ts +20 -0
- package/build/typescript/commonjs/native/NativeVoltra.d.ts.map +1 -1
- package/build/typescript/commonjs/types.d.ts +1 -1
- package/build/typescript/commonjs/types.d.ts.map +1 -1
- package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
- package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
- package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts +5 -7
- package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts.map +1 -1
- package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts +0 -3
- package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts.map +1 -1
- package/build/typescript/commonjs/widgets/server-credentials.d.ts +8 -0
- package/build/typescript/commonjs/widgets/server-credentials.d.ts.map +1 -1
- package/build/typescript/commonjs/widgets/server-update.d.ts +64 -0
- package/build/typescript/commonjs/widgets/server-update.d.ts.map +1 -0
- package/build/typescript/module/events.d.ts +7 -0
- package/build/typescript/module/events.d.ts.map +1 -1
- package/build/typescript/module/index.d.ts +3 -0
- package/build/typescript/module/index.d.ts.map +1 -1
- package/build/typescript/module/live-activity/api.d.ts +7 -0
- package/build/typescript/module/live-activity/api.d.ts.map +1 -1
- package/build/typescript/module/live-activity/dynamic-api.d.ts +54 -0
- package/build/typescript/module/live-activity/dynamic-api.d.ts.map +1 -0
- package/build/typescript/module/native/NativeVoltra.d.ts +20 -0
- package/build/typescript/module/native/NativeVoltra.d.ts.map +1 -1
- package/build/typescript/module/types.d.ts +1 -1
- package/build/typescript/module/types.d.ts.map +1 -1
- package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
- package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
- package/build/typescript/module/utils/enableWidgetHotReload.d.ts +5 -7
- package/build/typescript/module/utils/enableWidgetHotReload.d.ts.map +1 -1
- package/build/typescript/module/utils/useUpdateOnHMR.d.ts +0 -3
- package/build/typescript/module/utils/useUpdateOnHMR.d.ts.map +1 -1
- package/build/typescript/module/widgets/server-credentials.d.ts +8 -0
- package/build/typescript/module/widgets/server-credentials.d.ts.map +1 -1
- package/build/typescript/module/widgets/server-update.d.ts +64 -0
- package/build/typescript/module/widgets/server-update.d.ts.map +1 -0
- package/expo-plugin/build/cjs/constants.js +20 -1
- package/expo-plugin/build/cjs/constants.js.map +1 -1
- package/expo-plugin/build/cjs/index.js +6 -0
- package/expo-plugin/build/cjs/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios/index.js +1 -0
- package/expo-plugin/build/cjs/ios/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios/infoPlist.js +28 -5
- package/expo-plugin/build/cjs/ios/infoPlist.js.map +1 -1
- package/expo-plugin/build/cjs/ios/serverUpdate.js +25 -0
- package/expo-plugin/build/cjs/ios/serverUpdate.js.map +1 -0
- package/expo-plugin/build/cjs/ios-widget/clientRendered.js +4 -4
- package/expo-plugin/build/cjs/ios-widget/clientRendered.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js +5 -5
- package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js +130 -0
- package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js.map +1 -0
- package/expo-plugin/build/cjs/ios-widget/files/index.js +9 -2
- package/expo-plugin/build/cjs/ios-widget/files/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js +6 -3
- package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/files/manifest.js +24 -0
- package/expo-plugin/build/cjs/ios-widget/files/manifest.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js +7 -0
- package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js.map +1 -0
- package/expo-plugin/build/cjs/ios-widget/files/swift.js +56 -26
- package/expo-plugin/build/cjs/ios-widget/files/swift.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/index.js +9 -3
- package/expo-plugin/build/cjs/ios-widget/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/widgetPlist.js +34 -22
- package/expo-plugin/build/cjs/ios-widget/widgetPlist.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js +51 -11
- package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/index.js +21 -5
- package/expo-plugin/build/cjs/ios-widget/xcode/index.js.map +1 -1
- package/expo-plugin/build/cjs/utils/urlScheme.js +36 -14
- package/expo-plugin/build/cjs/utils/urlScheme.js.map +1 -1
- package/expo-plugin/build/cjs/validation.js +54 -0
- package/expo-plugin/build/cjs/validation.js.map +1 -1
- package/expo-plugin/build/esm/constants.js +17 -0
- package/expo-plugin/build/esm/constants.js.map +1 -1
- package/expo-plugin/build/esm/index.js +6 -0
- package/expo-plugin/build/esm/index.js.map +1 -1
- package/expo-plugin/build/esm/ios/index.js +1 -0
- package/expo-plugin/build/esm/ios/index.js.map +1 -1
- package/expo-plugin/build/esm/ios/infoPlist.js +28 -5
- package/expo-plugin/build/esm/ios/infoPlist.js.map +1 -1
- package/expo-plugin/build/esm/ios/serverUpdate.js +21 -0
- package/expo-plugin/build/esm/ios/serverUpdate.js.map +1 -0
- package/expo-plugin/build/esm/ios-widget/clientRendered.js +5 -5
- package/expo-plugin/build/esm/ios-widget/clientRendered.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js +6 -6
- package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js +122 -0
- package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js.map +1 -0
- package/expo-plugin/build/esm/ios-widget/files/index.js +10 -3
- package/expo-plugin/build/esm/ios-widget/files/index.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/files/infoPlist.js +6 -3
- package/expo-plugin/build/esm/ios-widget/files/infoPlist.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/files/manifest.js +23 -1
- package/expo-plugin/build/esm/ios-widget/files/manifest.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/files/swift-utils.js +4 -0
- package/expo-plugin/build/esm/ios-widget/files/swift-utils.js.map +1 -0
- package/expo-plugin/build/esm/ios-widget/files/swift.js +50 -20
- package/expo-plugin/build/esm/ios-widget/files/swift.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/index.js +9 -3
- package/expo-plugin/build/esm/ios-widget/index.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/widgetPlist.js +34 -22
- package/expo-plugin/build/esm/ios-widget/widgetPlist.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js +51 -11
- package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/index.js +21 -5
- package/expo-plugin/build/esm/ios-widget/xcode/index.js.map +1 -1
- package/expo-plugin/build/esm/utils/urlScheme.js +34 -14
- package/expo-plugin/build/esm/utils/urlScheme.js.map +1 -1
- package/expo-plugin/build/esm/validation.js +54 -1
- package/expo-plugin/build/esm/validation.js.map +1 -1
- package/expo-plugin/build/types/constants.d.ts +10 -1
- package/expo-plugin/build/types/constants.d.ts.map +1 -1
- package/expo-plugin/build/types/index.d.ts +1 -1
- package/expo-plugin/build/types/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios/index.d.ts +2 -0
- package/expo-plugin/build/types/ios/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios/infoPlist.d.ts +2 -0
- package/expo-plugin/build/types/ios/infoPlist.d.ts.map +1 -1
- package/expo-plugin/build/types/ios/serverUpdate.d.ts +11 -0
- package/expo-plugin/build/types/ios/serverUpdate.d.ts.map +1 -0
- package/expo-plugin/build/types/ios-widget/clientRendered.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/clientRenderedPrerender.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts +7 -0
- package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts.map +1 -0
- package/expo-plugin/build/types/ios-widget/files/index.d.ts +3 -1
- package/expo-plugin/build/types/ios-widget/files/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts +2 -1
- package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/files/manifest.d.ts +9 -2
- package/expo-plugin/build/types/ios-widget/files/manifest.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts +2 -0
- package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts.map +1 -0
- package/expo-plugin/build/types/ios-widget/files/swift.d.ts +6 -2
- package/expo-plugin/build/types/ios-widget/files/swift.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/index.d.ts +3 -1
- package/expo-plugin/build/types/ios-widget/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/widgetPlist.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts +3 -1
- package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/index.d.ts +2 -1
- package/expo-plugin/build/types/ios-widget/xcode/index.d.ts.map +1 -1
- package/expo-plugin/build/types/types.d.ts +23 -4
- package/expo-plugin/build/types/types.d.ts.map +1 -1
- package/expo-plugin/build/types/utils/urlScheme.d.ts +16 -0
- package/expo-plugin/build/types/utils/urlScheme.d.ts.map +1 -1
- package/expo-plugin/build/types/validation.d.ts +2 -1
- package/expo-plugin/build/types/validation.d.ts.map +1 -1
- package/expo-plugin/src/constants.ts +24 -1
- package/expo-plugin/src/index.ts +9 -0
- package/expo-plugin/src/ios/index.ts +3 -0
- package/expo-plugin/src/ios/infoPlist.ts +33 -5
- package/expo-plugin/src/ios/serverUpdate.ts +29 -0
- package/expo-plugin/src/ios-widget/clientRendered.ts +9 -5
- package/expo-plugin/src/ios-widget/clientRenderedPrerender.ts +11 -6
- package/expo-plugin/src/ios-widget/dynamic-live-activity/swift.ts +138 -0
- package/expo-plugin/src/ios-widget/files/index.ts +15 -4
- package/expo-plugin/src/ios-widget/files/infoPlist.ts +17 -3
- package/expo-plugin/src/ios-widget/files/manifest.ts +42 -2
- package/expo-plugin/src/ios-widget/files/swift-utils.ts +3 -0
- package/expo-plugin/src/ios-widget/files/swift.ts +69 -22
- package/expo-plugin/src/ios-widget/index.ts +13 -3
- package/expo-plugin/src/ios-widget/widgetPlist.ts +37 -23
- package/expo-plugin/src/ios-widget/xcode/__snapshots__/applyXcodeChanges.node.test.ts.snap +34 -28
- package/expo-plugin/src/ios-widget/xcode/buildPhases.ts +59 -12
- package/expo-plugin/src/ios-widget/xcode/index.ts +23 -6
- package/expo-plugin/src/types.ts +28 -4
- package/expo-plugin/src/utils/urlScheme.ts +47 -17
- package/expo-plugin/src/validation.ts +76 -1
- package/ios/Package.swift +32 -3
- package/ios/VoltraWidget.podspec +1 -0
- package/ios/app/NativeVoltra.mm +107 -7
- package/ios/app/VoltraLiveActivityManager.swift +30 -0
- package/ios/app/VoltraLiveActivityService.swift +77 -5
- package/ios/app/VoltraModule.swift +99 -1
- package/ios/app/VoltraModuleImpl.swift +100 -2
- package/ios/app/VoltraViewComponentView.mm +3 -3
- package/ios/app/VoltraWidgetService.swift +147 -12
- package/ios/app/dynamic-live-activity/VoltraDynamicLiveActivityService.swift +97 -0
- package/ios/app/dynamic-live-activity/VoltraLiveActivityChronology.swift +68 -0
- package/ios/shared/DynamicWidgetPropsStore.swift +73 -6
- package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerFetchResolver.swift +34 -0
- package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerProps.swift +55 -0
- package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerPropsStore.swift +146 -0
- package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerUpdateRunner.swift +193 -0
- package/ios/shared/ServerWidgetContentResolver.swift +60 -0
- package/ios/shared/ServerWidgetResponseStore.swift +25 -0
- package/ios/shared/ShortNames.swift +6 -0
- package/ios/shared/VoltraConfig.swift +41 -0
- package/ios/shared/VoltraConstants.swift +15 -0
- package/ios/shared/VoltraElement.swift +127 -43
- package/ios/shared/VoltraEventBus.swift +52 -7
- package/ios/shared/VoltraJSRenderer.swift +90 -22
- package/ios/shared/VoltraNode.swift +1 -109
- package/ios/shared/VoltraRefreshIntent.swift +1 -1
- package/ios/shared/VoltraWidgetKind.swift +41 -0
- package/ios/shared/VoltraWidgetServerFetcher.swift +52 -107
- package/ios/shared/WidgetServer/VoltraWidgetAppearance.swift +17 -0
- package/ios/shared/WidgetServer/VoltraWidgetServer.swift +138 -0
- package/ios/shared/WidgetServer/WidgetCanonicalConfiguration.swift +77 -0
- package/ios/shared/WidgetServer/WidgetScope.swift +55 -0
- package/ios/shared/WidgetServer/WidgetServerEtagStore.swift +55 -0
- package/ios/shared/WidgetServer/WidgetServerFetcher.swift +179 -0
- package/ios/shared/WidgetServer/WidgetServerRequestBuilder.swift +114 -0
- package/ios/shared/WidgetServer/WidgetServerSettingsCodec.swift +47 -0
- package/ios/shared/WidgetServer/WidgetServerSettingsResolver.swift +129 -0
- package/ios/shared/WidgetServer/WidgetServerSettingsStore.swift +170 -0
- package/ios/shared/WidgetServer/WidgetServerSettingsValidator.swift +73 -0
- package/ios/shared/WidgetServer/WidgetServerUpdateSettings.swift +123 -0
- package/ios/shared/WidgetServer/WidgetServerUpdateSettingsJson.swift +115 -0
- package/ios/shared/dynamic-live-activity/DynamicLiveActivityPropsCodec.swift +13 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityBundleSource.swift +156 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityObserver.swift +97 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityOperations.swift +105 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityPayloadValidator.swift +55 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRegistry.swift +143 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureQueue.swift +158 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureReporter.swift +120 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderer.swift +370 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityTypes.swift +87 -0
- package/ios/shared/dynamic-live-activity/VoltraLiveActivityOrder.swift +19 -0
- package/ios/target/VoltraClientWidgetRuntime.swift +44 -6
- package/ios/target/VoltraDynamicWidgetServerUpdateProvider.swift +204 -0
- package/ios/target/VoltraHomeWidget.swift +85 -90
- package/ios/ui/Extensions/VoltraNode+View.swift +113 -0
- package/ios/ui/Generated/Parameters/ChartParameters.swift +3 -0
- package/ios/ui/Helpers/VoltraDeepLinkResolver.swift +2 -2
- package/ios/ui/Views/VoltraChart.swift +108 -0
- package/package.json +4 -4
- package/src/events.ts +34 -0
- package/src/index.ts +22 -0
- package/src/live-activity/api.ts +2 -2
- package/src/live-activity/dynamic-api.ts +240 -0
- package/src/native/NativeVoltra.ts +21 -0
- package/src/types.ts +4 -0
- package/src/utils/enableDynamicLiveActivityHotReload.ts +50 -0
- package/src/utils/enableWidgetHotReload.ts +9 -16
- package/src/utils/useUpdateOnHMR.ts +3 -12
- package/src/widgets/server-credentials.ts +8 -0
- package/src/widgets/server-update.ts +84 -0
- /package/ios/shared/{VoltraKeychainHelper.swift → WidgetServer/VoltraKeychainHelper.swift} +0 -0
|
@@ -161,6 +161,103 @@ public struct VoltraChart: VoltraView {
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// MARK: - Y scale
|
|
165
|
+
|
|
166
|
+
/// Y-axis bounds pinned by the `yScale` prop; either side may be left to the data.
|
|
167
|
+
private struct YScale {
|
|
168
|
+
let min: Double?
|
|
169
|
+
let max: Double?
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private func parseYScale(from raw: String?) -> YScale? {
|
|
173
|
+
guard let raw,
|
|
174
|
+
let data = raw.data(using: .utf8),
|
|
175
|
+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
176
|
+
else { return nil }
|
|
177
|
+
|
|
178
|
+
let props = dict.compactMapValues { jsonValue(from: $0) }
|
|
179
|
+
let min = (props["mn"] ?? props["min"]).flatMap(num)
|
|
180
|
+
let max = (props["mx"] ?? props["max"]).flatMap(num)
|
|
181
|
+
if min == nil, max == nil {
|
|
182
|
+
return nil
|
|
183
|
+
}
|
|
184
|
+
return YScale(min: min, max: max)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// Domain to hand SwiftUI when `yScale` pins a bound. The open side follows the data exactly as
|
|
188
|
+
/// `computeYDomain` in Android's ChartBitmapRenderer.kt frames it, so both platforms agree.
|
|
189
|
+
private func yScaleDomain(_ marks: [WireMark], pinned: YScale) -> ClosedRange<Double> {
|
|
190
|
+
let auto = autoYDomain(marks)
|
|
191
|
+
var lower = pinned.min ?? auto.lowerBound
|
|
192
|
+
var upper = pinned.max ?? auto.upperBound
|
|
193
|
+
|
|
194
|
+
if upper <= lower {
|
|
195
|
+
// A pin that inverts the domain still has to leave something to draw in: keep the span the
|
|
196
|
+
// data asked for on the side the caller left open, and ignore a pair that cannot be drawn.
|
|
197
|
+
let span = auto.upperBound - auto.lowerBound
|
|
198
|
+
if pinned.max == nil {
|
|
199
|
+
upper = lower + span
|
|
200
|
+
} else if pinned.min == nil {
|
|
201
|
+
lower = upper - span
|
|
202
|
+
} else {
|
|
203
|
+
return auto
|
|
204
|
+
}
|
|
205
|
+
if upper <= lower {
|
|
206
|
+
upper = lower + 1
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return lower ... upper
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private func autoYDomain(_ marks: [WireMark]) -> ClosedRange<Double> {
|
|
214
|
+
var values = marks.flatMap { mark -> [Double] in
|
|
215
|
+
guard mark.type != "sector" else { return [] }
|
|
216
|
+
return (mark.data ?? []).map { xy($0).y }
|
|
217
|
+
}
|
|
218
|
+
values += marks.compactMap { $0.type == "rule" ? $0.props["yv"].flatMap(num) : nil }
|
|
219
|
+
|
|
220
|
+
let finite = values.filter(\.isFinite)
|
|
221
|
+
guard let dataMin = finite.min(), let dataMax = finite.max() else { return 0 ... 1 }
|
|
222
|
+
|
|
223
|
+
// Bars and areas measure their value against the baseline, so zero stays in range.
|
|
224
|
+
if marks.contains(where: { $0.type == "bar" || $0.type == "area" }) {
|
|
225
|
+
let lower = Swift.min(dataMin, 0)
|
|
226
|
+
let upper = Swift.max(dataMax, 0)
|
|
227
|
+
return upper > lower ? lower ... upper : lower ... (lower + 1)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
guard dataMax > dataMin else {
|
|
231
|
+
let padding = dataMin == 0 ? 1 : abs(dataMin) * Self.yDomainPaddingRatio
|
|
232
|
+
return (dataMin - padding) ... (dataMax + padding)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let padding = (dataMax - dataMin) * Self.yDomainPaddingRatio
|
|
236
|
+
var lower = dataMin - padding
|
|
237
|
+
var upper = dataMax + padding
|
|
238
|
+
|
|
239
|
+
// Padding must not invent values on the other side of the baseline, and data that almost
|
|
240
|
+
// reaches zero keeps it in view.
|
|
241
|
+
if dataMin >= 0, lower < 0 {
|
|
242
|
+
lower = 0
|
|
243
|
+
}
|
|
244
|
+
if dataMax <= 0, upper > 0 {
|
|
245
|
+
upper = 0
|
|
246
|
+
}
|
|
247
|
+
let span = upper - lower
|
|
248
|
+
if lower > 0, lower < span * Self.zeroSnapRatio {
|
|
249
|
+
lower = 0
|
|
250
|
+
}
|
|
251
|
+
if upper < 0, -upper < span * Self.zeroSnapRatio {
|
|
252
|
+
upper = 0
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return lower ... upper
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private static let yDomainPaddingRatio = 0.1
|
|
259
|
+
private static let zeroSnapRatio = 0.25
|
|
260
|
+
|
|
164
261
|
// MARK: - body
|
|
165
262
|
|
|
166
263
|
public var body: some View {
|
|
@@ -171,10 +268,12 @@ public struct VoltraChart: VoltraView {
|
|
|
171
268
|
let yAxisGridStyle = parseAxisGridStyle(from: params.yAxisGridStyle)
|
|
172
269
|
let legendVis = visibility(params.legendVisibility)
|
|
173
270
|
let legendItems = parseLegendItems(from: params.foregroundStyleScale)
|
|
271
|
+
let yDomain = parseYScale(from: params.yScale).map { yScaleDomain(wireMarks, pinned: $0) }
|
|
174
272
|
|
|
175
273
|
Chart {
|
|
176
274
|
buildAll(wireMarks)
|
|
177
275
|
}
|
|
276
|
+
.applyChartYScale(domain: yDomain)
|
|
178
277
|
.applyForegroundStyleScale(params.foregroundStyleScale)
|
|
179
278
|
.applyChartStyle(
|
|
180
279
|
element.style,
|
|
@@ -734,6 +833,15 @@ private extension View {
|
|
|
734
833
|
}
|
|
735
834
|
}
|
|
736
835
|
|
|
836
|
+
@ViewBuilder
|
|
837
|
+
func applyChartYScale(domain: ClosedRange<Double>?) -> some View {
|
|
838
|
+
if let domain {
|
|
839
|
+
chartYScale(domain: domain)
|
|
840
|
+
} else {
|
|
841
|
+
self
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
737
845
|
@ViewBuilder
|
|
738
846
|
func applyChartClipping(decoration: DecorationStyle, hasClipping: Bool) -> some View {
|
|
739
847
|
if hasClipping {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@use-voltra/ios-client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Client-only Voltra APIs for iOS",
|
|
5
5
|
"main": "./build/commonjs/index.js",
|
|
6
6
|
"module": "./build/module/index.js",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"@expo/plist": "^0.3.5",
|
|
40
40
|
"dedent": "^1.7.1",
|
|
41
41
|
"xcode": "^3.0.1",
|
|
42
|
-
"@use-voltra/compiler": "^2.
|
|
43
|
-
"@use-voltra/
|
|
44
|
-
"@use-voltra/
|
|
42
|
+
"@use-voltra/compiler": "^2.3.0",
|
|
43
|
+
"@use-voltra/ios": "^2.3.0",
|
|
44
|
+
"@use-voltra/expo-plugin": "^2.3.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"expo": "*",
|
package/src/events.ts
CHANGED
|
@@ -30,15 +30,28 @@ export type VoltraInteractionEvent = BasicVoltraEvent & {
|
|
|
30
30
|
payload: string
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export type VoltraDynamicLiveActivityRenderFailedEvent = BasicVoltraEvent & {
|
|
34
|
+
type: 'dynamicLiveActivityRenderFailed'
|
|
35
|
+
activityName: string
|
|
36
|
+
definitionId: string
|
|
37
|
+
message: string
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
const noopSubscription: EventSubscription = {
|
|
34
41
|
remove: () => {},
|
|
35
42
|
}
|
|
36
43
|
|
|
44
|
+
// Codegen EventEmitter subscriptions do not tell native code when their last
|
|
45
|
+
// JavaScript listener goes away. Keep this count at the public API boundary so
|
|
46
|
+
// the App Group queue is drained only while it can actually be delivered.
|
|
47
|
+
let dynamicRenderFailureListenerCount = 0
|
|
48
|
+
|
|
37
49
|
export type VoltraEventMap = {
|
|
38
50
|
activityTokenReceived: VoltraActivityTokenReceivedEvent
|
|
39
51
|
activityPushToStartTokenReceived: VoltraActivityPushToStartTokenReceivedEvent
|
|
40
52
|
stateChange: VoltraActivityUpdateEvent
|
|
41
53
|
interaction: VoltraInteractionEvent
|
|
54
|
+
dynamicLiveActivityRenderFailed: VoltraDynamicLiveActivityRenderFailedEvent
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
export function addVoltraListener<K extends keyof VoltraEventMap>(
|
|
@@ -63,6 +76,27 @@ export function addVoltraListener<K extends keyof VoltraEventMap>(
|
|
|
63
76
|
return voltraModule.onStateChanged(listener as (arg: VoltraActivityUpdateEvent) => void)
|
|
64
77
|
case 'interaction':
|
|
65
78
|
return voltraModule.onInteraction(listener as (arg: VoltraInteractionEvent) => void)
|
|
79
|
+
case 'dynamicLiveActivityRenderFailed':
|
|
80
|
+
const subscription = voltraModule.onDynamicLiveActivityRenderFailed(
|
|
81
|
+
listener as (arg: VoltraDynamicLiveActivityRenderFailedEvent) => void
|
|
82
|
+
)
|
|
83
|
+
dynamicRenderFailureListenerCount += 1
|
|
84
|
+
if (dynamicRenderFailureListenerCount === 1) {
|
|
85
|
+
voltraModule.setDynamicLiveActivityRenderFailureListenerActive(true)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let removed = false
|
|
89
|
+
return {
|
|
90
|
+
remove: () => {
|
|
91
|
+
if (removed) return
|
|
92
|
+
removed = true
|
|
93
|
+
subscription.remove()
|
|
94
|
+
dynamicRenderFailureListenerCount -= 1
|
|
95
|
+
if (dynamicRenderFailureListenerCount === 0) {
|
|
96
|
+
voltraModule.setDynamicLiveActivityRenderFailureListenerActive(false)
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
}
|
|
66
100
|
default:
|
|
67
101
|
console.warn(`[Voltra] Event '${event}' is not supported. Returning no-op subscription.`)
|
|
68
102
|
return noopSubscription
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,18 @@ export {
|
|
|
23
23
|
type UseLiveActivityOptions,
|
|
24
24
|
type UseLiveActivityResult,
|
|
25
25
|
} from './live-activity/api.js'
|
|
26
|
+
export {
|
|
27
|
+
getDynamicLiveActivityDefinitionIds,
|
|
28
|
+
startDynamicLiveActivity,
|
|
29
|
+
type StartDynamicLiveActivityOptions,
|
|
30
|
+
updateDynamicLiveActivity,
|
|
31
|
+
type UpdateDynamicLiveActivityOptions,
|
|
32
|
+
useDynamicLiveActivity,
|
|
33
|
+
type UseDynamicLiveActivityOptions,
|
|
34
|
+
type UseDynamicLiveActivityResult,
|
|
35
|
+
type DynamicLiveActivityProps,
|
|
36
|
+
type DynamicLiveActivityPropsValue,
|
|
37
|
+
} from './live-activity/dynamic-api.js'
|
|
26
38
|
export type { DismissalPolicy, LiveActivityVariants } from '@use-voltra/ios'
|
|
27
39
|
export {
|
|
28
40
|
clearPreloadedImages,
|
|
@@ -33,9 +45,19 @@ export {
|
|
|
33
45
|
} from './preload.js'
|
|
34
46
|
export { assertRunningOnApple } from './utils/assertRunningOnApple.js'
|
|
35
47
|
export { enableWidgetHotReload } from './utils/enableWidgetHotReload.js'
|
|
48
|
+
export { enableDynamicLiveActivityHotReload } from './utils/enableDynamicLiveActivityHotReload.js'
|
|
36
49
|
export { useUpdateOnHMR } from './utils/useUpdateOnHMR.js'
|
|
37
50
|
export * from './utils/helpers.js'
|
|
38
51
|
export type { VoltraElementJson, VoltraNodeJson } from './types.js'
|
|
52
|
+
export {
|
|
53
|
+
clearWidgetServerUpdate,
|
|
54
|
+
getWidgetServerUpdate,
|
|
55
|
+
setWidgetServerUpdate,
|
|
56
|
+
type WidgetServerUpdateBody,
|
|
57
|
+
type WidgetServerUpdateOptions,
|
|
58
|
+
type WidgetServerUpdateSettings,
|
|
59
|
+
type WidgetServerUpdateSnapshot,
|
|
60
|
+
} from './widgets/server-update.js'
|
|
39
61
|
export {
|
|
40
62
|
clearWidgetServerCredentials,
|
|
41
63
|
setWidgetServerCredentials,
|
package/src/live-activity/api.ts
CHANGED
|
@@ -40,7 +40,7 @@ export type UseLiveActivityResult = {
|
|
|
40
40
|
isActive: boolean
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const normalizeSharedLiveActivityOptions = (
|
|
43
|
+
export const normalizeSharedLiveActivityOptions = (
|
|
44
44
|
options?: SharedLiveActivityOptions
|
|
45
45
|
): SharedLiveActivityOptions | undefined => {
|
|
46
46
|
if (!options) return undefined
|
|
@@ -66,7 +66,7 @@ const normalizeSharedLiveActivityOptions = (
|
|
|
66
66
|
return Object.keys(normalizedOptions).length > 0 ? normalizedOptions : undefined
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const normalizeEndLiveActivityOptions = (
|
|
69
|
+
export const normalizeEndLiveActivityOptions = (
|
|
70
70
|
options?: EndLiveActivityOptions
|
|
71
71
|
): { dismissalPolicy?: { type: 'immediate' | 'after'; date?: number } } | undefined => {
|
|
72
72
|
if (!options?.dismissalPolicy) return undefined
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { addVoltraListener } from '../events.js'
|
|
4
|
+
import { assertRunningOnApple, useUpdateOnHMR } from '../utils/index.js'
|
|
5
|
+
import { getNativeVoltra } from '../VoltraModule.js'
|
|
6
|
+
import {
|
|
7
|
+
isLiveActivityActive,
|
|
8
|
+
normalizeSharedLiveActivityOptions,
|
|
9
|
+
stopLiveActivity,
|
|
10
|
+
type EndLiveActivityOptions,
|
|
11
|
+
type SharedLiveActivityOptions,
|
|
12
|
+
} from './api.js'
|
|
13
|
+
|
|
14
|
+
/** @experimental A JSON-compatible value accepted as Dynamic Live Activity props. */
|
|
15
|
+
export type DynamicLiveActivityPropsValue =
|
|
16
|
+
| string
|
|
17
|
+
| number
|
|
18
|
+
| boolean
|
|
19
|
+
| null
|
|
20
|
+
| ReadonlyArray<DynamicLiveActivityPropsValue>
|
|
21
|
+
| Readonly<{ [key: string]: DynamicLiveActivityPropsValue }>
|
|
22
|
+
|
|
23
|
+
/** @experimental A complete JSON-compatible props record for a Dynamic Live Activity update. */
|
|
24
|
+
export type DynamicLiveActivityProps = Readonly<{ [key: string]: DynamicLiveActivityPropsValue }>
|
|
25
|
+
|
|
26
|
+
/** @experimental Options used to start a Dynamic Live Activity. */
|
|
27
|
+
export type StartDynamicLiveActivityOptions = {
|
|
28
|
+
activityName?: string
|
|
29
|
+
deepLinkUrl?: string
|
|
30
|
+
channelId?: string
|
|
31
|
+
} & SharedLiveActivityOptions
|
|
32
|
+
|
|
33
|
+
/** @experimental Options used to update a Dynamic Live Activity. */
|
|
34
|
+
export type UpdateDynamicLiveActivityOptions = SharedLiveActivityOptions
|
|
35
|
+
|
|
36
|
+
/** @experimental Options for `useDynamicLiveActivity`. */
|
|
37
|
+
export type UseDynamicLiveActivityOptions = StartDynamicLiveActivityOptions & {
|
|
38
|
+
autoStart?: boolean
|
|
39
|
+
autoUpdate?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @experimental Lifecycle controls returned by `useDynamicLiveActivity`. */
|
|
43
|
+
export type UseDynamicLiveActivityResult = {
|
|
44
|
+
start: (options?: StartDynamicLiveActivityOptions) => Promise<void>
|
|
45
|
+
update: (options?: UpdateDynamicLiveActivityOptions) => Promise<void>
|
|
46
|
+
end: (options?: EndLiveActivityOptions) => Promise<void>
|
|
47
|
+
isActive: boolean
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function serializeDynamicLiveActivityProps(props: DynamicLiveActivityProps): string {
|
|
51
|
+
const ancestors = new Set<object>()
|
|
52
|
+
|
|
53
|
+
const validate = (value: unknown, path: string): void => {
|
|
54
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') return
|
|
55
|
+
|
|
56
|
+
if (typeof value === 'number') {
|
|
57
|
+
if (!Number.isFinite(value)) {
|
|
58
|
+
throw new TypeError(`Dynamic Live Activity props must be JSON-compatible; ${path} is not a finite number.`)
|
|
59
|
+
}
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
if (ancestors.has(value)) {
|
|
65
|
+
throw new TypeError(
|
|
66
|
+
`Dynamic Live Activity props must be JSON-compatible; ${path} contains a circular reference.`
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
ancestors.add(value)
|
|
70
|
+
value.forEach((item, index) => validate(item, `${path}[${index}]`))
|
|
71
|
+
ancestors.delete(value)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof value === 'object') {
|
|
76
|
+
const prototype = Object.getPrototypeOf(value)
|
|
77
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
78
|
+
throw new TypeError(`Dynamic Live Activity props must be JSON-compatible; ${path} must be a plain object.`)
|
|
79
|
+
}
|
|
80
|
+
if (ancestors.has(value)) {
|
|
81
|
+
throw new TypeError(
|
|
82
|
+
`Dynamic Live Activity props must be JSON-compatible; ${path} contains a circular reference.`
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
ancestors.add(value)
|
|
86
|
+
for (const [key, item] of Object.entries(value as Record<string, unknown>)) {
|
|
87
|
+
validate(item, `${path}.${key}`)
|
|
88
|
+
}
|
|
89
|
+
ancestors.delete(value)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
throw new TypeError(
|
|
94
|
+
`Dynamic Live Activity props must be JSON-compatible; ${path} has unsupported type ${typeof value}.`
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (props === null || Array.isArray(props) || typeof props !== 'object') {
|
|
99
|
+
throw new TypeError('Dynamic Live Activity props must be a JSON-compatible record.')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
validate(props, 'props')
|
|
103
|
+
return JSON.stringify(props)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Starts a bundled Dynamic Live Activity with a complete opaque props record.
|
|
108
|
+
* @experimental
|
|
109
|
+
*/
|
|
110
|
+
export async function startDynamicLiveActivity(
|
|
111
|
+
definitionId: string,
|
|
112
|
+
props: DynamicLiveActivityProps,
|
|
113
|
+
options?: StartDynamicLiveActivityOptions
|
|
114
|
+
): Promise<string> {
|
|
115
|
+
if (!assertRunningOnApple()) return ''
|
|
116
|
+
|
|
117
|
+
const propsJson = serializeDynamicLiveActivityProps(props)
|
|
118
|
+
const normalizedSharedOptions = normalizeSharedLiveActivityOptions(options)
|
|
119
|
+
return getNativeVoltra().startDynamicLiveActivity(definitionId, propsJson, {
|
|
120
|
+
target: 'liveActivity',
|
|
121
|
+
deepLinkUrl: options?.deepLinkUrl,
|
|
122
|
+
activityName: options?.activityName,
|
|
123
|
+
channelId: options?.channelId,
|
|
124
|
+
...normalizedSharedOptions,
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Replaces a Dynamic Live Activity's complete opaque props record.
|
|
130
|
+
* @experimental
|
|
131
|
+
*/
|
|
132
|
+
export async function updateDynamicLiveActivity(
|
|
133
|
+
activityName: string,
|
|
134
|
+
props: DynamicLiveActivityProps,
|
|
135
|
+
options?: UpdateDynamicLiveActivityOptions
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
if (!assertRunningOnApple()) return
|
|
138
|
+
|
|
139
|
+
const propsJson = serializeDynamicLiveActivityProps(props)
|
|
140
|
+
return getNativeVoltra().updateDynamicLiveActivity(
|
|
141
|
+
activityName,
|
|
142
|
+
propsJson,
|
|
143
|
+
normalizeSharedLiveActivityOptions(options) ?? {}
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Returns the Dynamic Live Activity definition IDs bundled in the installed app.
|
|
149
|
+
* @experimental
|
|
150
|
+
*/
|
|
151
|
+
export async function getDynamicLiveActivityDefinitionIds(): Promise<string[]> {
|
|
152
|
+
if (!assertRunningOnApple()) return []
|
|
153
|
+
return getNativeVoltra().getDynamicLiveActivityDefinitionIds()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Manages the lifecycle of one Dynamic Live Activity definition.
|
|
158
|
+
*
|
|
159
|
+
* Complete props are sent on every update. In development, call
|
|
160
|
+
* `enableDynamicLiveActivityHotReload()` once in the app host to reload only
|
|
161
|
+
* the definition changed by Fast Refresh.
|
|
162
|
+
* @experimental
|
|
163
|
+
*/
|
|
164
|
+
export function useDynamicLiveActivity<Props extends DynamicLiveActivityProps = DynamicLiveActivityProps>(
|
|
165
|
+
definitionId: string,
|
|
166
|
+
props: Props,
|
|
167
|
+
options?: UseDynamicLiveActivityOptions
|
|
168
|
+
): UseDynamicLiveActivityResult {
|
|
169
|
+
const [activityName, setActivityName] = useState<string | null>(() => {
|
|
170
|
+
if (options?.activityName) {
|
|
171
|
+
return isLiveActivityActive(options.activityName) ? options.activityName : null
|
|
172
|
+
}
|
|
173
|
+
return null
|
|
174
|
+
})
|
|
175
|
+
const propsRef = useRef(props)
|
|
176
|
+
const optionsRef = useRef(options)
|
|
177
|
+
const lastUpdateOptionsRef = useRef<UpdateDynamicLiveActivityOptions | undefined>(undefined)
|
|
178
|
+
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
propsRef.current = props
|
|
181
|
+
}, [props])
|
|
182
|
+
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
optionsRef.current = options
|
|
185
|
+
}, [options])
|
|
186
|
+
|
|
187
|
+
useUpdateOnHMR()
|
|
188
|
+
|
|
189
|
+
const start = useCallback(
|
|
190
|
+
async (startOptions?: StartDynamicLiveActivityOptions) => {
|
|
191
|
+
const id = await startDynamicLiveActivity(definitionId, propsRef.current, {
|
|
192
|
+
...optionsRef.current,
|
|
193
|
+
...startOptions,
|
|
194
|
+
})
|
|
195
|
+
setActivityName(id)
|
|
196
|
+
},
|
|
197
|
+
[definitionId]
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
const update = useCallback(
|
|
201
|
+
async (updateOptions?: UpdateDynamicLiveActivityOptions) => {
|
|
202
|
+
if (!activityName) return
|
|
203
|
+
|
|
204
|
+
const mergedOptions = { ...optionsRef.current, ...updateOptions }
|
|
205
|
+
lastUpdateOptionsRef.current = mergedOptions
|
|
206
|
+
await updateDynamicLiveActivity(activityName, propsRef.current, mergedOptions)
|
|
207
|
+
},
|
|
208
|
+
[activityName]
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
const end = useCallback(
|
|
212
|
+
async (endOptions?: EndLiveActivityOptions) => {
|
|
213
|
+
if (!activityName) return
|
|
214
|
+
|
|
215
|
+
await stopLiveActivity(activityName, endOptions)
|
|
216
|
+
setActivityName(null)
|
|
217
|
+
},
|
|
218
|
+
[activityName]
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
if (options?.autoStart) void start()
|
|
223
|
+
}, [options?.autoStart, start])
|
|
224
|
+
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (options?.autoUpdate) void update(lastUpdateOptionsRef.current)
|
|
227
|
+
}, [options?.autoUpdate, props, update])
|
|
228
|
+
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
if (!activityName) return
|
|
231
|
+
|
|
232
|
+
const subscription = addVoltraListener('stateChange', (event) => {
|
|
233
|
+
if (event.activityName !== activityName) return
|
|
234
|
+
if (event.activityState === 'dismissed' || event.activityState === 'ended') setActivityName(null)
|
|
235
|
+
})
|
|
236
|
+
return () => subscription.remove()
|
|
237
|
+
}, [activityName])
|
|
238
|
+
|
|
239
|
+
return { start, update, end, isActive: activityName !== null }
|
|
240
|
+
}
|
|
@@ -32,6 +32,15 @@ type VoltraInteractionEvent = Readonly<{
|
|
|
32
32
|
payload: string
|
|
33
33
|
}>
|
|
34
34
|
|
|
35
|
+
type VoltraDynamicLiveActivityRenderFailedEvent = Readonly<{
|
|
36
|
+
source: string
|
|
37
|
+
timestamp: number
|
|
38
|
+
type: 'dynamicLiveActivityRenderFailed'
|
|
39
|
+
activityName: string
|
|
40
|
+
definitionId: string
|
|
41
|
+
message: string
|
|
42
|
+
}>
|
|
43
|
+
|
|
35
44
|
type StartVoltraOptions = Readonly<{
|
|
36
45
|
target?: string
|
|
37
46
|
deepLinkUrl?: string
|
|
@@ -86,19 +95,26 @@ type WidgetServerCredentials = Readonly<{
|
|
|
86
95
|
|
|
87
96
|
export interface Spec extends TurboModule {
|
|
88
97
|
readonly onInteraction: CodegenTypes.EventEmitter<VoltraInteractionEvent>
|
|
98
|
+
readonly onDynamicLiveActivityRenderFailed: CodegenTypes.EventEmitter<VoltraDynamicLiveActivityRenderFailedEvent>
|
|
89
99
|
readonly onStateChanged: CodegenTypes.EventEmitter<VoltraActivityUpdateEvent>
|
|
90
100
|
readonly onActivityTokenReceived: CodegenTypes.EventEmitter<VoltraActivityTokenReceivedEvent>
|
|
91
101
|
readonly onActivityPushToStartTokenReceived: CodegenTypes.EventEmitter<VoltraActivityPushToStartTokenReceivedEvent>
|
|
102
|
+
drainDynamicLiveActivityRenderFailures(): void
|
|
103
|
+
setDynamicLiveActivityRenderFailureListenerActive(active: boolean): void
|
|
92
104
|
startLiveActivity(jsonString: string, options: StartVoltraOptions): Promise<string>
|
|
93
105
|
updateLiveActivity(activityId: string, jsonString: string, options: UpdateVoltraOptions): Promise<void>
|
|
106
|
+
startDynamicLiveActivity(definitionId: string, propsJson: string, options: StartVoltraOptions): Promise<string>
|
|
107
|
+
updateDynamicLiveActivity(activityId: string, propsJson: string, options: UpdateVoltraOptions): Promise<void>
|
|
94
108
|
endLiveActivity(activityId: string, options: EndVoltraOptions): Promise<void>
|
|
95
109
|
endAllLiveActivities(): Promise<void>
|
|
96
110
|
getLatestVoltraActivityId(): Promise<string | null>
|
|
97
111
|
listVoltraActivityIds(): Promise<string[]>
|
|
112
|
+
getDynamicLiveActivityDefinitionIds(): Promise<string[]>
|
|
98
113
|
isLiveActivityActive(activityName: string): boolean
|
|
99
114
|
isHeadless(): boolean
|
|
100
115
|
preloadImages(images: PreloadImageOptions[]): Promise<PreloadImagesResult>
|
|
101
116
|
reloadLiveActivities(activityNames?: string[] | null): Promise<void>
|
|
117
|
+
reloadDynamicLiveActivities(definitionIds?: string[] | null): Promise<void>
|
|
102
118
|
clearPreloadedImages(keys?: string[] | null): Promise<void>
|
|
103
119
|
updateDynamicWidget(dynamicWidgetId: string, dynamicWidgetPropsJson: string): Promise<void>
|
|
104
120
|
updateWidget(widgetId: string, jsonString: string, options: UpdateWidgetOptions): Promise<void>
|
|
@@ -107,6 +123,11 @@ export interface Spec extends TurboModule {
|
|
|
107
123
|
clearWidget(widgetId: string): Promise<void>
|
|
108
124
|
clearAllWidgets(): Promise<void>
|
|
109
125
|
getActiveWidgets<T = unknown>(): Promise<T[]>
|
|
126
|
+
/** Settings are passed as JSON so an arbitrary `body` survives the bridge unchanged. */
|
|
127
|
+
setWidgetServerUpdate(settingsJson: string, widgetId?: string | null): Promise<void>
|
|
128
|
+
clearWidgetServerUpdate(widgetId?: string | null): Promise<void>
|
|
129
|
+
/** Result is JSON so an arbitrary `body` survives the bridge unchanged, or null. */
|
|
130
|
+
getWidgetServerUpdate(widgetId?: string | null): Promise<string | null>
|
|
110
131
|
setWidgetServerCredentials(credentials: WidgetServerCredentials): Promise<void>
|
|
111
132
|
clearWidgetServerCredentials(): Promise<void>
|
|
112
133
|
}
|
package/src/types.ts
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getFastRefreshHub } from '@use-voltra/ios'
|
|
2
|
+
|
|
3
|
+
import { getNativeVoltra } from '../VoltraModule.js'
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
var __voltraDynamicLiveActivityDefinitionUpdated: ((definitionId: string) => void) | undefined
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Reload Dynamic Live Activities after a Metro Fast Refresh patch. When Metro
|
|
11
|
+
* re-evaluates a generated definition entry, only that definition is reloaded.
|
|
12
|
+
* Component-only refresh boundaries do not re-evaluate their importer, so those
|
|
13
|
+
* patches fall back to reloading every Dynamic Live Activity definition.
|
|
14
|
+
* Legacy activities and Dynamic Widgets are unaffected.
|
|
15
|
+
*
|
|
16
|
+
* Subscribes to the shared {@link getFastRefreshHub} so multiple `__accept`
|
|
17
|
+
* calls from one save collapse into a single native reload.
|
|
18
|
+
*/
|
|
19
|
+
export function enableDynamicLiveActivityHotReload(): () => void {
|
|
20
|
+
if (!__DEV__) return () => {}
|
|
21
|
+
|
|
22
|
+
const hub = getFastRefreshHub()
|
|
23
|
+
const previousDefinitionUpdated = global.__voltraDynamicLiveActivityDefinitionUpdated
|
|
24
|
+
const updatedDefinitionIds = new Set<string>()
|
|
25
|
+
|
|
26
|
+
global.__voltraDynamicLiveActivityDefinitionUpdated = (definitionId) => {
|
|
27
|
+
// A generated entry re-evaluated inside a patch: batch it so the debounced
|
|
28
|
+
// flush reloads each changed definition exactly once. A signal that arrives
|
|
29
|
+
// outside a patch (the first require of a definition) has no flush coming,
|
|
30
|
+
// so reload it immediately.
|
|
31
|
+
if (hub.isAccepting) {
|
|
32
|
+
updatedDefinitionIds.add(definitionId)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
void getNativeVoltra().reloadDynamicLiveActivities([definitionId])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const unsubscribe = hub.onPatch(() => {
|
|
39
|
+
const definitionIds = Array.from(updatedDefinitionIds)
|
|
40
|
+
updatedDefinitionIds.clear()
|
|
41
|
+
// No definition re-evaluated during this patch: a component-only boundary
|
|
42
|
+
// changed, so fall back to reloading every definition (null).
|
|
43
|
+
void getNativeVoltra().reloadDynamicLiveActivities(definitionIds.length > 0 ? definitionIds : null)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
unsubscribe()
|
|
48
|
+
global.__voltraDynamicLiveActivityDefinitionUpdated = previousDefinitionUpdated
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getFastRefreshHub } from '@use-voltra/ios'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
var __accept: (...args: unknown[]) => void
|
|
5
|
-
}
|
|
3
|
+
import { reloadWidgets } from '../widgets/widget-api.js'
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* Trigger `reloadWidgets()` on every Metro Fast Refresh patch (DEV only).
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Subscribes to the shared {@link getFastRefreshHub}, which wraps the global
|
|
9
|
+
* `__accept` callback Metro fires when a Fast Refresh patch lands in the host
|
|
10
|
+
* app's JS runtime. On each debounced patch it calls
|
|
12
11
|
* `WidgetCenter.shared.reloadAllTimelines()` so WidgetKit re-invokes each widget
|
|
13
12
|
* Provider, which re-fetches the freshest bundle from Metro and renders the
|
|
14
13
|
* updated UI.
|
|
@@ -19,21 +18,15 @@ declare global {
|
|
|
19
18
|
* workflow the dev still relies on WidgetKit's natural lifecycle refresh on
|
|
20
19
|
* app foreground.
|
|
21
20
|
*
|
|
22
|
-
* Call once at app startup. Returns `dispose()` to
|
|
23
|
-
*
|
|
21
|
+
* Call once at app startup. Returns `dispose()` to unsubscribe (rarely needed
|
|
22
|
+
* in practice). No-op in release builds.
|
|
24
23
|
*/
|
|
25
24
|
export function enableWidgetHotReload(): () => void {
|
|
26
25
|
if (!__DEV__) {
|
|
27
26
|
return () => {}
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
global['__accept'] = (...args) => {
|
|
29
|
+
return getFastRefreshHub().onPatch(() => {
|
|
32
30
|
void reloadWidgets()
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return () => {
|
|
37
|
-
global['__accept'] = oldAccept
|
|
38
|
-
}
|
|
31
|
+
})
|
|
39
32
|
}
|