From bf18ded2137f0780ccd76ceb4d7c23899bec6bd4 Mon Sep 17 00:00:00 2001 From: resneptacle Date: Wed, 25 Dec 2024 04:25:25 +0100 Subject: [PATCH] much changes --- AsciiArt/AsciiArt.cs | 2 +- Fahrplan.cs | 3 +- NetworkMethods.cs | 57 ++++++++++++++++++++++++++++++++++++++ Program.cs | 42 ++++++++++++++++++++++------ Screens/IntroScreen.cs | 2 +- TODO.txt | 10 +++++++ UiElements/FooterWindow.cs | 26 +++++++++++------ 7 files changed, 121 insertions(+), 21 deletions(-) create mode 100644 NetworkMethods.cs create mode 100644 TODO.txt diff --git a/AsciiArt/AsciiArt.cs b/AsciiArt/AsciiArt.cs index 73b4270..e3b8f26 100644 --- a/AsciiArt/AsciiArt.cs +++ b/AsciiArt/AsciiArt.cs @@ -1,6 +1,6 @@ using Mindmagma.Curses; -namespace ANSI_Fahrplan; +namespace Fahrplan; public class AsciiArt { /// diff --git a/Fahrplan.cs b/Fahrplan.cs index 39b5d50..68c5f37 100644 --- a/Fahrplan.cs +++ b/Fahrplan.cs @@ -1,9 +1,8 @@ #nullable disable - // Root myDeserializedClass = JsonConvert.DeserializeObject(myJsonResponse); using Newtonsoft.Json; -namespace Fahrplan; +namespace Fahrplan.Plan; public class Root { [JsonProperty("$schema")] diff --git a/NetworkMethods.cs b/NetworkMethods.cs new file mode 100644 index 0000000..dcd8b9d --- /dev/null +++ b/NetworkMethods.cs @@ -0,0 +1,57 @@ +using Newtonsoft.Json; +using System.Net; + +namespace Fahrplan.Network; + +/// +/// Class to help get the plan from the CCC API +/// +public class PlanPuller () { + public const string PlanJsonUrl = "https://fahrplan.events.ccc.de/congress/2024/fahrplan/schedule/export/schedule.json"; + public const string LocalPlanFilename = "ansi-fahrplan.json"; + + /// + /// Pulls the current fahrplan, stores it in a temp + /// directory and returns the parsed JSON object. + /// If another plan already exists in the temp directory + /// and is not older than three minutes, a new plan + /// will not yet be pulled to save on web requests + /// + /// If true, skips checking the timestamp on the plan cache + /// Parsed Fahrplan JSON object + public async static Task GetFahrplanAsync (bool forcePull = false) { + var localPlanFileFullpath = + Path.GetTempPath () + + LocalPlanFilename; + + if (File.Exists (localPlanFileFullpath)) { + var fileInfo = new FileInfo (localPlanFileFullpath); + + // File is not older than three minutes, return cached file + if (fileInfo.LastWriteTime + TimeSpan.FromMinutes (3) > DateTime.Now) { + var reader = fileInfo.OpenText (); + var content = await reader.ReadToEndAsync (); + + return JsonConvert.DeserializeObject (content); + } + } + + var httpClient = new HttpClient (); + + // Be a good netizen, add a proper UA string + httpClient.DefaultRequestHeaders.Add ("User-Agent", "AnsiFahrplan/1.0 (by Snep, c3@diskcat.com)"); + + var response = await httpClient.GetAsync (new Uri (PlanJsonUrl)); + + response.EnsureSuccessStatusCode (); + var responseContent = await response.Content.ReadAsStringAsync (); + + try { + File.WriteAllText (localPlanFileFullpath, responseContent); + } catch (Exception ex) { + Console.Error.WriteLine ("Warning: Failed to write Plan cache file: " + ex.Message); + } + + return JsonConvert.DeserializeObject (responseContent); + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index f01ac1a..1255a65 100755 --- a/Program.cs +++ b/Program.cs @@ -1,9 +1,10 @@ using SCI.CursesWrapper; using SCI.CursesWrapper.UiElements; -using ANSI_Fahrplan.Screens; +using Fahrplan.Screens; using Mindmagma.Curses; +using Fahrplan.Plan; -namespace ANSI_Fahrplan; +namespace Fahrplan; class Program { private static void Main (string [] args) { @@ -46,6 +47,9 @@ class Program { introScreen.RegisterInputHandler (inputHandler); introScreen.SetWindowActive (); + // Whilst the intro screen is shown, get the Fahrplan + var fahrplan = Network.PlanPuller.GetFahrplanAsync ().Result; + // Wait until intro screen is closed while (introScreen.WindowId > -1) Thread.Sleep (50); // TODO; Unjank this introScreen = null; @@ -55,7 +59,8 @@ class Program { footerWindow.DrawFooter ("Fahrplan", "LLLeft", "RRRRRight"); // -- Create menu bar -- // - var topMenu = new TopMenu (screen, inputHandler, CreateMenuItems (contentWindow)); + var menuItems = CreateMenuItems (contentWindow, footerWindow, fahrplan.schedule.conference); + var topMenu = new TopMenu (screen, inputHandler, menuItems); topMenu.ActivateItem (topMenu.MenuItems [1]); // Wait until the input handler routine stops @@ -66,12 +71,16 @@ class Program { Environment.Exit (1); } - private static List CreateMenuItems (ContentWindow contentWindow) { + private static List CreateMenuItems ( + ContentWindow contentWindow, + FooterWindow footer, + Plan.Conference planConference + ) { var helpItem = new MenuItem ("Help", "F1"); - var upcomingItem = new MenuItem ("Upcoming", "F2"); - var byDayItem = new MenuItem ("By Day", "F3"); - var byRoomItem = new MenuItem ("By Room", "F4"); - var bySpeakerItem = new MenuItem ("By Speaker", "F5"); + var upcomingItem = new MenuItem ("Upcoming", "u"); + var byDayItem = new MenuItem ("By Day", "d"); + var byRoomItem = new MenuItem ("By Room", "r"); + var bySpeakerItem = new MenuItem ("By Speaker", "s"); var quitItem = new MenuItem ("Quit (C-q)"); helpItem.OnItemActivated += (object sender, MenuItemActivatedEventArgs e) => { @@ -84,6 +93,23 @@ class Program { upcomingItem.OnItemActivated += (object sender, MenuItemActivatedEventArgs e) => { var scrollWindow = contentWindow.CreateInnerScrollWindow (); scrollWindow.AddContent ("-- Upcoming --"); + + scrollWindow.RenderCurrentViewport (); + }; + + byRoomItem.OnItemActivated += (object sender, MenuItemActivatedEventArgs e) => { + var scrollWindow = contentWindow.CreateInnerScrollWindow (); + footer.DrawFooter (" Day 1 ->"); + + // TODO: Do the actual foo-magic of formatting the plan as a nice text table + // and key handling to switch between days + foreach (var day in planConference.days) { + scrollWindow.AddContent (DateTime.Parse (day.date).ToString ("dd.MM.yyyy") + $" (Day {day.index})"); + foreach (var room in day.rooms) { + scrollWindow.AddContent ($"- {room.Key}"); + } + } + scrollWindow.RenderCurrentViewport (); }; diff --git a/Screens/IntroScreen.cs b/Screens/IntroScreen.cs index f760b88..3fa475d 100644 --- a/Screens/IntroScreen.cs +++ b/Screens/IntroScreen.cs @@ -1,6 +1,6 @@ using SCI.CursesWrapper; -namespace ANSI_Fahrplan.Screens; +namespace Fahrplan.Screens; public class IntroScreen : Window { /// diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..560d187 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,10 @@ +- [] Ensure minimum width and height of terminal +- [] Help text +- [] Actually format the different views as text and display them + - [] One class per view to store data in? + - [] Key events per view, like switching days +- [] Footer text +- [] Colors +- [] Cleanup + +- [] Make a TCP service out of this somehow \ No newline at end of file diff --git a/UiElements/FooterWindow.cs b/UiElements/FooterWindow.cs index 379ce75..78967d1 100644 --- a/UiElements/FooterWindow.cs +++ b/UiElements/FooterWindow.cs @@ -3,6 +3,10 @@ using Mindmagma.Curses; namespace SCI.CursesWrapper.UiElements; public class FooterWindow : Window { + private string leftText = ""; + private string rightText = ""; + private string titleText = ""; + /// /// Class constructor /// @@ -24,36 +28,40 @@ public class FooterWindow : Window { /// /// /// - public void DrawFooter (string title, string left = "", string right = "") { + public void DrawFooter (string? title = null, string? left = null, string? right = null) { Clear (); + if (title is not null) titleText = title; + if (left is not null) leftText = left; + if (right is not null) rightText = right; + // TODO: Describe the last-character-in-window bug(fix) here var pad = (WindowSize.Width / 2) - - (title.Length / 2) + (titleText.Length / 2) - 1; var finalString = - " " + left.PadRight (pad) + - title + - (string.IsNullOrEmpty (right)? + " " + leftText.PadRight (pad) + + titleText + + (string.IsNullOrEmpty (rightText)? "" : - right.PadLeft (pad) + rightText.PadLeft (pad) ); // String too long, leave out left and right text if (finalString.Length > WindowSize.Width - 1) { - finalString = " ".PadLeft (pad) + title; + finalString = " ".PadLeft (pad) + titleText; } // String still too long, leave out the padding and left align title if (finalString.Length > WindowSize.Width - 1) { - finalString = " " + title; + finalString = " " + titleText; } // String still too long, cut to size if (finalString.Length > WindowSize.Width - 1) { - finalString = title.Substring (0, WindowSize.Width - 1); + finalString = titleText.Substring (0, WindowSize.Width - 1); } try {