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
|