using Avalonia.Threading; using Nonemm.Core; using Nonemm.Network; namespace Nonemm.App; /// The log as a resync reads it. The link asks these questions on a socket /// thread and the log is only touched where the windows are, so every answer is /// fetched on the window thread and waited for. /// /// The wait is short — a block is an hour of contacts out of a list already in /// memory — and nothing on the window thread waits on the socket thread, so /// there is nothing here for the two to deadlock over. internal sealed class SessionContactLog : ContactLog { private readonly AppSession session; internal SessionContactLog(AppSession session) => this.session = session; public string ContestName => Ask(() => session.Logging?.Contest.Name ?? ""); public string MyCall => Ask(() => session.Settings.Station.Callsign); public string ContestSubType => Ask(() => session.Logging?.Instance.SubType ?? ""); public string CountryFileVersion => Ask(() => session.Countries?.Version ?? ""); public IReadOnlyList Contacts(string computerName, DateTime fromUtc, TimeSpan length) => Ask(() => session.Logging is not { } logging ? [] : logging.Log.Qsos .Where(q => string.Equals(q.StationName, computerName, StringComparison.OrdinalIgnoreCase) && q.TimestampUtc >= fromUtc && q.TimestampUtc < fromUtc + length) .OrderBy(q => q.TimestampUtc) .ThenBy(q => q.Call.Text, StringComparer.Ordinal) .ToList()); public DateTime LastContactUtc(string computerName) => Ask(() => session.Logging is not { } logging ? default : logging.Log.Qsos .Where(q => string.Equals(q.StationName, computerName, StringComparison.OrdinalIgnoreCase)) .Select(q => q.TimestampUtc) .DefaultIfEmpty(default) .Max()); public DateTime FirstContactUtc => Ask(() => session.Logging is not { } logging ? default : logging.Log.Qsos.Select(q => q.TimestampUtc).DefaultIfEmpty(default).Min()); private static T Ask(Func question) => Dispatcher.UIThread.CheckAccess() ? question() : Dispatcher.UIThread.Invoke(question); }