diff --git a/Directory.Build.props b/Directory.Build.props index 949a79009..e0d27f3d9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ enable enable true - 5.0.0.75 + 5.0.0.78 Debug;Release;SourceGen Highlighting AnyCPU true diff --git a/build-tools/linux-x64/BuildAppSettings b/build-tools/linux-x64/BuildAppSettings index 45ce8ddd2..e9b810ae0 100755 Binary files a/build-tools/linux-x64/BuildAppSettings and b/build-tools/linux-x64/BuildAppSettings differ diff --git a/build-tools/linux-x64/BuildAppSettings.dll b/build-tools/linux-x64/BuildAppSettings.dll index 95b942a48..f3f7d80f9 100644 Binary files a/build-tools/linux-x64/BuildAppSettings.dll and b/build-tools/linux-x64/BuildAppSettings.dll differ diff --git a/build-tools/linux-x64/BuildTemplates b/build-tools/linux-x64/BuildTemplates index 38b514fdb..26c8afa3f 100755 Binary files a/build-tools/linux-x64/BuildTemplates and b/build-tools/linux-x64/BuildTemplates differ diff --git a/build-tools/linux-x64/BuildTemplates.dll b/build-tools/linux-x64/BuildTemplates.dll index 2952335bb..449142278 100644 Binary files a/build-tools/linux-x64/BuildTemplates.dll and b/build-tools/linux-x64/BuildTemplates.dll differ diff --git a/build-tools/linux-x64/ConsoleDialog b/build-tools/linux-x64/ConsoleDialog index 636dd088d..9688d04bf 100755 Binary files a/build-tools/linux-x64/ConsoleDialog and b/build-tools/linux-x64/ConsoleDialog differ diff --git a/build-tools/linux-x64/ConsoleDialog.dll b/build-tools/linux-x64/ConsoleDialog.dll index 032fb3525..fa878f1e7 100644 Binary files a/build-tools/linux-x64/ConsoleDialog.dll and b/build-tools/linux-x64/ConsoleDialog.dll differ diff --git a/build-tools/linux-x64/ESBuild b/build-tools/linux-x64/ESBuild index 41912328b..9697cf550 100755 Binary files a/build-tools/linux-x64/ESBuild and b/build-tools/linux-x64/ESBuild differ diff --git a/build-tools/linux-x64/ESBuild.dll b/build-tools/linux-x64/ESBuild.dll index 08a551630..6afa44ea6 100644 Binary files a/build-tools/linux-x64/ESBuild.dll and b/build-tools/linux-x64/ESBuild.dll differ diff --git a/build-tools/linux-x64/ESBuildClearLocks b/build-tools/linux-x64/ESBuildClearLocks index bb0a0a176..e51daae4c 100755 Binary files a/build-tools/linux-x64/ESBuildClearLocks and b/build-tools/linux-x64/ESBuildClearLocks differ diff --git a/build-tools/linux-x64/ESBuildClearLocks.dll b/build-tools/linux-x64/ESBuildClearLocks.dll index abfa93d26..9477d1aa4 100644 Binary files a/build-tools/linux-x64/ESBuildClearLocks.dll and b/build-tools/linux-x64/ESBuildClearLocks.dll differ diff --git a/build-tools/linux-x64/FetchNuGetVersion b/build-tools/linux-x64/FetchNuGetVersion index 077ee4a1f..23e5b6523 100755 Binary files a/build-tools/linux-x64/FetchNuGetVersion and b/build-tools/linux-x64/FetchNuGetVersion differ diff --git a/build-tools/linux-x64/FetchNuGetVersion.dll b/build-tools/linux-x64/FetchNuGetVersion.dll index ad6a822ce..36c28045e 100644 Binary files a/build-tools/linux-x64/FetchNuGetVersion.dll and b/build-tools/linux-x64/FetchNuGetVersion.dll differ diff --git a/build-tools/linux-x64/GBTest b/build-tools/linux-x64/GBTest index 9b0d563e7..a8aff4498 100755 Binary files a/build-tools/linux-x64/GBTest and b/build-tools/linux-x64/GBTest differ diff --git a/build-tools/linux-x64/GBTest.dll b/build-tools/linux-x64/GBTest.dll index 3c4b2d934..bb1bd89a3 100644 Binary files a/build-tools/linux-x64/GBTest.dll and b/build-tools/linux-x64/GBTest.dll differ diff --git a/build-tools/linux-x64/RazorCopy b/build-tools/linux-x64/RazorCopy index 9c59a3ed6..193ac0f86 100755 Binary files a/build-tools/linux-x64/RazorCopy and b/build-tools/linux-x64/RazorCopy differ diff --git a/build-tools/linux-x64/RazorCopy.dll b/build-tools/linux-x64/RazorCopy.dll index a92417c1d..966b47df2 100644 Binary files a/build-tools/linux-x64/RazorCopy.dll and b/build-tools/linux-x64/RazorCopy.dll differ diff --git a/build-tools/linux-x64/Utilities.dll b/build-tools/linux-x64/Utilities.dll index 8baf0b956..d89f2b9d0 100644 Binary files a/build-tools/linux-x64/Utilities.dll and b/build-tools/linux-x64/Utilities.dll differ diff --git a/src/dymaptic.GeoBlazor.Core/Components/Layers/Layer.cs b/src/dymaptic.GeoBlazor.Core/Components/Layers/Layer.cs index eafe7685d..c76a3ebfe 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Layers/Layer.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Layers/Layer.cs @@ -446,7 +446,7 @@ public async Task UpdateLayer() _delayedUpdate = false; // ReSharper disable once RedundantCast - await JsComponentReference!.InvokeAsync("updateComponent", CancellationTokenSource.Token, + await JsComponentReference.InvokeAsync("updateComponent", CancellationTokenSource.Token, (object)this); } diff --git a/src/dymaptic.GeoBlazor.Core/Components/MapComponent.razor.cs b/src/dymaptic.GeoBlazor.Core/Components/MapComponent.razor.cs index 587f6ae31..c06259794 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/MapComponent.razor.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/MapComponent.razor.cs @@ -123,7 +123,7 @@ public Guid? ViewId /// The Id of the relevant Layer for the MapComponent. Not always applicable to every component type. /// [Parameter] - public Guid? LayerId { get; set; } + public virtual Guid? LayerId { get; set; } /// /// Indicates the visibility of the component. Default value: true. @@ -884,6 +884,18 @@ protected override async Task OnInitializedAsync() } } + /// + protected override void OnParametersSet() + { + base.OnParametersSet(); + LayerId ??= Layer?.Id; + Layer ??= Parent?.Layer; + View ??= Parent?.View; + CoreJsModule ??= Parent?.CoreJsModule; + ProJsModule ??= Parent?.ProJsModule; + } + + /// protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -1087,7 +1099,7 @@ private void CopyProperty(PropertyInfo prop, MapComponent deserializedComponent) /// /// Previously updated parent components /// - protected internal void UpdateGeoBlazorReferences(IJSObjectReference coreJsModule, IJSObjectReference? proJsModule, + protected virtual internal void UpdateGeoBlazorReferences(IJSObjectReference coreJsModule, IJSObjectReference? proJsModule, MapView? view, MapComponent? parent, Layer? layer, int depth = 0, HashSet? visited = null) { visited ??= new HashSet(); diff --git a/src/dymaptic.GeoBlazor.Core/Components/Views/MapView.razor.cs b/src/dymaptic.GeoBlazor.Core/Components/Views/MapView.razor.cs index 667b7c5b5..723c50e98 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Views/MapView.razor.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Views/MapView.razor.cs @@ -280,7 +280,7 @@ public async Task OnJavascriptError(JavascriptError error) if (IsDisposed) return; #if DEBUG ErrorMessage = error.Message?.Replace("\n", "
") ?? error.Stack; - StateHasChanged(); + await InvokeAsync(StateHasChanged); #endif var exception = new JavascriptException(error); @@ -1216,14 +1216,12 @@ public async Task OnJavascriptLayerViewCreateError(LayerViewCreateErrorEvent err #region Public Methods /// - public override ValueTask Refresh() + public override async ValueTask Refresh() { NeedsRender = true; ExtentSetByCode = false; ExtentChangedInJs = false; - StateHasChanged(); - - return ValueTask.CompletedTask; + await InvokeAsync(StateHasChanged); } /// @@ -1234,7 +1232,7 @@ public void Load() { _renderCalled = true; NeedsRender = true; - StateHasChanged(); + InvokeAsync(StateHasChanged); } /// @@ -1525,7 +1523,7 @@ public async Task ClearGraphics() /// /// If true, adds the layer as a Basemap Reference Layer. /// - public Task AddLayer(Layer layer, bool isBasemapLayer = false, bool isBasemapReferenceLayer = false) + public async Task AddLayer(Layer layer, bool isBasemapLayer = false, bool isBasemapReferenceLayer = false) { if (isBasemapLayer && layer.IsBasemapReferenceLayer != true) { @@ -1553,12 +1551,13 @@ public Task AddLayer(Layer layer, bool isBasemapLayer = false, bool isBasemapRef layer.View ??= this; - if (CoreJsModule is null || !MapRendered) return Task.CompletedTask; + if (CoreJsModule is null || !MapRendered) + { + return; + } _newLayers.Add((layer, isBasemapLayer, isBasemapReferenceLayer)); - StateHasChanged(); - - return Task.CompletedTask; + await InvokeAsync(StateHasChanged); } /// @@ -2353,7 +2352,7 @@ public void OnJavascriptHitTestResult(Guid eventId, string chunk) /// /// Adds a widget to the view. /// - public Task AddWidget(Widget widget) + public async Task AddWidget(Widget widget) { if (!_widgets.Contains(widget)) { @@ -2361,12 +2360,13 @@ public Task AddWidget(Widget widget) widget.UpdateGeoBlazorReferences(CoreJsModule!, ProJsModule, View, this, null); } - if (CoreJsModule is null || !widget.ArcGISWidget || !MapRendered) return Task.CompletedTask; + if (CoreJsModule is null || !widget.ArcGISWidget || !MapRendered) + { + return; + } _newWidgets.Add(widget); - StateHasChanged(); - - return Task.CompletedTask; + await InvokeAsync(StateHasChanged); } /// @@ -2538,7 +2538,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) catch (Exception ex) { ErrorMessage = ex.Message; - StateHasChanged(); + await InvokeAsync(StateHasChanged); throw; } @@ -2583,7 +2583,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } _firstRenderComplete = true; - StateHasChanged(); + await InvokeAsync(StateHasChanged); return; } @@ -2600,6 +2600,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (MapRendered) { + // layers added in code can have race conditions that require another render before adding to the map, + // so we store them in AddLayer and then add them here after the main render if (_newLayers.Any()) { (Layer Layer, bool IsBasemapLayer, bool IsBasemapReferenceLayer)[] newLayers = _newLayers.ToArray(); @@ -2609,6 +2611,17 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { await CoreJsModule!.InvokeVoidAsync("addLayer", CancellationTokenSource.Token, (object)newLayer, Map?.Id, Id, isBasemapLayer, isBasemapReferenceLayer); + + // this updates widgets that were assigned a LayerId instead of a Layer + if (_widgets.Where(w => w.LayerId == newLayer.Id).ToList() is { Count: > 0 } widgetMatches) + { + foreach (Widget widget in widgetMatches) + { + // set layer to null forces widget to update and call JavaScript + widget.Layer = null; + widget.UpdateGeoBlazorReferences(CoreJsModule!, ProJsModule, this, this, newLayer); + } + } } } diff --git a/src/dymaptic.GeoBlazor.Core/Components/Widgets/Widget.razor.cs b/src/dymaptic.GeoBlazor.Core/Components/Widgets/Widget.razor.cs index c02c5dee5..51d291f2c 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Widgets/Widget.razor.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Widgets/Widget.razor.cs @@ -454,6 +454,22 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } } + /// + protected internal override void UpdateGeoBlazorReferences(IJSObjectReference coreJsModule, IJSObjectReference? proJsModule, + MapView? view, MapComponent? parent, Layer? layer, int depth = 0, HashSet? visited = null) + { + bool needsUpdate = false; + if (layer is not null && layer.Id != Layer?.Id) + { + needsUpdate = true; + } + base.UpdateGeoBlazorReferences(coreJsModule, proJsModule, view, parent, layer, depth, visited); + if (needsUpdate) + { + InvokeAsync(UpdateWidget); + } + } + /// /// Updates the widget internally. Not intended for public use. /// @@ -477,7 +493,7 @@ protected async Task UpdateWidget() try { - JsComponentReference ??= await CoreJsModule!.InvokeAsync( + JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); } catch @@ -491,7 +507,7 @@ protected async Task UpdateWidget() } // ReSharper disable once RedundantCast - await JsComponentReference!.InvokeVoidAsync("updateComponent", CancellationTokenSource.Token, (object)this); + await JsComponentReference.InvokeVoidAsync("updateComponent", CancellationTokenSource.Token, (object)this); } private bool _externalWidgetRegistered; diff --git a/test/dymaptic.GeoBlazor.Core.Test.Blazor.Shared/Components/TestRunnerBase.razor.cs b/test/dymaptic.GeoBlazor.Core.Test.Blazor.Shared/Components/TestRunnerBase.razor.cs index b1b49ce90..74e2c13f0 100644 --- a/test/dymaptic.GeoBlazor.Core.Test.Blazor.Shared/Components/TestRunnerBase.razor.cs +++ b/test/dymaptic.GeoBlazor.Core.Test.Blazor.Shared/Components/TestRunnerBase.razor.cs @@ -24,7 +24,7 @@ public TestRunnerBase() var retryStrategyOptions = new RetryStrategyOptions { BackoffType = DelayBackoffType.Exponential, - MaxRetryAttempts = 3, + MaxRetryAttempts = 2, // one retry is usually plenty, two just in case Delay = TimeSpan.FromSeconds(1), ShouldHandle = new PredicateBuilder().Handle(ex => !ex.Message.Contains("Invalid GeoBlazor registration key") @@ -34,6 +34,10 @@ public TestRunnerBase() && !ex.Message.Contains("Map component view is in an invalid state")), OnRetry = async args => { + if (args.Context.OperationKey is null) + { + return; + } await CleanupTest(args.Context.OperationKey!, true); _testResults[args.Context.OperationKey!] = $"Attempt {args.AttemptNumber + 1} failed. Retrying..."; }