Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

Commit bd9ad28

Browse files
Fix issue with removing nodes in new ways, preserve existing nodes when merging with a new node, and fix changeset link when uploading to dev server
1 parent 3988c51 commit bd9ad28

10 files changed

Lines changed: 120 additions & 17 deletions

Alidade.Osm/Handlers/Editing/DeleteNode.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,37 @@ public Task<CommandResult> Handle(Command request, CancellationToken cancellatio
2525
? state.Nodes.Remove(request.NodeId)
2626
: state.Nodes;
2727

28-
editBufferState.SetState(state with { Nodes = nodes, EditStates = es });
28+
ImmutableDictionary<long, OsmWay> ways = state.Ways;
29+
foreach (OsmWay way in state.Ways.Values)
30+
{
31+
if (!way.NodeIds.Contains(request.NodeId))
32+
{
33+
continue;
34+
}
35+
36+
IReadOnlyList<long> cleaned = [.. way.NodeIds.Where(id => id != request.NodeId)];
37+
OsmElementRef wayRef = way.Ref;
38+
state.EditStates.TryGetValue(wayRef, out EditState wayState);
39+
40+
if (cleaned.Count < 2)
41+
{
42+
if (wayState == EditState.Created)
43+
{
44+
ways = ways.Remove(way.Id);
45+
es = es.Remove(wayRef);
46+
}
47+
else
48+
{
49+
es = es.SetItem(wayRef, EditState.Deleted);
50+
}
51+
}
52+
else
53+
{
54+
ways = ways.SetItem(way.Id, way with { NodeIds = cleaned });
55+
}
56+
}
57+
58+
editBufferState.SetState(state with { Nodes = nodes, Ways = ways, EditStates = es });
2959
return Task.FromResult(CommandResult.Pass());
3060
}
3161
}

Alidade.Osm/Handlers/Editing/MergeNodes.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,53 @@ public Task<CommandResult> Handle(Command request, CancellationToken cancellatio
2020
return Task.FromResult(CommandResult.Pass());
2121
}
2222

23+
// When one node is local (negative) and one is on the server (positive), always keep the server node.
24+
long sourceId = request.SourceNodeId > 0 && request.TargetNodeId < 0
25+
? request.TargetNodeId
26+
: request.SourceNodeId;
27+
long targetId = sourceId == request.SourceNodeId ? request.TargetNodeId : request.SourceNodeId;
28+
2329
ImmutableDictionary<long, OsmWay> ways = state.Ways;
2430
ImmutableDictionary<OsmElementRef, EditState> editStates = state.EditStates;
2531

2632
foreach ((long wayId, OsmWay way) in state.Ways)
2733
{
28-
if (!way.NodeIds.Contains(request.SourceNodeId)) { continue; }
29-
long[] newNodeIds = [.. way.NodeIds.Select(id => id == request.SourceNodeId ? request.TargetNodeId : id)];
34+
if (!way.NodeIds.Contains(sourceId))
35+
{
36+
continue;
37+
}
38+
39+
IReadOnlyList<long> newNodeIds = DeduplicateConsecutive([.. way.NodeIds.Select(id => id == sourceId ? targetId : id)]);
3040
OsmWay updated = way with { NodeIds = newNodeIds };
3141
ways = ways.SetItem(wayId, updated);
42+
3243
EditState es = editStates.TryGetValue(updated.Ref, out EditState ex) && ex == EditState.Created
3344
? EditState.Created
3445
: EditState.Modified;
3546
editStates = editStates.SetItem(updated.Ref, es);
3647
}
3748

38-
OsmElementRef sourceRef = new(OsmElementTypes.Node, request.SourceNodeId);
49+
OsmElementRef sourceRef = new(OsmElementTypes.Node, sourceId);
3950
editBufferState.SetState(state with
4051
{
41-
Nodes = state.Nodes.Remove(request.SourceNodeId),
52+
Nodes = state.Nodes.Remove(sourceId),
4253
Ways = ways,
4354
EditStates = editStates.Remove(sourceRef)
4455
});
4556
return Task.FromResult(CommandResult.Pass());
4657
}
58+
59+
private static IReadOnlyList<long> DeduplicateConsecutive(IReadOnlyList<long> ids)
60+
{
61+
List<long> result = [];
62+
foreach (long id in ids)
63+
{
64+
if (result.Count == 0 || result[^1] != id)
65+
{
66+
result.Add(id);
67+
}
68+
}
69+
70+
return result;
71+
}
4772
}

Alidade/Components/Dialogs/ChangesetUploadDialog.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
@foreach (int id in _uploadedChangesetIds)
2727
{
2828
<li>
29-
<a href="https://www.openstreetmap.org/changeset/@id" target="_blank">
29+
<a href="@ActiveOsmBaseUrl/changeset/@id" target="_blank">
3030
Changeset @id
3131
</a>
3232
</li>

Alidade/Components/Dialogs/ChangesetUploadDialog.razor.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ await mediator.Publish(
132132
}
133133
}
134134

