feat: align legacy operator and playout behavior

This commit is contained in:
2026-07-15 21:04:05 +09:00
parent 06a16e5741
commit 83398044c6
77 changed files with 6377 additions and 391 deletions

View File

@@ -33,6 +33,7 @@ public sealed class LegacyComparisonWorkflowController
private long _draftSequence;
private int _domesticSearchVersion;
private int _worldSearchVersion;
private int _activationGeneration;
private bool _isBusy;
public LegacyComparisonWorkflowController(
@@ -45,12 +46,44 @@ public sealed class LegacyComparisonWorkflowController
public LegacyComparisonWorkflowSnapshot Current => CreateSnapshot();
/// <summary>
/// Recreates UC3's per-control state before its saved CP949 pairs are loaded for
/// a new tab activation. Search and pair-load generations invalidate completions
/// that belong to the discarded control instance.
/// </summary>
public LegacyComparisonWorkflowSnapshot ResetForTabActivation()
{
Interlocked.Increment(ref _activationGeneration);
Interlocked.Increment(ref _domesticSearchVersion);
Interlocked.Increment(ref _worldSearchVersion);
_domesticResults = [];
_worldResults = [];
_savedPairs = [];
_firstTarget = null;
_secondTarget = null;
_selectedDomesticResultId = null;
_selectedWorldResultId = null;
_selectedFixedTargetId = null;
_selectedPairId = null;
_domesticQuery = string.Empty;
_worldQuery = string.Empty;
_domesticError = string.Empty;
_worldError = string.Empty;
_statusMessage = string.Empty;
_domesticStatus = LegacyComparisonSearchStatus.Idle;
_worldStatus = LegacyComparisonSearchStatus.Idle;
_statusKind = LegacyOperatorStatusKind.Neutral;
_isBusy = false;
Touch();
return CreateSnapshot();
}
public async Task<LegacyComparisonWorkflowSnapshot> SearchDomesticAsync(
string query,
CancellationToken cancellationToken = default)
{
var normalizedQuery = NormalizeSearchQuery(query);
var version = ++_domesticSearchVersion;
var version = Interlocked.Increment(ref _domesticSearchVersion);
_domesticQuery = normalizedQuery;
_domesticStatus = LegacyComparisonSearchStatus.Loading;
_domesticError = string.Empty;
@@ -64,7 +97,7 @@ public sealed class LegacyComparisonWorkflowController
normalizedQuery,
LegacyComparisonWorkflowCatalog.DomesticMaximumResults,
cancellationToken).ConfigureAwait(false);
if (version != _domesticSearchVersion)
if (version != Volatile.Read(ref _domesticSearchVersion))
{
return CreateSnapshot();
}
@@ -81,7 +114,7 @@ public sealed class LegacyComparisonWorkflowController
}
catch (OperationCanceledException)
{
if (version == _domesticSearchVersion)
if (version == Volatile.Read(ref _domesticSearchVersion))
{
_domesticStatus = LegacyComparisonSearchStatus.Idle;
_domesticError = string.Empty;
@@ -92,7 +125,7 @@ public sealed class LegacyComparisonWorkflowController
}
catch
{
if (version == _domesticSearchVersion)
if (version == Volatile.Read(ref _domesticSearchVersion))
{
_domesticResults = [];
_domesticStatus = LegacyComparisonSearchStatus.Error;
@@ -110,7 +143,7 @@ public sealed class LegacyComparisonWorkflowController
{
// UC3's blank world query loaded the complete US/TW universe, so blank remains valid.
var normalizedQuery = NormalizeSearchQuery(query);
var version = ++_worldSearchVersion;
var version = Interlocked.Increment(ref _worldSearchVersion);
_worldQuery = normalizedQuery;
_worldStatus = LegacyComparisonSearchStatus.Loading;
_worldError = string.Empty;
@@ -124,7 +157,7 @@ public sealed class LegacyComparisonWorkflowController
normalizedQuery,
LegacyComparisonWorkflowCatalog.WorldMaximumResults,
cancellationToken).ConfigureAwait(false);
if (version != _worldSearchVersion)
if (version != Volatile.Read(ref _worldSearchVersion))
{
return CreateSnapshot();
}
@@ -141,7 +174,7 @@ public sealed class LegacyComparisonWorkflowController
}
catch (OperationCanceledException)
{
if (version == _worldSearchVersion)
if (version == Volatile.Read(ref _worldSearchVersion))
{
_worldStatus = LegacyComparisonSearchStatus.Idle;
_worldError = string.Empty;
@@ -152,7 +185,7 @@ public sealed class LegacyComparisonWorkflowController
}
catch
{
if (version == _worldSearchVersion)
if (version == Volatile.Read(ref _worldSearchVersion))
{
_worldResults = [];
_worldStatus = LegacyComparisonSearchStatus.Error;
@@ -220,6 +253,7 @@ public sealed class LegacyComparisonWorkflowController
public async Task<LegacyComparisonWorkflowSnapshot> LoadSavedPairsAsync(
CancellationToken cancellationToken = default)
{
var activationGeneration = Volatile.Read(ref _activationGeneration);
BeginBusy();
try
{
@@ -251,6 +285,11 @@ public sealed class LegacyComparisonWorkflowController
}
}
if (activationGeneration != Volatile.Read(ref _activationGeneration))
{
return CreateSnapshot();
}
_savedPairs = candidate;
_pairSequence = Math.Max(_pairSequence, candidate.Count);
_selectedPairId = candidate.FirstOrDefault()?.RowId;
@@ -261,13 +300,21 @@ public sealed class LegacyComparisonWorkflowController
}
catch
{
if (activationGeneration != Volatile.Read(ref _activationGeneration))
{
return CreateSnapshot();
}
_statusMessage = "CP949 종목비교.dat를 안전하게 불러오지 못했습니다.";
_statusKind = LegacyOperatorStatusKind.Error;
throw;
}
finally
{
EndBusy();
if (activationGeneration == Volatile.Read(ref _activationGeneration))
{
EndBusy();
}
}
return CreateSnapshot();