using MeetingAssistant.LaunchProfiles; using MeetingAssistant.Recording; namespace MeetingAssistant.Taskbar; public enum MeetingTaskbarAction { EditRules, OpenSubmenu, StartRecording, StopRecording, AbortRecording, SwitchProfile, SelectMicrophone, Exit } public sealed record MeetingTaskbarMenu( RecordingProcessState State, string Tooltip, IReadOnlyList Items); public sealed record MeetingTaskbarMenuItem( string Text, MeetingTaskbarAction Action, string? ProfileName = null, string? MicrophoneDeviceId = null, bool IsChecked = false, IReadOnlyList? Items = null, bool StartsSection = false); public static class MeetingTaskbarMenuBuilder { public static MeetingTaskbarMenu Build( RecordingStatus status, IReadOnlyList launchProfiles, IReadOnlyList? microphones = null, string? currentMicrophoneDeviceId = null) { var items = new List { new("Open agent", MeetingTaskbarAction.EditRules) }; if (status.IsRecording) { items.Add(new MeetingTaskbarMenuItem( "Finish meeting", MeetingTaskbarAction.StopRecording, StartsSection: true)); } var secondaryControls = new List(); if (microphones is { Count: > 0 }) { secondaryControls.Add(BuildMicrophoneMenu(microphones, currentMicrophoneDeviceId)); } if (status.IsRecording) { secondaryControls.Add(new MeetingTaskbarMenuItem( "Cancel meeting recording and discard", MeetingTaskbarAction.AbortRecording)); foreach (var profile in launchProfiles.Where(profile => !IsActiveProfile(profile, status))) { secondaryControls.Add(new MeetingTaskbarMenuItem( AppendHotkey($"Switch to {profile.Name}", profile.Options.Hotkey.Toggle), MeetingTaskbarAction.SwitchProfile, profile.Name)); } } else { foreach (var profile in launchProfiles) { secondaryControls.Add(new MeetingTaskbarMenuItem( AppendHotkey($"Start meeting recording ({profile.Name})", profile.Options.Hotkey.Toggle), MeetingTaskbarAction.StartRecording, profile.Name)); } } AddSection(items, secondaryControls); items.Add(new MeetingTaskbarMenuItem( "Exit", MeetingTaskbarAction.Exit, StartsSection: true)); return new MeetingTaskbarMenu( status.State, BuildTooltip(status), items); } private static MeetingTaskbarMenuItem BuildMicrophoneMenu( IReadOnlyList microphones, string? currentMicrophoneDeviceId) { var microphoneItems = microphones .Select(microphone => new MeetingTaskbarMenuItem( microphone.Name, MeetingTaskbarAction.SelectMicrophone, MicrophoneDeviceId: microphone.Id, IsChecked: string.Equals(microphone.Id, currentMicrophoneDeviceId, StringComparison.Ordinal))) .ToArray(); return new MeetingTaskbarMenuItem( "Microphone", MeetingTaskbarAction.OpenSubmenu, Items: microphoneItems); } private static void AddSection( List items, IReadOnlyList section) { if (section.Count == 0) { return; } items.Add(section[0] with { StartsSection = true }); items.AddRange(section.Skip(1)); } private static string BuildTooltip(RecordingStatus status) { return status.State switch { RecordingProcessState.Recording => string.IsNullOrWhiteSpace(status.LaunchProfile) ? "Meeting Assistant - recording" : $"Meeting Assistant - recording ({status.LaunchProfile})", RecordingProcessState.Summarizing => "Meeting Assistant - summarizing", _ => "Meeting Assistant - idle" }; } private static bool IsActiveProfile( LaunchProfile profile, RecordingStatus status) { return string.Equals( profile.Name, status.LaunchProfile, StringComparison.OrdinalIgnoreCase); } private static string AppendHotkey(string text, string hotkey) { return string.IsNullOrWhiteSpace(hotkey) ? text : $"{text}\t{hotkey.Trim()}"; } } public static class MeetingTaskbarExitPolicy { public static bool RequiresConfirmation(RecordingProcessState state) { return state != RecordingProcessState.Idle; } }