Only notify clients actually affected by a conflict

Bisher bekam jeder Arbeitsplatz, der den Konflikt-Kontakt im Adressbuch hat,
den Hinweis - auch wenn er den Wert gar nicht selbst gepflegt hat.

Jetzt zeigt ein Client eine fremde Konflikt-Notiz nur, wenn er den Kontakt hat
UND sein eigener Feldwert vom uebernommenen (Gewinner-)Wert abweicht. Die
Pruefung laeuft VOR dem Sync (gegen den eigenen Mapping-Snapshot), bevor der
Sync den Stand auf den Gewinner-Wert angleicht.

- FieldConflict/ConflictNotice: stabiler FieldKey zusaetzlich zum Anzeige-Label.
- ContactMerger: GetValue/ValuesEqual per FieldKey (telefon-normalisiert).
- ConflictNotifier.GetPending: Filter "eigener Wert != Gewinner-Wert", bekommt
  StarfaceId -> eigener Kontaktstand.
- MainForm zeigt die Hinweise jetzt vor dem Sync und liefert den eigenen Stand
  aus den Mapping-Snapshots.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 13:01:02 +02:00
co-authored by Claude Opus 4.8
parent cf2970a41a
commit 257b6fb33d
7 changed files with 98 additions and 47 deletions
+16 -10
View File
@@ -361,6 +361,11 @@ namespace StarfaceOutlookSync.UI
return;
}
// Konflikt-Hinweise von ANDEREN Arbeitsplaetzen anzeigen, BEVOR der
// Sync den eigenen Stand auf den Gewinner-Wert aktualisiert (sonst
// koennte man nicht mehr erkennen, ob man betroffen war).
ShowRemoteConflictNotices(sharedDir);
SetStatus($"Synchronisiere '{profile.Name}'...");
_trayIcon.ShowBalloonTip(2000, "Starface Sync",
$"Synchronisiere '{profile.Name}'...", ToolTipIcon.Info);
@@ -388,9 +393,6 @@ namespace StarfaceOutlookSync.UI
_conflictNotifier.Write(sharedDir, result.Conflicts);
}
// Konflikt-Hinweise von ANDEREN Arbeitsplaetzen (zu meinen Kontakten) anzeigen.
ShowRemoteConflictNotices(sharedDir);
SetStatus(msg);
}
catch (Exception ex)
@@ -456,7 +458,7 @@ namespace StarfaceOutlookSync.UI
if (string.IsNullOrWhiteSpace(sharedDir)) return;
try
{
var pending = _conflictNotifier.GetPending(sharedDir, AllMappedStarfaceIds());
var pending = _conflictNotifier.GetPending(sharedDir, MyContactsByStarfaceId());
if (pending.Count == 0) return;
var detail = string.Join("\n", pending.Take(5).Select(p => p.ToString()));
@@ -468,19 +470,23 @@ namespace StarfaceOutlookSync.UI
catch { }
}
/// <summary>Alle von diesem Client gemappten StarfaceIds (ueber alle Profile).</summary>
private HashSet<string> AllMappedStarfaceIds()
/// <summary>
/// Eigener Kontaktstand je StarfaceId (ueber alle Profile), aus den
/// Mapping-Snapshots. Dient dazu, bei Konflikt-Notizen zu pruefen, ob der
/// eigene Feldwert ueberhaupt vom uebernommenen Wert abweicht.
/// </summary>
private Dictionary<string, UnifiedContact> MyContactsByStarfaceId()
{
var ids = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var map = new Dictionary<string, UnifiedContact>(StringComparer.OrdinalIgnoreCase);
try
{
foreach (var p in _profileManager.GetProfiles())
foreach (var m in _profileManager.GetMappings(p.Id))
if (!string.IsNullOrEmpty(m.StarfaceId))
ids.Add(m.StarfaceId);
if (!string.IsNullOrEmpty(m.StarfaceId) && !map.ContainsKey(m.StarfaceId))
map[m.StarfaceId] = m.LastOutlook;
}
catch { }
return ids;
return map;
}
private void SetStatus(string text)