Creator ruling 2026-11-12: "we don't care if a creator wants to edit their scene: move stuff around, shrink/grow a webcam view, etc. Why not?" Correct — the app was only ever *forcing* EDIT off at go-live, never enforcing it, so the gun-safety was a habit rather than a rule. App.LockEditWhileLive ships off: a show no longer takes EDIT away. Turning it on restores the old behaviour exactly — go-live flips EDIT off and stop flips it back, and only because the lock took it, so a creator who had already turned EDIT off does not get it handed back on stop. MainViewModel.ApplyEditLockForSession holds the rule so it is testable without starting a broadcast; EditWhileLiveTests covers both settings, the round trip, and the "don't hand it back" case. Settings → Editing carries the checkbox. This is the first slice of TASK 68 (editing while live). 678 pass, 0 warnings. Scope justification: Assets/starting-background.jpg is outside this unit — the creator's replacement art, still awaiting their visual check.
124 lines
4.1 KiB
C#
124 lines
4.1 KiB
C#
using System.Windows;
|
|
using Notification.Core;
|
|
|
|
namespace ytLive.ViewModels;
|
|
|
|
/// <summary>
|
|
/// App Settings as a right-edge drawer (creator ruling 2026-11-01): the gear rail tab opens it
|
|
/// immediately, in the same exclusive-slide pattern as Stream Settings / YPP / Socials / TEST.
|
|
///
|
|
/// <para>It is a separate flag from <see cref="IsSettingsOpen"/> because the overlay shell now
|
|
/// only carries Report Bug / Feature Request / About — settings stopped being a modal.</para>
|
|
/// </summary>
|
|
public partial class MainViewModel
|
|
{
|
|
private bool _settingsDrawerOpen;
|
|
private NotificationPosition _toastPosition = NotificationPosition.BottomRight;
|
|
private bool _stickyAlertsUntilDismissed;
|
|
private bool _lockEditWhileLive;
|
|
private bool _editingForcedOff;
|
|
|
|
public bool StickyAlertsUntilDismissed
|
|
{
|
|
get => _stickyAlertsUntilDismissed;
|
|
set
|
|
{
|
|
if (!SetProperty(ref _stickyAlertsUntilDismissed, value)) return;
|
|
_layoutStore.SaveStickyAlertsUntilDismissed(value);
|
|
}
|
|
}
|
|
|
|
/// <summary>Lock editing for the duration of a session. Off = edit while live (creator ruling
|
|
/// 2026-11-12); on = go-live flips EDIT off and stop flips it back, as it always did.</summary>
|
|
public bool LockEditWhileLive
|
|
{
|
|
get => _lockEditWhileLive;
|
|
set
|
|
{
|
|
if (!SetProperty(ref _lockEditWhileLive, value)) return;
|
|
_layoutStore.SaveLockEditWhileLive(value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies the edit lock when a session starts or stops. Split out so the rule is testable without
|
|
/// starting a real broadcast.
|
|
/// </summary>
|
|
internal void ApplyEditLockForSession(bool starting)
|
|
{
|
|
if (!LockEditWhileLive)
|
|
{
|
|
// The creator may edit during the show; nothing is forced either way.
|
|
_editingForcedOff = false;
|
|
return;
|
|
}
|
|
|
|
if (starting)
|
|
{
|
|
_editingForcedOff = true;
|
|
EditingActive = false;
|
|
}
|
|
else if (_editingForcedOff)
|
|
{
|
|
_editingForcedOff = false;
|
|
EditingActive = true;
|
|
}
|
|
}
|
|
|
|
public NotificationPosition ToastPosition
|
|
{
|
|
get => _toastPosition;
|
|
set
|
|
{
|
|
if (!IsSupportedToastPosition(value) || !SetProperty(ref _toastPosition, value)) return;
|
|
_layoutStore.SaveNotificationPosition(value);
|
|
OnPropertyChanged(nameof(ToastAreaMargin));
|
|
}
|
|
}
|
|
|
|
public Thickness ToastAreaMargin => ToastPosition switch
|
|
{
|
|
NotificationPosition.TopLeft => new Thickness(12, 12, 0, 0),
|
|
NotificationPosition.TopRight => new Thickness(0, 12, 12, 0),
|
|
NotificationPosition.CenterLeft => new Thickness(12, 0, 0, 0),
|
|
NotificationPosition.CenterRight => new Thickness(0, 0, 12, 0),
|
|
NotificationPosition.BottomLeft => new Thickness(12, 0, 0, 96),
|
|
_ => new Thickness(0, 0, 12, 96),
|
|
};
|
|
|
|
private static bool IsSupportedToastPosition(NotificationPosition position)
|
|
=> position is NotificationPosition.TopLeft or NotificationPosition.TopRight
|
|
or NotificationPosition.CenterLeft or NotificationPosition.CenterRight
|
|
or NotificationPosition.BottomLeft or NotificationPosition.BottomRight;
|
|
|
|
public bool SettingsDrawerOpen
|
|
{
|
|
get => _settingsDrawerOpen;
|
|
private set => SetProperty(ref _settingsDrawerOpen, value);
|
|
}
|
|
|
|
internal void ToggleSettingsDrawer()
|
|
{
|
|
if (_settingsDrawerOpen)
|
|
{
|
|
SettingsDrawerOpen = false;
|
|
return;
|
|
}
|
|
|
|
// One drawer at a time, like the rail's other tabs.
|
|
BroadcastForm.IsDrawerOpen = false;
|
|
Ypp.IsDrawerOpen = false;
|
|
TestSession.IsDrawerOpen = false;
|
|
if (SocialsDrawerOpen) CloseSocialsDrawer();
|
|
|
|
SettingsDrawerOpen = true;
|
|
}
|
|
|
|
/// <summary>Called by the other drawers when they open, so settings yields to them — and by the
|
|
/// window's click-outside guard, so the gear tab retracts like every other drawer (2026-11-05).</summary>
|
|
internal void CloseSettingsDrawerIfOpen()
|
|
{
|
|
if (_settingsDrawerOpen) SettingsDrawerOpen = false;
|
|
}
|
|
}
|