using System.IO; using System.Text.Json; namespace TeamsISO.App.Services; /// /// Persistent UI-side toggles that don't belong in /// (which is the engine's domain model — framerate, NDI groups, ISO assignments). /// /// Each toggle is a property on a single record persisted as JSON at /// %LOCALAPPDATA%\TeamsISO\ui-prefs.json. Defaults match the original /// in-memory behavior: HideLocalSelf=true (filter the operator's own preview /// out of the participants list) and AutoDisableOnDeparture=false (a participant /// going offline doesn't tear down their pipeline by default — operators /// usually want to keep the routing in case they reconnect). /// /// Centralizing these here means the settings VM doesn't have to plumb /// individual Set methods to dedicated services for every new bool. /// public static class UIPreferences { private static readonly object _gate = new(); private static string PrefsPath => Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TeamsISO", "ui-prefs.json"); /// /// Sort modes for the participants DataGrid. is the default /// and matches the engine's discovery order (operators with custom Stream Deck /// layouts sometimes prefer Alphabetical for stability across meetings). /// resorts at the 1Hz stats tick so the active /// speaker bubbles to the top — useful for operators reacting to who's talking. /// public enum SortMode { JoinOrder, Alphabetical, OnlineFirst, LoudestFirst } /// The on-disk shape. New fields added here become opt-in for older files via default values. public sealed record Prefs( bool HideLocalSelf = true, bool AutoDisableOnDeparture = false, SortMode ParticipantSort = SortMode.JoinOrder, bool MinimizeToTray = false, bool ControlSurfaceLanReachable = false, // Phase E.1 / E.2 quality-of-life. With both true, the operator launches // TeamsISO and never sees the Teams UI — Teams auto-starts in the // background and its windows are auto-hidden as soon as they materialize. // All control happens via the IN-CALL bar + participants DataGrid. bool LaunchTeamsOnStartup = false, bool AutoHideTeamsWindows = false, // Experimental Phase E.4. SetParent-reparents Teams' main window // into a TeamsISO-owned host. WebView2 in modern Teams can render // weirdly after reparent; if so the operator unticks and falls // back to auto-hide mode. Off by default. bool EmbedTeamsWindow = false); public static Prefs Load() { try { if (!File.Exists(PrefsPath)) return new Prefs(); var json = File.ReadAllText(PrefsPath); return JsonSerializer.Deserialize(json) ?? new Prefs(); } catch { return new Prefs(); } } public static void Save(Prefs prefs) { try { lock (_gate) { var dir = Path.GetDirectoryName(PrefsPath); if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir); var json = JsonSerializer.Serialize(prefs, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(PrefsPath, json); } } catch { // Disk full / permission denied — in-memory state still holds for this session. } } }