feat: add retained underlay runtime controls

This commit is contained in:
tei
2026-09-22 16:18:58 +09:00
parent 9ad2fe5d58
commit 8384c8bd24
31 changed files with 1181 additions and 59 deletions

View File

@@ -63,6 +63,21 @@ class WeatherRuntimeBridgeModule(private val context: ReactApplicationContext) :
}
@ReactMethod override fun getState(reactTag: Double, promise: Promise) = withRuntime(reactTag, promise) { runtime -> promise.resolve(snapshot(runtime)) }
@ReactMethod override fun setRetainedUnderlayEnabled(reactTag: Double, enabled: Boolean, promise: Promise) = withMap(reactTag, promise) { map ->
map.setRetainedUnderlayEnabled(enabled)
promise.resolve("ACCEPTED")
}
@ReactMethod override fun getRetainedUnderlayDiagnostics(reactTag: Double, promise: Promise) = withMap(reactTag, promise) { map ->
val value = map.retainedUnderlayDiagnostics
promise.resolve(Arguments.createMap().apply {
putBoolean("enabled", value.enabled)
putDouble("build", value.build.toDouble())
putDouble("reuse", value.reuse.toDouble())
putDouble("fallback", value.fallback.toDouble())
putDouble("invalidation", value.invalidation.toDouble())
putString("lastReason", value.lastReason)
})
}
@ReactMethod override fun clearWindFields(reactTag: Double, promise: Promise) = command(reactTag, promise) { it.clearWindFields() }
@ReactMethod override fun clearFields(reactTag: Double, promise: Promise) = command(reactTag, promise) { it.clearFields() }

View File

@@ -158,6 +158,20 @@ RCT_EXPORT_MODULE(WeatherRuntimeBridge)
[self withMapForTag:reactTag requireRuntime:YES resolve:resolve reject:reject action:^(MLNMapView *map, MLNParticleFieldRuntime *runtime) { [map disposeParticleFieldRuntime]; resolve(nil); }];
}
- (void)getState:(double)reactTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self withMapForTag:reactTag requireRuntime:YES resolve:resolve reject:reject action:^(MLNMapView *map, MLNParticleFieldRuntime *runtime) { resolve(snapshot(runtime)); }]; }
- (void)setRetainedUnderlayEnabled:(double)reactTag enabled:(BOOL)enabled resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self withMapForTag:reactTag requireRuntime:NO resolve:resolve reject:reject action:^(MLNMapView *map, MLNParticleFieldRuntime *runtime) {
map.retainedUnderlayEnabled = enabled;
resolve(@"ACCEPTED");
}];
}
- (void)getRetainedUnderlayDiagnostics:(double)reactTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self withMapForTag:reactTag requireRuntime:NO resolve:resolve reject:reject action:^(MLNMapView *map, MLNParticleFieldRuntime *runtime) {
MLNRetainedUnderlayDiagnostics *value = map.retainedUnderlayDiagnostics;
resolve(@{ @"enabled": @(value.enabled), @"build": @(value.build), @"reuse": @(value.reuse),
@"fallback": @(value.fallback), @"invalidation": @(value.invalidation),
@"lastReason": value.lastReason });
}];
}
- (void)command:(double)tag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject action:(MLNParticleFieldRuntimeCommandResult (^)(MLNParticleFieldRuntime *, NSError **))action {
[self withMapForTag:tag requireRuntime:YES resolve:resolve reject:reject action:^(MLNMapView *map, MLNParticleFieldRuntime *runtime) { NSError *error = nil; MLNParticleFieldRuntimeCommandResult result = action(runtime, &error); resolve(error ? @"REJECTED" : commandString(result)); }];

View File

@@ -9,10 +9,13 @@ type FieldSubmitResult = "REJECTED_SOURCE_MODE" | "REJECTED_ADAPTER" | "REJECTED
type EncodedFieldSubmitResult = "ACCEPTED_INSERTED" | "ACCEPTED_REPLACED_REVISION" | "DUPLICATE" | "REJECTED_DECODE" | "REJECTED_CONTRACT" | "REJECTED_INVALID_FIELD_KEY" | "REJECTED_IDENTITY_CONFLICT" | "REJECTED_STALE_REVISION" | "REJECTED_INCOMPATIBLE" | "REJECTED_CAPACITY";
type ColorStop = { speedMps: number; red: number; green: number; blue: number; alpha: number };
type RuntimeSnapshot = { state: RuntimeState; playbackState: PlaybackState; targetFps: TargetFps; disposed: boolean };
type RetainedUnderlaySnapshot = { enabled: boolean; build: number; reuse: number; fallback: number; invalidation: number; lastReason: string };
export interface Spec extends TurboModule {
attach(reactTag: number): Promise<RuntimeSnapshot>;
detach(reactTag: number): Promise<void>;
getState(reactTag: number): Promise<RuntimeSnapshot>;
setRetainedUnderlayEnabled(reactTag: number, enabled: boolean): Promise<CommandResult>;
getRetainedUnderlayDiagnostics(reactTag: number): Promise<RetainedUnderlaySnapshot>;
clearWindFields(reactTag: number): Promise<CommandResult>;
submitWindField(reactTag: number, metadataJson: string, payloadFileUri: string): Promise<FieldSubmitResult>;
clearFields(reactTag: number): Promise<CommandResult>;

View File

@@ -0,0 +1,18 @@
import { getNativeFieldRuntimeBridge } from "./NativeWeatherRuntimeBridge";
import type { CommandResult, RetainedUnderlayDiagnostics } from "./types";
function nativeBridge() {
const bridge = getNativeFieldRuntimeBridge();
if (bridge == null) {
throw new Error("@navsea/maplibre-particle-rn RendererRuntime requires the WeatherRuntimeBridge native module");
}
return bridge;
}
/** Pull-based, map-scoped renderer controls. No renderer cache state is owned by React. */
export const RendererRuntime = {
setRetainedUnderlayEnabled: (reactTag: number, enabled: boolean): Promise<CommandResult> =>
nativeBridge().setRetainedUnderlayEnabled(reactTag, enabled),
getRetainedUnderlayDiagnostics: (reactTag: number): Promise<RetainedUnderlayDiagnostics> =>
nativeBridge().getRetainedUnderlayDiagnostics(reactTag),
} as const;

View File

@@ -1,4 +1,5 @@
export { FieldRuntime } from "./FieldRuntime";
export { RendererRuntime } from "./RendererRuntime";
export type {
ColorChannel,
CommandResult,
@@ -14,6 +15,7 @@ export type {
RequiredPlaneContractInput,
ResolvedFieldProfileContractInput,
RuntimeSnapshot,
RetainedUnderlayDiagnostics,
RuntimeState,
TargetFps,
} from "./types";

View File

@@ -21,3 +21,11 @@ export type FieldValueSource = "SCALAR" | "VECTOR_MAGNITUDE";
export type ColorChannel = number;
export type FieldColorStop = Readonly<{ value: number; red: ColorChannel; green: ColorChannel; blue: ColorChannel; alpha: ColorChannel }>;
export type RuntimeSnapshot = Readonly<{ state: RuntimeState; playbackState: PlaybackState; targetFps: TargetFps; disposed: boolean }>;
export type RetainedUnderlayDiagnostics = Readonly<{
enabled: boolean;
build: number;
reuse: number;
fallback: number;
invalidation: number;
lastReason: string;
}>;