Propagate deletions in bidirectional sync via baseline tombstone
Im Both-Modus wurde ein auf einer Seite geloeschter Kontakt bisher auf der anderen Seite einfach wieder angelegt, statt die Loeschung zu spiegeln. Jetzt wird anhand der gespeicherten Baseline (LastOutlookHash / LastStarfaceHash) entschieden: - Gegenseite seit letztem Sync unveraendert -> es war eine Loeschung -> auf der anderen Seite ebenfalls loeschen. - Gegenseite wurde geaendert -> Bearbeitung gewinnt -> neu anlegen (kein Datenverlust). In den Ein-Richtungs-Modi bleibt die Quelle fuehrend: eine Loeschung im Ziel wird aus der Quelle wiederhergestellt (StarfaceToOutlook legt einen in Outlook geloeschten Kontakt jetzt ebenfalls wieder an statt ein totes Mapping zu behalten). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,13 @@ Versionsschema ist `x.x.x.x` (siehe `release.sh`).
|
||||
|
||||
### Geaendert
|
||||
|
||||
- **Bidirektionale Loeschungen** werden jetzt erkannt. Wird ein Kontakt auf
|
||||
einer Seite geloescht und ist die andere Seite seit dem letzten Sync
|
||||
unveraendert (Abgleich ueber die gespeicherte Baseline), wird die Loeschung
|
||||
auf die andere Seite gespiegelt – statt den Kontakt wieder anzulegen. Wurde
|
||||
die andere Seite zwischenzeitlich bearbeitet, gewinnt die Bearbeitung und der
|
||||
Kontakt wird neu angelegt (kein Datenverlust). In den Ein-Richtungs-Modi
|
||||
bleibt die jeweilige Quelle fuehrend (Loeschung im Ziel wird wiederhergestellt).
|
||||
- Starface-Kontaktdetails werden beim Laden parallel abgerufen (8 gleichzeitig)
|
||||
statt einzeln nacheinander – deutlich schneller bei grossen Adressbuechern.
|
||||
- `UpdateContact` (Outlook) und `UpdateContactAsync` (Starface) geben jetzt den
|
||||
|
||||
@@ -163,19 +163,46 @@ namespace StarfaceOutlookSync.Services
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wirklich geloescht -> in Starface auch loeschen
|
||||
if (profile.SyncDirection == SyncDirection.Both || profile.SyncDirection == SyncDirection.OutlookToStarface)
|
||||
// Wirklich in Outlook geloescht.
|
||||
if (profile.SyncDirection == SyncDirection.OutlookToStarface)
|
||||
{
|
||||
// Outlook ist fuehrend -> Loeschung nach Starface spiegeln.
|
||||
if (await starface.DeleteContactAsync(mapping.StarfaceId))
|
||||
{
|
||||
result.Updated++;
|
||||
Log($" Geloescht (OL->SF): {sc.DisplayName}");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
||||
if (profile.SyncDirection == SyncDirection.Both)
|
||||
{
|
||||
newMappings.Add(mapping);
|
||||
// Bidirektional: anhand der Baseline pruefen, ob die
|
||||
// Starface-Seite seit dem letzten Sync unveraendert ist.
|
||||
bool sfUnchanged = !string.IsNullOrEmpty(mapping.LastStarfaceHash)
|
||||
&& sc.GetHash() == mapping.LastStarfaceHash;
|
||||
if (sfUnchanged)
|
||||
{
|
||||
// Unveraendert + in Outlook geloescht -> Loeschung gilt
|
||||
// -> auch aus Starface entfernen.
|
||||
if (await starface.DeleteContactAsync(mapping.StarfaceId))
|
||||
{
|
||||
result.Updated++;
|
||||
Log($" Geloescht (OL->SF): {sc.DisplayName}");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// In Outlook geloescht, aber in Starface geaendert ->
|
||||
// Bearbeitung gewinnt, in Outlook neu anlegen (Phase 3).
|
||||
Log($" In Outlook geloescht, in Starface geaendert -> neu anlegen: {sc.DisplayName}");
|
||||
processedStarfaceIds.Remove(sc.StarfaceId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// StarfaceToOutlook: Starface ist alleinige Quelle -> in Outlook
|
||||
// neu anlegen (Loeschung im Ziel zaehlt nicht).
|
||||
Log($" Outlook-Kontakt geloescht, wird neu angelegt: {sc.DisplayName}");
|
||||
processedStarfaceIds.Remove(sc.StarfaceId);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -197,21 +224,10 @@ namespace StarfaceOutlookSync.Services
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wirklich geloescht.
|
||||
if (profile.SyncDirection == SyncDirection.Both
|
||||
|| profile.SyncDirection == SyncDirection.OutlookToStarface)
|
||||
// Wirklich in Starface geloescht.
|
||||
if (profile.SyncDirection == SyncDirection.StarfaceToOutlook)
|
||||
{
|
||||
// Outlook ist (mit-)fuehrend -> Kontakt in Starface neu
|
||||
// anlegen. Mapping verwerfen und oc wieder freigeben,
|
||||
// damit Phase 2 ihn anlegt (inkl. Duplikat-Pruefung).
|
||||
Log($" Starface-Kontakt geloescht, wird neu angelegt: {oc.DisplayName}");
|
||||
processedOutlookIds.Remove(oc.OutlookEntryId);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// StarfaceToOutlook: Starface ist fuehrend, Loeschung
|
||||
// nach Outlook spiegeln.
|
||||
// Starface ist fuehrend -> Loeschung nach Outlook spiegeln.
|
||||
if (_outlookService.DeleteContact(oc.OutlookEntryId))
|
||||
{
|
||||
result.Updated++;
|
||||
@@ -219,6 +235,36 @@ namespace StarfaceOutlookSync.Services
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (profile.SyncDirection == SyncDirection.Both)
|
||||
{
|
||||
// Bidirektional: anhand der Baseline entscheiden, ob der
|
||||
// Outlook-Kontakt seit dem letzten Sync unveraendert ist.
|
||||
bool olUnchanged = !string.IsNullOrEmpty(mapping.LastOutlookHash)
|
||||
&& oc.GetHash() == mapping.LastOutlookHash;
|
||||
if (olUnchanged)
|
||||
{
|
||||
// Unveraendert + in Starface geloescht -> Loeschung gilt
|
||||
// -> aus Outlook entfernen.
|
||||
if (_outlookService.DeleteContact(oc.OutlookEntryId))
|
||||
{
|
||||
result.Updated++;
|
||||
Log($" Geloescht (SF->OL): {oc.DisplayName}");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// In Starface geloescht, aber in Outlook geaendert ->
|
||||
// Bearbeitung gewinnt, in Starface neu anlegen.
|
||||
Log($" In Starface geloescht, in Outlook geaendert -> neu anlegen: {oc.DisplayName}");
|
||||
processedOutlookIds.Remove(oc.OutlookEntryId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// OutlookToStarface: Outlook ist alleinige Quelle -> Kontakt
|
||||
// in Starface neu anlegen (Loeschung im Ziel zaehlt nicht).
|
||||
Log($" Starface-Kontakt geloescht, wird neu angelegt: {oc.DisplayName}");
|
||||
processedOutlookIds.Remove(oc.OutlookEntryId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oc != null && sc != null)
|
||||
|
||||
Reference in New Issue
Block a user