about summary refs log tree commit diff
diff options
context:
space:
mode:
m---------LibMatrix0
-rw-r--r--ModerationClient/Services/MatrixAuthenticationService.cs2
-rw-r--r--ModerationClient/ViewModels/ClientViewModel.cs138
-rw-r--r--ModerationClient/Views/ClientView.axaml50
-rw-r--r--ModerationClient/Views/ClientView.axaml.cs6
-rw-r--r--ModerationClient/Views/LoginView.axaml.cs19
-rw-r--r--ModerationClient/Views/MainWindow.axaml.cs37
7 files changed, 158 insertions, 94 deletions
diff --git a/LibMatrix b/LibMatrix
-Subproject d04269ea2bc944099d95a32aada7bcd4639969b
+Subproject 3b488242050bbc0521d846bd31cb6ea59b8d4e3
diff --git a/ModerationClient/Services/MatrixAuthenticationService.cs b/ModerationClient/Services/MatrixAuthenticationService.cs
index 69f8810..7e9ce70 100644
--- a/ModerationClient/Services/MatrixAuthenticationService.cs
+++ b/ModerationClient/Services/MatrixAuthenticationService.cs
@@ -44,8 +44,8 @@ public class MatrixAuthenticationService(ILogger<MatrixAuthenticationService> lo
         var mxidParts = username.Split(':', 2);
         var res = await hsProvider.Login(mxidParts[1], username, password);
         await File.WriteAllTextAsync(Path.Combine(cfg.ProfileDirectory, "login.json"), res.ToJson());
-        IsLoggedIn = true;
 
+        await LoadProfileAsync();
         // Console.WriteLine("Login result: " + res.ToJson());
     }
 
diff --git a/ModerationClient/ViewModels/ClientViewModel.cs b/ModerationClient/ViewModels/ClientViewModel.cs
index 340eb56..1e287ec 100644
--- a/ModerationClient/ViewModels/ClientViewModel.cs
+++ b/ModerationClient/ViewModels/ClientViewModel.cs
@@ -1,63 +1,135 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 using ArcaneLibs.Collections;
+using LibMatrix.EventTypes.Spec.State;
 using LibMatrix.Helpers;
 using LibMatrix.Responses;
+using MatrixUtils.Abstractions;
 using Microsoft.Extensions.Logging;
 using ModerationClient.Services;
 
 namespace ModerationClient.ViewModels;
 
