dragon-iso/src/TeamsISO.App/NotesWindow.xaml.cs

137 lines
4.3 KiB
C#

using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Threading;
using TeamsISO.App.Services;
namespace TeamsISO.App;
/// <summary>
/// Inline viewer for the daily show-notes file. Reads
/// <see cref="NotesService.TodayPath"/> 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.
/// </summary>
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();
/// <summary>
/// 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).
/// </summary>
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;
}
}
/// <summary>
/// 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.
/// </summary>
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();
}
}
/// <summary>Enter key in the input commits the note, same as the Add button.</summary>
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.
}
}
}