diff --git a/ModerationClient/ViewModels/RoomViewModel.cs b/ModerationClient/ViewModels/RoomViewModel.cs
new file mode 100644
index 0000000..c18b842
--- /dev/null
+++ b/ModerationClient/ViewModels/RoomViewModel.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Frozen;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using ArcaneLibs;
+using Avalonia;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+using LibMatrix;
+using LibMatrix.EventTypes.Spec.State;
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Homeservers;
+using ModerationClient.Models.SpaceTreeNodes;
+
+namespace ModerationClient.ViewModels;
+
+public class RoomViewModel : ViewModelBase {
+ public RoomViewModel(AuthenticatedHomeserverGeneric homeserver, RoomNode room) {
+ Homeserver = homeserver;
+ Room = room;
+ room.Timeline.CollectionChanged += (_, args) => {
+ foreach (var obj in args.NewItems ?? ImmutableList<object>.Empty) {
+ if (obj is StateEventResponse evt) {
+ if (evt.Type == "m.room.member")
+ Users = room.State.Where(x => x.Type == "m.room.member").Select(x => {
+ var tc = x.TypedContent as RoomMemberEventContent;
+ return new RoomMember(homeserver) {
+ AvatarUrl = tc.AvatarUrl,
+ DisplayName = tc.DisplayName,
+ UserId = x.StateKey!
+ };
+ }).ToFrozenSet();
+ }
+ }
+ };
+
+ Users = room.State.Where(x => x.Type == "m.room.member" && x.ContentAs<RoomMemberEventContent>()?.Membership == "join").Select(x => {
+ var tc = x.TypedContent as RoomMemberEventContent;
+ return new RoomMember(homeserver) {
+ AvatarUrl = tc.AvatarUrl,
+ DisplayName = tc.DisplayName,
+ UserId = x.StateKey!
+ };
+ }).ToFrozenSet();
+ }
+
+ public AuthenticatedHomeserverGeneric Homeserver { get; }
+ public RoomNode Room { get; }
+ public Bitmap? RoomIconBitmap { get; }
+ public FrozenSet<RoomMember> Users { get; private set; } = FrozenSet<RoomMember>.Empty;
+}
+
+public class RoomMember(AuthenticatedHomeserverGeneric homeserver) : NotifyPropertyChanged {
+ public string UserId { get; set; }
+ public string DisplayName { get; set; }
+ private static readonly string MediaDir = Path.Combine("/tmp", "ModerationClient", "media");
+
+ public string AvatarUrl {
+ get;
+ set {
+ field = value;
+
+ if(!Directory.Exists(MediaDir))
+ Directory.CreateDirectory(MediaDir);
+
+ var fsPath = Path.Combine(MediaDir, AvatarUrl.Replace("/", "_"));
+ Console.WriteLine($"Avatar path for {UserId}: {fsPath}");
+ if (File.Exists(fsPath)) {
+ UserAvatarBitmap = new Bitmap(fsPath);
+ Console.WriteLine($"Avatar bitmap for {UserId} loaded: {UserAvatarBitmap.GetHashCode()}, Path: {fsPath}");
+ return;
+ }
+
+ homeserver.GetMediaStreamAsync(field).ContinueWith(async streamTask => {
+ try {
+ await using var stream = await streamTask;
+ Console.WriteLine($"Avatar stream for {UserId} received: {stream.GetHashCode()}");
+ // await using var ms = new MemoryStream();
+ // await stream.CopyToAsync(ms);
+ // var fs = new FileStream("avatar.png", FileMode.Create);
+ // ms.Seek(0, SeekOrigin.Begin);
+ // Console.WriteLine($"Avatar stream for {UserId} copied: {ms.GetHashCode()}");
+ // var bm = new Bitmap(ms);
+ // Console.WriteLine($"Avatar bitmap for {UserId} loaded: {bm.GetHashCode()}");
+ // var sbm = bm.CreateScaledBitmap(new(32, 32));
+ // Console.WriteLine($"Avatar bitmap for {UserId} loaded: {sbm.GetHashCode()}");
+ // UserAvatarBitmap = sbm;
+ // Console.WriteLine($"Bitmap for {UserId} set to {UserAvatarBitmap.GetHashCode()}");
+
+ var fs = new FileStream(fsPath, FileMode.Create);
+ await stream.CopyToAsync(fs);
+ fs.Close();
+ UserAvatarBitmap = new Bitmap(fsPath);
+ Console.WriteLine($"Avatar bitmap for {UserId} loaded: {UserAvatarBitmap.GetHashCode()}, Path: {fsPath}");
+ }
+ catch (Exception e) {
+ Console.WriteLine($"Failed to load avatar for {UserId}: {e}");
+ UserAvatarBitmap = new Bitmap(PixelFormat.Rgba8888, AlphaFormat.Unpremul, 1, PixelSize.Empty, Vector.Zero, 0);
+ }
+ });
+ // UserAvatarBitmap = new Bitmap();
+ }
+ }
+
+ public Bitmap UserAvatarBitmap {
+ get;
+ private set => SetField(ref field, value);
+ }
+}
\ No newline at end of file
|