-public partial class ClientViewModel : ViewModelBase
-{
-    public ClientViewModel(ILogger<ClientViewModel> logger, MatrixAuthenticationService authService) {
-        this.logger = logger;
-        this.authService = authService;
+public partial class ClientViewModel : ViewModelBase {
+    public ClientViewModel(ILogger<ClientViewModel> logger, MatrixAuthenticationService authService, CommandLineConfiguration cfg) {
+        _logger = logger;
+        _authService = authService;
+        _cfg = cfg;
+        DisplayedSpaces.Add(_allRoomsNode = new AllRoomsSpaceNode(this));
         _ = Task.Run(Run);
     }
-    
-    private readonly ILogger<ClientViewModel> logger;
-    private readonly MatrixAuthenticationService authService;
-    
-    private Exception? _exception;
-
-    public Exception? Exception {
-        get => _exception;
-        private set => SetProperty(ref _exception, value);
-    }
 
-    public ObservableCollection<SpaceNode> DisplayedSpaces { get; } = [
-        new SpaceNode { Name = "Root", Children = [
-            new SpaceNode { Name = "Child 1" },
-            new SpaceNode { Name = "Child 2" },
-            new SpaceNode { Name = "Child 3" }
-        ] },
-        new SpaceNode { Name = "Root 2", Children = [
-            new SpaceNode { Name = "Child 4" },
-            new SpaceNode { Name = "Child 5" },
-            new SpaceNode { Name = "Child 6" }
-        ] }
-    ];
+    private readonly ILogger<ClientViewModel> _logger;
+    private readonly MatrixAuthenticationService _authService;
+    private readonly CommandLineConfiguration _cfg;
+    private SpaceNode? _currentSpace;
+    private readonly SpaceNode _allRoomsNode;
+    public ObservableCollection<SpaceNode> DisplayedSpaces { get; } = [];
+    public ObservableDictionary<string, RoomNode> AllRooms { get; } = new();
+
+    public SpaceNode CurrentSpace {
+        get => _currentSpace ?? _allRoomsNode;
+        set => SetProperty(ref _currentSpace, value);
+    }
 
     public async Task Run() {
-        var sh = new SyncStateResolver(authService.Homeserver, logger);
+        var sh = new SyncStateResolver(_authService.Homeserver, _logger, storageProvider: new FileStorageProvider(Path.Combine(_cfg.ProfileDirectory, "syncCache")));
         // var res = await sh.SyncAsync();
+        //await sh.OptimiseStore();
         while (true) {
             var res = await sh.ContinueAsync();
-            Console.WriteLine("mow");
+            await ApplySpaceChanges(res.next);
+            //OnPropertyChanged(nameof(CurrentSpace));
+            //OnPropertyChanged(nameof(CurrentSpace.ChildRooms));
+            // Console.WriteLine($"mow A={AllRooms.Count}|D={DisplayedSpaces.Count}");
+            // for (int i = 0; i < GC.MaxGeneration; i++) {
+            // GC.Collect(i, GCCollectionMode.Forced, blocking: true);
+            // GC.WaitForPendingFinalizers();
+            // }
         }
     }
 
-    private void ApplySpaceChanges(SyncResponse newSync) {
+    private async Task ApplySpaceChanges(SyncResponse newSync) {
+        List<Task> tasks = [];
+        foreach (var room in newSync.Rooms?.Join ?? []) {
+            if (!AllRooms.ContainsKey(room.Key)) {
+                AllRooms.Add(room.Key, new RoomNode { Name = "Loading..." });
+            }
+
+            if (room.Value.State?.Events is not null) {
+                var nameEvent = room.Value.State!.Events!.FirstOrDefault(x => x.Type == "m.room.name" && x.StateKey == "");
+                AllRooms[room.Key].Name = (nameEvent?.TypedContent as RoomNameEventContent)?.Name ?? "";
+                if (string.IsNullOrWhiteSpace(AllRooms[room.Key].Name)) {
+                    AllRooms[room.Key].Name = "Loading...";
+                    tasks.Add(_authService.Homeserver!.GetRoom(room.Key).GetNameOrFallbackAsync().ContinueWith(r => AllRooms[room.Key].Name = r.Result));
+                }
+            }
+        }
+        
+        // await Task.WhenAll(tasks);
+
+        return;
+
         List<string> handledRoomIds = [];
-       
+        var spaces = newSync.Rooms?.Join?
+            .Where(x => x.Value.State?.Events is not null)
+            .Where(x => x.Value.State!.Events!.Any(y => y.Type == "m.room.create" && (y.TypedContent as RoomCreateEventContent)!.Type == "m.space"))
+            .ToList();
+        Console.WriteLine("spaces: " + spaces.Count);
+        var nonRootSpaces = spaces
+            .Where(x => spaces.Any(x => x.Value.State!.Events!.Any(y => y.Type == "m.space.child" && y.StateKey == x.Key)))
+            .ToDictionary();
+
+        var rootSpaces = spaces
+            .Where(x => !nonRootSpaces.ContainsKey(x.Key))
+            .ToDictionary();
+        // var rootSpaces = spaces
+        // .Where(x=>!spaces.Any(x=>x.Value.State!.Events!.Any(y=>y.Type == "m.space.child" && y.StateKey == x.Key)))
+        // .ToList();
+
+        foreach (var (roomId, room) in rootSpaces) {
+            var space = new SpaceNode { Name = (room.State!.Events!.First(x => x.Type == "m.room.name")!.TypedContent as RoomNameEventContent).Name };
+            DisplayedSpaces.Add(space);
+            handledRoomIds.Add(roomId);
+        }
     }
 }
 
-public class SpaceNode {
+public class SpaceNode : RoomNode {
+    public ObservableCollection<SpaceNode> ChildSpaces { get; set; } = [];
+    public ObservableCollection<RoomNode> ChildRooms { get; set; } = [];
+}
+
+public class RoomNode {
     public string Name { get; set; }
-    public ObservableCollection<SpaceNode> Children { get; set;  } = [];
+}
+
+// implementation details
+public class AllRoomsSpaceNode : SpaceNode {
+    public AllRoomsSpaceNode(ClientViewModel vm) {
+        Name = "All rooms";
+        vm.AllRooms.CollectionChanged += (_, args) => {
+            switch (args.Action) {
+                case NotifyCollectionChangedAction.Add:
+                case NotifyCollectionChangedAction.Remove:
+                case NotifyCollectionChangedAction.Replace: {
+                    foreach (var room in args.NewItems?.Cast<KeyValuePair<string, RoomNode>>() ?? []) ChildRooms.Add(room.Value);
+                    foreach (var room in args.OldItems?.Cast<KeyValuePair<string, RoomNode>>() ?? []) ChildRooms.Remove(room.Value);
+                    break;
+                }
+
+                case NotifyCollectionChangedAction.Reset: {
+                    ChildSpaces.Clear();
+                    ChildRooms.Clear();
+                    break;
+                }
+            }
+        };
+    }
 }
\ No newline at end of file
diff --git a/ModerationClient/Views/ClientView.axaml b/ModerationClient/Views/ClientView.axaml
index 21ce5d9..0ed8021 100644
--- a/ModerationClient/Views/ClientView.axaml
+++ b/ModerationClient/Views/ClientView.axaml
@@ -7,24 +7,36 @@
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              x:Class="ModerationClient.Views.ClientView"
              x:DataType="viewModels:ClientViewModel">
-    <Grid Width="{Binding $parent.Width}">
-        <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="128" MinWidth="16" />
-            <ColumnDefinition Width="1" />
-            <ColumnDefinition Width="128" MinWidth="16" />
-            <ColumnDefinition Width="1" />
-            <ColumnDefinition Width="*" MinWidth="16" />
-        </Grid.ColumnDefinitions>
-        <TreeView Grid.Column="0" Background="Red" ItemsSource="{CompiledBinding DisplayedSpaces}">
-            <TreeView.ItemTemplate>
-                <TreeDataTemplate ItemsSource="{Binding Children}">
-                    <TextBlock Text="{Binding Name}" />
-                </TreeDataTemplate>
-            </TreeView.ItemTemplate>
-        </TreeView>
-        <GridSplitter Grid.Column="1" Background="Black" ResizeDirection="Columns" />
-        <Rectangle Grid.Column="2" Fill="Green" />
-        <GridSplitter Grid.Column="3" Background="Black" ResizeDirection="Columns" />
-        <Rectangle Grid.Column="4" Fill="Blue" />
+    <Grid Width="{Binding $parent.Width}" Height="{Binding $parent.Height}" RowDefinitions="*,20">
+        <Grid Grid.Row="0">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="128" MinWidth="16" />
+                <ColumnDefinition Width="1" />
+                <ColumnDefinition Width="128" MinWidth="16" />
+                <ColumnDefinition Width="1" />
+                <ColumnDefinition Width="*" MinWidth="16" />
+            </Grid.ColumnDefinitions>
+            <TreeView Grid.Column="0" Background="Red" ItemsSource="{CompiledBinding DisplayedSpaces}" SelectedItem="{CompiledBinding CurrentSpace}">
+                <TreeView.ItemTemplate>
+                    <TreeDataTemplate ItemsSource="{Binding ChildSpaces}">
+                        <TextBlock Text="{Binding Name}" />
+                    </TreeDataTemplate>
+                </TreeView.ItemTemplate>
+            </TreeView>
+            <GridSplitter Grid.Column="1" Background="Black" ResizeDirection="Columns" />
+            <!-- <Rectangle Grid.Column="2" Fill="Green" /> -->
+            <ListBox Grid.Column="2" Background="Green" ItemsSource="{CompiledBinding CurrentSpace.ChildRooms}">
+                <ListBox.ItemTemplate>
+                    <DataTemplate DataType="viewModels:RoomNode">
+                        <Label Content="{CompiledBinding Name}" />
+                    </DataTemplate>
+                </ListBox.ItemTemplate>
+            </ListBox>
+            <GridSplitter Grid.Column="3" Background="Black" ResizeDirection="Columns" />
+            <Rectangle Grid.Column="4" Fill="Blue" />
+        </Grid>
+        <Grid Grid.Row="1" ColumnDefinitions="Auto, *, Auto">
+            <Label Grid.Column="2">Text here</Label>
+        </Grid>
     </Grid>
 </UserControl>
\ No newline at end of file
diff --git a/ModerationClient/Views/ClientView.axaml.cs b/ModerationClient/Views/ClientView.axaml.cs
index 1ca5a89..894e807 100644
--- a/ModerationClient/Views/ClientView.axaml.cs
+++ b/ModerationClient/Views/ClientView.axaml.cs
@@ -1,5 +1,3 @@
-using System;
-using System.Linq;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 
@@ -26,8 +24,4 @@ public partial class ClientView : UserControl {
         //     }
         // };
     }
-    
-    private void InitializeComponent() {
-        AvaloniaXamlLoader.Load(this);
-    }
 }
diff --git a/ModerationClient/Views/LoginView.axaml.cs b/ModerationClient/Views/LoginView.axaml.cs
index 2e95e80..5e84ace 100644
--- a/ModerationClient/Views/LoginView.axaml.cs
+++ b/ModerationClient/Views/LoginView.axaml.cs
@@ -1,33 +1,18 @@
 using System;
-using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Interactivity;
 using Avalonia.Markup.Xaml;
-using Avalonia.VisualTree;
-using Microsoft.Extensions.DependencyInjection;
-using ModerationClient.Services;
 using ModerationClient.ViewModels;
 
 namespace ModerationClient.Views;
 
 public partial class LoginView : UserControl {
-    private MatrixAuthenticationService AuthService { get; set; }
-    
     public LoginView() {
         InitializeComponent();
     }
-    
-    private void InitializeComponent() {
-        Console.WriteLine("LoginWindow loaded");
-
-        AvaloniaXamlLoader.Load(this);
-        Console.WriteLine("LoginWindow loaded 2");
-    }
 
     // ReSharper disable once AsyncVoidMethod
-    private async void Login(object? sender, RoutedEventArgs e) {
-        Console.WriteLine("Login????");
-        // await AuthService.LoginAsync(Username, Password);
-        await ((LoginViewModel)DataContext).LoginAsync();
+    private async void Login(object? _, RoutedEventArgs __) {
+        await (DataContext as LoginViewModel ?? throw new InvalidCastException("LoginView did not receive LoginViewModel?")).LoginAsync();
     }
 }
diff --git a/ModerationClient/Views/MainWindow.axaml.cs b/ModerationClient/Views/MainWindow.axaml.cs
index ccabd71..884e90c 100644
--- a/ModerationClient/Views/MainWindow.axaml.cs
+++ b/ModerationClient/Views/MainWindow.axaml.cs
@@ -1,10 +1,8 @@
 using System;
-using System.Threading;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Diagnostics;
 using Avalonia.Input;
-using CommunityToolkit.Mvvm.Input;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using ModerationClient.Services;
@@ -13,11 +11,10 @@ using ModerationClient.ViewModels;
 namespace ModerationClient.Views;
 
 public partial class MainWindow : Window {
-    //viewmodel
-    private MainWindowViewModel? _viewModel { get; set; }
-
     public MainWindow(CommandLineConfiguration cfg, MainWindowViewModel dataContext, IHostApplicationLifetime appLifetime) {
         InitializeComponent();
+        DataContext = dataContext;
+        _ = dataContext.AuthService.LoadProfileAsync();
         Console.WriteLine("mainwnd");
 #if DEBUG
         this.AttachDevTools(new DevToolsOptions() {
@@ -30,19 +27,17 @@ public partial class MainWindow : Window {
             switch (args.Property.Name) {
                 case nameof(Height):
                 case nameof(Width): {
-                    if (_viewModel is null) {
+                    if (DataContext is not MainWindowViewModel viewModel) {
                         Console.WriteLine("WARN: MainWindowViewModel is null, ignoring height/width change!");
                         return;
                     }
 
                     // Console.WriteLine("height/width changed");
-                    _viewModel.Scale = _viewModel.Scale;
+                    viewModel.Scale = viewModel.Scale;
                     break;
                 }
             }
         };
-        DataContext = _viewModel = dataContext;
-        _ = dataContext.AuthService.LoadProfileAsync();
         dataContext.AuthService.PropertyChanged += (sender, args) => {
             if (args.PropertyName == nameof(MatrixAuthenticationService.IsLoggedIn)) {
                 if (dataContext.AuthService.IsLoggedIn) {
@@ -68,26 +63,32 @@ public partial class MainWindow : Window {
     protected override void OnKeyDown(KeyEventArgs e) => OnKeyDown(this, e);
 
     private void OnKeyDown(object? _, KeyEventArgs e) {
-        if (_viewModel is null) {
-            Console.WriteLine("WARN: MainWindowViewModel is null, ignoring key press!");
+        if (DataContext is not MainWindowViewModel viewModel) {
+            Console.WriteLine($"WARN: DataContext is {DataContext?.GetType().Name ?? "null"}, ignoring key press!");
             return;
         }
 
         // Console.WriteLine("MainWindow KeyDown: " + e.Key);
         if (e.Key == Key.Escape) {
-            _viewModel.Scale = 1.0f;
+            viewModel.Scale = 1.0f;
         }
         else if (e.Key == Key.F1) {
-            _viewModel.Scale -= 0.1f;
-            if (_viewModel.Scale < 0.1f) {
-                _viewModel.Scale = 0.1f;
+            viewModel.Scale -= 0.1f;
+            if (viewModel.Scale < 0.1f) {
+                viewModel.Scale = 0.1f;
             }
         }
         else if (e.Key == Key.F2) {
-            _viewModel.Scale += 0.1f;
-            if (_viewModel.Scale > 5.0f) {
-                _viewModel.Scale = 5.0f;
+            viewModel.Scale += 0.1f;
+            if (viewModel.Scale > 5.0f) {
+                viewModel.Scale = 5.0f;
+            }
+        }
+        else if (e.Key == Key.K && e.KeyModifiers == KeyModifiers.Control) {
+            if(viewModel.CurrentViewModel is ClientViewModel clientViewModel) {
+                Console.WriteLine("QuickSwitcher invoked");
             }
+            else Console.WriteLine("WARN: CurrentViewModel is not ClientViewModel, ignoring Quick Switcher");
         }
     }
 }
\ No newline at end of file