diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
index 7a1f5de..502fe5b 100644
--- a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
+++ b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
@@ -59,6 +59,18 @@ public class AuthenticatedHomeServer : IHomeServer
return rooms;
}
+ public async Task<string> UploadFile(string fileName, Stream fileStream, string contentType = "application/octet-stream")
+ {
+ var res = await _httpClient.PostAsync($"/_matrix/media/r0/upload?filename={fileName}", new StreamContent(fileStream));
+ if (!res.IsSuccessStatusCode)
+ {
+ Console.WriteLine($"Failed to upload file: {await res.Content.ReadAsStringAsync()}");
+ throw new InvalidDataException($"Failed to upload file: {await res.Content.ReadAsStringAsync()}");
+ }
+ var resJson = await res.Content.ReadFromJsonAsync<JsonElement>();
+ return resJson.GetProperty("content_uri").GetString()!;
+ }
+
diff --git a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs
index 7bcd75f..b4b8d19 100644
--- a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs
+++ b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs
@@ -1,5 +1,6 @@
using System.Net.Http.Json;
using System.Text.Json;
+using MatrixRoomUtils.Core.Extensions;
using MatrixRoomUtils.Core.Responses;
namespace MatrixRoomUtils.Core.Authentication;
@@ -33,7 +34,7 @@ public class MatrixAuth
await Task.Delay(retryAfter.GetInt32());
return await Login(homeserver, username, password);
}
-
+ Console.WriteLine($"Login: {data.ToJson()}");
return data.Deserialize<LoginResponse>();
//var token = data.GetProperty("access_token").GetString();
//return token;
diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
index 8fb8b2c..9f7bfee 100644
--- a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
+++ b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
@@ -108,7 +108,7 @@ public class IHomeServer
_profileCache[mxid] = profile;
return profile;
}
- public async Task<string> ResolveMediaUri(string mxc)
+ public string ResolveMediaUri(string mxc)
{
return mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/r0/download/");
}
diff --git a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs
new file mode 100644
index 0000000..6949b1a
--- /dev/null
+++ b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs
@@ -0,0 +1,217 @@
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
+
+namespace MatrixRoomUtils.Core.Responses;
+
+public class CreateRoomRequest
+{
+ [JsonPropertyName("name")] public string Name { get; set; } = null!;
+
+ [JsonPropertyName("room_alias_name")] public string RoomAliasName { get; set; } = null!;
+
+ //we dont want to use this, we want more control
+ // [JsonPropertyName("preset")]
+ // public string Preset { get; set; } = null!;
+ [JsonPropertyName("initial_state")] public List<StateEvent> InitialState { get; set; } = null!;
+ [JsonPropertyName("visibility")] public string Visibility { get; set; } = null!;
+
+ [JsonPropertyName("power_level_content_override")]
+ public PowerLevelEvent PowerLevelContentOverride { get; set; } = null!;
+
+ [JsonPropertyName("creation_content")] public JsonObject CreationContent { get; set; } = new();
+
+ /// <summary>
+ /// For use only when you can't use the CreationContent property
+ /// </summary>
+
+
+ //extra properties
+ [JsonIgnore]
+ public string HistoryVisibility
+ {
+ get
+ {
+ var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.history_visibility");
+ if (stateEvent == null)
+ {
+ InitialState.Add(new StateEvent()
+ {
+ Type = "m.room.history_visibility",
+ Content = new JsonObject()
+ {
+ ["history_visibility"] = "shared"
+ }
+ });
+ return "shared";
+ }
+
+ return stateEvent.ContentAsJsonNode["history_visibility"].GetValue<string>();
+ }
+ set
+ {
+ var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.history_visibility");
+ if (stateEvent == null)
+ {
+ InitialState.Add(new StateEvent()
+ {
+ Type = "m.room.history_visibility",
+ Content = new JsonObject()
+ {
+ ["history_visibility"] = value
+ }
+ });
+ }
+ else
+ {
+ var v = stateEvent.ContentAsJsonNode;
+ v["history_visibility"] = value;
+ stateEvent.ContentAsJsonNode = v;
+ }
+ }
+ }
+
+ [JsonIgnore]
+ public string RoomIcon
+ {
+ get
+ {
+ var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.avatar");
+ if (stateEvent == null)
+ {
+ InitialState.Add(new StateEvent()
+ {
+ Type = "m.room.avatar",
+ Content = new JsonObject()
+ {
+ ["url"] = ""
+ }
+ });
+ return "";
+ }
+
+ return stateEvent.ContentAsJsonNode["url"].GetValue<string>();
+ }
+ set
+ {
+ var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.avatar");
+ if (stateEvent == null)
+ {
+ InitialState.Add(new StateEvent()
+ {
+ Type = "m.room.avatar",
+ Content = new JsonObject()
+ {
+ ["url"] = value
+ }
+ });
+ }
+ else
+ {
+ var v = stateEvent.ContentAsJsonNode;
+ v["url"] = value;
+ stateEvent.ContentAsJsonNode = v;
+ }
+ }
+ }
+
+ [JsonIgnore]
+ public string GuestAccess
+ {
+ get
+ {
+ var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.guest_access");
+ if (stateEvent == null)
+ {
+ InitialState.Add(new StateEvent()
+ {
+ Type = "m.room.guest_access",
+ Content = new JsonObject()
+ {
+ ["guest_access"] = "can_join"
+ }
+ });
+ return "can_join";
+ }
+
+ return stateEvent.ContentAsJsonNode["guest_access"].GetValue<string>();
+ }
+ set
+ {
+ var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.guest_access");
+ if (stateEvent == null)
+ {
+ InitialState.Add(new StateEvent()
+ {
+ Type = "m.room.guest_access",
+ Content = new JsonObject()
+ {
+ ["guest_access"] = value
+ }
+ });
+ }
+ else
+ {
+ var v = stateEvent.ContentAsJsonNode;
+ v["guest_access"] = value;
+ stateEvent.ContentAsJsonNode = v;
+ }
+ }
+ }
+
+
+ [JsonIgnore] public CreationContentBaseType _creationContentBaseType;
+
+ public CreateRoomRequest() => _creationContentBaseType = new(this);
+
+
+ public Dictionary<string, string> Validate()
+ {
+ Dictionary<string, string> errors = new();
+ if (!Regex.IsMatch(RoomAliasName, @"[a-zA-Z0-9_\-]+$"))
+ errors.Add("room_alias_name", "Room alias name must only contain letters, numbers, underscores, and hyphens.");
+
+ return errors;
+ }
+}
+
+public class CreationContentBaseType
+{
+ private readonly CreateRoomRequest createRoomRequest;
+
+ public CreationContentBaseType(CreateRoomRequest createRoomRequest)
+ {
+ this.createRoomRequest = createRoomRequest;
+ }
+
+ [JsonPropertyName("type")]
+ public string Type
+ {
+ get => (string)createRoomRequest.CreationContent["type"];
+ set
+ {
+ if (value is "null" or "") createRoomRequest.CreationContent.Remove("type");
+ else createRoomRequest.CreationContent["type"] = value;
+ }
+ }
+}
+
+public class PowerLevelEvent
+{
+ [JsonPropertyName("ban")] public int Ban { get; set; } // = 50;
+ [JsonPropertyName("events_default")] public int EventsDefault { get; set; } // = 0;
+ [JsonPropertyName("events")] public Dictionary<string, int> Events { get; set; } // = null!;
+ [JsonPropertyName("invite")] public int Invite { get; set; } // = 50;
+ [JsonPropertyName("kick")] public int Kick { get; set; } // = 50;
+ [JsonPropertyName("notifications")] public NotificationsPL NotificationsPl { get; set; } // = null!;
+ [JsonPropertyName("redact")] public int Redact { get; set; } // = 50;
+ [JsonPropertyName("state_default")] public int StateDefault { get; set; } // = 50;
+ [JsonPropertyName("users")] public Dictionary<string, int> Users { get; set; } // = null!;
+ [JsonPropertyName("users_default")] public int UsersDefault { get; set; } // = 0;
+}
+
+public class NotificationsPL
+{
+ [JsonPropertyName("room")] public int Room { get; set; } = 50;
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs
new file mode 100644
index 0000000..d86f546
--- /dev/null
+++ b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs
@@ -0,0 +1,34 @@
+using System.Text.Json.Serialization;
+
+namespace MatrixRoomUtils.Core;
+
+public class StateEventResponse
+{
+ [JsonPropertyName("Content")]
+ public dynamic Content { get; set; }
+ [JsonPropertyName("origin_server_ts")]
+ public long OriginServerTs { get; set; }
+ [JsonPropertyName("RoomId")]
+ public string RoomId { get; set; }
+ [JsonPropertyName("Sender")]
+ public string Sender { get; set; }
+ [JsonPropertyName("StateKey")]
+ public string StateKey { get; set; }
+ [JsonPropertyName("Type")]
+ public string Type { get; set; }
+ [JsonPropertyName("Unsigned")]
+ public dynamic Unsigned { get; set; }
+ [JsonPropertyName("EventId")]
+ public string EventId { get; set; }
+ [JsonPropertyName("UserId")]
+ public string UserId { get; set; }
+ [JsonPropertyName("ReplacesState")]
+ public string ReplacesState { get; set; }
+ [JsonPropertyName("PrevContent")]
+ public dynamic PrevContent { get; set; }
+}
+
+public class StateEventResponse<T> : StateEventResponse where T : class
+{
+ public T content { get; set; }
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs
index 362abf4..b96546e 100644
--- a/MatrixRoomUtils.Core/Room.cs
+++ b/MatrixRoomUtils.Core/Room.cs
@@ -40,14 +40,6 @@ public class Room
}
var cache = RuntimeCache.GenericResponseCache[cache_key];
- cache.DefaultExpiry = type switch
- {
- "m.room.name" => TimeSpan.FromMinutes(30),
- "org.matrix.mjolnir.shortcode" => TimeSpan.FromHours(4),
- "" => TimeSpan.FromSeconds(0),
- _ => TimeSpan.FromMinutes(15)
- };
-
if (cache.ContainsKey(stateCombo))
{
if (cache[stateCombo].ExpiryTime > DateTime.Now)
@@ -76,14 +68,28 @@ public class Room
}
var result = await res.Content.ReadFromJsonAsync<JsonElement>();
-
- cache[stateCombo] = new GenericResult<object>()
+ var expiryTime = type switch
{
- Result = result
+ "m.room.name" => TimeSpan.FromMinutes(30),
+ "org.matrix.mjolnir.shortcode" => TimeSpan.FromHours(4),
+ "" => TimeSpan.FromSeconds(0),
+ _ => TimeSpan.FromMinutes(15)
};
+ if(!string.IsNullOrWhiteSpace(type) && !string.IsNullOrWhiteSpace(state_key))
+ cache[stateCombo] = new GenericResult<object>()
+ {
+ Result = result,
+ ExpiryTime = DateTime.Now.Add(expiryTime)
+ };
_semaphore.Release();
return result;
}
+ public async Task<T?> GetStateAsync<T>(string type, string state_key = "", bool logOnFailure = false)
+ {
+ var res = await GetStateAsync(type, state_key, logOnFailure);
+ if (res == null) return default;
+ return res.Value.Deserialize<T>();
+ }
public async Task<string> GetNameAsync()
{
@@ -115,8 +121,8 @@ public class Room
var members = new List<string>();
foreach (var member in res.Value.EnumerateArray())
{
- if(member.GetProperty("type").GetString() != "m.room.member") continue;
- var member_id = member.GetProperty("state_key").GetString();
+ if(member.GetProperty("Type").GetString() != "m.room.member") continue;
+ var member_id = member.GetProperty("StateKey").GetString();
members.Add(member_id);
}
@@ -196,7 +202,7 @@ public class CreateEvent
[JsonPropertyName("room_version")]
public string RoomVersion { get; set; }
[JsonPropertyName("type")]
- public string Type { get; set; }
+ public string? Type { get; set; }
[JsonPropertyName("predecessor")]
public object? Predecessor { get; set; }
diff --git a/MatrixRoomUtils.Core/RuntimeCache.cs b/MatrixRoomUtils.Core/RuntimeCache.cs
index 4e756a7..380a150 100644
--- a/MatrixRoomUtils.Core/RuntimeCache.cs
+++ b/MatrixRoomUtils.Core/RuntimeCache.cs
@@ -23,6 +23,21 @@ public class RuntimeCache
{
Console.WriteLine($"RuntimeCache.SaveObject({key}, {value}) was called, but no callback was set!");
};
+
+ static RuntimeCache()
+ {
+ Task.Run(async () =>
+ {
+ while (true)
+ {
+ await Task.Delay(1000);
+ foreach (var (key, value) in RuntimeCache.GenericResponseCache)
+ {
+ SaveObject("rory.matrixroomutils.generic_cache:" + key, value);
+ }
+ }
+ });
+ }
}
@@ -41,7 +56,6 @@ public class HomeServerResolutionResult
public class ObjectCache<T> where T : class
{
public Dictionary<string, GenericResult<T>> Cache { get; set; } = new();
- public TimeSpan DefaultExpiry { get; set; } = new(0, 0, 0);
public string Name { get; set; } = null!;
public GenericResult<T> this[string key]
{
@@ -49,19 +63,12 @@ public class ObjectCache<T> where T : class
{
if (Cache.ContainsKey(key))
{
- Console.WriteLine($"cache.get({key}): hit");
+ // Console.WriteLine($"cache.get({key}): hit");
// Console.WriteLine($"Found item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}");
- if(Cache[key].ExpiryTime > DateTime.Now)
- return Cache[key];
- Console.WriteLine($"Expired item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}");
- try
- {
- Cache.Remove(key);
- }
- catch (Exception e)
- {
- Console.WriteLine($"Failed to remove {key} from cache: {e.Message}");
- }
+ if(Cache[key].ExpiryTime < DateTime.Now)
+ Console.WriteLine($"WARNING: item {key} in cache {Name} expired at {Cache[key].ExpiryTime}:\n{Cache[key].Result.ToJson(indent: false)}");
+ return Cache[key];
+
}
Console.WriteLine($"cache.get({key}): miss");
return null;
@@ -69,7 +76,6 @@ public class ObjectCache<T> where T : class
set
{
Cache[key] = value;
- if(Cache[key].ExpiryTime == null) Cache[key].ExpiryTime = DateTime.Now.Add(DefaultExpiry);
Console.WriteLine($"set({key}) = {Cache[key].Result.ToJson(indent:false)}");
Console.WriteLine($"new_state: {this.ToJson(indent:false)}");
// Console.WriteLine($"New item in cache: {key} - {Cache[key].Result.ToJson(indent: false)}");
@@ -90,7 +96,7 @@ public class ObjectCache<T> where T : class
// Console.WriteLine($"Removing {x.Key} from cache");
Cache.Remove(x.Key);
}
- RuntimeCache.SaveObject("rory.matrixroomutils.generic_cache:" + Name, this);
+ //RuntimeCache.SaveObject("rory.matrixroomutils.generic_cache:" + Name, this);
}
});
}
diff --git a/MatrixRoomUtils.Core/StateEvent.cs b/MatrixRoomUtils.Core/StateEvent.cs
index df7267d..2201587 100644
--- a/MatrixRoomUtils.Core/StateEvent.cs
+++ b/MatrixRoomUtils.Core/StateEvent.cs
@@ -1,53 +1,38 @@
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+
namespace MatrixRoomUtils.Core;
public class StateEvent
{
- //example:
- /*
- {
- "content": {
- "avatar_url": "mxc://matrix.org/BnmEjNvGAkStmAoUiJtEbycT",
- "displayname": "X ⊂ Shekhinah | she/her | you",
- "membership": "join"
- },
- "origin_server_ts": 1682668449785,
- "room_id": "!wDPwzxYCNPTkHGHCFT:the-apothecary.club",
- "sender": "@kokern:matrix.org",
- "state_key": "@kokern:matrix.org",
- "type": "m.room.member",
- "unsigned": {
- "replaces_state": "$7BWfzN15LN8FFUing1hiUQWFfxnOusrEHYFNiOnNrlM",
- "prev_content": {
- "avatar_url": "mxc://matrix.org/hEQbGywixsjpxDrWvUYEFNur",
- "displayname": "X ⊂ Shekhinah | she/her | you",
- "membership": "join"
- },
- "prev_sender": "@kokern:matrix.org"
- },
- "event_id": "$6AGoMCaxqcOeIIDbez1f0VKwLkOEq3EiVLdlsoxDpNg",
- "user_id": "@kokern:matrix.org",
- "replaces_state": "$7BWfzN15LN8FFUing1hiUQWFfxnOusrEHYFNiOnNrlM",
- "prev_content": {
- "avatar_url": "mxc://matrix.org/hEQbGywixsjpxDrWvUYEFNur",
- "displayname": "X ⊂ Shekhinah | she/her | you",
- "membership": "join"
+ [JsonPropertyName("content")]
+ public dynamic Content { get; set; } = new{};
+ [JsonPropertyName("state_key")]
+ public string? StateKey { get; set; }
+ [JsonPropertyName("type")]
+ public string Type { get; set; }
+ [JsonPropertyName("replaces_state")]
+ public string? ReplacesState { get; set; }
+
+ //extra properties
+ [JsonIgnore]
+ public JsonNode ContentAsJsonNode
+ {
+ get => JsonSerializer.SerializeToNode(Content);
+ set => Content = value;
}
- }
- */
- public dynamic content { get; set; }
- public long origin_server_ts { get; set; }
- public string room_id { get; set; }
- public string sender { get; set; }
- public string state_key { get; set; }
- public string type { get; set; }
- public dynamic unsigned { get; set; }
- public string event_id { get; set; }
- public string user_id { get; set; }
- public string replaces_state { get; set; }
- public dynamic prev_content { get; set; }
}
public class StateEvent<T> : StateEvent where T : class
{
- public T content { get; set; }
+ public new T content { get; set; }
+
+
+ [JsonIgnore]
+ public new JsonNode ContentAsJsonNode
+ {
+ get => JsonSerializer.SerializeToNode(Content);
+ set => Content = value.Deserialize<T>();
+ }
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml
index 5e41c43..0125c85 100644
--- a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml
+++ b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml
@@ -14,7 +14,7 @@
<body>
<div class="main">
- <div class="content px-4">
+ <div class="Content px-4">
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
diff --git a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
index 4a00a8a..bd44f7f 100644
--- a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
+++ b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
@@ -5,6 +5,7 @@ namespace MatrixRoomUtils.Web.Classes;
public partial class LocalStorageWrapper
{
+ private static SemaphoreSlim _semaphoreSlim = new(1);
public static Settings Settings { get; set; } = new();
//some basic logic
@@ -15,6 +16,9 @@ public partial class LocalStorageWrapper
}
public static async Task LoadFromLocalStorage(ILocalStorageService localStorage)
{
+ await _semaphoreSlim.WaitAsync();
+ if (RuntimeCache.WasLoaded) return;
+ RuntimeCache.WasLoaded = true;
Settings = await localStorage.GetItemAsync<Settings>("rory.matrixroomutils.settings") ?? new();
//RuntimeCache stuff
@@ -43,7 +47,8 @@ public partial class LocalStorageWrapper
Console.WriteLine($"Loading generic cache entry {s}");
RuntimeCache.GenericResponseCache[s.Replace("rory.matrixroomutils.generic_cache:", "")] = await localStorage.GetItemAsync<ObjectCache<object>>(s);
}
- RuntimeCache.WasLoaded = true;
+
+ _semaphoreSlim.Release();
}
public static async Task SaveToLocalStorage(ILocalStorageService localStorage)
@@ -70,7 +75,7 @@ public partial class LocalStorageWrapper
if (key == "rory.matrixroomutils.user_cache") await localStorage.SetItemAsync(key, RuntimeCache.LoginSessions);
if (key == "rory.matrixroomutils.last_used_token") await localStorage.SetItemAsync(key, RuntimeCache.LastUsedToken);
if (key == "rory.matrixroomutils.homeserver_resolution_cache") await localStorage.SetItemAsync(key, RuntimeCache.HomeserverResolutionCache);
- if (key == "rory.matrixroomutils.generic_cache") await localStorage.SetItemAsync(key, RuntimeCache.GenericResponseCache);
+ //if (key == "rory.matrixroomutils.generic_cache") await localStorage.SetItemAsync(key, RuntimeCache.GenericResponseCache);
}
}
diff --git a/MatrixRoomUtils.Web/FileUploadTest.razor b/MatrixRoomUtils.Web/FileUploadTest.razor
new file mode 100644
index 0000000..2e25b54
--- /dev/null
+++ b/MatrixRoomUtils.Web/FileUploadTest.razor
@@ -0,0 +1,19 @@
+@page "/FileUploadTest"
+<h3>FileUploadTest</h3>
+
+<InputFile OnChange="FilePicked" multiple="false"></InputFile>
+
+
+@code {
+
+ private async void FilePicked(InputFileChangeEventArgs obj)
+ {
+ Console.WriteLine("FilePicked");
+ Console.WriteLine(obj.File.Name);
+ Console.WriteLine(obj.File.Size);
+ Console.WriteLine(obj.File.ContentType);
+ var res = await RuntimeCache.CurrentHomeServer.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType);
+ Console.WriteLine(res);
+ }
+
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
index 77a039c..12555c3 100644
--- a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
+++ b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
@@ -15,13 +15,5 @@
<ItemGroup>
<ProjectReference Include="..\MatrixRoomUtils.Core\MatrixRoomUtils.Core.csproj" />
</ItemGroup>
-
- <ItemGroup>
- <_ContentIncludedByDefault Remove="wwwroot\sample-data\weather.json" />
- </ItemGroup>
-
- <ItemGroup>
- <None Include="wwwroot\homeservers.txt" />
- </ItemGroup>
-
+
</Project>
diff --git a/MatrixRoomUtils.Web/Pages/DataExportPage.razor b/MatrixRoomUtils.Web/Pages/DataExportPage.razor
index 6e1b5ec..58e4111 100644
--- a/MatrixRoomUtils.Web/Pages/DataExportPage.razor
+++ b/MatrixRoomUtils.Web/Pages/DataExportPage.razor
@@ -15,7 +15,7 @@
{
@foreach (var (token, user) in RuntimeCache.LoginSessions)
{
- <IndexUserItem User="@user"/>
+ @* <IndexUserItem User="@user"/> *@
<pre>
@user.LoginResponse.UserId[1..].Split(":")[0]\auth\access_token=@token
@user.LoginResponse.UserId[1..].Split(":")[0]\auth\device_id=@user.LoginResponse.DeviceId
@@ -45,32 +45,31 @@ else
if (!RuntimeCache.WasLoaded)
{
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
-
- var homeservers = RuntimeCache.LoginSessions.Values.Select(x => x.LoginResponse.HomeServer).Distinct();
- totalHomeservers = homeservers.Count();
- StateHasChanged();
- foreach (var hs in homeservers)
+ }
+ var homeservers = RuntimeCache.LoginSessions.Values.Select(x => x.LoginResponse.HomeServer).Distinct();
+ totalHomeservers = homeservers.Count();
+ StateHasChanged();
+ foreach (var hs in homeservers)
+ {
+ if (RuntimeCache.HomeserverResolutionCache.ContainsKey(hs))
{
- if (RuntimeCache.HomeserverResolutionCache.ContainsKey(hs))
- {
- resolvedHomeservers++;
- continue;
- }
- var resolvedHomeserver = (await new RemoteHomeServer(hs).Configure()).FullHomeServerDomain;
-
- RuntimeCache.HomeserverResolutionCache.Add(hs, new() { Result = resolvedHomeserver, ResolutionTime = DateTime.Now });
- await LocalStorageWrapper.SaveToLocalStorage(LocalStorage);
-
- Console.WriteLine("Saved to local storage:");
- Console.WriteLine(JsonSerializer.Serialize(RuntimeCache.HomeserverResolutionCache, new JsonSerializerOptions()
- {
- WriteIndented = true
- }));
resolvedHomeservers++;
- StateHasChanged();
+ continue;
}
+ var resolvedHomeserver = (await new RemoteHomeServer(hs).Configure()).FullHomeServerDomain;
+
+ RuntimeCache.HomeserverResolutionCache.Add(hs, new() { Result = resolvedHomeserver, ResolutionTime = DateTime.Now });
+ await LocalStorageWrapper.SaveToLocalStorage(LocalStorage);
+
+ Console.WriteLine("Saved to local storage:");
+ Console.WriteLine(JsonSerializer.Serialize(RuntimeCache.HomeserverResolutionCache, new JsonSerializerOptions()
+ {
+ WriteIndented = true
+ }));
+ resolvedHomeservers++;
StateHasChanged();
}
+ StateHasChanged();
_isLoaded = true;
}
diff --git a/MatrixRoomUtils.Web/Pages/DebugTools.razor b/MatrixRoomUtils.Web/Pages/DebugTools.razor
index ffa2134..c8fabaa 100644
--- a/MatrixRoomUtils.Web/Pages/DebugTools.razor
+++ b/MatrixRoomUtils.Web/Pages/DebugTools.razor
@@ -40,7 +40,7 @@ else
public List<string> Rooms { get; set; } = new();
protected override async Task OnInitializedAsync()
{
- if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
if (RuntimeCache.CurrentHomeServer == null)
{
diff --git a/MatrixRoomUtils.Web/Pages/DevOptions.razor b/MatrixRoomUtils.Web/Pages/DevOptions.razor
index e1b6ac0..9ade1b8 100644
--- a/MatrixRoomUtils.Web/Pages/DevOptions.razor
+++ b/MatrixRoomUtils.Web/Pages/DevOptions.razor
@@ -23,7 +23,6 @@
{
<li>
@item.Key: @item.Value.Cache.Count entries<br/>
- Default expiry: @item.Value.DefaultExpiry<br/>
@if (item.Value.Cache.Count > 0)
{
<p>Earliest expiry: @(item.Value.Cache.Min(x => x.Value.ExpiryTime)) (@string.Format("{0:g}", item.Value.Cache.Min(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now)) from now)</p>
@@ -45,7 +44,7 @@
{
while (true)
{
- await Task.Delay(100);
+ await Task.Delay(1000);
StateHasChanged();
}
});
diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor
index 7be4149..8be8570 100644
--- a/MatrixRoomUtils.Web/Pages/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Index.razor
@@ -9,7 +9,7 @@
Small collection of tools to do not-so-everyday things.
<br/><br/>
-<h5>Signed in accounts - <a href="/Login">Add new account</a> or <a href="/ImportUsers">Import from TSV</a></h5>
+<h5>Signed in accounts - <a href="/Login">Add new account</a></h5>
<hr/>
<form>
@foreach (var (token, user) in RuntimeCache.LoginSessions)
diff --git a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor
index 882dd1e..13b717d 100644
--- a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor
+++ b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor
@@ -84,8 +84,8 @@ else
await semaphore.WaitAsync();
progress.ProcessedUsers.Add(room, new());
Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})");
- var states = (await room.GetStateAsync("")).Value.Deserialize<List<StateEvent>>();
- states.RemoveAll(x => x.type != "m.room.member" || x.content.GetProperty("membership").GetString() != "join");
+ var states = (await room.GetStateAsync("")).Value.Deserialize<List<StateEventResponse>>();
+ states.RemoveAll(x => x.Type != "m.room.member" || x.Content.GetProperty("membership").GetString() != "join");
Console.WriteLine($"Room {room.RoomId} has {states.Count} members");
if (states.Count > memberLimit)
{
@@ -119,13 +119,13 @@ else
{
progress.ProcessedUsers[room].Slowmode = false;
}
- if (!homeServers.Any(x => x.Server == state.state_key.Split(':')[1]))
+ if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1]))
{
- homeServers.Add(new HomeServerInfo() { Server = state.state_key.Split(':')[1] });
+ homeServers.Add(new HomeServerInfo() { Server = state.StateKey.Split(':')[1] });
}
- var hs = homeServers.First(x => x.Server == state.state_key.Split(':')[1]);
- if (!hs.KnownUsers.Contains(state.state_key.Split(':')[0]))
- hs.KnownUsers.Add(state.state_key.Split(':')[0]);
+ var hs = homeServers.First(x => x.Server == state.StateKey.Split(':')[1]);
+ if (!hs.KnownUsers.Contains(state.StateKey.Split(':')[0]))
+ hs.KnownUsers.Add(state.StateKey.Split(':')[0]);
if (++progress.ProcessedUsers[room].Processed % updateInterval == 0 && progressCallback != null)
{
await semLock.WaitAsync();
diff --git a/MatrixRoomUtils.Web/Pages/LoginPage.razor b/MatrixRoomUtils.Web/Pages/LoginPage.razor
index c986d40..9fcedd1 100644
--- a/MatrixRoomUtils.Web/Pages/LoginPage.razor
+++ b/MatrixRoomUtils.Web/Pages/LoginPage.razor
@@ -1,42 +1,123 @@
@page "/Login"
+@using System.Text.Json
@using MatrixRoomUtils.Core.Authentication
@inject ILocalStorageService LocalStorage
+@inject IJSRuntime JsRuntime
<h3>Login</h3>
+<hr/>
-<label>Homeserver:</label>
-<input @bind="homeserver"/>
-<br/>
-<label>Username:</label>
-<input @bind="username"/>
+<span>
+ <label>@@</label>
+ @if (inputVisible.username)
+ {
+ <input autofocus @bind="newRecordInput.username" @onfocusout="() => inputVisible.username = false" @ref="elementToFocus"/>
+ }
+ else
+ {
+ <span tabindex="0" style="border-bottom: #ccc solid 1px; min-width: 50px; display: inline-block; height: 1.4em;" @onfocusin="() => inputVisible.username = true">@newRecordInput.username</span>
+ }
+ <label>:</label>
+ @if (inputVisible.homeserver)
+ {
+ <input autofocus @bind="newRecordInput.homeserver" @onfocusout="() => inputVisible.homeserver = false" @ref="elementToFocus"/>
+ }
+ else
+ {
+ <span tabindex="0" style="border-bottom: #ccc solid 1px; min-width: 50px; display: inline-block; margin-left: 2px; height: 1.4em;" @onfocusin="() => inputVisible.homeserver = true">@newRecordInput.homeserver</span>
+ }
+</span>
+<span style="display: block;">
+ <label>Password:</label>
+ @if (inputVisible.password)
+ {
+ <input autofocus="true" @bind="newRecordInput.password" @onfocusout="() => inputVisible.password = false" @ref="elementToFocus" type="password"/>
+ }
+ else
+ {
+ <span tabindex="0" style="border-bottom: #ccc solid 1px; min-width: 50px; display: inline-block; height: 1.4em;" @onfocusin="() => inputVisible.password = true">@string.Join("", newRecordInput.password.Select(x => '*'))</span>
+ }
+</span>
+<button @onclick="AddRecord">Add account to queue</button>
<br/>
-<label>Password:</label>
-<input @bind="password" type="password"/>
+
+<InputFile OnChange="@FileChanged" accept=".tsv"></InputFile>
<br/>
<button @onclick="Login">Login</button>
+<br/><br/>
+<h4>Parsed records</h4>
+<hr/>
+<table border="1">
+ @foreach (var (homeserver, username, password) in records)
+ {
+ <tr style="background-color: @(RuntimeCache.LoginSessions.Any(x => x.Value.LoginResponse.UserId == $"@{username}:{homeserver}") ? "green" : "unset")">
+ <td style="border-width: 1px;">@username</td>
+ <td style="border-width: 1px;">@homeserver</td>
+ <td style="border-width: 1px;">@password.Length chars</td>
+ </tr>
+ }
+</table>
<br/>
<br/>
<LogView></LogView>
@code {
- string homeserver = "";
- string username = "";
- string password = "";
+ List<(string homeserver, string username, string password)> records = new();
+ (string homeserver, string username, string password) newRecordInput = ("", "", "");
+ (bool homeserver, bool username, bool password) inputVisible = (false, false, false);
async Task Login()
{
- var result = await MatrixAuth.Login(homeserver, username, password);
- Console.WriteLine($"Obtained access token for {result.UserId}!");
+ foreach (var (homeserver, username, password) in records)
+ {
+ if (RuntimeCache.LoginSessions.Any(x => x.Value.LoginResponse.UserId == $"@{username}:{homeserver}")) continue;
+ var result = await MatrixAuth.Login(homeserver, username, password);
+ Console.WriteLine($"Obtained access token for {result.UserId}!");
- RuntimeCache.LastUsedToken = result.AccessToken;
+ var userinfo = new UserInfo()
+ {
+ LoginResponse = result
+ };
+ userinfo.Profile = await (await new AuthenticatedHomeServer(result.UserId, result.AccessToken, result.HomeServer).Configure()).GetProfile(result.UserId);
+ RuntimeCache.LastUsedToken = result.AccessToken;
- var userinfo = new UserInfo()
- {
- LoginResponse = result,
- Profile = await (await new RemoteHomeServer(result.HomeServer).Configure()).GetProfile(result.UserId)
- };
- RuntimeCache.LoginSessions.Add(userinfo.AccessToken, userinfo);
+ RuntimeCache.LoginSessions.Add(result.AccessToken, userinfo);
+ StateHasChanged();
+ }
await LocalStorageWrapper.SaveToLocalStorage(LocalStorage);
}
+ private async Task FileChanged(InputFileChangeEventArgs obj)
+ {
+ Console.WriteLine(JsonSerializer.Serialize(obj, new JsonSerializerOptions()
+ {
+ WriteIndented = true
+ }));
+ await using var rs = obj.File.OpenReadStream();
+ using var sr = new StreamReader(rs);
+ string TsvData = await sr.ReadToEndAsync();
+ records.Clear();
+ foreach (var line in TsvData.Split('\n'))
+ {
+ var parts = line.Split('\t');
+ if (parts.Length != 3)
+ continue;
+ records.Add((parts[0], parts[1], parts[2]));
+ }
+ }
+
+
+ private ElementReference elementToFocus;
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ await JsRuntime.InvokeVoidAsync("BlazorFocusElement", elementToFocus);
+ }
+
+ private void AddRecord()
+ {
+ records.Add(newRecordInput);
+ newRecordInput = ("", "", "");
+ }
+
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor
index d0f9b87..08cdc2c 100644
--- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor
+++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor
@@ -8,13 +8,14 @@
<hr/>
<p>
- This policy list contains @PolicyEvents.Count(x => x.type == "m.policy.rule.server") server bans,
- @PolicyEvents.Count(x => x.type == "m.policy.rule.room") room bans and
- @PolicyEvents.Count(x => x.type == "m.policy.rule.user") user bans.
+ This policy list contains @PolicyEvents.Count(x => x.Type == "m.policy.rule.server") server bans,
+ @PolicyEvents.Count(x => x.Type == "m.policy.rule.room") room bans and
+ @PolicyEvents.Count(x => x.Type == "m.policy.rule.user") user bans.
</p>
+<InputCheckbox @bind-Value="_enableAvatars" @oninput="GetAllAvatars"></InputCheckbox><label>Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!)</label>
-@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.server"))
+@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.server"))
{
<p>No server policies</p>
}
@@ -22,7 +23,7 @@ else
{
<h3>Server policies</h3>
<hr/>
- <table class="table table-striped table-hover" style="width: fit-content;">
+ <table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
<th scope="col" style="max-width: 50vw;">Server</th>
@@ -32,10 +33,10 @@ else
</tr>
</thead>
<tbody>
- @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.server" && x.content.Entity != null))
+ @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && x.content.Entity != null))
{
<tr>
- <td>Entity: @policyEvent.content.Entity<br/>State: @policyEvent.state_key</td>
+ <td>Entity: @policyEvent.content.Entity<br/>State: @policyEvent.StateKey</td>
<td>@policyEvent.content.Reason</td>
<td>
@policyEvent.content.ExpiryDateTime
@@ -50,18 +51,18 @@ else
</table>
<details>
<summary>Invalid events</summary>
- <table class="table table-striped table-hover" style="width: fit-content;">
+ <table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
<th scope="col" style="max-width: 50vw;">State key</th>
- <th scope="col">Serialised contents</th>
+ <th scope="col">Serialised Contents</th>
</tr>
</thead>
<tbody>
- @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.server" && x.content.Entity == null))
+ @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && x.content.Entity == null))
{
<tr>
- <td>@policyEvent.state_key</td>
+ <td>@policyEvent.StateKey</td>
<td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td>
</tr>
}
@@ -69,7 +70,7 @@ else
</table>
</details>
}
-@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.room"))
+@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.room"))
{
<p>No room policies</p>
}
@@ -77,7 +78,7 @@ else
{
<h3>Room policies</h3>
<hr/>
- <table class="table table-striped table-hover" style="width: fit-content;">
+ <table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
<th scope="col" style="max-width: 50vw;">Room</th>
@@ -87,10 +88,10 @@ else
</tr>
</thead>
<tbody>
- @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.room" && x.content.Entity != null))
+ @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && x.content.Entity != null))
{
<tr>
- <td>Entity: @policyEvent.content.Entity<br/>State: @policyEvent.state_key</td>
+ <td>Entity: @policyEvent.content.Entity<br/>State: @policyEvent.StateKey</td>
<td>@policyEvent.content.Reason</td>
<td>
@policyEvent.content.ExpiryDateTime
@@ -104,18 +105,18 @@ else
</table>
<details>
<summary>Invalid events</summary>
- <table class="table table-striped table-hover" style="width: fit-content;">
+ <table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
<th scope="col" style="max-width: 50vw;">State key</th>
- <th scope="col">Serialised contents</th>
+ <th scope="col">Serialised Contents</th>
</tr>
</thead>
<tbody>
- @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.room" && x.content.Entity == null))
+ @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && x.content.Entity == null))
{
<tr>
- <td>@policyEvent.state_key</td>
+ <td>@policyEvent.StateKey</td>
<td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td>
</tr>
}
@@ -123,7 +124,7 @@ else
</table>
</details>
}
-@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.user"))
+@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.user"))
{
<p>No user policies</p>
}
@@ -131,9 +132,13 @@ else
{
<h3>User policies</h3>
<hr/>
- <table class="table table-striped table-hover" style="width: fit-content;">
+ <table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
+ @if (_enableAvatars)
+ {
+ <th scope="col"></th>
+ }
<th scope="col" style="max-width: 0.2vw; word-wrap: anywhere;">User</th>
<th scope="col">Reason</th>
<th scope="col">Expires</th>
@@ -141,10 +146,14 @@ else
</tr>
</thead>
<tbody>
- @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.user" && x.content.Entity != null))
+ @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.content.Entity != null))
{
<tr>
- <td style="word-wrap: anywhere;">Entity: @string.Join("", policyEvent.content.Entity.Take(64))<br/>State: @string.Join("", policyEvent.state_key.Take(64))</td>
+ @if (_enableAvatars)
+ {
+ <td scope="col"><img style="width: 48px; height: 48px; aspect-ratio: unset; border-radius: 50%;" src="@(avatars.ContainsKey(policyEvent.content.Entity) ? avatars[policyEvent.content.Entity] : "")"/></td>
+ }
+ <td style="word-wrap: anywhere;">Entity: @string.Join("", policyEvent.content.Entity.Take(64))<br/>State: @string.Join("", policyEvent.StateKey.Take(64))</td>
<td>@policyEvent.content.Reason</td>
<td>
@policyEvent.content.ExpiryDateTime
@@ -158,18 +167,18 @@ else
</table>
<details>
<summary>Invalid events</summary>
- <table class="table table-striped table-hover" style="width: fit-content;">
+ <table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
<th scope="col">State key</th>
- <th scope="col">Serialised contents</th>
+ <th scope="col">Serialised Contents</th>
</tr>
</thead>
<tbody>
- @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.user" && x.content.Entity == null))
+ @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.content.Entity == null))
{
<tr>
- <td>@policyEvent.state_key</td>
+ <td>@policyEvent.StateKey</td>
<td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td>
</tr>
}
@@ -183,19 +192,24 @@ else
@code {
//get room list
// - sync withroom list filter
- // type = support.feline.msc3784
+ // Type = support.feline.msc3784
//support.feline.policy.lists.msc.v1
[Parameter]
public string? RoomId { get; set; }
+
+ private bool _enableAvatars = false;
+
+ static Dictionary<string, string> avatars = new Dictionary<string, string>();
+ static Dictionary<string, RemoteHomeServer> servers = new Dictionary<string, RemoteHomeServer>();
- public List<StateEvent<PolicyRuleStateEventData>> PolicyEvents { get; set; } = new();
+ public static List<StateEventResponse<PolicyRuleStateEventData>> PolicyEvents { get; set; } = new();
protected override async Task OnInitializedAsync()
{
- if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null)
+ // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null)
if (RuntimeCache.CurrentHomeServer == null)
{
NavigationManager.NavigateTo("/Login");
@@ -208,21 +222,49 @@ else
private async Task LoadStatesAsync()
{
- // using var client = new HttpClient();
- // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken);
- // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/r0/rooms/{RoomId}/state");
- // var content = await response.Content.ReadAsStringAsync();
- // Console.WriteLine(JsonSerializer.Deserialize<object>(content).ToJson());
- // var stateEvents = JsonSerializer.Deserialize<List<StateEvent>>(content);
+ // using var client = new HttpClient();
+ // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken);
+ // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/r0/rooms/{RoomId}/state");
+ // var Content = await response.Content.ReadAsStringAsync();
+ // Console.WriteLine(JsonSerializer.Deserialize<object>(Content).ToJson());
+ // var stateEvents = JsonSerializer.Deserialize<List<StateEventResponse>>(Content);
var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId);
var stateEventsQuery = await room.GetStateAsync("");
if (stateEventsQuery == null)
{
Console.WriteLine("state events query is null!!!");
}
- var stateEvents = stateEventsQuery.Value.Deserialize<List<StateEvent>>();
- PolicyEvents = stateEvents.Where(x => x.type.StartsWith("m.policy.rule"))
- .Select(x => JsonSerializer.Deserialize<StateEvent<PolicyRuleStateEventData>>(JsonSerializer.Serialize(x))).ToList();
+ var stateEvents = stateEventsQuery.Value.Deserialize<List<StateEventResponse>>();
+ PolicyEvents = stateEvents.Where(x => x.Type.StartsWith("m.policy.rule"))
+ .Select(x => JsonSerializer.Deserialize<StateEventResponse<PolicyRuleStateEventData>>(JsonSerializer.Serialize(x))).ToList();
+ StateHasChanged();
+ }
+
+ private async Task GetAvatar(string userId)
+ {
+ try
+ {
+ if (avatars.ContainsKey(userId)) return;
+ var hs = userId.Split(':')[1];
+ RemoteHomeServer server = servers.ContainsKey(hs) ? servers[hs] : await new RemoteHomeServer(userId.Split(':')[1]).Configure();
+ if (!servers.ContainsKey(hs)) servers.Add(hs, server);
+ var profile = await server.GetProfile(userId);
+ avatars.Add(userId, server.ResolveMediaUri(profile.AvatarUrl));
+ servers.Add(userId, server);
+ StateHasChanged();
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ private async Task GetAllAvatars()
+ {
+ foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.content.Entity != null))
+ {
+ await GetAvatar(policyEvent.content.Entity);
+ }
StateHasChanged();
}
diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor
index f25fbae..e61598a 100644
--- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor
+++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor
@@ -35,7 +35,7 @@ else
@code {
//get room list
// - sync withroom list filter
- // type = support.feline.msc3784
+ // Type = support.feline.msc3784
//support.feline.policy.lists.msc.v1
public List<PolicyRoomInfo> PolicyRoomList { get; set; } = new();
@@ -45,7 +45,7 @@ else
protected override async Task OnInitializedAsync()
{
- if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
if (RuntimeCache.CurrentHomeServer == null)
{
diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor
index 6d27679..35bf501 100644
--- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor
@@ -11,21 +11,28 @@
else
{
<p>You are in @Rooms.Count rooms and @Spaces.Count spaces</p>
+ <p>
+ <a href="/RoomManagerCreateRoom">Create room</a>
+ </p>
+
<details open>
<summary>Space List</summary>
@foreach (var room in Spaces)
{
- <a style="color: unset; text-decoration: unset;" href="/RoomManager/Space/@room.RoomId.Replace('.', '~')"><RoomListItem Room="@room" ShowOwnProfile="true"></RoomListItem></a>
+ <a style="color: unset; text-decoration: unset;" href="/RoomManager/Space/@room.RoomId.Replace('.', '~')">
+ <RoomListItem Room="@room" ShowOwnProfile="true"></RoomListItem>
+ </a>
}
</details>
<details open>
<summary>Room List</summary>
@foreach (var room in Rooms)
{
- <a style="color: unset; text-decoration: unset;" href="/RoomManager/Room/@room.RoomId.Replace('.', '~')"><RoomListItem Room="@room" ShowOwnProfile="true"></RoomListItem></a>
+ <a style="color: unset; text-decoration: unset;" href="/RoomManager/Room/@room.RoomId.Replace('.', '~')">
+ <RoomListItem Room="@room" ShowOwnProfile="true"></RoomListItem>
+ </a>
}
</details>
-
}
<div style="margin-bottom: 4em;"></div>
@@ -34,9 +41,10 @@ else
@code {
public List<Room> Rooms { get; set; } = new();
public List<Room> Spaces { get; set; } = new();
+
protected override async Task OnInitializedAsync()
{
- if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
if (RuntimeCache.CurrentHomeServer == null)
{
@@ -45,40 +53,47 @@ else
}
Rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms();
StateHasChanged();
- var semaphore = new SemaphoreSlim(1000);
+ Console.WriteLine($"Got {Rooms.Count} rooms");
+ var semaphore = new SemaphoreSlim(10);
var tasks = new List<Task<Room?>>();
foreach (var room in Rooms)
{
tasks.Add(CheckIfSpace(room, semaphore));
}
await Task.WhenAll(tasks);
-
+
Console.WriteLine("Fetched joined rooms!");
}
-
+
private async Task<Room?> CheckIfSpace(Room room, SemaphoreSlim semaphore)
{
await semaphore.WaitAsync();
+ // Console.WriteLine($"Checking if {room.RoomId} is a space");
try
{
- var state = await room.GetStateAsync("m.room.create");
+ var state = await room.GetStateAsync<CreateEvent>("m.room.create");
if (state != null)
{
- //Console.WriteLine(state.Value.ToJson());
- if(state.Value.TryGetProperty("type", out var type))
+ //Console.WriteLine(state.Value.ToJson());
+ if (state.Type != null)
{
- if(type.ToString() == "m.space")
+ if (state.Type == "m.space")
{
+ Console.WriteLine($"Room {room.RoomId} is a space!");
Spaces.Add(room);
Rooms.Remove(room);
StateHasChanged();
return room;
}
+ else
+ {
+ Console.WriteLine($"Encountered unknown room type {state.Type}");
+ }
}
else
{
- //this is fine, apprently...
- //Console.WriteLine($"Room {room.RoomId} has no content.type in m.room.create!");
+ //this is fine, apprently...
+ // Console.WriteLine($"Room {room.RoomId} has no Content.type in m.room.create!");
}
}
}
@@ -93,4 +108,5 @@ else
}
return null;
}
-}
\ No newline at end of file
+
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor
new file mode 100644
index 0000000..7b4db37
--- /dev/null
+++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor
@@ -0,0 +1,298 @@
+@page "/RoomManagerCreateRoom"
+@using System.Text.Json
+@using MatrixRoomUtils.Core.Extensions
+@using MatrixRoomUtils.Core.Responses
+@using System.Runtime.Intrinsics.X86
+<h3>Room Manager - Create Room</h3>
+
+@* <pre Contenteditable="true" @onkeypress="@JsonChanged" ="JsonString">@JsonString</pre> *@
+<table>
+ <tr >
+ <td style="padding-bottom: 16px;">Preset:</td>
+ <td style="padding-bottom: 16px;">
+ <InputSelect @bind-Value="@RoomPreset">
+ @foreach (var createRoomRequest in Presets)
+ {
+ <option value="@createRoomRequest.Key">@createRoomRequest.Key</option>
+ }
+ @* <option value="private_chat">Private chat</option> *@
+ @* <option value="trusted_private_chat">Trusted private chat</option> *@
+ @* <option value="public_chat">Public chat</option> *@
+ </InputSelect>
+ </td>
+ </tr>
+ <tr>
+ <td>Room name:</td>
+ <td>
+ <InputText @bind-Value="@creationEvent.Name"></InputText>
+ </td>
+ </tr>
+ <tr>
+ <td>Room alias (localpart):</td>
+ <td>
+ <InputText @bind-Value="@creationEvent.RoomAliasName"></InputText>
+ </td>
+ </tr>
+ <tr>
+ <td>Room type:</td>
+ <td>
+ <InputSelect @bind-Value="@creationEvent._creationContentBaseType.Type">
+ <option value="">Room</option>
+ <option value="m.space">Space</option>
+ </InputSelect>
+ <InputText @bind-Value="@creationEvent._creationContentBaseType.Type"></InputText>
+ </td>
+ </tr>
+ <tr>
+ <td style="padding-top: 16px;">History visibility:</td>
+ <td style="padding-top: 16px;">
+ <InputSelect @bind-Value="@creationEvent.HistoryVisibility">
+ <option value="invited">Invited</option>
+ <option value="joined">Joined</option>
+ <option value="shared">Shared</option>
+ <option value="world_readable">World readable</option>
+ </InputSelect>
+ </td>
+ </tr>
+ <tr>
+ <td>Guest access:</td>
+ <td>
+ <InputSelect @bind-Value="@creationEvent.GuestAccess">
+ <option value="can_join">Can join</option>
+ <option value="forbidden">Forbidden</option>
+ </InputSelect>
+ </td>
+ </tr>
+
+ <tr>
+ <td>Room icon:</td>
+ <td>
+ <img src="@RuntimeCache.CurrentHomeServer?.ResolveMediaUri(creationEvent.RoomIcon ?? "")" style="max-width: 100px; max-height: 100px; border-radius: 50%;"/>
+ @* <InputText @bind-Value="@creationEvent.RoomIcon"></InputText> *@
+ </td>
+
+ </tr>
+
+ <tr>
+ <td style="vertical-align: top;">Initial states:</td>
+ <td>
+ <details>
+ @code{
+
+ private static string[] ImplementedStates = new[] { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", };
+
+ }
+ <summary>@creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states</summary>
+ <table>
+ @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type)))
+ {
+ <tr>
+ <td style="vertical-align: top;">@(initialState.Type):</td>
+
+ <td>
+ <pre>@JsonSerializer.Serialize(initialState.Content, new JsonSerializerOptions { WriteIndented = true })</pre>
+ </td>
+ </tr>
+ }
+ </table>
+ </details>
+ <details>
+ <summary>@creationEvent.InitialState.Count initial states</summary>
+ <table>
+ @foreach (var initialState in creationEvent.InitialState.Where(x => !new[] { "m.room.avatar", "m.room.history_visibility" }.Contains(x.Type)))
+ {
+ <tr>
+ <td style="vertical-align: top;">@(initialState.Type):</td>
+
+ <td>
+ <pre>@JsonSerializer.Serialize(initialState.Content, new JsonSerializerOptions { WriteIndented = true })</pre>
+ </td>
+ </tr>
+ }
+ </table>
+ </details>
+ </td>
+ </tr>
+</table>
+<br/>
+<details>
+ <summary>Creation JSON</summary>
+ <pre>
+ @creationEvent.ToJson(ignoreNull: true)
+ </pre>
+</details>
+<details open>
+ <summary>Creation JSON (with null values)</summary>
+ <EditablePre @bind-Value="@JsonString" oninput="@JsonChanged"></EditablePre>
+</details>
+
+
+@code {
+
+ private string JsonString
+ {
+ get => creationEvent.ToJson();
+ set
+ {
+ creationEvent = JsonSerializer.Deserialize<CreateRoomRequest>(value);
+ JsonChanged();
+ }
+ }
+
+ private string RoomPreset
+ {
+ get
+ {
+ if (Presets.ContainsValue(creationEvent))
+ {
+ return Presets.First(x => x.Value == creationEvent).Key;
+ }
+ return "Not a preset";
+ }
+ set
+ {
+ creationEvent = Presets[value];
+ JsonChanged();
+ }
+ }
+
+ private Dictionary<string, string> creationEventValidationErrors { get; set; } = new();
+
+ private CreateRoomRequest creationEvent { get; set; }
+
+ private Dictionary<string, CreateRoomRequest> Presets { get; set; } = new();
+
+ protected override async Task OnInitializedAsync()
+ {
+ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+
+ creationEvent = Presets["Default room"] = new CreateRoomRequest
+ {
+ Name = "My new room",
+ RoomAliasName = "myroom",
+ InitialState = new()
+ {
+ new()
+ {
+ Type = "m.room.history_visibility",
+ Content = new
+ {
+ history_visibility = "world_readable"
+ }
+ },
+ new()
+ {
+ Type = "m.room.guest_access",
+ Content = new
+ {
+ guest_access = "can_join"
+ }
+ },
+ new()
+ {
+ Type = "m.room.join_rules",
+ Content = new
+ {
+ join_rule = "public"
+ }
+ },
+ new()
+ {
+ Type = "m.room.server_acl",
+ Content = new
+ {
+ allow = new[] { "*" },
+ deny = new[]
+ {
+ "midov.pl",
+ "qoto.org",
+ "matrix.kiwifarms.net",
+ "plan9.rocks",
+ "thisisjoes.site",
+ "konqi.work",
+ "austinhuang.lol",
+ "arcticfox.ems.host",
+ "*.thisisjoes.site",
+ "*.abuser.eu",
+ "*.austinhuang.lol"
+ },
+ allow_ip_literals = false
+ }
+ },
+ new()
+ {
+ Type = "m.room.avatar",
+ Content = new
+ {
+ url = "mxc://feline.support/UKNhEyrVsrAbYteVvZloZcFj"
+ }
+ }
+ },
+ Visibility = "public",
+ PowerLevelContentOverride = new()
+ {
+ UsersDefault = 0,
+ EventsDefault = 100,
+ StateDefault = 50,
+ Invite = 0,
+ Redact = 50,
+ Kick = 50,
+ Ban = 50,
+ NotificationsPl = new()
+ {
+ Room = 50
+ },
+ Events = new()
+ {
+ { "im.vector.modular.widgets", 50 },
+ { "io.element.voice_broadcast_info", 50 },
+ { "m.reaction", 100 },
+ { "m.room.avatar", 50 },
+ { "m.room.canonical_alias", 50 },
+ { "m.room.encryption", 100 },
+ { "m.room.history_visibility", 100 },
+ { "m.room.name", 50 },
+ { "m.room.pinned_events", 50 },
+ { "m.room.power_levels", 100 },
+ { "m.room.redaction", 100 },
+ { "m.room.server_acl", 100 },
+ { "m.room.tombstone", 100 },
+ { "m.room.topic", 50 },
+ { "m.space.child", 50 },
+ { "org.matrix.msc3401.call", 50 },
+ { "org.matrix.msc3401.call.member", 50 }
+ },
+ Users = new()
+ {
+ { "@alicia:rory.gay", 100 },
+ { "@emma:rory.gay", 100 },
+ { "@root:rory.gay", 100 },
+ { "@rory:rory.gay", 100 }
+ },
+ },
+ CreationContent = new()
+ {
+ { "type", null }
+ }
+ };
+
+
+ await base.OnInitializedAsync();
+ }
+
+ private void JsonChanged()
+ {
+ Console.WriteLine(JsonString);
+ }
+
+
+ private string GetStateFriendlyName(string key) => key switch {
+ "m.room.history_visibility" => "History visibility",
+ "m.room.guest_access" => "Guest access",
+ "m.room.join_rules" => "Join rules",
+ "m.room.server_acl" => "Server ACL",
+ "m.room.avatar" => "Avatar",
+ _ => key
+ };
+
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor
index 4a5bddf..a44b2b4 100644
--- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor
@@ -13,9 +13,9 @@
<br/>
<details style="background: #0002;">
<summary style="background: #fff1;">State list</summary>
- @foreach (var stateEvent in States.OrderBy(x => x.state_key).ThenBy(x => x.type))
+ @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type))
{
- <p>@stateEvent.state_key/@stateEvent.type:</p>
+ <p>@stateEvent.StateKey/@stateEvent.Type:</p>
<pre>@stateEvent.content.ToJson()</pre>
}
</details>
@@ -27,7 +27,7 @@
private Room? Room { get; set; }
- private StateEvent<object>[] States { get; set; } = Array.Empty<StateEvent<object>>();
+ private StateEventResponse<object>[] States { get; set; } = Array.Empty<StateEventResponse<object>>();
private List<Room> Rooms { get; set; } = new();
protected override async Task OnInitializedAsync()
@@ -38,14 +38,14 @@
if (state != null)
{
Console.WriteLine(state.Value.ToJson());
- States = state.Value.Deserialize<StateEvent<object>[]>()!;
+ States = state.Value.Deserialize<StateEventResponse<object>[]>()!;
foreach (var stateEvent in States)
{
- if (stateEvent.type == "m.space.child")
+ if (stateEvent.Type == "m.space.child")
{
- // if (stateEvent.content.ToJson().Length < 5) return;
- var roomId = stateEvent.state_key;
+ // if (stateEvent.Content.ToJson().Length < 5) return;
+ var roomId = stateEvent.StateKey;
var room = await RuntimeCache.CurrentHomeServer.GetRoom(roomId);
if (room != null)
{
@@ -54,13 +54,13 @@
}
}
- // if(state.Value.TryGetProperty("type", out var type))
+ // if(state.Value.TryGetProperty("Type", out var Type))
// {
// }
// else
// {
// //this is fine, apprently...
- // //Console.WriteLine($"Room {room.RoomId} has no content.type in m.room.create!");
+ // //Console.WriteLine($"Room {room.RoomId} has no Content.Type in m.room.create!");
// }
}
await base.OnInitializedAsync();
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
index 638d728..3037dcc 100644
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
@@ -12,7 +12,7 @@
<br/>
<InputSelect @bind-Value="shownStateKey">
<option value="">-- State key --</option>
- @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key != "").Select(x => x.state_key).Distinct().OrderBy(x => x))
+ @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != "").Select(x => x.StateKey).Distinct().OrderBy(x => x))
{
<option value="@stateEvent">@stateEvent</option>
Console.WriteLine(stateEvent);
@@ -21,33 +21,33 @@
<br/>
<InputSelect @bind-Value="shownType">
<option value="">-- Type --</option>
- @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key != shownStateKey).Select(x => x.type).Distinct().OrderBy(x => x))
+ @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != shownStateKey).Select(x => x.Type).Distinct().OrderBy(x => x))
{
<option value="@stateEvent">@stateEvent</option>
}
</InputSelect>
<br/>
-<textarea @bind="shownEventJson" style="width: 100%; height: fit-content;"></textarea>
+<textarea @bind="shownEventJson" style="width: 100%; height: fit-Content;"></textarea>
<LogView></LogView>
@code {
//get room list
// - sync withroom list filter
- // type = support.feline.msc3784
+ // Type = support.feline.msc3784
//support.feline.policy.lists.msc.v1
[Parameter]
public string? RoomId { get; set; }
- public List<StateEvent> FilteredEvents { get; set; } = new();
- public List<StateEvent> Events { get; set; } = new();
+ public List<StateEventResponse> FilteredEvents { get; set; } = new();
+ public List<StateEventResponse> Events { get; set; } = new();
public string status = "";
protected override async Task OnInitializedAsync()
{
- if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
if (RuntimeCache.CurrentHomeServer != null)
{
@@ -71,18 +71,18 @@
// var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json");
//var _events = await response.Content.ReadFromJsonAsync<Queue<StateEventStruct>>();
var _data = await response.Content.ReadAsStreamAsync();
- var __events = JsonSerializer.DeserializeAsyncEnumerable<StateEvent>(_data);
+ var __events = JsonSerializer.DeserializeAsyncEnumerable<StateEventResponse>(_data);
await foreach (var _ev in __events)
{
- var e = new StateEvent()
+ var e = new StateEventResponse()
{
- type = _ev.type,
- state_key = _ev.state_key,
- origin_server_ts = _ev.origin_server_ts,
- content = _ev.content
+ Type = _ev.Type,
+ StateKey = _ev.StateKey,
+ OriginServerTs = _ev.OriginServerTs,
+ Content = _ev.Content
};
Events.Add(e);
- if (string.IsNullOrEmpty(e.state_key))
+ if (string.IsNullOrEmpty(e.StateKey))
{
FilteredEvents.Add(e);
}
@@ -106,7 +106,7 @@
await Task.Delay(1);
var _FilteredEvents = Events;
if (!ShowMembershipEvents)
- _FilteredEvents = _FilteredEvents.Where(x => x.type != "m.room.member").ToList();
+ _FilteredEvents = _FilteredEvents.Where(x => x.Type != "m.room.member").ToList();
status = "Done, rerendering!";
StateHasChanged();
@@ -114,7 +114,7 @@
FilteredEvents = _FilteredEvents;
if(_shownType != null)
- shownEventJson = _FilteredEvents.Where(x => x.type == _shownType).First().content.ToJson(indent: true, ignoreNull: true);
+ shownEventJson = _FilteredEvents.Where(x => x.Type == _shownType).First().Content.ToJson(indent: true, ignoreNull: true);
StateHasChanged();
}
@@ -126,10 +126,10 @@
public long origin_server_ts { get; set; }
public string state_key { get; set; }
public string type { get; set; }
- // public string sender { get; set; }
- // public string event_id { get; set; }
- // public string user_id { get; set; }
- // public string replaces_state { get; set; }
+ // public string Sender { get; set; }
+ // public string EventId { get; set; }
+ // public string UserId { get; set; }
+ // public string ReplacesState { get; set; }
}
public bool ShowMembershipEvents
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor
index ba1b0ec..c654b13 100644
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor
@@ -23,7 +23,7 @@ else
public List<string> Rooms { get; set; } = new();
protected override async Task OnInitializedAsync()
{
- if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
if (RuntimeCache.CurrentHomeServer == null)
{
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
index 0f40cb9..c7f9f3c 100644
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
@@ -11,7 +11,7 @@
<input type="checkbox" id="showAll" @bind="ShowMembershipEvents"/> Show member events
-<table class="table table-striped table-hover" style="width: fit-content;">
+<table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
<th scope="col">Type</th>
@@ -23,7 +23,7 @@
{
<tr>
<td>@stateEvent.type</td>
- <td style="max-width: fit-content;">
+ <td style="max-width: fit-Content;">
<pre>@stateEvent.content</pre>
</td>
</tr>
@@ -35,7 +35,7 @@
{
<details>
<summary>@group.Key</summary>
- <table class="table table-striped table-hover" style="width: fit-content;">
+ <table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
<th scope="col">Type</th>
@@ -47,7 +47,7 @@
{
<tr>
<td>@stateEvent.type</td>
- <td style="max-width: fit-content;">
+ <td style="max-width: fit-Content;">
<pre>@stateEvent.content</pre>
</td>
</tr>
@@ -62,7 +62,7 @@
@code {
//get room list
// - sync withroom list filter
- // type = support.feline.msc3784
+ // Type = support.feline.msc3784
//support.feline.policy.lists.msc.v1
[Parameter]
@@ -74,7 +74,7 @@
protected override async Task OnInitializedAsync()
{
- if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+ await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
if (RuntimeCache.CurrentHomeServer == null)
{
@@ -149,10 +149,10 @@
public long origin_server_ts { get; set; }
public string state_key { get; set; }
public string type { get; set; }
- // public string sender { get; set; }
- // public string event_id { get; set; }
- // public string user_id { get; set; }
- // public string replaces_state { get; set; }
+ // public string Sender { get; set; }
+ // public string EventId { get; set; }
+ // public string UserId { get; set; }
+ // public string ReplacesState { get; set; }
}
public bool ShowMembershipEvents
diff --git a/MatrixRoomUtils.Web/Pages/UserImportPage.razor b/MatrixRoomUtils.Web/Pages/UserImportPage.razor
deleted file mode 100644
index 6f3045e..0000000
--- a/MatrixRoomUtils.Web/Pages/UserImportPage.razor
+++ /dev/null
@@ -1,71 +0,0 @@
-@page "/ImportUsers"
-@using System.Text.Json
-@using MatrixRoomUtils.Core.Authentication
-@inject ILocalStorageService LocalStorage
-<h3>Login</h3>
-
-<InputFile OnChange="@FileChanged" accept=".tsv"></InputFile>
-<br/>
-<button @onclick="Login">Login</button>
-<br/><br/>
-<h4>Parsed records</h4>
-<hr/>
-<table border="1">
- @foreach (var (homeserver, username, password) in records)
- {
- <tr style="background-color: @(RuntimeCache.LoginSessions.Any(x => x.Value.LoginResponse.UserId == $"@{username}:{homeserver}") ? "green" : "unset")">
- <td style="border-width: 1px;">@username</td>
- <td style="border-width: 1px;">@homeserver</td>
- <td style="border-width: 1px;">@password.Length chars</td>
- </tr>
- }
-</table>
-<br/>
-<br/>
-<LogView></LogView>
-
-@code {
- List<(string homeserver, string username, string password)> records = new();
-
- async Task Login()
- {
- foreach (var (homeserver, username, password) in records)
- {
- if(RuntimeCache.LoginSessions.Any(x => x.Value.LoginResponse.UserId == $"@{username}:{homeserver}")) continue;
- var result = await MatrixAuth.Login(homeserver, username, password);
- Console.WriteLine($"Obtained access token for {result.UserId}!");
-
- var userinfo = new UserInfo()
- {
- LoginResponse = result
- };
- userinfo.Profile = await MatrixAuth.GetProfile(result.HomeServer, result.UserId);
- RuntimeCache.LastUsedToken = result.AccessToken;
-
- RuntimeCache.LoginSessions.Add(result.AccessToken, userinfo);
- StateHasChanged();
- }
-
- await LocalStorageWrapper.SaveToLocalStorage(LocalStorage);
- }
-
- private async Task FileChanged(InputFileChangeEventArgs obj)
- {
- Console.WriteLine(JsonSerializer.Serialize(obj, new JsonSerializerOptions()
- {
- WriteIndented = true
- }));
- await using var rs = obj.File.OpenReadStream();
- using var sr = new StreamReader(rs);
- string TsvData = await sr.ReadToEndAsync();
- records.Clear();
- foreach (var line in TsvData.Split('\n'))
- {
- var parts = line.Split('\t');
- if (parts.Length != 3)
- continue;
- records.Add((parts[0], parts[1], parts[2]));
- }
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/EditablePre.razor b/MatrixRoomUtils.Web/Shared/EditablePre.razor
new file mode 100644
index 0000000..01bea0d
--- /dev/null
+++ b/MatrixRoomUtils.Web/Shared/EditablePre.razor
@@ -0,0 +1,18 @@
+@inherits InputBase<string>
+<pre id="@Id" class="@CssClass" @onkeyup="Callback" contenteditable="true">@CurrentValue</pre>
+@code {
+ protected override bool TryParseValueFromString(string? value, out string result, out string? validationErrorMessage)
+ {
+ result = value;
+ validationErrorMessage = null;
+ return true;
+ }
+
+ public object Id { get; set; }
+
+ private async Task Callback()
+ {
+ Console.WriteLine("beep");
+ }
+
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor
index 87ef831..016b993 100644
--- a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor
+++ b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor
@@ -31,7 +31,7 @@
{
var hs = await new AuthenticatedHomeServer(User.LoginResponse.UserId, User.AccessToken, User.LoginResponse.HomeServer).Configure();
if (User.Profile.AvatarUrl != null && User.Profile.AvatarUrl != "")
- _avatarUrl = await hs.ResolveMediaUri(User.Profile.AvatarUrl);
+ _avatarUrl = hs.ResolveMediaUri(User.Profile.AvatarUrl);
else _avatarUrl = "https://api.dicebear.com/6.x/identicon/svg?seed=" + User.LoginResponse.UserId;
_roomCount = (await hs.GetJoinedRooms()).Count;
await base.OnInitializedAsync();
diff --git a/MatrixRoomUtils.Web/Shared/MainLayout.razor b/MatrixRoomUtils.Web/Shared/MainLayout.razor
index b1b0b53..cdb1205 100644
--- a/MatrixRoomUtils.Web/Shared/MainLayout.razor
+++ b/MatrixRoomUtils.Web/Shared/MainLayout.razor
@@ -17,7 +17,7 @@
}
</div>
- <article class="content px-4">
+ <article class="Content px-4">
@Body
</article>
</main>
diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor b/MatrixRoomUtils.Web/Shared/RoomListItem.razor
index 317d25a..b7394c1 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListItem.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListItem.razor
@@ -1,7 +1,7 @@
@using MatrixRoomUtils.Core.Authentication
@using System.Text.Json
@using MatrixRoomUtils.Core.Extensions
-<div style="background-color: #ffffff11; border-radius: 25px; margin: 8px; width: fit-content; @(hasDangerousRoomVersion ? "border: red 4px solid;" : hasOldRoomVersion ? "border: #FF0 1px solid;" : "")">
+<div style="background-color: #ffffff11; border-radius: 25px; margin: 8px; width: fit-Content; @(hasDangerousRoomVersion ? "border: red 4px solid;" : hasOldRoomVersion ? "border: #FF0 1px solid;" : "")">
@if (ShowOwnProfile)
{
<img style="@(ChildContent != null ? "vertical-align: baseline;":"") width: 32px; height: 32px; border-radius: 50%; @(hasCustomProfileAvatar ? "border-color: red; border-width: 3px; border-style: dashed;" : "")" src="@profileAvatar"/>
@@ -96,7 +96,7 @@
var url = state.Value.GetProperty("url").GetString();
if (url != null)
{
- roomIcon = await RuntimeCache.CurrentHomeServer.ResolveMediaUri(url);
+ roomIcon = RuntimeCache.CurrentHomeServer.ResolveMediaUri(url);
}
}
catch (InvalidOperationException e)
@@ -116,7 +116,7 @@
if (_avatar.ValueKind == JsonValueKind.String)
{
hasCustomProfileAvatar = _avatar.GetString() != profile.AvatarUrl;
- profileAvatar = await RuntimeCache.CurrentHomeServer.ResolveMediaUri(_avatar.GetString());
+ profileAvatar = RuntimeCache.CurrentHomeServer.ResolveMediaUri(_avatar.GetString());
}
else
{
diff --git a/MatrixRoomUtils.Web/wwwroot/css/app.css b/MatrixRoomUtils.Web/wwwroot/css/app.css
index acbbfce..c6d71d1 100644
--- a/MatrixRoomUtils.Web/wwwroot/css/app.css
+++ b/MatrixRoomUtils.Web/wwwroot/css/app.css
@@ -1,5 +1,9 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
+article > h3:first-child {
+ padding-top: 24px;
+}
+
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background-color: #222;
diff --git a/MatrixRoomUtils.Web/wwwroot/index.html b/MatrixRoomUtils.Web/wwwroot/index.html
index 580a88b..028e56b 100644
--- a/MatrixRoomUtils.Web/wwwroot/index.html
+++ b/MatrixRoomUtils.Web/wwwroot/index.html
@@ -2,31 +2,42 @@
<html lang="en">
<head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>MatrixRoomUtils.Web</title>
- <base href="/" />
- <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
- <link href="css/app.css" rel="stylesheet" />
- <link rel="icon" type="image/png" href="favicon.png" />
- <link href="MatrixRoomUtils.Web.styles.css" rel="stylesheet" />
+ <base href="/"/>
+ <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet"/>
+ <link href="css/app.css" rel="stylesheet"/>
+ <link rel="icon" type="image/png" href="favicon.png"/>
+ <link href="MatrixRoomUtils.Web.styles.css" rel="stylesheet"/>
</head>
<body>
- <div id="app">
- <svg class="loading-progress">
- <circle r="40%" cx="50%" cy="50%" />
- <circle r="40%" cx="50%" cy="50%" />
- </svg>
- <div class="loading-progress-text"></div>
- </div>
+<div id="app">
+ <svg class="loading-progress">
+ <circle r="40%" cx="50%" cy="50%"/>
+ <circle r="40%" cx="50%" cy="50%"/>
+ </svg>
+ <div class="loading-progress-text"></div>
+</div>
- <div id="blazor-error-ui">
- An unhandled error has occurred.
- <a href="" class="reload">Reload</a>
- <a class="dismiss">🗙</a>
- </div>
- <script src="_framework/blazor.webassembly.js"></script>
+<div id="blazor-error-ui">
+ An unhandled error has occurred.
+ <a href="" class="reload">Reload</a>
+ <a class="dismiss">🗙</a>
+</div>
+<script>
+ function BlazorFocusElement(element) {
+ if (element == null) return;
+ if (element instanceof HTMLElement) {
+ console.log(element);
+ element.focus();
+ } else {
+ console.log("Element is not an HTMLElement", element);
+ }
+ }
+</script>
+<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
|