135+
private string ActiveOsmBaseUrl
136+
=> ApiEndpointCatalog.Endpoints[settingsState.State.ActiveEndpoint].OsmBaseUrl;
137+
135138
private static IReadOnlyList<OsmElementRef> CollectRefs(OsmChange change)
136139
{
137140
List<OsmElementRef> refs = [];

Alidade/Components/Dialogs/SwitchAccountDialog.razor

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@
1414
</p>
1515
</div>
1616
<div class="dialog-actions">
17-
<button class="btn btn-primary" @onclick="Upload">Upload Changes</button>
17+
@if (ActiveEndpointState == EndpointState.Enabled)
18+
{
19+
<button class="btn btn-primary" @onclick="Upload">Upload Changes</button>
20+
}
21+
else {
22+
<button class="btn btn-primary" disabled>Upload Changes</button>
23+
}
24+
1825
<button class="btn btn-danger" @onclick="() => Discard(pending)">Discard Changes</button>
1926
<button class="btn btn-secondary" @onclick="Cancel">Cancel</button>
2027
</div>
Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,47 @@
1+
using Alidade.Handlers.Auth;
2+
using Alidade.Handlers.Map;
3+
14
namespace Alidade.Components.Dialogs;
25

36
/// <summary>
47
/// Dialog shown when the user attempts to switch accounts while the edit buffer is dirty.
58
/// </summary>
6-
public partial class SwitchAccountDialog(AuthStateService authState, IMediator mediator) : IDisposable
9+
public partial class SwitchAccountDialog(AuthStateService authState, SettingsStateService settingsState, IMediator mediator)
10+
: IDisposable
711
{
12+
private EndpointState ActiveEndpointState
13+
=> ApiEndpointCatalog.Endpoints[settingsState.State.ActiveEndpoint].State;
14+
815
/// <inheritdoc />
916
protected override void OnInitialized()
1017
{
1118
authState.StateChanged += OnStateChanged;
19+
settingsState.StateChanged += OnStateChanged;
1220
}
1321

1422
private void OnStateChanged(object? sender, EventArgs e) => StateHasChanged();
1523

1624
private void Upload()
1725
{
1826
// Open the upload dialog; once the buffer is clean the user can retry the switch.
19-
_ = mediator.Send(new Handlers.Map.ToggleUploadDialog.Command());
27+
_ = mediator.Send(new ToggleUploadDialog.Command());
28+
29+
// Cancel the pending account switch
30+
_ = mediator.Send(new CancelAccountSwitch.Command());
2031
}
2132

2233
private void Discard(StoredAccount pending)
2334
{
24-
_ = mediator.Send(new Handlers.Auth.ConfirmAccountSwitch.Command(pending));
35+
_ = mediator.Send(new ConfirmAccountSwitch.Command(pending));
2536
}
2637

2738
private void Cancel()
28-
=> _ = mediator.Send(new Handlers.Auth.CancelAccountSwitch.Command());
39+
=> _ = mediator.Send(new CancelAccountSwitch.Command());
2940

3041
/// <inheritdoc />
3142
public void Dispose()
32-
=> authState.StateChanged -= OnStateChanged;
43+
{
44+
authState.StateChanged -= OnStateChanged;
45+
settingsState.StateChanged -= OnStateChanged;
46+
}
3347
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.btn:disabled {
2+
opacity: 0.4;
3+
cursor: not-allowed;
4+
}

Alidade/Components/Dialogs/SwitchEndpointDialog.razor

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@
1414
</p>
1515
</div>
1616
<div class="dialog-actions">
17-
<button class="btn btn-primary" @onclick="Upload">Upload Changes</button>
17+
@if (ActiveEndpointState == EndpointState.Enabled)
18+
{
19+
<button class="btn btn-primary" @onclick="Upload">Upload Changes</button>
20+
}
21+
else {
22+
<button class="btn btn-primary" disabled>Upload Changes</button>
23+
}
24+
1825
<button class="btn btn-danger" @onclick="() => Discard(pending)">Discard Changes</button>
1926
<button class="btn btn-secondary" @onclick="Cancel">Cancel</button>
2027
</div>

Alidade/Components/Dialogs/SwitchEndpointDialog.razor.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
using Alidade.Handlers.Map;
2+
using Alidade.Handlers.Settings;
3+
14
namespace Alidade.Components.Dialogs;
25

36
/// <summary>
47
/// Dialog shown when the user attempts to switch API endpoints with a non-empty edit buffer.
58
/// </summary>
69
public partial class SwitchEndpointDialog(SettingsStateService settingsState, IMediator mediator) : IDisposable
710
{
11+
private EndpointState ActiveEndpointState
12+
=> ApiEndpointCatalog.Endpoints[settingsState.State.ActiveEndpoint].State;
13+
814
/// <inheritdoc />
915
protected override void OnInitialized()
1016
{
@@ -17,18 +23,21 @@ private void Upload()
1723
{
1824
// Open the upload dialog. ApplyDiffResult handler detects the pending switch
1925
// and auto-confirms via ConfirmEndpointSwitch once the buffer is clean.
20-
_ = mediator.Send(new Handlers.Map.ToggleUploadDialog.Command());
26+
_ = mediator.Send(new ToggleUploadDialog.Command());
27+
28+
// Cancel the pending endpoint switch
29+
_ = mediator.Send(new CancelEndpointSwitch.Command());
2130
}
2231

2332
private void Discard(ApiEndpoints pending)
2433
{
25-
_ = mediator.Send(new Handlers.EditBuffer.ClearEditBuffer.Command());
34+
_ = mediator.Send(new ClearEditBuffer.Command());
2635
// ConfirmEndpointSwitch handles the auth transition
27-
_ = mediator.Send(new Handlers.Settings.ConfirmEndpointSwitch.Command(pending));
36+
_ = mediator.Send(new ConfirmEndpointSwitch.Command(pending));
2837
}
2938

3039
private void Cancel()
31-
=> _ = mediator.Send(new Handlers.Settings.CancelEndpointSwitch.Command());
40+
=> _ = mediator.Send(new CancelEndpointSwitch.Command());
3241

3342
/// <inheritdoc />
3443
public void Dispose()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.btn:disabled {
2+
opacity: 0.4;
3+
cursor: not-allowed;
4+
}

0 commit comments

Comments
 (0)