using System.Diagnostics; using System.IO; using System.Windows; using System.Windows.Threading; using TeamsISO.App.Services; namespace TeamsISO.App; /// /// Inline viewer for the daily show-notes file. Reads /// on open and polls every 2s while /// shown so REST/OSC-driven note appends surface live without the operator /// having to click Refresh. /// /// We don't allow editing here โ€” the file is intentionally a one-way log /// (operator stamps, post-show review). If someone wants to edit, they /// click "Open in editor" and use Notepad. /// public partial class NotesWindow : Window { private readonly DispatcherTimer _refreshTimer; private long _lastFileSize = -1; private DateTime _lastFileWrite = DateTime.MinValue; public NotesWindow() { InitializeComponent(); _refreshTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2), }; _refreshTimer.Tick += (_, _) => RefreshIfChanged(); Loaded += (_, _) => { DateLine.Text = $"{DateTimeOffset.Now:dddd, MMMM d, yyyy} ยท {NotesService.TodayPath}"; ReloadFromDisk(); _refreshTimer.Start(); }; Closed += (_, _) => _refreshTimer.Stop(); } private void OnClose(object sender, RoutedEventArgs e) => Close(); private void OnRefresh(object sender, RoutedEventArgs e) => ReloadFromDisk(); /// /// Cheap mtime/size check โ€” only re-reads the file when something changed. /// Saves the textbox a flicker on every 2s tick when no notes are being /// added. Falls through to a full reload if the file got smaller (operator /// might have edited externally). /// private void RefreshIfChanged() { try { var path = NotesService.TodayPath; if (!File.Exists(path)) return; var info = new FileInfo(path); if (info.Length != _lastFileSize || info.LastWriteTimeUtc != _lastFileWrite) ReloadFromDisk(); } catch { // Disk hiccups shouldn't stop the timer. } } private void ReloadFromDisk() { try { var path = NotesService.TodayPath; if (!File.Exists(path)) { NotesText.Text = "No notes yet. Stamp one via the REST or OSC endpoint and refresh."; return; } var info = new FileInfo(path); _lastFileSize = info.Length; _lastFileWrite = info.LastWriteTimeUtc; NotesText.Text = File.ReadAllText(path); // Scroll to bottom so the latest stamp is visible โ€” operators are // typically reading "what just happened" not "what happened first." Dispatcher.BeginInvoke(new Action(() => { Scroller.ScrollToEnd(); }), DispatcherPriority.Background); } catch (Exception ex) { NotesText.Text = "Couldn't read notes file: " + ex.Message; } } /// /// Append the input box's text to today's notes file via NotesService, /// then clear the box and refresh the view. Bound to the "Add" button + /// Enter key in the input. Empty/whitespace input is a no-op. /// private void OnAddNote(object sender, RoutedEventArgs e) { var text = NewNoteBox.Text?.Trim(); if (string.IsNullOrEmpty(text)) return; if (NotesService.Append(text)) { NewNoteBox.Clear(); ReloadFromDisk(); NewNoteBox.Focus(); } } /// Enter key in the input commits the note, same as the Add button. private void OnNewNoteKey(object sender, System.Windows.Input.KeyEventArgs e) { if (e.Key == System.Windows.Input.Key.Enter) { OnAddNote(sender, e); e.Handled = true; } } private void OnOpenInEditor(object sender, RoutedEventArgs e) { try { Process.Start(new ProcessStartInfo { FileName = NotesService.TodayPath, UseShellExecute = true, }); } catch { // Best-effort; if no .md handler is registered the OS shows its own dialog. } } }