diff --git a/Bink.cs b/Bink.cs index 27bd611..df91445 100644 --- a/Bink.cs +++ b/Bink.cs @@ -1,6 +1,4 @@ -using Microsoft.VisualBasic; -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; @@ -21,24 +19,24 @@ namespace wakka { private static SshConnection Ssh { get; set; } = new SshConnection(); - public string RunBinkCommand(string command) + public void PostBink(string message) { - return Ssh.RunCommand($"town bink --{command}"); + Ssh.RunCommandWithInput("town bink --pipe", message); } public List? AllBinks() { - return JsonSerializer.Deserialize>(RunBinkCommand("dump")); + return JsonSerializer.Deserialize>(Ssh.RunCommand("town bink --dump")); } public List? BinksBefore(long TimeStamp) { - return JsonSerializer.Deserialize>(RunBinkCommand("dump-before")); + return JsonSerializer.Deserialize>(Ssh.RunCommand($"town bink --dump-before {TimeStamp}")); } public List? BinksAfter(long TimeStamp) { - return JsonSerializer.Deserialize>(RunBinkCommand("dump-after")); + return JsonSerializer.Deserialize>(Ssh.RunCommand($"town bink --dump-after {TimeStamp}")); } } } diff --git a/BinkPage.xaml b/BinkPage.xaml index f97a979..3051548 100644 --- a/BinkPage.xaml +++ b/BinkPage.xaml @@ -7,6 +7,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> + @@ -14,52 +15,39 @@ - + + + + + + - + - + - - diff --git a/BinkPage.xaml.cs b/BinkPage.xaml.cs index 166d9fc..a0d6079 100644 --- a/BinkPage.xaml.cs +++ b/BinkPage.xaml.cs @@ -1,18 +1,12 @@ -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Navigation; +using Org.BouncyCastle.Tls; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.UI.Xaml; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -24,24 +18,88 @@ namespace wakka /// public sealed partial class BinkPage : Page { + private DispatcherTimer timer; + private readonly ObservableCollection Binks = []; - private static SshConnection Ssh { get; set; } = new SshConnection(); + private static Bink Bink { get; set; } = new Bink(); public BinkPage() { this.InitializeComponent(); - var Bink = new Bink(); + StartTimer(); var BinkList = Bink.AllBinks(); if (BinkList != null) { foreach (var bink in BinkList) { - var date = DateTimeOffset.FromUnixTimeSeconds(bink.Time / 1000000000).DateTime.ToLocalTime(); - bink.TimeString = date.ToString("HH:mm (dddd, MMMM dd, yyyy)"); + SetBinkDateString(bink); Binks.Add(bink); } } binksListView.ItemsSource = Binks; } + public void SetBinkDateString (BinkPost bink) + { + var date = DateTimeOffset.FromUnixTimeSeconds(bink.Time / 1000000000).DateTime.ToLocalTime(); + bink.TimeString = date.ToString("HH:mm (dddd, MMMM dd, yyyy)"); + } + public void GetNewBinks() + { + var BinkList = Bink.BinksAfter(Binks[0].Time); + if (BinkList != null) + { + BinkList.Reverse(); + foreach (var bink in BinkList) + { + SetBinkDateString(bink); + Binks.Insert(0, bink); + } + } + } + private void BinkSubmit(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + var message = binkComposeBox.Text; + if (!string.IsNullOrEmpty(message)) + { + Bink.PostBink(message); + binkComposeBox.Text = string.Empty; + GetNewBinks(); + } + + } + private void OnTextChanged(object sender, TextChangedEventArgs e) + { + if (string.IsNullOrEmpty(binkComposeBox.Text)) + { + binkComposeDialog.IsPrimaryButtonEnabled = false; + } + else + { + binkComposeDialog.IsPrimaryButtonEnabled = true; + } + } + + private async void OnBinkCreate(object sender, RoutedEventArgs e) + { + binkComposeDialog.IsPrimaryButtonEnabled = false; + await binkComposeDialog.ShowAsync(); + } + public void GetNewBinksButton(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + { + GetNewBinks(); + } + + private void StartTimer() + { + timer = new DispatcherTimer(); + timer.Interval = TimeSpan.FromSeconds(20); + timer.Tick += Timer_Tick; + timer.Start(); + } + + private void Timer_Tick(object? sender, object? e) + { + GetNewBinks(); + } } } diff --git a/Feels.cs b/Feels.cs new file mode 100644 index 0000000..f454d36 --- /dev/null +++ b/Feels.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace wakka +{ + public class FeelsPost + { + [JsonPropertyName("user")] + public string? User { get; set; } + [JsonPropertyName("path")] + public string? Path { get; set; } + [JsonPropertyName("m_time")] + public long M_Time { get; set; } + public string TimeString { get; set; } = string.Empty; + public string Body { get; set; } = string.Empty; + public int WordCount { get; set; } = 0; + } + + internal class Feels + { + private static SshConnection Ssh { get; set; } = new SshConnection(); + + public List? AllFeels() + { + return JsonSerializer.Deserialize>(Ssh.RunCommand("~nebula/bin/dumpfeels")); + } + + public List? FeelsAfter(long TimeStamp) + { + return JsonSerializer.Deserialize>(Ssh.RunCommand($"~nebula/bin/dumpfeels --after {TimeStamp}")); + } + + public List? FeelsFromUser(string User, List Feels) + { + return Feels.FindAll(i => i.User == User); + } + + public string GetFeelsBody(FeelsPost Post) + { + return Ssh.RunCommand($"cat {Post.Path}"); + } + } +} diff --git a/FeelsFeedPage.xaml b/FeelsFeedPage.xaml new file mode 100644 index 0000000..23d13e9 --- /dev/null +++ b/FeelsFeedPage.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FeelsFeedPage.xaml.cs b/FeelsFeedPage.xaml.cs new file mode 100644 index 0000000..b13c80c --- /dev/null +++ b/FeelsFeedPage.xaml.cs @@ -0,0 +1,59 @@ +using Microsoft.UI.Xaml.Controls; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace wakka +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class FeelsFeedPage : Page + { + private readonly Feels Feels = new Feels(); + private readonly ObservableCollection LiveFeelsPosts = []; + private readonly List> AllFeels; + private int CurrentChunk = 0; + + public FeelsFeedPage() + { + this.InitializeComponent(); + var feels = Feels.AllFeels(); + AllFeels = (List>)feels.Chunk(4).Select(chunk => chunk.ToList()).ToList(); + LoadFeelsChunk(); + feelsListView.ItemsSource = LiveFeelsPosts; + } + + private void LoadFeelsChunk() + { + var chunk = AllFeels[CurrentChunk]; + foreach (var feel in chunk) + { + var date = DateTimeOffset.FromUnixTimeSeconds(feel.M_Time).DateTime.ToLocalTime(); + feel.TimeString = date.ToString("HH:mm (dddd, MMMM dd, yyyy)"); + var body = Feels.GetFeelsBody(feel); + char[] delimiters = new char[] { ' ', '\n' }; + feel.WordCount = body.Split(delimiters, StringSplitOptions.RemoveEmptyEntries).Length; + feel.Body = body; + LiveFeelsPosts.Add(feel); + } + CurrentChunk++; + } + + private void ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) + { + var scrollViewer = sender as ScrollViewer; + if (scrollViewer != null) + { + if (scrollViewer.VerticalOffset >= scrollViewer.ScrollableHeight) + { + LoadFeelsChunk(); + } + } + } + } +} diff --git a/FeelsNavigationPage.xaml b/FeelsNavigationPage.xaml new file mode 100644 index 0000000..90d0898 --- /dev/null +++ b/FeelsNavigationPage.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + diff --git a/FeelsNavigationPage.xaml.cs b/FeelsNavigationPage.xaml.cs new file mode 100644 index 0000000..28fa9f2 --- /dev/null +++ b/FeelsNavigationPage.xaml.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Microsoft.UI.Xaml.Media.Animation; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace wakka +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class FeelsNavigationPage : Page + { + public FeelsNavigationPage() + { + this.InitializeComponent(); + } + + private void SelectorChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args) + { + FeelsNavigationFrame.Navigate(typeof(FeelsFeedPage)); + } + } +} diff --git a/FeelsUserPage.xaml b/FeelsUserPage.xaml new file mode 100644 index 0000000..cecd6ee --- /dev/null +++ b/FeelsUserPage.xaml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/FeelsUserPage.xaml.cs b/FeelsUserPage.xaml.cs new file mode 100644 index 0000000..458b599 --- /dev/null +++ b/FeelsUserPage.xaml.cs @@ -0,0 +1,31 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace wakka +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class FeelsUserPage : Page + { + public FeelsUserPage() + { + this.InitializeComponent(); + } + } +} diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index f61a766..53f68d0 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -1,18 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using Windows.Foundation; -using Windows.Foundation.Collections; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -28,28 +15,27 @@ namespace wakka { private static SshConnection Ssh { get; set; } = new SshConnection(); public Frame RootFrame; - public MainWindow() { this.InitializeComponent(); - this.AppWindow.MoveAndResize(new Windows.Graphics.RectInt32(100, 100, 854, 480)); - Ssh.DeleteSshKey(); RootFrame = rootFrame; + Ssh.DeleteSshKey(); if (Ssh.KeyExists() & (string)App.LocalSettingsData.Values["username"] != null) { - Ssh.InitializeConnection((string)App.LocalSettingsData.Values["username"]); - RootFrame.Content = new NavigationPage(); + try + { + Ssh.InitializeConnection((string)App.LocalSettingsData.Values["username"]); + RootFrame.Content = new NavigationPage(); + } + catch (Renci.SshNet.Common.SshAuthenticationException) + { + RootFrame.Content = new StartupSshPage(); + } } else { RootFrame.Content = new StartupSshPage(); } } - - - //private void myButton_Click(object sender, RoutedEventArgs e) - //{ - // myButton.Content = "Clicked"; - //} } } diff --git a/NavigationPage.xaml b/NavigationPage.xaml index f44a08f..4a78e12 100644 --- a/NavigationPage.xaml +++ b/NavigationPage.xaml @@ -6,13 +6,13 @@ xmlns:local="using:wakka" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d"> + mc:Ignorable="d" + Background="{ThemeResource SolidBackgroundFillColorSecondaryBrush}"> - + - - + diff --git a/NavigationPage.xaml.cs b/NavigationPage.xaml.cs index 9c6a964..6af7980 100644 --- a/NavigationPage.xaml.cs +++ b/NavigationPage.xaml.cs @@ -26,7 +26,21 @@ namespace wakka public NavigationPage() { this.InitializeComponent(); - navigationFrame.Content = new BinkPage(); + } + + private void navigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) + { + if (args.SelectedItem is NavigationViewItem selectedItem) + { + if (selectedItem == binkPageNav) + { + navigationFrame.Navigate(typeof(BinkPage)); + } + else if (selectedItem == feelsPageNav) + { + navigationFrame.Navigate(typeof(FeelsNavigationPage)); + } + } } } } diff --git a/SshConnection.cs b/SshConnection.cs index 1ff9ade..7904e88 100644 --- a/SshConnection.cs +++ b/SshConnection.cs @@ -6,6 +6,10 @@ using Renci.SshNet; using Renci.SshNet.Common; using System; using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Windows.Media.Protection.PlayReady; namespace wakka { @@ -56,6 +60,25 @@ namespace wakka return rc.Result; } + public async void RunCommandWithInput(string command, string input) + { + if (Client == null || !Client.IsConnected) + { + throw new InvalidOperationException("SSH client is not connected."); + } + using (SshCommand task = Client.CreateCommand(command)) + { + Task executeTask = task.ExecuteAsync(CancellationToken.None); + + using (Stream inputStream = task.CreateInputStream()) + { + inputStream.Write(Encoding.UTF8.GetBytes(input)); + } + + await executeTask; + } + } + public string CreateSshKey() { var keygen = new SshKeyGenerator.SshKeyGenerator(2048); diff --git a/StartupSshPage.xaml b/StartupSshPage.xaml index 45e86e5..76f9a05 100644 --- a/StartupSshPage.xaml +++ b/StartupSshPage.xaml @@ -7,7 +7,11 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - + @@ -18,9 +22,9 @@ - -