Migrate remaining legacy operator workflows
This commit is contained in:
124
MainWindow.CloseConfirmation.cs
Normal file
124
MainWindow.CloseConfirmation.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using MBN_STOCK_WEBVIEW.Core.Playout;
|
||||
using Microsoft.UI.Windowing;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace MBN_STOCK_WEBVIEW;
|
||||
|
||||
public sealed partial class MainWindow
|
||||
{
|
||||
private AppWindow? _closeConfirmationAppWindow;
|
||||
private int _closeConfirmationPending;
|
||||
private int _closeConfirmed;
|
||||
|
||||
private void EnsureCloseConfirmationAttached()
|
||||
{
|
||||
if (_closeConfirmationAppWindow is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var windowHandle = WindowNative.GetWindowHandle(this);
|
||||
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
|
||||
var appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
appWindow.Closing += OnAppWindowClosing;
|
||||
_closeConfirmationAppWindow = appWindow;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Root.Loaded retries the attachment after the native window has been created.
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnAppWindowClosing(AppWindow sender, AppWindowClosingEventArgs args)
|
||||
{
|
||||
if (Volatile.Read(ref _closeConfirmed) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// AppWindow.Closing cannot be deferred. Cancel this close synchronously and issue a
|
||||
// second, explicitly confirmed close only after the modal result is known.
|
||||
args.Cancel = true;
|
||||
if (Interlocked.CompareExchange(ref _closeConfirmationPending, 1, 0) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Root.XamlRoot is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
XamlRoot = Root.XamlRoot,
|
||||
Title = "프로그램 종료",
|
||||
Content = BuildCloseConfirmationMessage(),
|
||||
PrimaryButtonText = "종료",
|
||||
CloseButtonText = "취소",
|
||||
DefaultButton = ContentDialogButton.Close
|
||||
};
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
if (result != ContentDialogResult.Primary)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref _closeConfirmed, 1);
|
||||
Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If the confirmation surface cannot be shown, fail closed and keep the app open.
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref _closeConfirmationPending, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildCloseConfirmationMessage()
|
||||
{
|
||||
var status = _playoutEngine?.Status;
|
||||
var hasPreparedScene = !string.IsNullOrWhiteSpace(status?.PreparedSceneName);
|
||||
var hasOnAirScene = !string.IsNullOrWhiteSpace(status?.OnAirSceneName);
|
||||
var commandPending = Volatile.Read(ref _playoutCommandInFlight) != 0 ||
|
||||
status?.IsPlayCompletionPending == true;
|
||||
var outcomeUnknown = Volatile.Read(ref _browserCorrelationQuarantined) != 0 ||
|
||||
status?.State == PlayoutConnectionState.OutcomeUnknown;
|
||||
|
||||
if (outcomeUnknown)
|
||||
{
|
||||
return "송출 결과를 확정할 수 없는 상태입니다. PGM과 Network Monitoring을 확인한 뒤 종료하세요. " +
|
||||
"종료 과정에서 TAKE OUT 또는 다른 송출 명령을 자동으로 반복하지 않습니다. 그래도 종료하시겠습니까?";
|
||||
}
|
||||
|
||||
if (commandPending)
|
||||
{
|
||||
return "송출 명령 또는 callback 처리가 진행 중입니다. 결과를 확인한 뒤 종료하는 것이 안전합니다. " +
|
||||
"그래도 종료하시겠습니까?";
|
||||
}
|
||||
|
||||
if (hasOnAirScene || hasPreparedScene)
|
||||
{
|
||||
return "PREPARED 또는 PROGRAM 장면이 남아 있습니다. 필요한 경우 먼저 TAKE OUT을 실행하고 " +
|
||||
"PGM을 확인하세요. 종료 과정에서는 TAKE OUT을 자동 실행하지 않습니다. 그래도 종료하시겠습니까?";
|
||||
}
|
||||
|
||||
return "프로그램을 종료하시겠습니까?";
|
||||
}
|
||||
|
||||
private void DetachCloseConfirmation()
|
||||
{
|
||||
var appWindow = Interlocked.Exchange(ref _closeConfirmationAppWindow, null);
|
||||
if (appWindow is not null)
|
||||
{
|
||||
appWindow.Closing -= OnAppWindowClosing;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user