diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
index 368aa20..ee6be72 100644
--- a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
+++ b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs
@@ -9,63 +9,47 @@ using MatrixRoomUtils.Core.Responses.Admin;
namespace MatrixRoomUtils.Core;
-public class AuthenticatedHomeServer : IHomeServer
-{
- public string UserId { get; set; }
- public string AccessToken { get; set; }
+public class AuthenticatedHomeServer : IHomeServer {
public readonly HomeserverAdminApi Admin;
- public AuthenticatedHomeServer(string userId, string accessToken, string canonicalHomeServerDomain)
- {
+ public AuthenticatedHomeServer(string userId, string accessToken, string canonicalHomeServerDomain) {
UserId = userId;
AccessToken = accessToken;
HomeServerDomain = canonicalHomeServerDomain;
Admin = new HomeserverAdminApi(this);
- _httpClient = new HttpClient();
+ _httpClient = new MatrixHttpClient();
}
- public async Task<AuthenticatedHomeServer> Configure()
- {
+ public string UserId { get; set; }
+ public string AccessToken { get; set; }
+
+ public async Task<AuthenticatedHomeServer> Configure() {
FullHomeServerDomain = await ResolveHomeserverFromWellKnown(HomeServerDomain);
_httpClient.Dispose();
- _httpClient = new HttpClient { BaseAddress = new Uri(FullHomeServerDomain) };
+ _httpClient = new MatrixHttpClient { BaseAddress = new Uri(FullHomeServerDomain) };
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
Console.WriteLine("[AHS] Finished setting up http client");
return this;
}
- public async Task<Room> GetRoom(string roomId)
- {
- return new Room(_httpClient, roomId);
- }
+ public async Task<Room> GetRoom(string roomId) => new Room(_httpClient, roomId);
- public async Task<List<Room>> GetJoinedRooms()
- {
+ public async Task<List<Room>> GetJoinedRooms() {
var rooms = new List<Room>();
var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms");
- if (!roomQuery.IsSuccessStatusCode)
- {
- Console.WriteLine($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}");
- throw new InvalidDataException($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}");
- }
var roomsJson = await roomQuery.Content.ReadFromJsonAsync<JsonElement>();
- foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray())
- {
- rooms.Add(new Room(_httpClient, room.GetString()));
- }
+ foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) rooms.Add(new Room(_httpClient, room.GetString()));
Console.WriteLine($"Fetched {rooms.Count} rooms");
return rooms;
}
- public async Task<string> UploadFile(string fileName, Stream fileStream, string contentType = "application/octet-stream")
- {
+ 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)
- {
+ if (!res.IsSuccessStatusCode) {
Console.WriteLine($"Failed to upload file: {await res.Content.ReadAsStringAsync()}");
throw new InvalidDataException($"Failed to upload file: {await res.Content.ReadAsStringAsync()}");
}
@@ -74,11 +58,9 @@ public class AuthenticatedHomeServer : IHomeServer
return resJson.GetProperty("content_uri").GetString()!;
}
- public async Task<Room> CreateRoom(CreateRoomRequest creationEvent)
- {
+ public async Task<Room> CreateRoom(CreateRoomRequest creationEvent) {
var res = await _httpClient.PostAsJsonAsync("/_matrix/client/r0/createRoom", creationEvent);
- if (!res.IsSuccessStatusCode)
- {
+ if (!res.IsSuccessStatusCode) {
Console.WriteLine($"Failed to create room: {await res.Content.ReadAsStringAsync()}");
throw new InvalidDataException($"Failed to create room: {await res.Content.ReadAsStringAsync()}");
}
@@ -86,47 +68,34 @@ public class AuthenticatedHomeServer : IHomeServer
return await GetRoom((await res.Content.ReadFromJsonAsync<JsonObject>())!["room_id"]!.ToString());
}
- public class HomeserverAdminApi
- {
+ public class HomeserverAdminApi {
private readonly AuthenticatedHomeServer _authenticatedHomeServer;
- public HomeserverAdminApi(AuthenticatedHomeServer authenticatedHomeServer)
- {
- _authenticatedHomeServer = authenticatedHomeServer;
- }
+ public HomeserverAdminApi(AuthenticatedHomeServer authenticatedHomeServer) => _authenticatedHomeServer = authenticatedHomeServer;
- public async IAsyncEnumerable<AdminRoomListingResult.AdminRoomListingResultRoom> SearchRoomsAsync(int limit = int.MaxValue, string orderBy = "name", string dir = "f", string? searchTerm = null, string? contentSearch = null)
- {
+ public async IAsyncEnumerable<AdminRoomListingResult.AdminRoomListingResultRoom> SearchRoomsAsync(int limit = int.MaxValue, string orderBy = "name", string dir = "f", string? searchTerm = null, string? contentSearch = null) {
AdminRoomListingResult? res = null;
- int i = 0;
+ var i = 0;
int? totalRooms = null;
- do
- {
+ do {
var url = $"/_synapse/admin/v1/rooms?limit={Math.Min(limit, 100)}&dir={dir}&order_by={orderBy}";
- if (!string.IsNullOrEmpty(searchTerm))
- {
- url += $"&search_term={searchTerm}";
- }
+ if (!string.IsNullOrEmpty(searchTerm)) url += $"&search_term={searchTerm}";
+
+ if (res?.NextBatch != null) url += $"&from={res.NextBatch}";
- if (res?.NextBatch != null)
- {
- url += $"&from={res.NextBatch}";
- }
Console.WriteLine($"--- ADMIN Querying Room List with URL: {url} - Already have {i} items... ---");
res = await _authenticatedHomeServer._httpClient.GetFromJsonAsync<AdminRoomListingResult>(url);
totalRooms ??= res?.TotalRooms;
- Console.WriteLine(res.ToJson(indent:false));
- foreach (var room in res.Rooms)
- {
+ Console.WriteLine(res.ToJson(false));
+ foreach (var room in res.Rooms) {
if (contentSearch != null && !string.IsNullOrEmpty(contentSearch) &&
!(
room.Name?.Contains(contentSearch, StringComparison.InvariantCultureIgnoreCase) == true ||
room.CanonicalAlias?.Contains(contentSearch, StringComparison.InvariantCultureIgnoreCase) == true ||
room.Creator?.Contains(contentSearch, StringComparison.InvariantCultureIgnoreCase) == true
)
- )
- {
+ ) {
totalRooms--;
continue;
}
diff --git a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs
index b4b8d19..0f9eb58 100644
--- a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs
+++ b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs
@@ -5,35 +5,31 @@ using MatrixRoomUtils.Core.Responses;
namespace MatrixRoomUtils.Core.Authentication;
-public class MatrixAuth
-{
- public static async Task<LoginResponse> Login(string homeserver, string username, string password)
- {
+public class MatrixAuth {
+ public static async Task<LoginResponse> Login(string homeserver, string username, string password) {
Console.WriteLine($"Logging in to {homeserver} as {username}...");
homeserver = (await new RemoteHomeServer(homeserver).Configure()).FullHomeServerDomain;
var hc = new HttpClient();
- var payload = new
- {
+ var payload = new {
type = "m.login.password",
- identifier = new
- {
+ identifier = new {
type = "m.id.user",
user = username
},
- password = password,
+ password,
initial_device_display_name = "Rory&::MatrixRoomUtils"
};
Console.WriteLine($"Sending login request to {homeserver}...");
var resp = await hc.PostAsJsonAsync($"{homeserver}/_matrix/client/r0/login", payload);
Console.WriteLine($"Login: {resp.StatusCode}");
var data = await resp.Content.ReadFromJsonAsync<JsonElement>();
- if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data.ToString());
- if (data.TryGetProperty("retry_after_ms", out var retryAfter))
- {
+ if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data);
+ if (data.TryGetProperty("retry_after_ms", out var retryAfter)) {
Console.WriteLine($"Login: Waiting {retryAfter.GetInt32()}ms before retrying");
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();
@@ -41,20 +37,16 @@ public class MatrixAuth
}
[Obsolete("Migrate to IHomeServer instance")]
- public static async Task<ProfileResponse> GetProfile(string homeserver, string mxid) =>
- await (await new RemoteHomeServer(homeserver).Configure()).GetProfile(mxid);
+ public static async Task<ProfileResponse> GetProfile(string homeserver, string mxid) => await (await new RemoteHomeServer(homeserver).Configure()).GetProfile(mxid);
- private static async Task<bool> CheckSuccessStatus(string url)
- {
+ private static async Task<bool> CheckSuccessStatus(string url) {
//cors causes failure, try to catch
- try
- {
+ try {
using var hc = new HttpClient();
var resp = await hc.GetAsync(url);
return resp.IsSuccessStatusCode;
}
- catch (Exception e)
- {
+ catch (Exception e) {
Console.WriteLine($"Failed to check success status: {e.Message}");
return false;
}
diff --git a/MatrixRoomUtils.Core/Extensions/DictionaryExtensions.cs b/MatrixRoomUtils.Core/Extensions/DictionaryExtensions.cs
index cce71dd..c51baec 100644
--- a/MatrixRoomUtils.Core/Extensions/DictionaryExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/DictionaryExtensions.cs
@@ -1,15 +1,13 @@
namespace MatrixRoomUtils.Core.Extensions;
-public static class DictionaryExtensions
-{
- public static bool ChangeKey<TKey, TValue>(this IDictionary<TKey, TValue> dict,
- TKey oldKey, TKey newKey)
- {
+public static class DictionaryExtensions {
+ public static bool ChangeKey<TKey, TValue>(this IDictionary<TKey, TValue> dict,
+ TKey oldKey, TKey newKey) {
TValue value;
if (!dict.Remove(oldKey, out value))
return false;
- dict[newKey] = value; // or dict.Add(newKey, value) depending on ur comfort
+ dict[newKey] = value; // or dict.Add(newKey, value) depending on ur comfort
return true;
}
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
index 8eb0226..47b3121 100644
--- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
@@ -1,19 +1,40 @@
+using System.Text.Json;
+
namespace MatrixRoomUtils.Core.Extensions;
-public static class HttpClientExtensions
-{
- public static async Task<bool> CheckSuccessStatus(this HttpClient hc, string url)
- {
+public static class HttpClientExtensions {
+ public static async Task<bool> CheckSuccessStatus(this HttpClient hc, string url) {
//cors causes failure, try to catch
- try
- {
+ try {
var resp = await hc.GetAsync(url);
return resp.IsSuccessStatusCode;
}
- catch (Exception e)
- {
+ catch (Exception e) {
Console.WriteLine($"Failed to check success status: {e.Message}");
return false;
}
}
+}
+
+public class MatrixHttpClient : HttpClient {
+ public override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
+ var a = await base.SendAsync(request, cancellationToken);
+ if (!a.IsSuccessStatusCode) {
+ Console.WriteLine($"Failed to send request: {a.StatusCode}");
+ var content = await a.Content.ReadAsStringAsync(cancellationToken);
+ if (content.StartsWith('{')) {
+ var ex = JsonSerializer.Deserialize<MatrixException>(content);
+ if (ex?.RetryAfterMs != null) {
+ await Task.Delay(ex.RetryAfterMs.Value, cancellationToken);
+ return await SendAsync(request, cancellationToken);
+ }
+
+ throw ex!;
+ }
+
+ throw new InvalidDataException("Encountered invalid data:\n" + content);
+ }
+
+ return a;
+ }
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs
index 725c832..b007136 100644
--- a/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/JsonElementExtensions.cs
@@ -5,18 +5,16 @@ using System.Text.Json.Serialization;
namespace MatrixRoomUtils.Core.Extensions;
-public static class JsonElementExtensions
-{
- public static void FindExtraJsonFields([DisallowNull] this JsonElement? res, Type t)
- {
+public static class JsonElementExtensions {
+ public static void FindExtraJsonFields([DisallowNull] this JsonElement? res, Type t) {
var props = t.GetProperties();
var unknownPropertyFound = false;
- foreach (var field in res.Value.EnumerateObject())
- {
+ foreach (var field in res.Value.EnumerateObject()) {
if (props.Any(x => x.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name == field.Name)) continue;
Console.WriteLine($"[!!] Unknown property {field.Name} in {t.Name}!");
unknownPropertyFound = true;
}
- if(unknownPropertyFound) Console.WriteLine(res.Value.ToJson());
+
+ if (unknownPropertyFound) Console.WriteLine(res.Value.ToJson());
}
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs b/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs
index 5aa9645..812c81c 100644
--- a/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/ObjectExtensions.cs
@@ -1,15 +1,14 @@
+using System.Text.Encodings.Web;
using System.Text.Json;
namespace MatrixRoomUtils.Core.Extensions;
-public static class ObjectExtensions
-{
- public static string ToJson(this object obj, bool indent = true, bool ignoreNull = false, bool unsafeContent = false)
- {
+public static class ObjectExtensions {
+ public static string ToJson(this object obj, bool indent = true, bool ignoreNull = false, bool unsafeContent = false) {
var jso = new JsonSerializerOptions();
- if(indent) jso.WriteIndented = true;
- if(ignoreNull) jso.IgnoreNullValues = true;
- if(unsafeContent) jso.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
+ if (indent) jso.WriteIndented = true;
+ if (ignoreNull) jso.IgnoreNullValues = true;
+ if (unsafeContent) jso.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
return JsonSerializer.Serialize(obj, jso);
}
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Extensions/StringExtensions.cs b/MatrixRoomUtils.Core/Extensions/StringExtensions.cs
index 8fadc6d..b81d59f 100644
--- a/MatrixRoomUtils.Core/Extensions/StringExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/StringExtensions.cs
@@ -1,7 +1,6 @@
namespace MatrixRoomUtils.Core.Extensions;
-public static class StringExtensions
-{
+public static class StringExtensions {
// public static async Task<string> GetMediaUrl(this string MxcUrl)
// {
// //MxcUrl: mxc://rory.gay/ocRVanZoUTCcifcVNwXgbtTg
@@ -11,5 +10,4 @@ public static class StringExtensions
// var mediaId = MxcUrl.Split('/')[3];
// return $"{(await new RemoteHomeServer(server).Configure()).FullHomeServerDomain}/_matrix/media/v3/download/{server}/{mediaId}";
// }
-
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
index 3ae1355..9a9ba7a 100644
--- a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
+++ b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs
@@ -5,35 +5,32 @@ using MatrixRoomUtils.Core.Responses;
namespace MatrixRoomUtils.Core.Interfaces;
-public class IHomeServer
-{
- private Dictionary<string, ProfileResponse?> _profileCache = new();
+public class IHomeServer {
+ private readonly Dictionary<string, ProfileResponse?> _profileCache = new();
public string HomeServerDomain { get; set; }
public string FullHomeServerDomain { get; set; }
- private protected HttpClient _httpClient { get; set; } = new();
+ private protected MatrixHttpClient _httpClient { get; set; } = new();
- public async Task<string> ResolveHomeserverFromWellKnown(string homeserver)
- {
+ public async Task<string> ResolveHomeserverFromWellKnown(string homeserver) {
var res = await _resolveHomeserverFromWellKnown(homeserver);
- if(!res.StartsWith("http")) res = "https://" + res;
- if(res.EndsWith(":443")) res = res.Substring(0, res.Length - 4);
+ if (!res.StartsWith("http")) res = "https://" + res;
+ if (res.EndsWith(":443")) res = res.Substring(0, res.Length - 4);
return res;
}
- private async Task<string> _resolveHomeserverFromWellKnown(string homeserver)
- {
- if (RuntimeCache.HomeserverResolutionCache.Count == 0)
- {
+
+ private async Task<string> _resolveHomeserverFromWellKnown(string homeserver) {
+ if (RuntimeCache.HomeserverResolutionCache.Count == 0) {
Console.WriteLine("No cached homeservers, resolving...");
await Task.Delay(Random.Shared.Next(1000, 5000));
- }
- if (RuntimeCache.HomeserverResolutionCache.ContainsKey(homeserver))
- {
- if (RuntimeCache.HomeserverResolutionCache[homeserver].ResolutionTime < DateTime.Now.AddHours(1))
- {
+ }
+
+ if (RuntimeCache.HomeserverResolutionCache.ContainsKey(homeserver)) {
+ if (RuntimeCache.HomeserverResolutionCache[homeserver].ResolutionTime < DateTime.Now.AddHours(1)) {
Console.WriteLine($"Found cached homeserver: {RuntimeCache.HomeserverResolutionCache[homeserver].Result}");
return RuntimeCache.HomeserverResolutionCache[homeserver].Result;
}
+
Console.WriteLine($"Cached homeserver expired, removing: {RuntimeCache.HomeserverResolutionCache[homeserver].Result}");
RuntimeCache.HomeserverResolutionCache.Remove(homeserver);
}
@@ -42,29 +39,25 @@ public class IHomeServer
string result = null;
Console.WriteLine($"Resolving homeserver: {homeserver}");
if (!homeserver.StartsWith("http")) homeserver = "https://" + homeserver;
- if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/client"))
- {
- Console.WriteLine($"Got successful response for client well-known...");
+ if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/client")) {
+ Console.WriteLine("Got successful response for client well-known...");
var resp = await _httpClient.GetFromJsonAsync<JsonElement>($"{homeserver}/.well-known/matrix/client");
Console.WriteLine($"Response: {resp.ToString()}");
var hs = resp.GetProperty("m.homeserver").GetProperty("base_url").GetString();
result = hs;
}
- else
- {
- Console.WriteLine($"No client well-known...");
- if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/server"))
- {
+ else {
+ Console.WriteLine("No client well-known...");
+ if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/server")) {
var resp = await _httpClient.GetFromJsonAsync<JsonElement>($"{homeserver}/.well-known/matrix/server");
var hs = resp.GetProperty("m.server").GetString();
result = hs;
}
- else
- {
- Console.WriteLine($"No server well-known...");
- if (await _httpClient.CheckSuccessStatus($"{homeserver}/_matrix/client/versions")) result = homeserver;
- else
- {
+ else {
+ Console.WriteLine("No server well-known...");
+ if (await _httpClient.CheckSuccessStatus($"{homeserver}/_matrix/client/versions"))
+ result = homeserver;
+ else {
Console.WriteLine("No homeserver on shortname...");
if (await _httpClient.CheckSuccessStatus($"{homeserver.Replace("//", "//matrix.")}/_matrix/client/versions")) result = homeserver.Replace("//", "//matrix.");
else Console.WriteLine($"Failed to resolve homeserver, not on {homeserver}, nor do client or server well-knowns exist!");
@@ -72,30 +65,27 @@ public class IHomeServer
}
}
- if (result != null)
- {
+ if (result != null) {
Console.WriteLine($"Resolved homeserver: {homeserver} -> {result}");
- RuntimeCache.HomeserverResolutionCache.TryAdd(homeserver, new()
- {
+ RuntimeCache.HomeserverResolutionCache.TryAdd(homeserver, new HomeServerResolutionResult {
Result = result,
ResolutionTime = DateTime.Now
});
return result;
}
+
throw new InvalidDataException($"Failed to resolve homeserver, not on {homeserver}, nor do client or server well-knowns exist!");
}
- public async Task<ProfileResponse> GetProfile(string mxid, bool debounce = false, bool cache = true)
- {
- if (cache)
- {
- if(debounce) await Task.Delay(Random.Shared.Next(100, 500));
- if (_profileCache.ContainsKey(mxid))
- {
- while (_profileCache[mxid] == null)
- {
+
+ public async Task<ProfileResponse> GetProfile(string mxid, bool debounce = false, bool cache = true) {
+ if (cache) {
+ if (debounce) await Task.Delay(Random.Shared.Next(100, 500));
+ if (_profileCache.ContainsKey(mxid)) {
+ while (_profileCache[mxid] == null) {
Console.WriteLine($"Waiting for profile cache for {mxid}, currently {_profileCache[mxid]?.ToJson() ?? "null"} within {_profileCache.Count} profiles...");
await Task.Delay(Random.Shared.Next(50, 500));
}
+
return _profileCache[mxid];
}
}
@@ -103,13 +93,11 @@ public class IHomeServer
_profileCache.Add(mxid, null);
var resp = await _httpClient.GetAsync($"/_matrix/client/r0/profile/{mxid}");
var data = await resp.Content.ReadFromJsonAsync<JsonElement>();
- if(!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data.ToString());
+ if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data);
var profile = data.Deserialize<ProfileResponse>();
_profileCache[mxid] = profile;
return profile;
}
- public string? ResolveMediaUri(string mxc)
- {
- return mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/r0/download/");
- }
+
+ public string? ResolveMediaUri(string mxc) => mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/r0/download/");
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/MatrixException.cs b/MatrixRoomUtils.Core/MatrixException.cs
new file mode 100644
index 0000000..3df70e1
--- /dev/null
+++ b/MatrixRoomUtils.Core/MatrixException.cs
@@ -0,0 +1,57 @@
+using System.Text.Json.Serialization;
+using MatrixRoomUtils.Core.Extensions;
+
+namespace MatrixRoomUtils.Core;
+
+public class MatrixException : Exception {
+ [JsonPropertyName("errcode")]
+ public string ErrorCode { get; set; }
+
+ [JsonPropertyName("error")]
+ public string Error { get; set; }
+
+ [JsonPropertyName("soft_logout")]
+ public bool? SoftLogout { get; set; }
+
+ [JsonPropertyName("retry_after_ms")]
+ public int? RetryAfterMs { get; set; }
+
+ public override string Message =>
+ ErrorCode switch {
+ // common
+ "M_FORBIDDEN" => "You do not have permission to perform this action: " + Error,
+ "M_UNKNOWN_TOKEN" => "The access token specified was not recognised: " + Error + (SoftLogout == true ? " (soft logout)" : ""),
+ "M_MISSING_TOKEN" => "No access token was specified: " + Error,
+ "M_BAD_JSON" => "Request contained valid JSON, but it was malformed in some way: " + Error,
+ "M_NOT_JSON" => "Request did not contain valid JSON: " + Error,
+ "M_NOT_FOUND" => "The requested resource was not found: " + Error,
+ "M_LIMIT_EXCEEDED" => "Too many requests have been sent in a short period of time. Wait a while then try again: " + Error,
+ "M_UNRECOGNISED" => "The server did not recognise the request: " + Error,
+ "M_UNKOWN" => "The server encountered an unexpected error: " + Error,
+ // endpoint specific
+ "M_UNAUTHORIZED" => "The request did not contain valid authentication information for the target of the request: " + Error,
+ "M_USER_DEACTIVATED" => "The user ID associated with the request has been deactivated: " + Error,
+ "M_USER_IN_USE" => "The user ID associated with the request is already in use: " + Error,
+ "M_INVALID_USERNAME" => "The requested user ID is not valid: " + Error,
+ "M_ROOM_IN_USE" => "The room alias requested is already taken: " + Error,
+ "M_INVALID_ROOM_STATE" => "The room associated with the request is not in a valid state to perform the request: " + Error,
+ "M_THREEPID_IN_USE" => "The threepid requested is already associated with a user ID on this server: " + Error,
+ "M_THREEPID_NOT_FOUND" => "The threepid requested is not associated with any user ID: " + Error,
+ "M_THREEPID_AUTH_FAILED" => "The provided threepid and/or token was invalid: " + Error,
+ "M_THREEPID_DENIED" => "The homeserver does not permit the third party identifier in question: " + Error,
+ "M_SERVER_NOT_TRUSTED" => "The homeserver does not trust the identity server: " + Error,
+ "M_UNSUPPORTED_ROOM_VERSION" => "The room version is not supported: " + Error,
+ "M_INCOMPATIBLE_ROOM_VERSION" => "The room version is incompatible: " + Error,
+ "M_BAD_STATE" => "The request was invalid because the state was invalid: " + Error,
+ "M_GUEST_ACCESS_FORBIDDEN" => "Guest access is forbidden: " + Error,
+ "M_CAPTCHA_NEEDED" => "Captcha needed: " + Error,
+ "M_CAPTCHA_INVALID" => "Captcha invalid: " + Error,
+ "M_MISSING_PARAM" => "Missing parameter: " + Error,
+ "M_INVALID_PARAM" => "Invalid parameter: " + Error,
+ "M_TOO_LARGE" => "The request or entity was too large: " + Error,
+ "M_EXCLUSIVE" => "The resource being requested is reserved by an application service, or the application service making the request has not created the resource: " + Error,
+ "M_RESOURCE_LIMIT_EXCEEDED" => "Exceeded resource limit: " + Error,
+ "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => "Cannot leave server notice room: " + Error,
+ _ => "Unknown error: " + new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson()
+ };
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/RatelimitedHttpClient.cs b/MatrixRoomUtils.Core/RatelimitedHttpClient.cs
deleted file mode 100644
index 61eab07..0000000
--- a/MatrixRoomUtils.Core/RatelimitedHttpClient.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MatrixRoomUtils.Core;
-
-public class RatelimitedHttpClient : HttpClient
-{
-
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/RemoteHomeServer.cs b/MatrixRoomUtils.Core/RemoteHomeServer.cs
index 942f873..3f50d2e 100644
--- a/MatrixRoomUtils.Core/RemoteHomeServer.cs
+++ b/MatrixRoomUtils.Core/RemoteHomeServer.cs
@@ -1,50 +1,39 @@
using System.Net.Http.Json;
using System.Text.Json;
+using MatrixRoomUtils.Core.Extensions;
using MatrixRoomUtils.Core.Interfaces;
namespace MatrixRoomUtils.Core;
-public class RemoteHomeServer : IHomeServer
-{
-
-
- public RemoteHomeServer(string canonicalHomeServerDomain)
- {
+public class RemoteHomeServer : IHomeServer {
+ public RemoteHomeServer(string canonicalHomeServerDomain) {
HomeServerDomain = canonicalHomeServerDomain;
- _httpClient = new HttpClient();
+ _httpClient = new MatrixHttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(5);
}
- public async Task<RemoteHomeServer> Configure()
- {
+
+ public async Task<RemoteHomeServer> Configure() {
FullHomeServerDomain = await ResolveHomeserverFromWellKnown(HomeServerDomain);
_httpClient.Dispose();
- _httpClient = new HttpClient { BaseAddress = new Uri(FullHomeServerDomain) };
+ _httpClient = new MatrixHttpClient { BaseAddress = new Uri(FullHomeServerDomain) };
_httpClient.Timeout = TimeSpan.FromSeconds(5);
Console.WriteLine("[RHS] Finished setting up http client");
return this;
}
-
- public async Task<Room> GetRoom(string roomId)
- {
- return new Room(_httpClient, roomId);
- }
- public async Task<List<Room>> GetJoinedRooms()
- {
+ public async Task<Room> GetRoom(string roomId) => new Room(_httpClient, roomId);
+
+ public async Task<List<Room>> GetJoinedRooms() {
var rooms = new List<Room>();
var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms");
- if (!roomQuery.IsSuccessStatusCode)
- {
+ if (!roomQuery.IsSuccessStatusCode) {
Console.WriteLine($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}");
throw new InvalidDataException($"Failed to get rooms: {await roomQuery.Content.ReadAsStringAsync()}");
}
var roomsJson = await roomQuery.Content.ReadFromJsonAsync<JsonElement>();
- foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray())
- {
- rooms.Add(new Room(_httpClient, room.GetString()));
- }
+ foreach (var room in roomsJson.GetProperty("joined_rooms").EnumerateArray()) rooms.Add(new Room(_httpClient, room.GetString()));
return rooms;
}
diff --git a/MatrixRoomUtils.Core/Responses/Admin/AdminRoomListingResult.cs b/MatrixRoomUtils.Core/Responses/Admin/AdminRoomListingResult.cs
index 8ec0e4f..37bb3ba 100644
--- a/MatrixRoomUtils.Core/Responses/Admin/AdminRoomListingResult.cs
+++ b/MatrixRoomUtils.Core/Responses/Admin/AdminRoomListingResult.cs
@@ -2,8 +2,7 @@ using System.Text.Json.Serialization;
namespace MatrixRoomUtils.Core.Responses.Admin;
-public class AdminRoomListingResult
-{
+public class AdminRoomListingResult {
[JsonPropertyName("offset")]
public int Offset { get; set; }
@@ -19,8 +18,7 @@ public class AdminRoomListingResult
[JsonPropertyName("rooms")]
public List<AdminRoomListingResultRoom> Rooms { get; set; } = new();
- public class AdminRoomListingResultRoom
- {
+ public class AdminRoomListingResultRoom {
[JsonPropertyName("room_id")]
public string RoomId { get; set; }
diff --git a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs
index 5df99f7..da7d569 100644
--- a/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs
+++ b/MatrixRoomUtils.Core/Responses/CreateRoomRequest.cs
@@ -6,58 +6,56 @@ using MatrixRoomUtils.Core.Extensions;
namespace MatrixRoomUtils.Core.Responses;
-public class CreateRoomRequest
-{
- [JsonPropertyName("name")] public string Name { get; set; } = null!;
+public class CreateRoomRequest {
+ [JsonIgnore] public CreationContentBaseType _creationContentBaseType;
+
+ public CreateRoomRequest() => _creationContentBaseType = new CreationContentBaseType(this);
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = null!;
- [JsonPropertyName("room_alias_name")] public string RoomAliasName { 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("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();
+ [JsonPropertyName("creation_content")]
+ public JsonObject CreationContent { get; set; } = new();
/// <summary>
- /// For use only when you can't use the CreationContent property
+ /// For use only when you can't use the CreationContent property
/// </summary>
- public StateEvent this[string event_type, string event_key = ""]
- {
+ public StateEvent this[string event_type, string event_key = ""] {
get => InitialState.First(x => x.Type == event_type && x.StateKey == event_key);
- set
- {
+ set {
var stateEvent = InitialState.FirstOrDefault(x => x.Type == event_type && x.StateKey == event_key);
if (stateEvent == null)
- {
InitialState.Add(value);
- }
else
- {
InitialState[InitialState.IndexOf(stateEvent)] = value;
- }
}
}
//extra properties
[JsonIgnore]
- public string HistoryVisibility
- {
- get
- {
+ public string HistoryVisibility {
+ get {
var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.history_visibility");
- if (stateEvent == null)
- {
- InitialState.Add(new StateEvent()
- {
+ if (stateEvent == null) {
+ InitialState.Add(new StateEvent {
Type = "m.room.history_visibility",
- Content = new JsonObject()
- {
+ Content = new JsonObject {
["history_visibility"] = "shared"
}
});
@@ -66,22 +64,16 @@ public class CreateRoomRequest
return stateEvent.ContentAsJsonNode["history_visibility"].GetValue<string>();
}
- set
- {
+ set {
var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.history_visibility");
if (stateEvent == null)
- {
- InitialState.Add(new StateEvent()
- {
+ InitialState.Add(new StateEvent {
Type = "m.room.history_visibility",
- Content = new JsonObject()
- {
+ Content = new JsonObject {
["history_visibility"] = value
}
});
- }
- else
- {
+ else {
var v = stateEvent.ContentAsJsonNode;
v["history_visibility"] = value;
stateEvent.ContentAsJsonNode = v;
@@ -90,18 +82,13 @@ public class CreateRoomRequest
}
[JsonIgnore]
- public string RoomIcon
- {
- get
- {
+ public string RoomIcon {
+ get {
var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.avatar");
- if (stateEvent == null)
- {
- InitialState.Add(new StateEvent()
- {
+ if (stateEvent == null) {
+ InitialState.Add(new StateEvent {
Type = "m.room.avatar",
- Content = new JsonObject()
- {
+ Content = new JsonObject {
["url"] = ""
}
});
@@ -110,22 +97,16 @@ public class CreateRoomRequest
return stateEvent.ContentAsJsonNode["url"].GetValue<string>();
}
- set
- {
+ set {
var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.avatar");
if (stateEvent == null)
- {
- InitialState.Add(new StateEvent()
- {
+ InitialState.Add(new StateEvent {
Type = "m.room.avatar",
- Content = new JsonObject()
- {
+ Content = new JsonObject {
["url"] = value
}
});
- }
- else
- {
+ else {
var v = stateEvent.ContentAsJsonNode;
v["url"] = value;
stateEvent.ContentAsJsonNode = v;
@@ -177,64 +158,50 @@ public class CreateRoomRequest
// }
// }
- public ServerACL ServerACLs
- {
- get
- {
+ public ServerACL ServerACLs {
+ get {
var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.server_acl");
- if (stateEvent == null)
- {
- InitialState.Add(new StateEvent()
- {
+ if (stateEvent == null) {
+ InitialState.Add(new StateEvent {
Type = "m.room.server_acl",
- Content = new JsonObject()
- {
- ["allow"] = new JsonArray()
- {
+ Content = new JsonObject {
+ ["allow"] = new JsonArray {
"*"
},
["deny"] = new JsonArray()
}
});
- return new ServerACL()
- {
- Allow = new List<string>()
- {
+ return new ServerACL {
+ Allow = new List<string> {
"*"
},
Deny = new List<string>(),
AllowIpLiterals = true
};
}
- return new ServerACL()
- {
- Allow = JsonSerializer.Deserialize<List<string>>(stateEvent.ContentAsJsonNode["allow"]),
- Deny = JsonSerializer.Deserialize<List<string>>(stateEvent.ContentAsJsonNode["deny"]),
+
+ return new ServerACL {
+ Allow = stateEvent.ContentAsJsonNode["allow"].Deserialize<List<string>>(),
+ Deny = stateEvent.ContentAsJsonNode["deny"].Deserialize<List<string>>(),
AllowIpLiterals = true
};
}
- set
- {
+ set {
Console.WriteLine($"Setting server acl to {value.ToJson()}");
var stateEvent = InitialState.FirstOrDefault(x => x.Type == "m.room.server_acl");
if (stateEvent == null)
- {
- InitialState.Add(new StateEvent()
- {
+ InitialState.Add(new StateEvent {
Type = "m.room.server_acl",
- Content = new JsonObject()
- {
- ["allow"] = JsonArray.Parse(JsonSerializer.Serialize(value.Allow)),
- ["deny"] = JsonArray.Parse(JsonSerializer.Serialize(value.Deny))
- ["allow_ip_literals"] = value.AllowIpLiterals
+ Content = new JsonObject {
+ ["allow"] = JsonNode.Parse(JsonSerializer.Serialize(value.Allow)),
+ ["deny"] = JsonNode.Parse(JsonSerializer.Serialize(value.Deny))
+ ["allow_ip_literals"] = value.AllowIpLiterals
}
});
- }
- else
- {
+ else {
var v = stateEvent.ContentAsJsonNode;
- v["allow"] = JsonArray.Parse(JsonSerializer.Serialize(value.Allow));
- v["deny"] = JsonArray.Parse(JsonSerializer.Serialize(value.Deny));
+ v["allow"] = JsonNode.Parse(JsonSerializer.Serialize(value.Allow));
+ v["deny"] = JsonNode.Parse(JsonSerializer.Serialize(value.Deny));
v["allow_ip_literals"] = value.AllowIpLiterals;
stateEvent.ContentAsJsonNode = v;
Console.WriteLine($"v={v.ToJson()}");
@@ -243,14 +210,7 @@ public class CreateRoomRequest
}
}
-
- [JsonIgnore] public CreationContentBaseType _creationContentBaseType;
-
- public CreateRoomRequest() => _creationContentBaseType = new(this);
-
-
- public Dictionary<string, string> Validate()
- {
+ 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.");
@@ -259,49 +219,65 @@ public class CreateRoomRequest
}
}
-public class CreationContentBaseType
-{
+public class CreationContentBaseType {
private readonly CreateRoomRequest createRoomRequest;
- public CreationContentBaseType(CreateRoomRequest createRoomRequest)
- {
- this.createRoomRequest = createRoomRequest;
- }
+ public CreationContentBaseType(CreateRoomRequest createRoomRequest) => this.createRoomRequest = createRoomRequest;
[JsonPropertyName("type")]
- public string Type
- {
+ public string Type {
get => (string)createRoomRequest.CreationContent["type"];
- set
- {
+ 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 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;
+public class NotificationsPL {
+ [JsonPropertyName("room")]
+ public int Room { get; set; } = 50;
}
-public class ServerACL
-{
- [JsonPropertyName("allow")] public List<string> Allow { get; set; } // = null!;
- [JsonPropertyName("deny")] public List<string> Deny { get; set; } // = null!;
- [JsonPropertyName("allow_ip_literals")] public bool AllowIpLiterals { get; set; } // = false;
+public class ServerACL {
+ [JsonPropertyName("allow")]
+ public List<string> Allow { get; set; } // = null!;
+
+ [JsonPropertyName("deny")]
+ public List<string> Deny { get; set; } // = null!;
+
+ [JsonPropertyName("allow_ip_literals")]
+ public bool AllowIpLiterals { get; set; } // = false;
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Responses/LoginResponse.cs b/MatrixRoomUtils.Core/Responses/LoginResponse.cs
index 34b42d1..3259e44 100644
--- a/MatrixRoomUtils.Core/Responses/LoginResponse.cs
+++ b/MatrixRoomUtils.Core/Responses/LoginResponse.cs
@@ -4,27 +4,26 @@ using System.Text.Json.Serialization;
namespace MatrixRoomUtils.Core.Responses;
-public class LoginResponse
-{
+public class LoginResponse {
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
+
[JsonPropertyName("device_id")]
public string DeviceId { get; set; }
+
[JsonPropertyName("home_server")]
public string HomeServer { get; set; }
+
[JsonPropertyName("user_id")]
public string UserId { get; set; }
-
- public async Task<ProfileResponse> GetProfile()
- {
+
+ public async Task<ProfileResponse> GetProfile() {
var hc = new HttpClient();
var resp = await hc.GetAsync($"{HomeServer}/_matrix/client/r0/profile/{UserId}");
var data = await resp.Content.ReadFromJsonAsync<JsonElement>();
- if(!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data.ToString());
+ if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data);
return data.Deserialize<ProfileResponse>();
}
- public async Task<string> GetCanonicalHomeserverUrl()
- {
- return (await new RemoteHomeServer(HomeServer).Configure()).FullHomeServerDomain;
- }
+
+ public async Task<string> GetCanonicalHomeserverUrl() => (await new RemoteHomeServer(HomeServer).Configure()).FullHomeServerDomain;
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Responses/ProfileResponse.cs b/MatrixRoomUtils.Core/Responses/ProfileResponse.cs
index 2c0b679..db72386 100644
--- a/MatrixRoomUtils.Core/Responses/ProfileResponse.cs
+++ b/MatrixRoomUtils.Core/Responses/ProfileResponse.cs
@@ -2,10 +2,10 @@ using System.Text.Json.Serialization;
namespace MatrixRoomUtils.Core.Responses;
-public class ProfileResponse
-{
+public class ProfileResponse {
[JsonPropertyName("avatar_url")]
public string? AvatarUrl { get; set; } = "";
+
[JsonPropertyName("displayname")]
public string? DisplayName { get; set; } = "";
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs
index 670c121..36f0a36 100644
--- a/MatrixRoomUtils.Core/Responses/StateEventResponse.cs
+++ b/MatrixRoomUtils.Core/Responses/StateEventResponse.cs
@@ -2,43 +2,47 @@ using System.Text.Json.Serialization;
namespace MatrixRoomUtils.Core;
-public class StateEventResponse : StateEvent
-{
+public class StateEventResponse : StateEvent {
[JsonPropertyName("origin_server_ts")]
public ulong OriginServerTs { get; set; }
+
[JsonPropertyName("room_id")]
public string RoomId { get; set; }
+
[JsonPropertyName("sender")]
public string Sender { get; set; }
+
[JsonPropertyName("unsigned")]
public UnsignedData? Unsigned { get; set; }
+
[JsonPropertyName("event_id")]
public string EventId { get; set; }
+
[JsonPropertyName("user_id")]
public string UserId { get; set; }
+
[JsonPropertyName("replaces_state")]
public string ReplacesState { get; set; }
+
[JsonPropertyName("prev_content")]
public dynamic PrevContent { get; set; }
-
-
- public class UnsignedData
- {
+
+ public class UnsignedData {
[JsonPropertyName("age")]
public ulong Age { get; set; }
+
[JsonPropertyName("prev_content")]
public dynamic? PrevContent { get; set; }
+
[JsonPropertyName("redacted_because")]
public dynamic? RedactedBecause { get; set; }
+
[JsonPropertyName("transaction_id")]
public string? TransactionId { get; set; }
-
}
}
-public class StateEventResponse<T> : StateEventResponse where T : class
-{
-
+public class StateEventResponse<T> : StateEventResponse where T : class {
[JsonPropertyName("content")]
public T Content { get; set; }
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs
index f228271..a867c0c 100644
--- a/MatrixRoomUtils.Core/Room.cs
+++ b/MatrixRoomUtils.Core/Room.cs
@@ -1,6 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Json;
-using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Web;
@@ -8,26 +6,23 @@ using MatrixRoomUtils.Core.Extensions;
namespace MatrixRoomUtils.Core;
-public class Room
-{
+public class Room {
private readonly HttpClient _httpClient;
- public string RoomId { get; set; }
- public Room(HttpClient httpClient, string roomId)
- {
+ public Room(HttpClient httpClient, string roomId) {
_httpClient = httpClient;
RoomId = roomId;
}
- public async Task<JsonElement?> GetStateAsync(string type, string stateKey = "", bool logOnFailure = true)
- {
+ public string RoomId { get; set; }
+
+ public async Task<JsonElement?> GetStateAsync(string type, string stateKey = "", bool logOnFailure = true) {
var url = $"/_matrix/client/v3/rooms/{RoomId}/state";
if (!string.IsNullOrEmpty(type)) url += $"/{type}";
if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}";
var res = await _httpClient.GetAsync(url);
- if (!res.IsSuccessStatusCode)
- {
+ if (!res.IsSuccessStatusCode) {
if (logOnFailure) Console.WriteLine($"{RoomId}/{stateKey}/{type} - got status: {res.StatusCode}");
return null;
}
@@ -36,20 +31,17 @@ public class Room
return result;
}
- public async Task<T?> GetStateAsync<T>(string type, string stateKey = "", bool logOnFailure = false)
- {
+ public async Task<T?> GetStateAsync<T>(string type, string stateKey = "", bool logOnFailure = false) {
var res = await GetStateAsync(type, stateKey, logOnFailure);
if (res == null) return default;
return res.Value.Deserialize<T>();
}
-
- public async Task<MessagesResponse> GetMessagesAsync(string from = "", int limit = 10, string dir = "b", string filter = "")
- {
+
+ public async Task<MessagesResponse> GetMessagesAsync(string from = "", int limit = 10, string dir = "b", string filter = "") {
var url = $"/_matrix/client/r0/rooms/{RoomId}/messages?from={from}&limit={limit}&dir={dir}";
if (!string.IsNullOrEmpty(filter)) url += $"&filter={filter}";
var res = await _httpClient.GetAsync(url);
- if (!res.IsSuccessStatusCode)
- {
+ if (!res.IsSuccessStatusCode) {
Console.WriteLine($"Failed to get messages for {RoomId} - got status: {res.StatusCode}");
throw new Exception($"Failed to get messages for {RoomId} - got status: {res.StatusCode}");
}
@@ -58,11 +50,9 @@ public class Room
return result ?? new MessagesResponse();
}
- public async Task<string> GetNameAsync()
- {
+ public async Task<string> GetNameAsync() {
var res = await GetStateAsync("m.room.name");
- if (!res.HasValue)
- {
+ if (!res.HasValue) {
Console.WriteLine($"Room {RoomId} has no name!");
return RoomId;
}
@@ -72,22 +62,19 @@ public class Room
return resn;
}
- public async Task JoinAsync(string[]? homeservers = null)
- {
- string join_url = $"/_matrix/client/r0/join/{HttpUtility.UrlEncode(RoomId)}";
+ public async Task JoinAsync(string[]? homeservers = null) {
+ var join_url = $"/_matrix/client/r0/join/{HttpUtility.UrlEncode(RoomId)}";
Console.WriteLine($"Calling {join_url} with {homeservers?.Length ?? 0} via's...");
if (homeservers == null || homeservers.Length == 0) homeservers = new[] { RoomId.Split(':')[1] };
var fullJoinUrl = $"{join_url}?server_name=" + string.Join("&server_name=", homeservers);
var res = await _httpClient.PostAsync(fullJoinUrl, null);
}
- public async Task<List<string>> GetMembersAsync(bool joinedOnly = true)
- {
+ public async Task<List<string>> GetMembersAsync(bool joinedOnly = true) {
var res = await GetStateAsync("");
if (!res.HasValue) return new List<string>();
var members = new List<string>();
- foreach (var member in res.Value.EnumerateArray())
- {
+ foreach (var member in res.Value.EnumerateArray()) {
if (member.GetProperty("type").GetString() != "m.room.member") continue;
if (joinedOnly && member.GetProperty("content").GetProperty("membership").GetString() != "join") continue;
var memberId = member.GetProperty("state_key").GetString();
@@ -97,63 +84,52 @@ public class Room
return members;
}
- public async Task<List<string>> GetAliasesAsync()
- {
+ public async Task<List<string>> GetAliasesAsync() {
var res = await GetStateAsync("m.room.aliases");
if (!res.HasValue) return new List<string>();
var aliases = new List<string>();
- foreach (var alias in res.Value.GetProperty("aliases").EnumerateArray())
- {
- aliases.Add(alias.GetString() ?? "");
- }
+ foreach (var alias in res.Value.GetProperty("aliases").EnumerateArray()) aliases.Add(alias.GetString() ?? "");
return aliases;
}
- public async Task<string> GetCanonicalAliasAsync()
- {
+ public async Task<string> GetCanonicalAliasAsync() {
var res = await GetStateAsync("m.room.canonical_alias");
if (!res.HasValue) return "";
return res.Value.GetProperty("alias").GetString() ?? "";
}
- public async Task<string> GetTopicAsync()
- {
+ public async Task<string> GetTopicAsync() {
var res = await GetStateAsync("m.room.topic");
if (!res.HasValue) return "";
return res.Value.GetProperty("topic").GetString() ?? "";
}
- public async Task<string> GetAvatarUrlAsync()
- {
+ public async Task<string> GetAvatarUrlAsync() {
var res = await GetStateAsync("m.room.avatar");
if (!res.HasValue) return "";
return res.Value.GetProperty("url").GetString() ?? "";
}
- public async Task<JoinRules> GetJoinRuleAsync()
- {
+ public async Task<JoinRules> GetJoinRuleAsync() {
var res = await GetStateAsync("m.room.join_rules");
if (!res.HasValue) return new JoinRules();
return res.Value.Deserialize<JoinRules>() ?? new JoinRules();
}
- public async Task<string> GetHistoryVisibilityAsync()
- {
+ public async Task<string> GetHistoryVisibilityAsync() {
var res = await GetStateAsync("m.room.history_visibility");
if (!res.HasValue) return "";
return res.Value.GetProperty("history_visibility").GetString() ?? "";
}
- public async Task<string> GetGuestAccessAsync()
- {
+ public async Task<string> GetGuestAccessAsync() {
var res = await GetStateAsync("m.room.guest_access");
if (!res.HasValue) return "";
return res.Value.GetProperty("guest_access").GetString() ?? "";
}
- public async Task<CreateEvent> GetCreateEventAsync()
- {
+ public async Task<CreateEvent> GetCreateEventAsync() {
var res = await GetStateAsync("m.room.create");
if (!res.HasValue) return new CreateEvent();
@@ -163,33 +139,45 @@ public class Room
}
}
-public class MessagesResponse
-{
+public class MessagesResponse {
[JsonPropertyName("start")]
public string Start { get; set; }
+
[JsonPropertyName("end")]
public string? End { get; set; }
+
[JsonPropertyName("chunk")]
public List<StateEventResponse> Chunk { get; set; } = new();
+
[JsonPropertyName("state")]
public List<StateEventResponse> State { get; set; } = new();
}
-public class CreateEvent
-{
- [JsonPropertyName("creator")] public string Creator { get; set; }
- [JsonPropertyName("room_version")] public string RoomVersion { get; set; }
- [JsonPropertyName("type")] public string? Type { get; set; }
- [JsonPropertyName("predecessor")] public object? Predecessor { get; set; }
- [JsonPropertyName("m.federate")] public bool Federate { get; set; }
+public class CreateEvent {
+ [JsonPropertyName("creator")]
+ public string Creator { get; set; }
+
+ [JsonPropertyName("room_version")]
+ public string RoomVersion { get; set; }
+
+ [JsonPropertyName("type")]
+ public string? Type { get; set; }
+
+ [JsonPropertyName("predecessor")]
+ public object? Predecessor { get; set; }
+
+ [JsonPropertyName("m.federate")]
+ public bool Federate { get; set; }
}
-public class JoinRules
-{
+public class JoinRules {
private const string Public = "public";
private const string Invite = "invite";
private const string Knock = "knock";
- [JsonPropertyName("join_rule")] public string JoinRule { get; set; }
- [JsonPropertyName("allow")] public List<string> Allow { get; set; }
+ [JsonPropertyName("join_rule")]
+ public string JoinRule { get; set; }
+
+ [JsonPropertyName("allow")]
+ public List<string> Allow { get; set; }
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/RuntimeCache.cs b/MatrixRoomUtils.Core/RuntimeCache.cs
index 4f73341..a2fcf40 100644
--- a/MatrixRoomUtils.Core/RuntimeCache.cs
+++ b/MatrixRoomUtils.Core/RuntimeCache.cs
@@ -3,9 +3,21 @@ using MatrixRoomUtils.Core.Responses;
namespace MatrixRoomUtils.Core;
-public class RuntimeCache
-{
+public class RuntimeCache {
public static bool WasLoaded = false;
+
+ static RuntimeCache() =>
+ Task.Run(async () => {
+ while (true) {
+ await Task.Delay(1000);
+ foreach (var (key, value) in GenericResponseCache)
+ if (value.Cache.Any())
+ SaveObject("rory.matrixroomutils.generic_cache:" + key, value);
+ else
+ RemoveObject("rory.matrixroomutils.generic_cache:" + key);
+ }
+ });
+
public static string? LastUsedToken { get; set; }
public static AuthenticatedHomeServer CurrentHomeServer { get; set; }
public static Dictionary<string, UserInfo> LoginSessions { get; set; } = new();
@@ -18,110 +30,69 @@ public class RuntimeCache
public static Action Save { get; set; } = () => { Console.WriteLine("RuntimeCache.Save() was called, but no callback was set!"); };
public static Action<string, object> SaveObject { get; set; } = (key, value) => { Console.WriteLine($"RuntimeCache.SaveObject({key}, {value}) was called, but no callback was set!"); };
public static Action<string> RemoveObject { get; set; } = key => { Console.WriteLine($"RuntimeCache.RemoveObject({key}) was called, but no callback was set!"); };
-
- static RuntimeCache()
- {
- Task.Run(async () =>
- {
- while(true)
- {
- await Task.Delay(1000);
- foreach (var (key, value) in GenericResponseCache)
- {
- if (value.Cache.Any())
- SaveObject("rory.matrixroomutils.generic_cache:" + key, value);
- else
- {
- RemoveObject("rory.matrixroomutils.generic_cache:" + key);
- }
- }
- }
- });
- }
}
-public class UserInfo
-{
+public class UserInfo {
public ProfileResponse Profile { get; set; } = new();
public LoginResponse LoginResponse { get; set; }
- public string AccessToken
- {
- get => LoginResponse.AccessToken;
- }
+ public string AccessToken => LoginResponse.AccessToken;
}
-public class HomeServerResolutionResult
-{
+public class HomeServerResolutionResult {
public string Result { get; set; }
public DateTime ResolutionTime { get; set; }
}
-public class ObjectCache<T> where T : class
-{
+public class ObjectCache<T> where T : class {
+ public ObjectCache() =>
+ //expiry timer
+ Task.Run(async () => {
+ while (Cache.Any()) {
+ await Task.Delay(1000);
+ foreach (var x in Cache.Where(x => x.Value.ExpiryTime < DateTime.Now).OrderBy(x => x.Value.ExpiryTime).Take(15).ToList())
+ // Console.WriteLine($"Removing {x.Key} from cache");
+ Cache.Remove(x.Key);
+ //RuntimeCache.SaveObject("rory.matrixroomutils.generic_cache:" + Name, this);
+ }
+ });
+
public Dictionary<string, GenericResult<T>> Cache { get; set; } = new();
public string Name { get; set; } = null!;
- public GenericResult<T> this[string key]
- {
- get
- {
- if (Cache.ContainsKey(key))
- {
+ public GenericResult<T> this[string key] {
+ get {
+ if (Cache.ContainsKey(key)) {
// 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)
- Console.WriteLine($"WARNING: item {key} in cache {Name} expired at {Cache[key].ExpiryTime}:\n{Cache[key].Result.ToJson(indent: false)}");
+ Console.WriteLine($"WARNING: item {key} in cache {Name} expired at {Cache[key].ExpiryTime}:\n{Cache[key].Result.ToJson(false)}");
return Cache[key];
}
Console.WriteLine($"cache.get({key}): miss");
return null;
}
- set
- {
- Cache[key] = value;
- // 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)}");
- // Console.Error.WriteLine("Full cache: " + Cache.ToJson());
- }
- }
-
- public ObjectCache()
- {
- //expiry timer
- Task.Run(async () =>
- {
- while (Cache.Any())
- {
- await Task.Delay(1000);
- foreach (var x in Cache.Where(x => x.Value.ExpiryTime < DateTime.Now).OrderBy(x => x.Value.ExpiryTime).Take(15).ToList())
- {
- // Console.WriteLine($"Removing {x.Key} from cache");
- Cache.Remove(x.Key);
- }
- //RuntimeCache.SaveObject("rory.matrixroomutils.generic_cache:" + Name, this);
- }
- });
+ set => Cache[key] = value;
+ // 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)}");
+ // Console.Error.WriteLine("Full cache: " + Cache.ToJson());
}
public bool ContainsKey(string key) => Cache.ContainsKey(key);
}
-public class GenericResult<T>
-{
- public T? Result { get; set; }
- public DateTime? ExpiryTime { get; set; } = DateTime.Now;
-
- public GenericResult()
- {
+public class GenericResult<T> {
+ public GenericResult() {
//expiry timer
}
- public GenericResult(T? result, DateTime? expiryTime = null) : this()
- {
+ public GenericResult(T? result, DateTime? expiryTime = null) : this() {
Result = result;
ExpiryTime = expiryTime;
}
+
+ public T? Result { get; set; }
+ public DateTime? ExpiryTime { get; set; } = DateTime.Now;
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/StateEvent.cs b/MatrixRoomUtils.Core/StateEvent.cs
index 6321fb6..a8c1fac 100644
--- a/MatrixRoomUtils.Core/StateEvent.cs
+++ b/MatrixRoomUtils.Core/StateEvent.cs
@@ -4,52 +4,48 @@ using System.Text.Json.Serialization;
namespace MatrixRoomUtils.Core;
-public class StateEvent
-{
- [JsonPropertyName("content")] public dynamic Content { get; set; } = new { };
+public class StateEvent {
+ [JsonPropertyName("content")]
+ public dynamic Content { get; set; } = new { };
+
+ [JsonPropertyName("state_key")]
+ public string StateKey { get; set; } = "";
- [JsonPropertyName("state_key")] public string StateKey { get; set; } = "";
- [JsonPropertyName("type")] public string Type { get; set; }
- [JsonPropertyName("replaces_state")] public string? ReplacesState { get; set; }
+ [JsonPropertyName("type")]
+ public string Type { get; set; }
+
+ [JsonPropertyName("replaces_state")]
+ public string? ReplacesState { get; set; }
//extra properties
[JsonIgnore]
- public JsonNode ContentAsJsonNode
- {
+ public JsonNode ContentAsJsonNode {
get => JsonSerializer.SerializeToNode(Content);
set => Content = value;
}
- public StateEvent<T> As<T>() where T : class
- {
- return (StateEvent<T>)this;
- }
-
- public string dtype
- {
- get
- {
- string res = GetType().Name switch
- {
+ public string dtype {
+ get {
+ var res = GetType().Name switch {
"StateEvent`1" => $"StateEvent<{Content.GetType().Name}>",
_ => GetType().Name
};
return res;
}
}
+
+ public StateEvent<T> As<T>() where T : class => (StateEvent<T>)this;
}
-public class StateEvent<T> : StateEvent where T : class
-{
- public StateEvent()
- {
+public class StateEvent<T> : StateEvent where T : class {
+ public StateEvent() {
//import base content if not an empty object
- if (base.Content.GetType() == typeof(T))
- {
+ if (base.Content.GetType() == typeof(T)) {
Console.WriteLine($"StateEvent<{typeof(T)}> created with base content of type {base.Content.GetType()}. Importing base content.");
Content = base.Content;
}
}
+
[JsonPropertyName("content")]
public new T Content { get; set; }
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/StateEventStruct.cs b/MatrixRoomUtils.Core/StateEventStruct.cs
index bfda594..cd301ac 100644
--- a/MatrixRoomUtils.Core/StateEventStruct.cs
+++ b/MatrixRoomUtils.Core/StateEventStruct.cs
@@ -1,7 +1,6 @@
namespace MatrixRoomUtils.Core;
-public struct StateEventStruct
-{
+public struct StateEventStruct {
public object content { get; set; }
public long origin_server_ts { get; set; }
public string sender { get; set; }
diff --git a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs
index a927ace..6f6d082 100644
--- a/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs
+++ b/MatrixRoomUtils.Core/StateEventTypes/PolicyRuleStateEventData.cs
@@ -2,51 +2,50 @@ using System.Text.Json.Serialization;
namespace MatrixRoomUtils.Core.StateEventTypes;
-public class PolicyRuleStateEventData
-{
+public class PolicyRuleStateEventData {
/// <summary>
- /// Entity this ban applies to, can use * and ? as globs.
+ /// Entity this ban applies to, can use * and ? as globs.
/// </summary>
[JsonPropertyName("entity")]
public string Entity { get; set; }
+
/// <summary>
- /// Reason this user is banned
+ /// Reason this user is banned
/// </summary>
[JsonPropertyName("reason")]
public string? Reason { get; set; }
+
/// <summary>
- /// Suggested action to take
+ /// Suggested action to take
/// </summary>
[JsonPropertyName("recommendation")]
public string? Recommendation { get; set; }
/// <summary>
- /// Expiry time in milliseconds since the unix epoch, or null if the ban has no expiry.
+ /// Expiry time in milliseconds since the unix epoch, or null if the ban has no expiry.
/// </summary>
[JsonPropertyName("support.feline.policy.expiry.rev.2")] //stable prefix: expiry, msc pending
public long? Expiry { get; set; }
-
-
+
//utils
/// <summary>
- /// Readable expiry time, provided for easy interaction
+ /// Readable expiry time, provided for easy interaction
/// </summary>
[JsonPropertyName("gay.rory.matrix_room_utils.readable_expiry_time_utc")]
- public DateTime? ExpiryDateTime
- {
+ public DateTime? ExpiryDateTime {
get => Expiry == null ? null : DateTimeOffset.FromUnixTimeMilliseconds(Expiry.Value).DateTime;
- set => Expiry = ((DateTimeOffset) value).ToUnixTimeMilliseconds();
+ set => Expiry = ((DateTimeOffset)value).ToUnixTimeMilliseconds();
}
}
-public static class PolicyRecommendationTypes
-{
+public static class PolicyRecommendationTypes {
/// <summary>
- /// Ban this user
+ /// Ban this user
/// </summary>
public static string Ban = "m.ban";
+
/// <summary>
- /// Mute this user
+ /// Mute this user
/// </summary>
public static string Mute = "support.feline.policy.recommendation_mute"; //stable prefix: m.mute, msc pending
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj b/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj
index 71c3d08..a34cd2c 100644
--- a/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj
+++ b/MatrixRoomUtils.Web.Server/MatrixRoomUtils.Web.Server.csproj
@@ -1,22 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
- <PropertyGroup>
- <TargetFramework>net7.0</TargetFramework>
- <Nullable>enable</Nullable>
- <ImplicitUsings>enable</ImplicitUsings>
- </PropertyGroup>
+ <PropertyGroup>
+ <TargetFramework>net7.0</TargetFramework>
+ <Nullable>enable</Nullable>
+ <ImplicitUsings>enable</ImplicitUsings>
+ </PropertyGroup>
- <ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.3" />
- </ItemGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.3"/>
+ </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\MatrixRoomUtils.Web\MatrixRoomUtils.Web.csproj" />
- </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\MatrixRoomUtils.Web\MatrixRoomUtils.Web.csproj"/>
+ </ItemGroup>
- <ItemGroup>
- <Folder Include="Controllers" />
- </ItemGroup>
+ <ItemGroup>
+ <Folder Include="Controllers"/>
+ </ItemGroup>
</Project>
diff --git a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml
index 0125c85..04b2c2a 100644
--- a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml
+++ b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml
@@ -5,38 +5,37 @@
<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>Error</title>
- <link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
- <link href="~/css/app.css" rel="stylesheet" asp-append-version="true" />
+ <link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet"/>
+ <link href="~/css/app.css" rel="stylesheet" asp-append-version="true"/>
</head>
<body>
- <div class="main">
- <div class="Content px-4">
- <h1 class="text-danger">Error.</h1>
- <h2 class="text-danger">An error occurred while processing your request.</h2>
+<div class="main">
+ <div class="Content px-4">
+ <h1 class="text-danger">Error.</h1>
+ <h2 class="text-danger">An error occurred while processing your request.</h2>
- @if (Model.ShowRequestId)
- {
- <p>
- <strong>Request ID:</strong> <code>@Model.RequestId</code>
- </p>
- }
-
- <h3>Development Mode</h3>
- <p>
- Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
- </p>
+ @if (Model.ShowRequestId) {
<p>
- <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
- It can result in displaying sensitive information from exceptions to end users.
- For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
- and restarting the app.
+ <strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
- </div>
+ }
+
+ <h3>Development Mode</h3>
+ <p>
+ Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
+ </p>
+ <p>
+ <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
+ It can result in displaying sensitive information from exceptions to end users.
+ For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
+ and restarting the app.
+ </p>
</div>
+</div>
</body>
-</html>
+</html>
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs
index b70d895..4b918c1 100644
--- a/MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs
+++ b/MatrixRoomUtils.Web.Server/Pages/Error.cshtml.cs
@@ -6,21 +6,14 @@ namespace MatrixRoomUtils.Web.Server.Pages;
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
-public class ErrorModel : PageModel
-{
- public string? RequestId { get; set; }
+public class ErrorModel : PageModel {
+ private readonly ILogger<ErrorModel> _logger;
- public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
+ public ErrorModel(ILogger<ErrorModel> logger) => _logger = logger;
- private readonly ILogger<ErrorModel> _logger;
+ public string? RequestId { get; set; }
- public ErrorModel(ILogger<ErrorModel> logger)
- {
- _logger = logger;
- }
+ public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
- public void OnGet()
- {
- RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
- }
-}
+ public void OnGet() => RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web.Server/Program.cs b/MatrixRoomUtils.Web.Server/Program.cs
index a486226..b5c3869 100644
--- a/MatrixRoomUtils.Web.Server/Program.cs
+++ b/MatrixRoomUtils.Web.Server/Program.cs
@@ -9,11 +9,8 @@ var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
-{
app.UseWebAssemblyDebugging();
-}
-else
-{
+else {
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
@@ -26,9 +23,8 @@ app.UseStaticFiles();
app.UseRouting();
-
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
-app.Run();
+app.Run();
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web.Server/Properties/launchSettings.json b/MatrixRoomUtils.Web.Server/Properties/launchSettings.json
index 3dcf07b..ad7ef88 100644
--- a/MatrixRoomUtils.Web.Server/Properties/launchSettings.json
+++ b/MatrixRoomUtils.Web.Server/Properties/launchSettings.json
@@ -1,40 +1,40 @@
{
- "iisSettings": {
- "windowsAuthentication": false,
- "anonymousAuthentication": true,
- "iisExpress": {
- "applicationUrl": "http://localhost:6314",
- "sslPort": 44318
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:6314",
+ "sslPort": 44318
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "applicationUrl": "http://localhost:5167",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "applicationUrl": "https://localhost:7235;http://localhost:5167",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
}
},
- "profiles": {
- "http": {
- "commandName": "Project",
- "dotnetRunMessages": true,
- "launchBrowser": true,
- "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
- "applicationUrl": "http://localhost:5167",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- },
- "https": {
- "commandName": "Project",
- "dotnetRunMessages": true,
- "launchBrowser": true,
- "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
- "applicationUrl": "https://localhost:7235;http://localhost:5167",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- },
- "IIS Express": {
- "commandName": "IISExpress",
- "launchBrowser": true,
- "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
+}
diff --git a/MatrixRoomUtils.Web/App.razor b/MatrixRoomUtils.Web/App.razor
index 4e2789d..e58212b 100644
--- a/MatrixRoomUtils.Web/App.razor
+++ b/MatrixRoomUtils.Web/App.razor
@@ -12,10 +12,9 @@
</Router>
@code {
- protected override async Task OnInitializedAsync()
- {
- if (!RuntimeCache.WasLoaded)
- {
+
+ protected override async Task OnInitializedAsync() {
+ if (!RuntimeCache.WasLoaded) {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
Console.WriteLine("Loaded from local storage");
StateHasChanged();
diff --git a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
index f70572b..4e7117d 100644
--- a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
+++ b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs
@@ -3,23 +3,20 @@ using MatrixRoomUtils.Core;
namespace MatrixRoomUtils.Web.Classes;
-public partial class LocalStorageWrapper
-{
- private static SemaphoreSlim _semaphoreSlim = new(1);
+public class LocalStorageWrapper {
+ private static readonly SemaphoreSlim _semaphoreSlim = new(1);
public static Settings Settings { get; set; } = new();
//some basic logic
- public static async Task InitialiseRuntimeVariables(ILocalStorageService localStorage)
- {
+ public static async Task InitialiseRuntimeVariables(ILocalStorageService localStorage) {
//RuntimeCache stuff
async void Save() => await SaveToLocalStorage(localStorage);
RuntimeCache.Save = Save;
RuntimeCache.SaveObject = async (key, obj) => await localStorage.SetItemAsync(key, obj);
- RuntimeCache.RemoveObject = async (key) => await localStorage.RemoveItemAsync(key);
- if (RuntimeCache.LastUsedToken != null)
- {
- Console.WriteLine($"Access token is not null, creating authenticated home server");
+ RuntimeCache.RemoveObject = async key => await localStorage.RemoveItemAsync(key);
+ if (RuntimeCache.LastUsedToken != null) {
+ Console.WriteLine("Access token is not null, creating authenticated home server");
Console.WriteLine($"Homeserver cache: {RuntimeCache.HomeserverResolutionCache.Count} entries");
// Console.WriteLine(RuntimeCache.HomeserverResolutionCache.ToJson());
RuntimeCache.CurrentHomeServer = await new AuthenticatedHomeServer(RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.UserId, RuntimeCache.LastUsedToken,
@@ -28,26 +25,24 @@ public partial class LocalStorageWrapper
}
}
- public static async Task LoadFromLocalStorage(ILocalStorageService localStorage)
- {
+ public static async Task LoadFromLocalStorage(ILocalStorageService localStorage) {
await _semaphoreSlim.WaitAsync();
- if (RuntimeCache.WasLoaded)
- {
+ if (RuntimeCache.WasLoaded) {
_semaphoreSlim.Release();
return;
}
+
Console.WriteLine("Loading from local storage...");
- Settings = await localStorage.GetItemAsync<Settings>("rory.matrixroomutils.settings") ?? new();
+ Settings = await localStorage.GetItemAsync<Settings>("rory.matrixroomutils.settings") ?? new Settings();
RuntimeCache.LastUsedToken = await localStorage.GetItemAsync<string>("rory.matrixroomutils.last_used_token");
- RuntimeCache.LoginSessions = await localStorage.GetItemAsync<Dictionary<string, UserInfo>>("rory.matrixroomutils.login_sessions") ?? new();
- RuntimeCache.HomeserverResolutionCache = await localStorage.GetItemAsync<Dictionary<string, HomeServerResolutionResult>>("rory.matrixroomutils.homeserver_resolution_cache") ?? new();
+ RuntimeCache.LoginSessions = await localStorage.GetItemAsync<Dictionary<string, UserInfo>>("rory.matrixroomutils.login_sessions") ?? new Dictionary<string, UserInfo>();
+ RuntimeCache.HomeserverResolutionCache = await localStorage.GetItemAsync<Dictionary<string, HomeServerResolutionResult>>("rory.matrixroomutils.homeserver_resolution_cache") ?? new Dictionary<string, HomeServerResolutionResult>();
Console.WriteLine($"[LocalStorageWrapper] Loaded {RuntimeCache.LoginSessions.Count} login sessions, {RuntimeCache.HomeserverResolutionCache.Count} homeserver resolution cache entries");
//RuntimeCache.GenericResponseCache = await localStorage.GetItemAsync<Dictionary<string, ObjectCache<object>>>("rory.matrixroomutils.generic_cache") ?? new();
- foreach (var s in (await localStorage.KeysAsync()).Where(x => x.StartsWith("rory.matrixroomutils.generic_cache:")).ToList())
- {
+ foreach (var s in (await localStorage.KeysAsync()).Where(x => x.StartsWith("rory.matrixroomutils.generic_cache:")).ToList()) {
Console.WriteLine($"Loading generic cache entry {s}");
RuntimeCache.GenericResponseCache[s.Replace("rory.matrixroomutils.generic_cache:", "")] = await localStorage.GetItemAsync<ObjectCache<object>>(s);
}
@@ -57,36 +52,31 @@ public partial class LocalStorageWrapper
_semaphoreSlim.Release();
}
- public static async Task SaveToLocalStorage(ILocalStorageService localStorage)
- {
+ public static async Task SaveToLocalStorage(ILocalStorageService localStorage) {
Console.WriteLine("Saving to local storage...");
await localStorage.SetItemAsync("rory.matrixroomutils.settings", Settings);
if (RuntimeCache.LoginSessions != null) await localStorage.SetItemAsync("rory.matrixroomutils.login_sessions", RuntimeCache.LoginSessions);
if (RuntimeCache.LastUsedToken != null) await localStorage.SetItemAsync("rory.matrixroomutils.last_used_token", RuntimeCache.LastUsedToken);
}
- public static async Task SaveCacheToLocalStorage(ILocalStorageService localStorage, bool awaitSave = true, bool saveGenericCache = true)
- {
+ public static async Task SaveCacheToLocalStorage(ILocalStorageService localStorage, bool awaitSave = true, bool saveGenericCache = true) {
await localStorage.SetItemAsync("rory.matrixroomutils.homeserver_resolution_cache",
RuntimeCache.HomeserverResolutionCache.DistinctBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Value));
//await localStorage.SetItemAsync("rory.matrixroomutils.generic_cache", RuntimeCache.GenericResponseCache);
- if(saveGenericCache)
- foreach (var s in RuntimeCache.GenericResponseCache.Keys)
- {
+ if (saveGenericCache)
+ foreach (var s in RuntimeCache.GenericResponseCache.Keys) {
var t = localStorage.SetItemAsync($"rory.matrixroomutils.generic_cache:{s}", RuntimeCache.GenericResponseCache[s]);
if (awaitSave) await t;
}
}
}
-public class Settings
-{
+public class Settings {
public DeveloperSettings DeveloperSettings { get; set; } = new();
}
-public class DeveloperSettings
-{
+public class DeveloperSettings {
public bool EnableLogViewers { get; set; } = false;
public bool EnableConsoleLogging { get; set; } = true;
public bool EnablePortableDevtools { get; set; } = false;
diff --git a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
index c43bd3c..77c8281 100644
--- a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
+++ b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
@@ -1,110 +1,89 @@
+using System.Text.Json.Nodes;
using MatrixRoomUtils.Core;
using MatrixRoomUtils.Core.Responses;
namespace MatrixRoomUtils.Web.Classes.RoomCreationTemplates;
-public class DefaultRoomCreationTemplate : IRoomCreationTemplate
-{
+public class DefaultRoomCreationTemplate : IRoomCreationTemplate {
public string Name => "Default";
- public CreateRoomRequest CreateRoomRequest
- {
- get
- {
- return new()
- {
- Name = "My new room",
- RoomAliasName = "myroom",
- InitialState = new()
- {
- new()
- {
- Type = "m.room.history_visibility",
- Content = new
- {
- history_visibility = "world_readable"
- }
- },
- new StateEvent<Pages.RoomManager.RoomManagerCreateRoom.GuestAccessContent>
- {
- Type = "m.room.guest_access",
- Content = new()
- {
- GuestAccess = "can_join"
- }
- },
- new()
- {
- Type = "m.room.join_rules",
- Content = new
- {
- join_rule = "public"
- }
- },
- new()
- {
- Type = "m.room.server_acl",
- Content = new
- {
- allow = new[] { "*" },
- deny = Array.Empty<string>(),
- allow_ip_literals = false
- }
- },
- new()
- {
- Type = "m.room.avatar",
- Content = new
- {
- url = "mxc://feline.support/UKNhEyrVsrAbYteVvZloZcFj"
- }
+
+ public CreateRoomRequest CreateRoomRequest =>
+ new CreateRoomRequest {
+ Name = "My new room",
+ RoomAliasName = "myroom",
+ InitialState = new List<StateEvent> {
+ new() {
+ Type = "m.room.history_visibility",
+ Content = new {
+ history_visibility = "world_readable"
}
},
- 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()
- {
- { RuntimeCache.CurrentHomeServer.UserId, 100 },
- },
+ new StateEvent<Pages.RoomManager.RoomManagerCreateRoom.GuestAccessContent> {
+ Type = "m.room.guest_access",
+ Content = new Pages.RoomManager.RoomManagerCreateRoom.GuestAccessContent {
+ GuestAccess = "can_join"
+ }
},
- CreationContent = new()
- {
- {
- "type", null
+ new() {
+ Type = "m.room.join_rules",
+ Content = new {
+ join_rule = "public"
+ }
+ },
+ new() {
+ Type = "m.room.server_acl",
+ Content = new {
+ allow = new[] { "*" },
+ deny = Array.Empty<string>(),
+ allow_ip_literals = false
}
+ },
+ new() {
+ Type = "m.room.avatar",
+ Content = new {
+ url = "mxc://feline.support/UKNhEyrVsrAbYteVvZloZcFj"
+ }
+ }
+ },
+ Visibility = "public",
+ PowerLevelContentOverride = new PowerLevelEvent {
+ UsersDefault = 0,
+ EventsDefault = 100,
+ StateDefault = 50,
+ Invite = 0,
+ Redact = 50,
+ Kick = 50,
+ Ban = 50,
+ NotificationsPl = new NotificationsPL {
+ Room = 50
+ },
+ Events = new Dictionary<string, int> {
+ { "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 Dictionary<string, int> {
+ { RuntimeCache.CurrentHomeServer.UserId, 100 }
+ }
+ },
+ CreationContent = new JsonObject {
+ {
+ "type", null
}
- };
- }
- }
+ }
+ };
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/IRoomCreationTemplate.cs b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/IRoomCreationTemplate.cs
index 7f84dc4..bbb09b7 100644
--- a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/IRoomCreationTemplate.cs
+++ b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/IRoomCreationTemplate.cs
@@ -2,8 +2,7 @@ using MatrixRoomUtils.Core.Responses;
namespace MatrixRoomUtils.Web.Classes.RoomCreationTemplates;
-public interface IRoomCreationTemplate
-{
+public interface IRoomCreationTemplate {
public CreateRoomRequest CreateRoomRequest { get; }
public string Name { get; }
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/FileUploadTest.razor b/MatrixRoomUtils.Web/FileUploadTest.razor
index 2e25b54..478a3e4 100644
--- a/MatrixRoomUtils.Web/FileUploadTest.razor
+++ b/MatrixRoomUtils.Web/FileUploadTest.razor
@@ -6,8 +6,7 @@
@code {
- private async void FilePicked(InputFileChangeEventArgs obj)
- {
+ private async void FilePicked(InputFileChangeEventArgs obj) {
Console.WriteLine("FilePicked");
Console.WriteLine(obj.File.Name);
Console.WriteLine(obj.File.Size);
diff --git a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
index cf6ce87..c76452f 100644
--- a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
+++ b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
@@ -7,17 +7,17 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Blazored.LocalStorage" Version="4.3.0" />
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.3" />
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.3" PrivateAssets="all" />
+ <PackageReference Include="Blazored.LocalStorage" Version="4.3.0"/>
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.3"/>
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.3" PrivateAssets="all"/>
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\MatrixRoomUtils.Core\MatrixRoomUtils.Core.csproj" />
+ <ProjectReference Include="..\MatrixRoomUtils.Core\MatrixRoomUtils.Core.csproj"/>
</ItemGroup>
<ItemGroup>
- <Folder Include="Shared\TimelineComponents\TimelineMessageComponents" />
+ <Folder Include="Shared\TimelineComponents\TimelineMessageComponents"/>
</ItemGroup>
-
+
</Project>
diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor
index d47e60b..cf43c4f 100644
--- a/MatrixRoomUtils.Web/Pages/About.razor
+++ b/MatrixRoomUtils.Web/Pages/About.razor
@@ -13,26 +13,23 @@
<br/><br/>
<p>You can find the source code on <a href="https://git.rory.gay/MatrixRoomUtils.git/">my git server</a>.<br/></p>
<p>You can also join the <a href="https://matrix.to/#/%23mru%3Arory.gay?via=rory.gay&via=matrix.org&via=feline.support">Matrix room</a> for this project.</p>
-@if (showBinDownload)
-{
+@if (showBinDownload) {
<p>This deployment also serves a copy of the compiled, hosting-ready binaries at <a href="MRU-BIN.tar.xz">/MRU-BIN.tar.xz</a>!</p>
}
-@if (showSrcDownload)
-{
+@if (showSrcDownload) {
<p>This deployment also serves a copy of the compiled, hosting-ready binaries at <a href="MRU-SRC.tar.xz">/MRU-SRC.tar.xz</a>!</p>
}
@code {
- private bool showBinDownload { get; set; } = false;
- private bool showSrcDownload { get; set; } = false;
+ private bool showBinDownload { get; set; }
+ private bool showSrcDownload { get; set; }
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
using var hc = new HttpClient();
- var hr = await hc.SendAsync(new(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri));
+ var hr = await hc.SendAsync(new HttpRequestMessage(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri));
showBinDownload = hr.StatusCode == HttpStatusCode.OK;
- hr = await hc.SendAsync(new(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-SRC.tar.xz").AbsoluteUri));
+ hr = await hc.SendAsync(new HttpRequestMessage(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-SRC.tar.xz").AbsoluteUri));
showSrcDownload = hr.StatusCode == HttpStatusCode.OK;
await base.OnInitializedAsync();
}
diff --git a/MatrixRoomUtils.Web/Pages/DataExportPage.razor b/MatrixRoomUtils.Web/Pages/DataExportPage.razor
index 49fb700..732cd74 100644
--- a/MatrixRoomUtils.Web/Pages/DataExportPage.razor
+++ b/MatrixRoomUtils.Web/Pages/DataExportPage.razor
@@ -1,5 +1,4 @@
@page "/Export"
-@using MatrixRoomUtils.Web.Shared.IndexComponents
@using System.Text.Json
@inject NavigationManager NavigationManager
@inject ILocalStorageService LocalStorage
@@ -11,12 +10,10 @@
<br/><br/>
<h5>Signed in accounts - <a href="/Login">Add new account</a> or <a href="/ImportUsers">Import from TSV</a></h5>
<hr/>
-@if (_isLoaded)
-{
-@foreach (var (token, user) in RuntimeCache.LoginSessions)
-{
+@if (_isLoaded) {
+ @foreach (var (token, user) in RuntimeCache.LoginSessions) {
@* <IndexUserItem User="@user"/> *@
- <pre>
+ <pre>
@user.LoginResponse.UserId[1..].Split(":")[0]\auth\access_token=@token
@user.LoginResponse.UserId[1..].Split(":")[0]\auth\device_id=@user.LoginResponse.DeviceId
@user.LoginResponse.UserId[1..].Split(":")[0]\auth\home_server=@(RuntimeCache.HomeserverResolutionCache.ContainsKey(user.LoginResponse.HomeServer) ? RuntimeCache.HomeserverResolutionCache[user.LoginResponse.HomeServer].Result : "loading...")
@@ -26,10 +23,9 @@
@user.LoginResponse.UserId[1..].Split(":")[0]\user\online_key_backup=true
@user.LoginResponse.UserId[1..].Split(":")[0]\user\only_share_keys_with_verified_users=false
</pre>
+ }
}
-}
-else
-{
+else {
<p>Loading...</p>
<p>@resolvedHomeservers/@totalHomeservers homeservers resolved...</p>
}
@@ -39,31 +35,26 @@ else
private int resolvedHomeservers;
private int totalHomeservers;
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
- if (!RuntimeCache.WasLoaded)
- {
+ 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)
- {
- if (RuntimeCache.HomeserverResolutionCache.ContainsKey(hs))
- {
+ foreach (var hs in homeservers) {
+ 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 });
+ RuntimeCache.HomeserverResolutionCache.Add(hs, new HomeServerResolutionResult { Result = resolvedHomeserver, ResolutionTime = DateTime.Now });
await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage);
Console.WriteLine("Saved to local storage:");
- Console.WriteLine(JsonSerializer.Serialize(RuntimeCache.HomeserverResolutionCache, new JsonSerializerOptions()
- {
+ Console.WriteLine(JsonSerializer.Serialize(RuntimeCache.HomeserverResolutionCache, new JsonSerializerOptions {
WriteIndented = true
}));
resolvedHomeservers++;
diff --git a/MatrixRoomUtils.Web/Pages/DebugTools.razor b/MatrixRoomUtils.Web/Pages/DebugTools.razor
index c8fabaa..da5c172 100644
--- a/MatrixRoomUtils.Web/Pages/DebugTools.razor
+++ b/MatrixRoomUtils.Web/Pages/DebugTools.razor
@@ -1,26 +1,24 @@
@page "/Debug"
-@using MatrixRoomUtils.Core.Interfaces
-@using MatrixRoomUtils.Core.Extensions
@using System.Reflection
+@using MatrixRoomUtils.Core.Extensions
+@using MatrixRoomUtils.Core.Interfaces
@inject ILocalStorageService LocalStorage
@inject NavigationManager NavigationManager
<h3>Debug Tools</h3>
<hr/>
-@if (Rooms.Count == 0)
-{
+@if (Rooms.Count == 0) {
<p>You are not in any rooms!</p>
@* <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> *@
}
-else
-{
+else {
<details>
<summary>Room List</summary>
- @foreach (var room in Rooms)
- {
- <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.', '~')"><RoomListItem RoomId="@room"></RoomListItem></a>
+ @foreach (var room in Rooms) {
+ <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.', '~')">
+ <RoomListItem RoomId="@room"></RoomListItem>
+ </a>
}
</details>
-
}
<details open>
@@ -38,50 +36,44 @@ else
@code {
public List<string> Rooms { get; set; } = new();
- protected override async Task OnInitializedAsync()
- {
+
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- if (RuntimeCache.CurrentHomeServer == null)
- {
+ if (RuntimeCache.CurrentHomeServer == null) {
NavigationManager.NavigateTo("/Login");
return;
}
- Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x=>x.RoomId).ToList();
+ Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x => x.RoomId).ToList();
Console.WriteLine("Fetched joined rooms!");
}
-
//send req
string get_request_url { get; set; } = "";
string get_request_result { get; set; } = "";
- private async Task SendGetRequest()
- {
+
+ private async Task SendGetRequest() {
var field = typeof(IHomeServer).GetRuntimeFields().First(x => x.ToString().Contains("<_httpClient>k__BackingField"));
var httpClient = field.GetValue(RuntimeCache.CurrentHomeServer) as HttpClient;
- try
- {
+ try {
var res = await httpClient.GetAsync(get_request_url);
- if (res.IsSuccessStatusCode)
- {
- if(res.Content.Headers.ContentType.MediaType == "application/json")
+ if (res.IsSuccessStatusCode) {
+ if (res.Content.Headers.ContentType.MediaType == "application/json")
get_request_result = (await res.Content.ReadFromJsonAsync<object>()).ToJson();
else
get_request_result = await res.Content.ReadAsStringAsync();
StateHasChanged();
return;
}
- if(res.Content.Headers.ContentType.MediaType == "application/json")
+ if (res.Content.Headers.ContentType.MediaType == "application/json")
get_request_result = $"Error: {res.StatusCode}\n" + (await res.Content.ReadFromJsonAsync<object>()).ToJson();
else
get_request_result = $"Error: {res.StatusCode}\n" + await res.Content.ReadAsStringAsync();
-
}
- catch (Exception e)
- {
+ catch (Exception e) {
get_request_result = $"Error: {e}";
}
StateHasChanged();
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/DevOptions.razor b/MatrixRoomUtils.Web/Pages/DevOptions.razor
index 3ca86b4..0843d5f 100644
--- a/MatrixRoomUtils.Web/Pages/DevOptions.razor
+++ b/MatrixRoomUtils.Web/Pages/DevOptions.razor
@@ -19,15 +19,13 @@
<summary>View caches</summary>
<p>Generic cache:</p>
<ul>
- @foreach (var item in RuntimeCache.GenericResponseCache)
- {
+ @foreach (var item in RuntimeCache.GenericResponseCache) {
<li>
@item.Key: @item.Value.Cache.Count entries<br/>
- @if (item.Value.Cache.Count > 0)
- {
+ @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>
@* <p>Average expiry: @(item.Value.Cache.Average(x => x.Value.ExpiryTime.Value))(@item.Value.Cache.Average(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now) from now)</p> *@
- <p>Last expiry: @(item.Value.Cache.Max(x => x.Value.ExpiryTime)) (@string.Format("{0:g}", item.Value.Cache.Max(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now)) from now)</p>
+ <p>Last expiry: @(item.Value.Cache.Max(x => x.Value.ExpiryTime)) (@string.Format("{0:g}", item.Value.Cache.Max(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now)) from now)</p>
}
</li>
}
@@ -35,50 +33,42 @@
</details>
@code {
- protected override async Task OnInitializedAsync()
- {
+
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- Task.Run(async () =>
- {
- while (true)
- {
+ Task.Run(async () => {
+ while (true) {
await Task.Delay(1000);
StateHasChanged();
}
});
}
- protected async Task LogStuff()
- {
+ protected async Task LogStuff() {
await Task.Delay(100);
Console.WriteLine($"Settings: {LocalStorageWrapper.Settings.ToJson()}");
-
+
await LocalStorageWrapper.SaveToLocalStorage(LocalStorage);
}
- protected async Task DropCaches()
- {
- foreach (var (key, value) in RuntimeCache.GenericResponseCache)
- {
+ protected async Task DropCaches() {
+ foreach (var (key, value) in RuntimeCache.GenericResponseCache) {
value.Cache.Clear();
}
-
- //RuntimeCache.GenericResponseCache.Clear();
+
+ //RuntimeCache.GenericResponseCache.Clear();
RuntimeCache.HomeserverResolutionCache.Clear();
await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage);
}
- protected async Task RandomiseCacheTimers()
- {
- foreach (var keyValuePair in RuntimeCache.GenericResponseCache)
- {
+ protected async Task RandomiseCacheTimers() {
+ foreach (var keyValuePair in RuntimeCache.GenericResponseCache) {
Console.WriteLine($"Randomising cache timer for {keyValuePair.Key}");
- foreach (var cacheItem in keyValuePair.Value.Cache)
- {
+ foreach (var cacheItem in keyValuePair.Value.Cache) {
cacheItem.Value.ExpiryTime = DateTime.Now.AddSeconds(Random.Shared.Next(15, 120));
}
-
+
await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage);
}
}
diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor
index b77012b..f972236 100644
--- a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor
+++ b/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor
@@ -2,6 +2,10 @@
<h3>Homeserver Admininistration</h3>
<hr/>
+<h4>Synapse tools</h4>
+<hr/>
+<a href="/HSAdmin/RoomQuery">Query rooms</a>
+
@code {
-
+
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor
index 109ad7d..e6f95c7 100644
--- a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor
+++ b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor
@@ -1,73 +1,78 @@
-@page "/RoomQuery"
+@page "/HSAdmin/RoomQuery"
@using MatrixRoomUtils.Core.Extensions
-@using System.Runtime.InteropServices
-@using System.ComponentModel
@using MatrixRoomUtils.Core.Responses.Admin
<h3>Homeserver Administration - Room Query</h3>
<label>Search name: </label>
-<InputText @bind-Value="SearchTerm" /><br/>
+<InputText @bind-Value="SearchTerm"/><br/>
<label>Search id/name/creator (slow!): </label>
-<InputText @bind-Value="ContentSearchTerm" /><br/>
+<InputText @bind-Value="ContentSearchTerm"/><br/>
<label>Order by: </label>
<select @bind="OrderBy">
- @foreach (var item in validOrderBy)
- {
+ @foreach (var item in validOrderBy) {
<option value="@item.Key">@item.Value</option>
}
</select><br/>
<label>Ascending: </label>
-<InputCheckbox @bind-Value="Ascending" /><br/>
+<InputCheckbox @bind-Value="Ascending"/><br/>
<button class="btn btn-primary" @onclick="Search">Search</button>
<br/>
-@foreach (var res in Results)
-{
+@foreach (var res in Results) {
<div style="background-color: #ffffff11; border-radius: 0.5em; display: block; margin-top: 4px; padding: 4px;">
- <RoomListItem RoomId="@res.RoomId"></RoomListItem>
- <p>@res.CanonicalAlias, created by <InlineUserItem UserId="@res.Creator"></InlineUserItem></p>
+ <RoomListItem RoomName="@res.Name" RoomId="@res.RoomId"></RoomListItem>
+ <p>
+ @res.CanonicalAlias
+ @if (!string.IsNullOrWhiteSpace(res.Creator)) {
+ <span>
+ , created by <InlineUserItem UserId="@res.Creator"></InlineUserItem>
+ </span>
+ }
+ </p>
<p>@res.StateEvents state events</p>
<p>@res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server</p>
</div>
}
@code {
-
- [Parameter, SupplyParameterFromQuery(Name = "order_by")]
+
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "order_by")]
public string? OrderBy { get; set; }
-
- [Parameter, SupplyParameterFromQuery(Name = "search_term")]
+
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "search_term")]
public string SearchTerm { get; set; }
-
- [Parameter, SupplyParameterFromQuery(Name = "content_search_term")]
+
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "content_search_term")]
public string ContentSearchTerm { get; set; }
-
- [Parameter, SupplyParameterFromQuery(Name = "ascending")]
+
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "ascending")]
public bool Ascending { get; set; }
public List<AdminRoomListingResult.AdminRoomListingResultRoom> Results { get; set; } = new();
-
- protected override async Task OnParametersSetAsync()
- {
- if(Ascending == null)
+
+ private string Status { get; set; }
+
+ protected override async Task OnParametersSetAsync() {
+ if (Ascending == null)
Ascending = true;
OrderBy ??= "name";
}
-
- private async Task Search()
- {
+
+ private async Task Search() {
Results.Clear();
var searchRooms = RuntimeCache.CurrentHomeServer.Admin.SearchRoomsAsync(orderBy: OrderBy!, dir: Ascending ? "f" : "b", searchTerm: SearchTerm, contentSearch: ContentSearchTerm).GetAsyncEnumerator();
- while (await searchRooms.MoveNextAsync())
- {
+ while (await searchRooms.MoveNextAsync()) {
var room = searchRooms.Current;
- Console.WriteLine("Hit: " + room.ToJson(indent: false));
+ Console.WriteLine("Hit: " + room.ToJson(false));
Results.Add(room);
}
}
- private Dictionary<string, string> validOrderBy = new Dictionary<string, string>()
- {
+ private readonly Dictionary<string, string> validOrderBy = new() {
{ "name", "Room name" },
{ "canonical_alias", "Main alias address" },
{ "joined_members", "Number of members (reversed)" },
@@ -80,7 +85,7 @@
{ "join_rules", "Join rules" },
{ "guest_access", "Guest access" },
{ "history_visibility", "Visibility of history" },
- { "state_events", "Number of state events" },
+ { "state_events", "Number of state events" }
};
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor
index decdb0c..33cca61 100644
--- a/MatrixRoomUtils.Web/Pages/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Index.razor
@@ -12,18 +12,15 @@ Small collection of tools to do not-so-everyday things.
<h5>Signed in accounts - <a href="/Login">Add new account</a></h5>
<hr/>
<form>
- @foreach (var (token, user) in RuntimeCache.LoginSessions)
- {
+ @foreach (var (token, user) in RuntimeCache.LoginSessions) {
<IndexUserItem User="@user"/>
}
</form>
@code
{
- protected override async Task OnInitializedAsync()
- {
- if (!RuntimeCache.WasLoaded)
- {
+ protected override async Task OnInitializedAsync() {
+ if (!RuntimeCache.WasLoaded) {
Console.WriteLine("[INDEX] !!! LOCALSTORAGE WAS NOT LOADED !!!");
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
}
diff --git a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor
index 13b717d..92a8445 100644
--- a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor
+++ b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor
@@ -5,33 +5,25 @@
<h3>Known Homeserver List</h3>
<hr/>
-@if (!IsFinished)
-{
+@if (!IsFinished) {
<p>Loading... Please wait...</p>
<progress value="@QueryProgress.ProcessedRooms" max="@QueryProgress.TotalRooms"></progress>
<p>@QueryProgress.ProcessedRooms / @QueryProgress.TotalRooms</p>
- @foreach (var (room, state) in QueryProgress.ProcessedUsers.Where(x => !x.Value.IsFinished).OrderByDescending(x => x.Value.Total).ToList())
- {
- @if (state.Blocked)
- {
+ @foreach (var (room, state) in QueryProgress.ProcessedUsers.Where(x => !x.Value.IsFinished).OrderByDescending(x => x.Value.Total).ToList()) {
+ @if (state.Blocked) {
<p>🔒 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p>
}
- else if (state.Slowmode)
- {
-
+ else if (state.Slowmode) {
<p>🐢 @room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p>
}
- else
- {
+ else {
<p>@room.RoomId - @state.Processed / @state.Total, @state.Timing.Elapsed elapsed...</p>
}
<progress value="@state.Processed" max="@state.Total"></progress>
}
}
-else
-{
- @foreach (var server in HomeServers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList())
- {
+else {
+ @foreach (var server in HomeServers.OrderByDescending(x => x.KnownUserCount).ThenBy(x => x.Server).ToList()) {
<p>@server.Server - @server.KnownUserCount</p>
}
}
@@ -42,15 +34,12 @@ else
bool IsFinished { get; set; }
HomeServerInfoQueryProgress QueryProgress { get; set; } = new();
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
var sw = Stopwatch.StartNew();
- HomeServers = await GetHomeservers(progressCallback: async progress =>
- {
- if (sw.ElapsedMilliseconds > 1000)
- {
+ HomeServers = await GetHomeservers(progressCallback: async progress => {
+ if (sw.ElapsedMilliseconds > 1000) {
Console.WriteLine("Progress updated...");
QueryProgress = progress;
StateHasChanged();
@@ -69,9 +58,7 @@ else
await base.OnInitializedAsync();
}
-
- private async Task<List<HomeServerInfo>> GetHomeservers(int memberLimit = 1000, Func<HomeServerInfoQueryProgress, Task<bool>>? progressCallback = null)
- {
+ private async Task<List<HomeServerInfo>> GetHomeservers(int memberLimit = 1000, Func<HomeServerInfoQueryProgress, Task<bool>>? progressCallback = null) {
HomeServerInfoQueryProgress progress = new();
List<HomeServerInfo> homeServers = new();
var rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms();
@@ -79,16 +66,14 @@ else
var semaphore = new SemaphoreSlim(4);
var semLock = new SemaphoreSlim(1);
- var tasks = rooms.Select(async room =>
- {
+ var tasks = rooms.Select(async room => {
await semaphore.WaitAsync();
- progress.ProcessedUsers.Add(room, new());
+ progress.ProcessedUsers.Add(room, new HomeServerInfoQueryProgress.State());
Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})");
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)
- {
+ if (states.Count > memberLimit) {
Console.WriteLine("Skipping!");
semaphore.Release();
progress.ProcessedUsers.Remove(room);
@@ -97,37 +82,31 @@ else
}
progress.ProcessedUsers[room].Total = states.Count;
var updateInterval = progress.ProcessedUsers[room].Total >= 1000 ? 1000 : 100;
- while (progress.ProcessedUsers.Any(x => x.Value.Total == 0) && progress.ProcessedUsers[room].Total >= 1000)
- {
+ while (progress.ProcessedUsers.Any(x => x.Value.Total == 0) && progress.ProcessedUsers[room].Total >= 1000) {
progress.ProcessedUsers[room].Blocked = true;
await Task.Delay(1000);
- // if(progressCallback != null)
- // await progressCallback.Invoke(progress);
+ // if(progressCallback != null)
+ // await progressCallback.Invoke(progress);
}
progress.ProcessedUsers[room].Blocked = false;
- int processedStates = 0;
- foreach (var state in states)
- {
+ var processedStates = 0;
+ foreach (var state in states) {
await semLock.WaitAsync();
semLock.Release();
- if (progress.ProcessedUsers.Count(x => x.Value.Total == 0) > 5 && progress.ProcessedUsers[room].Total >= 200)
- {
+ if (progress.ProcessedUsers.Count(x => x.Value.Total == 0) > 5 && progress.ProcessedUsers[room].Total >= 200) {
progress.ProcessedUsers[room].Slowmode = true;
await Task.Delay(progress.ProcessedUsers[room].Total >= 500 ? 1000 : 100);
}
- else
- {
+ else {
progress.ProcessedUsers[room].Slowmode = false;
}
- if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1]))
- {
- homeServers.Add(new HomeServerInfo() { Server = state.StateKey.Split(':')[1] });
+ if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) {
+ homeServers.Add(new HomeServerInfo { Server = state.StateKey.Split(':')[1] });
}
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)
- {
+ if (++progress.ProcessedUsers[room].Processed % updateInterval == 0 && progressCallback != null) {
await semLock.WaitAsync();
var _ = await progressCallback.Invoke(progress);
semLock.Release();
@@ -148,29 +127,26 @@ else
return homeServers;
}
- class HomeServerInfo
- {
+ class HomeServerInfo {
public string Server { get; set; }
public int? KnownUserCount { get; set; }
- public List<string> KnownUsers { get; set; } = new();
+ public List<string> KnownUsers { get; } = new();
}
- class HomeServerInfoQueryProgress
- {
+ class HomeServerInfoQueryProgress {
public int ProcessedRooms { get; set; }
public int TotalRooms { get; set; }
- public Dictionary<Room, State> ProcessedUsers { get; set; } = new();
+ public Dictionary<Room, State> ProcessedUsers { get; } = new();
public List<HomeServerInfo> CurrentState { get; set; } = new();
- public class State
- {
+ public class State {
public int Processed { get; set; }
public int Total { get; set; }
public bool Blocked { get; set; }
public bool Slowmode { get; set; }
public float Progress => (float)Processed / Total;
public bool IsFinished { get; set; }
- public Stopwatch Timing { get; set; } = Stopwatch.StartNew();
+ public Stopwatch Timing { get; } = Stopwatch.StartNew();
}
}
diff --git a/MatrixRoomUtils.Web/Pages/LoginPage.razor b/MatrixRoomUtils.Web/Pages/LoginPage.razor
index c46dc9a..16fdd24 100644
--- a/MatrixRoomUtils.Web/Pages/LoginPage.razor
+++ b/MatrixRoomUtils.Web/Pages/LoginPage.razor
@@ -1,6 +1,6 @@
@page "/Login"
-@using System.Text.Json
@using MatrixRoomUtils.Core.Authentication
+@using System.Text.Json
@using MatrixRoomUtils.Web.Shared.SimpleComponents
@inject ILocalStorageService LocalStorage
@inject IJSRuntime JsRuntime
@@ -27,8 +27,7 @@
<h4>Parsed records</h4>
<hr/>
<table border="1">
- @foreach (var (homeserver, username, password) in records)
- {
+ @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>
@@ -41,19 +40,16 @@
<LogView></LogView>
@code {
- List<(string homeserver, string username, string password)> records = new();
+ readonly List<(string homeserver, string username, string password)> records = new();
(string homeserver, string username, string password) newRecordInput = ("", "", "");
- async Task Login()
- {
- foreach (var (homeserver, username, password) in records)
- {
+ 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()
- {
+ var userinfo = new UserInfo {
LoginResponse = result
};
userinfo.Profile = await (await new AuthenticatedHomeServer(result.UserId, result.AccessToken, result.HomeServer).Configure()).GetProfile(result.UserId);
@@ -66,18 +62,15 @@
await LocalStorageWrapper.SaveToLocalStorage(LocalStorage);
}
- private async Task FileChanged(InputFileChangeEventArgs obj)
- {
- Console.WriteLine(JsonSerializer.Serialize(obj, new JsonSerializerOptions()
- {
+ 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();
+ var TsvData = await sr.ReadToEndAsync();
records.Clear();
- foreach (var line in TsvData.Split('\n'))
- {
+ foreach (var line in TsvData.Split('\n')) {
var parts = line.Split('\t');
if (parts.Length != 3)
continue;
@@ -85,8 +78,7 @@
}
}
- private void AddRecord()
- {
+ private void AddRecord() {
records.Add(newRecordInput);
newRecordInput = ("", "", "");
}
diff --git a/MatrixRoomUtils.Web/Pages/MediaLocator.razor b/MatrixRoomUtils.Web/Pages/MediaLocator.razor
index 19a0d4e..36cd8e6 100644
--- a/MatrixRoomUtils.Web/Pages/MediaLocator.razor
+++ b/MatrixRoomUtils.Web/Pages/MediaLocator.razor
@@ -7,8 +7,7 @@
<details>
<summary>Checked homeserver list (@homeservers.Count entries)</summary>
<ul>
- @foreach (var hs in homeservers)
- {
+ @foreach (var hs in homeservers) {
<li>@hs</li>
}
</ul>
@@ -16,26 +15,22 @@
<button @onclick="addMoreHomeservers">Add more homeservers</button>
<br/>
<span>MXC URL: </span>
-<input type="text" @bind="mxcUrl" />
+<input type="text" @bind="mxcUrl"/>
<button @onclick="executeSearch">Search</button>
-@if (successResults.Count > 0)
-{
+@if (successResults.Count > 0) {
<h4>Successes</h4>
<ul>
- @foreach (var result in successResults)
- {
+ @foreach (var result in successResults) {
<li>@result</li>
}
</ul>
}
-@if (errorResults.Count > 0)
-{
+@if (errorResults.Count > 0) {
<h4>Errors</h4>
<ul>
- @foreach (var result in errorResults)
- {
+ @foreach (var result in errorResults) {
<li>@result</li>
}
</ul>
@@ -44,15 +39,13 @@
@code {
string mxcUrl { get; set; }
- List<string> successResults = new List<string>();
- List<string> errorResults = new List<string>();
- List<string> homeservers = new List<string>();
+ readonly List<string> successResults = new();
+ readonly List<string> errorResults = new();
+ readonly List<string> homeservers = new();
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
- homeservers.AddRange(new []
- {
+ homeservers.AddRange(new[] {
"matrix.org",
"feline.support",
"rory.gay",
@@ -62,70 +55,58 @@
});
}
- async Task executeSearch()
- {
+ async Task executeSearch() {
var sem = new SemaphoreSlim(128, 128);
- homeservers.ForEach(async hs =>
- {
+ homeservers.ForEach(async hs => {
await sem.WaitAsync();
var httpClient = new HttpClient { BaseAddress = new Uri(hs) };
httpClient.Timeout = TimeSpan.FromSeconds(5);
var rmu = mxcUrl.Replace("mxc://", $"{hs}/_matrix/media/r0/download/");
- try
- {
+ try {
var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, rmu));
- if (res.IsSuccessStatusCode)
- {
+ if (res.IsSuccessStatusCode) {
successResults.Add($"{hs}: found - {res.Content.Headers.ContentLength} bytes");
StateHasChanged();
return;
}
errorResults.Add($"Error: {hs} - {res.StatusCode}\n" + await res.Content.ReadAsStringAsync());
}
- catch (Exception e)
- {
+ catch (Exception e) {
errorResults.Add($"Error: {e}");
}
- finally
- {
+ finally {
sem.Release();
}
StateHasChanged();
});
}
-
- async Task addMoreHomeservers()
- {
+ async Task addMoreHomeservers() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
var res = await Http.GetAsync("/homeservers.txt");
var content = await res.Content.ReadAsStringAsync();
homeservers.Clear();
var lines = content.Split("\n");
-
+
var rhs = new RemoteHomeServer("rory.gay");
var sem = new SemaphoreSlim(128, 128);
- lines.ToList().ForEach(async line =>
- {
+ lines.ToList().ForEach(async line => {
await sem.WaitAsync();
- try
- {
+ try {
homeservers.Add(await rhs.ResolveHomeserverFromWellKnown(line));
StateHasChanged();
- if(Random.Shared.Next(0,101) == 50)
+ if (Random.Shared.Next(0, 101) == 50)
await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage);
}
- catch (Exception e)
- {
+ catch (Exception e) {
Console.WriteLine(e);
}
- finally
- {
+ finally {
sem.Release();
}
});
-
StateHasChanged();
}
+
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor
index bf03ee3..b15928a 100644
--- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor
+++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor
@@ -1,7 +1,7 @@
@page "/PolicyListEditor/{RoomId}"
-@using System.Text.Json
@using MatrixRoomUtils.Core.Extensions
@using MatrixRoomUtils.Core.StateEventTypes
+@using System.Text.Json
@inject ILocalStorageService LocalStorage
@inject NavigationManager NavigationManager
<h3>Policy list editor - Editing @RoomId</h3>
@@ -15,12 +15,10 @@
<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>
}
-else
-{
+else {
<h3>Server policies</h3>
<hr/>
<table class="table table-striped table-hover" style="width: fit-Content;">
@@ -33,8 +31,7 @@ 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.StateKey</td>
<td>@policyEvent.Content.Reason</td>
@@ -59,23 +56,20 @@ 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>@policyEvent.StateKey</td>
- <td>@policyEvent.Content.ToJson(indent: false, ignoreNull: true)</td>
+ <td>@policyEvent.Content.ToJson(false, true)</td>
</tr>
}
</tbody>
</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>
}
-else
-{
+else {
<h3>Room policies</h3>
<hr/>
<table class="table table-striped table-hover" style="width: fit-Content;">
@@ -88,8 +82,7 @@ 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.StateKey</td>
<td>@policyEvent.Content.Reason</td>
@@ -113,30 +106,26 @@ 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>@policyEvent.StateKey</td>
- <td>@policyEvent.Content.ToJson(indent: false, ignoreNull: true)</td>
+ <td>@policyEvent.Content.ToJson(false, true)</td>
</tr>
}
</tbody>
</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>
}
-else
-{
+else {
<h3>User policies</h3>
<hr/>
<table class="table table-striped table-hover" style="width: fit-Content;">
<thead>
<tr>
- @if (_enableAvatars)
- {
+ @if (_enableAvatars) {
<th scope="col"></th>
}
<th scope="col" style="max-width: 0.2vw; word-wrap: anywhere;">User</th>
@@ -146,12 +135,12 @@ 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>
- @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>
+ @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>
@@ -175,11 +164,10 @@ 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>@policyEvent.StateKey</td>
- <td>@policyEvent.Content.ToJson(indent: false, ignoreNull: true)</td>
+ <td>@policyEvent.Content.ToJson(false, true)</td>
</tr>
}
</tbody>
@@ -197,21 +185,19 @@ else
[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>();
+
+ private bool _enableAvatars;
+
+ static readonly Dictionary<string, string?> avatars = new();
+ static readonly Dictionary<string, RemoteHomeServer> servers = new();
public static List<StateEventResponse<PolicyRuleStateEventData>> PolicyEvents { get; set; } = new();
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null)
- if (RuntimeCache.CurrentHomeServer == null)
- {
+ // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null)
+ if (RuntimeCache.CurrentHomeServer == null) {
NavigationManager.NavigateTo("/Login");
return;
}
@@ -220,18 +206,16 @@ else
Console.WriteLine("Policy list editor initialized!");
}
- 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<StateEventResponse>>(Content);
+ 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<StateEventResponse>>(Content);
var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId);
var stateEventsQuery = await room.GetStateAsync("");
- if (stateEventsQuery == null)
- {
+ if (stateEventsQuery == null) {
Console.WriteLine("state events query is null!!!");
}
var stateEvents = stateEventsQuery.Value.Deserialize<List<StateEventResponse>>();
@@ -239,30 +223,25 @@ else
.Select(x => JsonSerializer.Deserialize<StateEventResponse<PolicyRuleStateEventData>>(JsonSerializer.Serialize(x))).ToList();
StateHasChanged();
}
-
- private async Task GetAvatar(string userId)
- {
- try
- {
+
+ 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();
+ var 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
+ catch {
+ // ignored
}
}
-
- private async Task GetAllAvatars()
- {
- foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity != null))
- {
+
+ 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 e61598a..20eab7a 100644
--- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor
+++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor
@@ -1,29 +1,25 @@
@page "/PolicyListEditor"
-@using System.Text.Json
@using MatrixRoomUtils.Core.Extensions
@inject ILocalStorageService LocalStorage
@inject NavigationManager NavigationManager
<h3>Policy list editor - Room list</h3>
<hr/>
-@if (PolicyRoomList.Count == 0)
-{
+@if (PolicyRoomList.Count == 0) {
<p>No policy rooms found.</p>
<p>Loading progress: @checkedRoomCount/@totalRoomCount</p>
}
-else
-{
- @if (checkedRoomCount != totalRoomCount)
- {
+else {
+ @if (checkedRoomCount != totalRoomCount) {
<p>Loading progress: @checkedRoomCount/@totalRoomCount</p>
}
- foreach (var s in PolicyRoomList)
- {
-
- <a style="color: unset; text-decoration: unset;" href="/PolicyListEditor/@s.RoomId.Replace('.','~')"><RoomListItem RoomId="@s.RoomId">
- <br/>
- <span>Shortcode: @s.Shortcode</span>
- </RoomListItem></a>
+ foreach (var s in PolicyRoomList) {
+ <a style="color: unset; text-decoration: unset;" href="/PolicyListEditor/@s.RoomId.Replace('.', '~')">
+ <RoomListItem RoomId="@s.RoomId">
+ <br/>
+ <span>Shortcode: @s.Shortcode</span>
+ </RoomListItem>
+ </a>
@* <a href="@(NavigationManager.Uri + "/" + s.RoomId.Replace('.', '~'))">[@s.Shortcode] @s.Name (@s.RoomId)</a> *@
@* <br/> *@
}
@@ -40,15 +36,13 @@ else
public List<PolicyRoomInfo> PolicyRoomList { get; set; } = new();
- private int checkedRoomCount { get; set; } = 0;
- private int totalRoomCount { get; set; } = 0;
+ private int checkedRoomCount { get; set; }
+ private int totalRoomCount { get; set; }
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- if (RuntimeCache.CurrentHomeServer == null)
- {
+ if (RuntimeCache.CurrentHomeServer == null) {
NavigationManager.NavigateTo("/Login");
return;
}
@@ -56,59 +50,48 @@ else
Console.WriteLine("Policy list editor initialized!");
}
- private async Task EnumeratePolicyRooms()
- {
+ private async Task EnumeratePolicyRooms() {
var xxxrooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms();
totalRoomCount = xxxrooms.Count;
StateHasChanged();
var xxxsemaphore = new SemaphoreSlim(1000);
var xxxtasks = new List<Task<PolicyRoomInfo?>>();
- foreach (var room in xxxrooms)
- {
+ foreach (var room in xxxrooms) {
xxxtasks.Add(GetPolicyRoomInfo(room.RoomId, xxxsemaphore));
}
var xxxresults = await Task.WhenAll(xxxtasks);
PolicyRoomList.AddRange(xxxresults.Where(x => x != null).Select(x => x.Value));
Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}");
- return;
}
- private async Task<PolicyRoomInfo?> GetPolicyRoomInfo(string room, SemaphoreSlim semaphore)
- {
- try
- {
+ private async Task<PolicyRoomInfo?> GetPolicyRoomInfo(string room, SemaphoreSlim semaphore) {
+ try {
await semaphore.WaitAsync();
- PolicyRoomInfo roomInfo = new()
- {
+ PolicyRoomInfo roomInfo = new() {
RoomId = room
};
var r = await RuntimeCache.CurrentHomeServer.GetRoom(room);
var shortcodeState = await r.GetStateAsync("org.matrix.mjolnir.shortcode");
- if(!shortcodeState.HasValue) return null;
- roomInfo.Shortcode = shortcodeState.Value.TryGetProperty("shortcode", out JsonElement shortcode) ? shortcode.GetString() : null;
+ if (!shortcodeState.HasValue) return null;
+ roomInfo.Shortcode = shortcodeState.Value.TryGetProperty("shortcode", out var shortcode) ? shortcode.GetString() : null;
- if (roomInfo.Shortcode != null)
- {
+ if (roomInfo.Shortcode != null) {
roomInfo.Name = await r.GetNameAsync();
return roomInfo;
}
return null;
}
- finally
-
- {
+ finally {
checkedRoomCount++;
StateHasChanged();
semaphore.Release();
}
}
- public struct PolicyRoomInfo
-
- {
+ public struct PolicyRoomInfo {
public
string RoomId { get; set; }
@@ -118,4 +101,5 @@ else
public
string? Name { get; set; }
}
- }
\ No newline at end of file
+
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor
index 5daa97c..9b0bb88 100644
--- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor
@@ -3,12 +3,10 @@
@inject NavigationManager NavigationManager
<h3>Room manager</h3>
<hr/>
-@if (Rooms.Count == 0)
-{
+@if (Rooms.Count == 0) {
<p>You are not in any rooms!</p>
}
-else
-{
+else {
<p>You are in @Rooms.Count rooms and @Spaces.Count spaces</p>
<p>
<a href="/RoomManagerCreateRoom">Create room</a>
@@ -16,8 +14,7 @@ else
<details open>
<summary>Space List</summary>
- @foreach (var room in Spaces)
- {
+ @foreach (var room in Spaces) {
<a style="color: unset; text-decoration: unset;" href="/RoomManager/Space/@room.RoomId.Replace('.', '~')">
<RoomListItem Room="@room" ShowOwnProfile="false"></RoomListItem>
</a>
@@ -25,8 +22,7 @@ else
</details>
<details open>
<summary>Room List</summary>
- @foreach (var room in Rooms)
- {
+ @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>
@@ -41,15 +37,13 @@ else
public List<Room> Rooms { get; set; } = new();
public List<Room> Spaces { get; set; } = new();
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
Console.WriteLine("Initializing room manager");
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
Console.WriteLine("Loaded from local storage");
await base.OnInitializedAsync();
Console.WriteLine("Initialized base");
- if (RuntimeCache.CurrentHomeServer == null)
- {
+ if (RuntimeCache.CurrentHomeServer == null) {
NavigationManager.NavigateTo("/Login");
return;
}
@@ -59,8 +53,7 @@ else
Console.WriteLine($"Got {_rooms.Count} rooms");
var semaphore = new SemaphoreSlim(10);
var tasks = new List<Task<Room?>>();
- foreach (var room in _rooms)
- {
+ foreach (var room in _rooms) {
tasks.Add(CheckIfSpace(room, semaphore));
}
await Task.WhenAll(tasks);
@@ -68,45 +61,36 @@ else
Console.WriteLine("Fetched joined rooms!");
}
- private async Task<Room?> CheckIfSpace(Room room, SemaphoreSlim semaphore)
- {
+ private async Task<Room?> CheckIfSpace(Room room, SemaphoreSlim semaphore) {
await semaphore.WaitAsync();
- // Console.WriteLine($"Checking if {room.RoomId} is a space");
- try
- {
+ // Console.WriteLine($"Checking if {room.RoomId} is a space");
+ try {
var state = await room.GetStateAsync<CreateEvent>("m.room.create");
- if (state != null)
- {
+ if (state != null) {
//Console.WriteLine(state.Value.ToJson());
- if (state.Type != null)
- {
- if (state.Type == "m.space")
- {
+ if (state.Type != null) {
+ if (state.Type == "m.space") {
Console.WriteLine($"Room {room.RoomId} is a space!");
Spaces.Add(room);
StateHasChanged();
return room;
}
- else
- {
+ else {
Console.WriteLine($"Encountered unknown room type {state.Type}");
}
}
- else
- {
+ else {
Rooms.Add(room);
//this is fine, apprently...
// Console.WriteLine($"Room {room.RoomId} has no Content.type in m.room.create!");
}
}
}
- catch (Exception e)
- {
+ catch (Exception e) {
Console.WriteLine(e);
return null;
}
- finally
- {
+ finally {
semaphore.Release();
}
return null;
diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor
index d561eb0..5cfda77 100644
--- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor
@@ -1,12 +1,13 @@
@page "/RoomManagerCreateRoom"
-@using System.Text.Json
@using MatrixRoomUtils.Core.Extensions
@using MatrixRoomUtils.Core.Responses
-@using MatrixRoomUtils.Web.Shared.SimpleComponents
-@using System.Reflection
-@using System.Runtime.Intrinsics.X86
+@using System.Text.Json
@using System.Text.Json.Serialization
+@using System.Reflection
@using MatrixRoomUtils.Web.Classes.RoomCreationTemplates
+@* ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not *@
+@using MatrixRoomUtils.Web.Shared.SimpleComponents
+
<h3>Room Manager - Create Room</h3>
@* <pre Contenteditable="true" @onkeypress="@JsonChanged" ="JsonString">@JsonString</pre> *@
@@ -20,15 +21,13 @@
<td style="padding-bottom: 16px;">Preset:</td>
<td style="padding-bottom: 16px;">
<InputSelect @bind-Value="@RoomPreset">
- @foreach (var createRoomRequest in Presets)
- {
+ @foreach (var createRoomRequest in Presets) {
<option value="@createRoomRequest.Key">@createRoomRequest.Key</option>
}
</InputSelect>
</td>
</tr>
- @if (creationEvent != null)
- {
+ @if (creationEvent != null) {
<tr>
<td>Room name:</td>
<td>
@@ -89,8 +88,7 @@
<td>Permissions:</td>
<details>
<summary>@creationEvent.PowerLevelContentOverride.Users.Count members</summary>
- @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys)
- {
+ @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) {
var _event = user;
<tr>
<td><FancyTextBox Formatter="@GetPermissionFriendlyName" Value="@_event" ValueChanged="val => { creationEvent.PowerLevelContentOverride.Events.ChangeKey(_event, val); }"></FancyTextBox>:</td>
@@ -99,8 +97,7 @@
</td>
</tr>
}
- @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys)
- {
+ @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) {
var _user = user;
<tr>
<td><FancyTextBox Value="@_user" ValueChanged="val => { creationEvent.PowerLevelContentOverride.Users.ChangeKey(_user, val); creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"></FancyTextBox>:</td>
@@ -131,8 +128,7 @@
<details>
<summary>@creationEvent.InitialState.Count(x => x.Type == "m.room.member") members</summary>
<button @onclick="() => { RuntimeCache.LoginSessions.Select(x => x.Value.LoginResponse.UserId).ToList().ForEach(InviteMember); }">Invite all logged in accounts</button>
- @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != RuntimeCache.CurrentHomeServer.UserId))
- {
+ @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != RuntimeCache.CurrentHomeServer.UserId)) {
<UserListItem UserId="@member.StateKey"></UserListItem>
}
</details>
@@ -154,13 +150,11 @@
<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)))
- {
+ @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) {
<tr>
<td style="vertical-align: top;">
@(initialState.Type):
- @if (!string.IsNullOrEmpty(initialState.StateKey))
- {
+ @if (!string.IsNullOrEmpty(initialState.StateKey)) {
<br/>
<span>(@initialState.StateKey)</span>
}
@@ -176,8 +170,7 @@
<details>
<summary>@creationEvent.InitialState.Count initial states</summary>
<table>
- @foreach (var initialState in creationEvent.InitialState)
- {
+ @foreach (var initialState in creationEvent.InitialState) {
var _state = initialState;
<tr>
<td style="vertical-align: top;">
@@ -214,25 +207,21 @@
@code {
- private string RoomPreset
- {
- get
- {
- if (Presets.ContainsValue(creationEvent))
- {
+ private string RoomPreset {
+ get {
+ if (Presets.ContainsValue(creationEvent)) {
return Presets.First(x => x.Value == creationEvent).Key;
}
return "Not a preset";
}
- set
- {
+ set {
creationEvent = Presets[value];
JsonChanged();
OverwriteWrappedPropertiesFromEvent();
creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
guestAccessEvent = creationEvent["m.room.guest_access"].As<GuestAccessContent>().Content;
-
+
Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}");
Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As<GuestAccessContent>().ToJson()}");
creationEvent["m.room.guest_access"].As<GuestAccessContent>().Content.IsGuestAccessEnabled = true;
@@ -251,21 +240,18 @@
private Dictionary<string, CreateRoomRequest> Presets { get; set; } = new();
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
//creationEvent = Presets["Default room"] =
- foreach (var x in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Contains(typeof(IRoomCreationTemplate))).ToList())
- {
+ foreach (var x in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Contains(typeof(IRoomCreationTemplate))).ToList()) {
Console.WriteLine($"Found room creation template in class: {x.FullName}");
var instance = (IRoomCreationTemplate)Activator.CreateInstance(x);
Presets[instance.Name] = instance.CreateRoomRequest;
}
Presets = Presets.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
- if (!Presets.ContainsKey("Default"))
- {
+ if (!Presets.ContainsKey("Default")) {
Console.WriteLine($"No default room found in {Presets.Count} presets: {string.Join(", ", Presets.Keys)}");
}
else RoomPreset = "Default";
@@ -273,30 +259,23 @@
await base.OnInitializedAsync();
}
- private void JsonChanged()
- {
- Console.WriteLine(creationEvent.ToJson());
- }
-
+ private void JsonChanged() => Console.WriteLine(creationEvent.ToJson());
//wrappers
private List<string> ServerACLAllowRules { get; set; } = new();
private List<string> ServerACLDenyRules { get; set; } = new();
- private void OverwriteWrappedPropertiesFromEvent()
- {
+ private void OverwriteWrappedPropertiesFromEvent() {
Console.WriteLine("Overwriting wrapped properties from event");
ServerACLAllowRules = creationEvent.ServerACLs.Allow;
ServerACLDenyRules = creationEvent.ServerACLs.Deny;
}
- private async Task OverwriteWrappedProperties()
- {
+ private async Task OverwriteWrappedProperties() {
Console.WriteLine("Overwriting wrapped properties");
Console.WriteLine($"Allow: {ServerACLAllowRules.Count}: {string.Join(", ", ServerACLAllowRules)}");
Console.WriteLine($"Deny: {ServerACLDenyRules.Count}: {string.Join(", ", ServerACLDenyRules)}");
- creationEvent.ServerACLs = new()
- {
+ creationEvent.ServerACLs = new ServerACL {
Allow = ServerACLAllowRules,
Deny = ServerACLDenyRules,
AllowIpLiterals = creationEvent.ServerACLs.AllowIpLiterals
@@ -305,16 +284,14 @@
StateHasChanged();
}
- private async Task RoomIconFilePicked(InputFileChangeEventArgs obj)
- {
+ private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) {
var res = await RuntimeCache.CurrentHomeServer.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType);
Console.WriteLine(res);
creationEvent.RoomIcon = res;
StateHasChanged();
}
- private async Task CreateRoom()
- {
+ private async Task CreateRoom() {
Console.WriteLine("Create room");
Console.WriteLine(creationEvent.ToJson());
creationEvent.CreationContent.Add("rory.gay.created_using", "Rory&::MatrixRoomUtils (https://mru.rory.gay)");
@@ -323,22 +300,18 @@
// NavigationManager.NavigateTo($"/RoomManager/{id.RoomId.Replace('.','~')}");
}
- private void InviteMember(string mxid)
- {
+ private void InviteMember(string mxid) {
if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && RuntimeCache.CurrentHomeServer.UserId != mxid)
- creationEvent.InitialState.Add(new()
- {
+ creationEvent.InitialState.Add(new StateEvent {
Type = "m.room.member",
StateKey = mxid,
- Content = new
- {
+ Content = new {
membership = "invite",
reason = "Automatically invited at room creation time."
}
});
}
-
private string GetStateFriendlyName(string key) => key switch {
"m.room.history_visibility" => "History visibility",
"m.room.guest_access" => "Guest access",
@@ -363,19 +336,14 @@
_ => key
};
- public class GuestAccessContent
- {
+ public class GuestAccessContent {
[JsonPropertyName("guest_access")]
public string GuestAccess { get; set; }
- public bool IsGuestAccessEnabled
- {
+ public bool IsGuestAccessEnabled {
get => GuestAccess == "can_join";
set => GuestAccess = value ? "can_join" : "forbidden";
}
}
-
-
}
-
diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor
index 1e7e065..c5e1569 100644
--- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor
@@ -4,8 +4,7 @@
<h3>Room manager - Viewing Space</h3>
<button onclick="@JoinAllRooms">Join all rooms</button>
-@foreach (var room in Rooms)
-{
+@foreach (var room in Rooms) {
<RoomListItem Room="room" ShowOwnProfile="true"></RoomListItem>
}
@@ -13,8 +12,7 @@
<br/>
<details style="background: #0002;">
<summary style="background: #fff1;">State list</summary>
- @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type))
- {
+ @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type)) {
<p>@stateEvent.StateKey/@stateEvent.Type:</p>
<pre>@stateEvent.Content.ToJson()</pre>
}
@@ -24,61 +22,52 @@
[Parameter]
public string RoomId { get; set; } = "invalid!!!!!!";
-
+
private Room? Room { get; set; }
-
+
private StateEventResponse<object>[] States { get; set; } = Array.Empty<StateEventResponse<object>>();
- private List<Room> Rooms { get; set; } = new();
- private List<string> ServersInSpace { get; set; } = new();
-
- protected override async Task OnInitializedAsync()
- {
+ private List<Room> Rooms { get; } = new();
+ private List<string> ServersInSpace { get; } = new();
+
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
Room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId.Replace('~', '.'));
var state = await Room.GetStateAsync("");
- if (state != null)
- {
- // Console.WriteLine(state.Value.ToJson());
+ if (state != null) {
+ // Console.WriteLine(state.Value.ToJson());
States = state.Value.Deserialize<StateEventResponse<object>[]>()!;
-
- foreach (var stateEvent in States)
- {
- if (stateEvent.Type == "m.space.child")
- {
- // if (stateEvent.Content.ToJson().Length < 5) return;
+
+ foreach (var stateEvent in States) {
+ if (stateEvent.Type == "m.space.child") {
+ // if (stateEvent.Content.ToJson().Length < 5) return;
var roomId = stateEvent.StateKey;
var room = await RuntimeCache.CurrentHomeServer.GetRoom(roomId);
- if (room != null)
- {
+ if (room != null) {
Rooms.Add(room);
}
}
- else if (stateEvent.Type == "m.room.member")
- {
+ else if (stateEvent.Type == "m.room.member") {
var serverName = stateEvent.StateKey.Split(':').Last();
- if (!ServersInSpace.Contains(serverName))
- {
+ if (!ServersInSpace.Contains(serverName)) {
ServersInSpace.Add(serverName);
}
}
}
-
- // 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!");
- // }
+
+ // 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!");
+ // }
}
await base.OnInitializedAsync();
}
-
- private async Task JoinAllRooms()
- {
- foreach (var room in Rooms)
- {
+
+ private async Task JoinAllRooms() {
+ foreach (var room in Rooms) {
room.JoinAsync(ServersInSpace.ToArray());
}
}
diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor
index a8a7fc2..2db7cab 100644
--- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor
@@ -1,12 +1,10 @@
@page "/RoomManager/Timeline/{RoomId}"
@using MatrixRoomUtils.Web.Shared.TimelineComponents
-@using MatrixRoomUtils.Core.Extensions
<h3>RoomManagerTimeline</h3>
<hr/>
<p>Loaded @Events.Count events...</p>
-@foreach (var evt in Events)
-{
+@foreach (var evt in Events) {
<div type="@evt.Type" key="@evt.StateKey" itemid="@evt.EventId">
<DynamicComponent Type="@ComponentType(evt)" Parameters="@(new Dictionary<string, object> { { "Event", evt }, { "Events", Events } })"></DynamicComponent>
</div>
@@ -17,18 +15,16 @@
[Parameter]
public string RoomId { get; set; } = "invalid!!!!!!";
- private List<MessagesResponse> Messages { get; set; } = new();
- private List<StateEventResponse> Events { get; set; } = new();
+ private List<MessagesResponse> Messages { get; } = new();
+ private List<StateEventResponse> Events { get; } = new();
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
RoomId = RoomId.Replace('~', '.');
Console.WriteLine("RoomId: " + RoomId);
var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId);
MessagesResponse? msgs = null;
- do
- {
+ do {
msgs = await room.GetMessagesAsync(limit: 250, from: msgs?.End, dir: "b");
Messages.Add(msgs);
Console.WriteLine($"Got {msgs.Chunk.Count} messages");
@@ -37,13 +33,11 @@
StateHasChanged();
} while (msgs.End != null);
-
await base.OnInitializedAsync();
}
private StateEventResponse GetProfileEventBefore(StateEventResponse Event) => Events.TakeWhile(x => x != Event).Last(e => e.Type == "m.room.member" && e.StateKey == Event.Sender);
-
private Type ComponentType(StateEventResponse Event) => Event.Type switch {
"m.room.message" => typeof(TimelineMessageItem),
"m.room.member" => typeof(TimelineMemberItem),
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
index 3037dcc..74f4f92 100644
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
@@ -12,8 +12,7 @@
<br/>
<InputSelect @bind-Value="shownStateKey">
<option value="">-- State key --</option>
- @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != "").Select(x => x.StateKey).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,8 +20,7 @@
<br/>
<InputSelect @bind-Value="shownType">
<option value="">-- Type --</option>
- @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != 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>
@@ -45,12 +43,10 @@
public List<StateEventResponse> Events { get; set; } = new();
public string status = "";
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- if (RuntimeCache.CurrentHomeServer != null)
- {
+ if (RuntimeCache.CurrentHomeServer != null) {
NavigationManager.NavigateTo("/Login");
return;
}
@@ -61,34 +57,29 @@
private DateTime _lastUpdate = DateTime.Now;
- private async Task LoadStatesAsync()
- {
- int StateLoaded = 0;
+ private async Task LoadStatesAsync() {
+ var StateLoaded = 0;
using var client = new HttpClient();
- //TODO: can this be improved?
+ //TODO: can this be improved?
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken);
var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state");
// 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<StateEventResponse>(_data);
- await foreach (var _ev in __events)
- {
- var e = new StateEventResponse()
- {
+ await foreach (var _ev in __events) {
+ var e = new StateEventResponse {
Type = _ev.Type,
StateKey = _ev.StateKey,
OriginServerTs = _ev.OriginServerTs,
Content = _ev.Content
};
Events.Add(e);
- if (string.IsNullOrEmpty(e.StateKey))
- {
+ if (string.IsNullOrEmpty(e.StateKey)) {
FilteredEvents.Add(e);
}
StateLoaded++;
- if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100)
- {
+ if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) {
_lastUpdate = DateTime.Now;
status = $"Loaded {StateLoaded} state events";
StateHasChanged();
@@ -99,8 +90,7 @@
StateHasChanged();
}
- private async Task RebuildFilteredData()
- {
+ private async Task RebuildFilteredData() {
status = "Rebuilding filtered data...";
StateHasChanged();
await Task.Delay(1);
@@ -112,16 +102,14 @@
StateHasChanged();
await Task.Delay(1);
FilteredEvents = _FilteredEvents;
-
- if(_shownType != null)
+
+ if (_shownType != null)
shownEventJson = _FilteredEvents.Where(x => x.Type == _shownType).First().Content.ToJson(indent: true, ignoreNull: true);
-
+
StateHasChanged();
}
-
- public struct PreRenderedStateEvent
- {
+ public struct PreRenderedStateEvent {
public string content { get; set; }
public long origin_server_ts { get; set; }
public string state_key { get; set; }
@@ -132,11 +120,9 @@
// public string ReplacesState { get; set; }
}
- public bool ShowMembershipEvents
- {
+ public bool ShowMembershipEvents {
get => _showMembershipEvents;
- set
- {
+ set {
_showMembershipEvents = value;
RebuildFilteredData();
}
@@ -146,21 +132,17 @@
private string _shownStateKey;
private string _shownType;
- private string shownStateKey
- {
+ private string shownStateKey {
get => _shownStateKey;
- set
- {
+ set {
_shownStateKey = value;
RebuildFilteredData();
}
}
- private string shownType
- {
+ private string shownType {
get => _shownType;
- set
- {
+ set {
_shownType = value;
RebuildFilteredData();
}
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor
index c654b13..82b5d75 100644
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor
@@ -3,16 +3,15 @@
@inject NavigationManager NavigationManager
<h3>Room state viewer - Room list</h3>
<hr/>
-@if (Rooms.Count == 0)
-{
+@if (Rooms.Count == 0) {
<p>You are not in any rooms!</p>
@* <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> *@
}
-else
-{
- @foreach (var room in Rooms)
- {
- <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.','~')"><RoomListItem RoomId="@room"></RoomListItem></a>
+else {
+ @foreach (var room in Rooms) {
+ <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.', '~')">
+ <RoomListItem RoomId="@room"></RoomListItem>
+ </a>
}
<div style="margin-bottom: 4em;"></div>
}
@@ -21,16 +20,16 @@ else
@code {
public List<string> Rooms { get; set; } = new();
- protected override async Task OnInitializedAsync()
- {
+
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- if (RuntimeCache.CurrentHomeServer == null)
- {
+ if (RuntimeCache.CurrentHomeServer == null) {
NavigationManager.NavigateTo("/Login");
return;
}
- Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x=>x.RoomId).ToList();
+ Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x => x.RoomId).ToList();
Console.WriteLine("Fetched joined rooms!");
}
-}
\ No newline at end of file
+
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
index c7f9f3c..bfd4d10 100644
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
@@ -1,7 +1,7 @@
@page "/RoomStateViewer/{RoomId}"
+@using MatrixRoomUtils.Core.Extensions
@using System.Net.Http.Headers
@using System.Text.Json
-@using MatrixRoomUtils.Core.Extensions
@inject ILocalStorageService LocalStorage
@inject NavigationManager NavigationManager
<h3>Room state viewer - Viewing @RoomId</h3>
@@ -19,8 +19,7 @@
</tr>
</thead>
<tbody>
- @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key == "").OrderBy(x => x.origin_server_ts))
- {
+ @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key == "").OrderBy(x => x.origin_server_ts)) {
<tr>
<td>@stateEvent.type</td>
<td style="max-width: fit-Content;">
@@ -31,8 +30,7 @@
</tbody>
</table>
-@foreach (var group in FilteredEvents.GroupBy(x => x.state_key).OrderBy(x => x.Key).Where(x => x.Key != ""))
-{
+@foreach (var group in FilteredEvents.GroupBy(x => x.state_key).OrderBy(x => x.Key).Where(x => x.Key != "")) {
<details>
<summary>@group.Key</summary>
<table class="table table-striped table-hover" style="width: fit-Content;">
@@ -43,8 +41,7 @@
</tr>
</thead>
<tbody>
- @foreach (var stateEvent in group.OrderBy(x => x.origin_server_ts))
- {
+ @foreach (var stateEvent in group.OrderBy(x => x.origin_server_ts)) {
<tr>
<td>@stateEvent.type</td>
<td style="max-width: fit-Content;">
@@ -72,12 +69,10 @@
public List<PreRenderedStateEvent> Events { get; set; } = new();
public string status = "";
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
await base.OnInitializedAsync();
- if (RuntimeCache.CurrentHomeServer == null)
- {
+ if (RuntimeCache.CurrentHomeServer == null) {
NavigationManager.NavigateTo("/Login");
return;
}
@@ -85,56 +80,50 @@
await LoadStatesAsync();
Console.WriteLine("Policy list editor initialized!");
}
+
private DateTime _lastUpdate = DateTime.Now;
- private async Task LoadStatesAsync()
- {
- int StateLoaded = 0;
- //TODO: can we improve this?
+ private async Task LoadStatesAsync() {
+ var StateLoaded = 0;
+ //TODO: can we improve this?
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken);
- var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state");
- // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json");
+ var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state");
+ // 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<StateEventStruct>(_data);
- await foreach (var _ev in __events)
- {
- var e = new PreRenderedStateEvent()
- {
+ await foreach (var _ev in __events) {
+ var e = new PreRenderedStateEvent {
type = _ev.type,
state_key = _ev.state_key,
origin_server_ts = _ev.origin_server_ts,
- content = _ev.content.ToJson(indent: true, ignoreNull: true),
+ content = _ev.content.ToJson(true, true)
};
Events.Add(e);
- if (string.IsNullOrEmpty(e.state_key))
- {
+ if (string.IsNullOrEmpty(e.state_key)) {
FilteredEvents.Add(e);
}
StateLoaded++;
- if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100)
- {
+ if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) {
_lastUpdate = DateTime.Now;
status = $"Loaded {StateLoaded} state events";
StateHasChanged();
await Task.Delay(0);
}
-
}
StateHasChanged();
}
- private async Task RebuildFilteredData()
- {
+ private async Task RebuildFilteredData() {
status = "Rebuilding filtered data...";
StateHasChanged();
await Task.Delay(1);
var _FilteredEvents = Events;
if (!ShowMembershipEvents)
_FilteredEvents = _FilteredEvents.Where(x => x.type != "m.room.member").ToList();
-
+
status = "Done, rerendering!";
StateHasChanged();
await Task.Delay(1);
@@ -142,9 +131,7 @@
StateHasChanged();
}
-
- public struct PreRenderedStateEvent
- {
+ public struct PreRenderedStateEvent {
public string content { get; set; }
public long origin_server_ts { get; set; }
public string state_key { get; set; }
@@ -155,11 +142,9 @@
// public string ReplacesState { get; set; }
}
- public bool ShowMembershipEvents
- {
+ public bool ShowMembershipEvents {
get => _showMembershipEvents;
- set
- {
+ set {
_showMembershipEvents = value;
RebuildFilteredData();
}
diff --git a/MatrixRoomUtils.Web/Program.cs b/MatrixRoomUtils.Web/Program.cs
index 15c18f7..164f746 100644
--- a/MatrixRoomUtils.Web/Program.cs
+++ b/MatrixRoomUtils.Web/Program.cs
@@ -1,17 +1,16 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Blazored.LocalStorage;
+using MatrixRoomUtils.Web;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
-using MatrixRoomUtils.Web;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
-builder.Services.AddBlazoredLocalStorage(config =>
-{
+builder.Services.AddBlazoredLocalStorage(config => {
config.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
config.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
config.JsonSerializerOptions.IgnoreReadOnlyProperties = true;
diff --git a/MatrixRoomUtils.Web/Shared/EditablePre.razor b/MatrixRoomUtils.Web/Shared/EditablePre.razor
index 01bea0d..e759015 100644
--- a/MatrixRoomUtils.Web/Shared/EditablePre.razor
+++ b/MatrixRoomUtils.Web/Shared/EditablePre.razor
@@ -1,8 +1,9 @@
@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)
- {
+
+ protected override bool TryParseValueFromString(string? value, out string result, out string? validationErrorMessage) {
result = value;
validationErrorMessage = null;
return true;
@@ -10,9 +11,6 @@
public object Id { get; set; }
- private async Task Callback()
- {
- Console.WriteLine("beep");
- }
+ 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 03a7145..1fc70f2 100644
--- a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor
+++ b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor
@@ -1,10 +1,4 @@
-@using MatrixRoomUtils.Web.Classes
-@using System.Text.Json
-@using Blazored.LocalStorage
-@using MatrixRoomUtils.Core
@using MatrixRoomUtils.Core.Extensions
-@using Index = MatrixRoomUtils.Web.Pages.Index
-@using System.ComponentModel.DataAnnotations
@inject ILocalStorageService LocalStorage
@inject NavigationManager NavigationManager
@@ -27,30 +21,33 @@
private string? _avatarUrl { get; set; }
private int _roomCount { get; set; } = 0;
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
var hs = await new AuthenticatedHomeServer(User.LoginResponse.UserId, User.AccessToken, User.LoginResponse.HomeServer).Configure();
if (User.Profile.AvatarUrl != null && 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;
+ try {
+ _roomCount = (await hs.GetJoinedRooms()).Count;
+ }
+ catch {
+ _roomCount = -1;
+ }
await base.OnInitializedAsync();
}
- private async Task RemoveUser()
- {
+ private async Task RemoveUser() {
Console.WriteLine(User.ToJson());
RuntimeCache.LoginSessions.Remove(User.AccessToken);
StateHasChanged();
}
- private async Task SetCurrent()
- {
+ private async Task SetCurrent() {
RuntimeCache.LastUsedToken = User.AccessToken;
//RuntimeCache.CurrentHomeserver = await MatrixAuth.ResolveHomeserverFromWellKnown(LocalStorageWrapper.LoginSessions[Token].LoginResponse.HomeServer);
await LocalStorageWrapper.SaveToLocalStorage(LocalStorage);
await LocalStorageWrapper.InitialiseRuntimeVariables(LocalStorage);
StateHasChanged();
}
+
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
index 56131c8..9833c62 100644
--- a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
+++ b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
@@ -4,8 +4,7 @@
<span style="position: relative; top: -5px;">@ProfileName</span>
<div style="display: inline-block;">
- @if (ChildContent != null)
- {
+ @if (ChildContent != null) {
@ChildContent
}
</div>
@@ -25,15 +24,13 @@
[Parameter]
public string? ProfileAvatar { get; set; } = null;
-
+
[Parameter]
public string? ProfileName { get; set; } = null;
-
private static SemaphoreSlim _semaphoreSlim = new(128);
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
@@ -41,19 +38,14 @@
var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.UserId, RuntimeCache.CurrentHomeServer.AccessToken, RuntimeCache.CurrentHomeServer.HomeServerDomain).Configure();
- if (User == null)
- {
- if (UserId == null)
- {
+ if (User == null) {
+ if (UserId == null) {
throw new ArgumentNullException(nameof(UserId));
}
User = await hs.GetProfile(UserId);
}
- else
- {
- // UserId = User.;
- }
-
+
+ // UserId = User.;
ProfileAvatar ??= RuntimeCache.CurrentHomeServer.ResolveMediaUri(User.AvatarUrl);
ProfileName ??= User.DisplayName;
diff --git a/MatrixRoomUtils.Web/Shared/LogView.razor b/MatrixRoomUtils.Web/Shared/LogView.razor
index 80fd355..2f83cb2 100644
--- a/MatrixRoomUtils.Web/Shared/LogView.razor
+++ b/MatrixRoomUtils.Web/Shared/LogView.razor
@@ -1,6 +1,5 @@
@using System.Text
-@if (LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers)
-{
+@if (LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) {
<u>Logs</u>
<br/>
<pre>
@@ -10,11 +9,10 @@
@code {
StringBuilder _stringBuilder = new();
- protected override async Task OnInitializedAsync()
- {
+
+ protected override async Task OnInitializedAsync() {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
- if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging)
- {
+ if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) {
Console.WriteLine("Console logging disabled!");
var _sw = new StringWriter();
Console.SetOut(_sw);
@@ -22,19 +20,16 @@
return;
}
if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) return;
- //intecept stdout with textwriter to get logs
+ //intecept stdout with textwriter to get logs
var sw = new StringWriter(_stringBuilder);
Console.SetOut(sw);
Console.SetError(sw);
- //keep updated
- int length = 0;
- Task.Run(async () =>
- {
- while (true)
- {
+ //keep updated
+ var length = 0;
+ Task.Run(async () => {
+ while (true) {
await Task.Delay(100);
- if (_stringBuilder.Length != length)
- {
+ if (_stringBuilder.Length != length) {
StateHasChanged();
length = _stringBuilder.Length;
}
@@ -43,4 +38,5 @@
});
await base.OnInitializedAsync();
}
+
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/MainLayout.razor b/MatrixRoomUtils.Web/Shared/MainLayout.razor
index cdb1205..317f9e6 100644
--- a/MatrixRoomUtils.Web/Shared/MainLayout.razor
+++ b/MatrixRoomUtils.Web/Shared/MainLayout.razor
@@ -11,8 +11,7 @@
<PortableDevTools></PortableDevTools>
<a href="https://git.rory.gay/MatrixRoomUtils.git/" target="_blank">Git</a>
<a href="https://matrix.to/#/%23mru%3Arory.gay?via=rory.gay&via=matrix.org&via=feline.support" target="_blank">Matrix</a>
- @if (showDownload)
- {
+ @if (showDownload) {
<a href="/MRU.tar.xz" target="_blank">Download</a>
}
</div>
@@ -24,24 +23,22 @@
</div>
@code {
- private bool showDownload { get; set; } = false;
+ private bool showDownload { get; set; }
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
using var hc = new HttpClient();
- var hr = await hc.SendAsync(new(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri));
+ var hr = await hc.SendAsync(new HttpRequestMessage(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri));
showDownload = hr.StatusCode == HttpStatusCode.OK;
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
- if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging)
- {
+ if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) {
Console.WriteLine("Console logging disabled!");
var sw = new StringWriter();
Console.SetOut(sw);
Console.SetError(sw);
}
-
- await base.OnInitializedAsync();
+ await base.OnInitializedAsync();
}
+
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/MainLayout.razor.css b/MatrixRoomUtils.Web/Shared/MainLayout.razor.css
index c865427..01a5066 100644
--- a/MatrixRoomUtils.Web/Shared/MainLayout.razor.css
+++ b/MatrixRoomUtils.Web/Shared/MainLayout.razor.css
@@ -21,20 +21,20 @@ main {
align-items: center;
}
- .top-row ::deep a, .top-row ::deep .btn-link {
- white-space: nowrap;
- margin-left: 1.5rem;
- text-decoration: none;
- }
+.top-row ::deep a, .top-row ::deep .btn-link {
+ white-space: nowrap;
+ margin-left: 1.5rem;
+ text-decoration: none;
+}
- .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
- text-decoration: underline;
- }
+.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
+ text-decoration: underline;
+}
- .top-row ::deep a:first-child {
- overflow: hidden;
- text-overflow: ellipsis;
- }
+.top-row ::deep a:first-child {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
@media (max-width: 640.98px) {
.top-row:not(.auth) {
diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor
index 44dd9df..9ddbd84 100644
--- a/MatrixRoomUtils.Web/Shared/NavMenu.razor
+++ b/MatrixRoomUtils.Web/Shared/NavMenu.razor
@@ -61,6 +61,13 @@
@* <span class="oi oi-plus" aria-hidden="true"></span> Media locator *@
@* </NavLink> *@
@* </div> *@
+
+ <div class="nav-item px-3">
+ <NavLink class="nav-link" href="HSAdmin">
+ <span class="oi oi-plus" aria-hidden="true"></span> HS Admin
+ </NavLink>
+ </div>
+
<div class="nav-item px-3">
<h5 style="margin-left: 1em;">MRU</h5>
<hr style="margin-bottom: 0em;"/>
@@ -78,9 +85,6 @@
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
- private void ToggleNavMenu()
- {
- collapseNavMenu = !collapseNavMenu;
- }
+ private void ToggleNavMenu() => collapseNavMenu = !collapseNavMenu;
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor.css b/MatrixRoomUtils.Web/Shared/NavMenu.razor.css
index 604b7a1..447f2df 100644
--- a/MatrixRoomUtils.Web/Shared/NavMenu.razor.css
+++ b/MatrixRoomUtils.Web/Shared/NavMenu.razor.css
@@ -4,7 +4,7 @@
.top-row {
height: 3.5rem;
- background-color: rgba(0,0,0,0.4);
+ background-color: rgba(0, 0, 0, 0.4);
}
.navbar-brand {
@@ -23,30 +23,30 @@
padding-bottom: 0.5rem;
}
- .nav-item:first-of-type {
- padding-top: 1rem;
- }
+.nav-item:first-of-type {
+ padding-top: 1rem;
+}
- .nav-item:last-of-type {
- padding-bottom: 1rem;
- }
+.nav-item:last-of-type {
+ padding-bottom: 1rem;
+}
- .nav-item ::deep a {
- color: #d7d7d7;
- border-radius: 4px;
- height: 3rem;
- display: flex;
- align-items: center;
- line-height: 3rem;
- }
+.nav-item ::deep a {
+ color: #d7d7d7;
+ border-radius: 4px;
+ height: 3rem;
+ display: flex;
+ align-items: center;
+ line-height: 3rem;
+}
.nav-item ::deep a.active {
- background-color: rgba(255,255,255,0.25);
+ background-color: rgba(255, 255, 255, 0.25);
color: white;
}
.nav-item ::deep a:hover {
- background-color: rgba(255,255,255,0.1);
+ background-color: rgba(255, 255, 255, 0.1);
color: white;
}
@@ -59,7 +59,7 @@
/* Never collapse the sidebar for wide screens */
display: block;
}
-
+
.nav-scrollable {
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
diff --git a/MatrixRoomUtils.Web/Shared/PortableDevTools.razor b/MatrixRoomUtils.Web/Shared/PortableDevTools.razor
index 84e7791..ffd7082 100644
--- a/MatrixRoomUtils.Web/Shared/PortableDevTools.razor
+++ b/MatrixRoomUtils.Web/Shared/PortableDevTools.razor
@@ -1,9 +1,7 @@
-
-@if (Enabled)
-{
+@if (Enabled) {
<a href="/DevOptions">Portable devtools (enabled)</a>
<div id="PortableDevTools" style="position: fixed; bottom: 0; right: 0; min-width: 200px; min-height: 100px; background: #0002;" draggable>
- <p>Cache size: @RuntimeCache.GenericResponseCache.Sum(x=>x.Value.Cache.Count)</p>
+ <p>Cache size: @RuntimeCache.GenericResponseCache.Sum(x => x.Value.Cache.Count)</p>
</div>
}
else {
@@ -13,19 +11,16 @@ else {
@code {
private bool Enabled { get; set; } = LocalStorageWrapper.Settings.DeveloperSettings.EnablePortableDevtools;
- protected override async Task OnInitializedAsync()
- {
- // if(!RuntimeCache.WasLoaded)
- // await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
- // StateHasChanged();
- Task.Run(async () =>
- {
- while (true)
- {
+ protected override async Task OnInitializedAsync() =>
+ // if(!RuntimeCache.WasLoaded)
+ // await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
+ // StateHasChanged();
+ Task.Run(async () => {
+ while (true) {
await Task.Delay(100);
Enabled = LocalStorageWrapper.Settings.DeveloperSettings.EnablePortableDevtools;
StateHasChanged();
}
});
- }
+
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor b/MatrixRoomUtils.Web/Shared/RoomListItem.razor
index fb28c3c..f58ab3a 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListItem.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListItem.razor
@@ -1,18 +1,15 @@
-@using MatrixRoomUtils.Core.Authentication
-@using System.Text.Json
@using MatrixRoomUtils.Core.Extensions
+@using System.Text.Json
<div class="roomListItem" 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 class="imageUnloaded @(string.IsNullOrWhiteSpace(profileAvatar) ? "" : "imageLoaded")" 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 ?? "/icon-192.png")" @onload="Callback"/>
+ @if (ShowOwnProfile) {
+ <img class="imageUnloaded @(string.IsNullOrWhiteSpace(profileAvatar) ? "" : "imageLoaded")" 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 ?? "/icon-192.png")" @onload="Callback"/>
<span style="vertical-align: middle; margin-right: 8px; border-radius: 75px; @(hasCustomProfileName ? "background-color: red;" : "")">@(profileName ?? "Loading...")</span>
<span style="vertical-align: middle; padding-right: 8px; padding-left: 0px;">-></span>
}
- <img style="@(ChildContent != null ? "vertical-align: baseline;":"") width: 32px; height: 32px; border-radius: 50%;" src="@roomIcon"/>
+ <img style="@(ChildContent != null ? "vertical-align: baseline;" : "") width: 32px; height: 32px; border-radius: 50%;" src="@roomIcon"/>
<div style="display: inline-block;">
- <span style="vertical-align: middle; padding-right: 8px;">@roomName</span>
- @if (ChildContent != null)
- {
+ <span style="vertical-align: middle; padding-right: 8px;">@RoomName</span>
+ @if (ChildContent != null) {
@ChildContent
}
</div>
@@ -33,103 +30,86 @@
[Parameter]
public bool ShowOwnProfile { get; set; } = false;
- private string roomName { get; set; } = "Loading...";
+ [Parameter]
+ public string? RoomName { get; set; }
+
private string? roomIcon { get; set; } = "/icon-192.png";
private string? profileAvatar { get; set; }
private string? profileName { get; set; }
private bool hasCustomProfileAvatar { get; set; } = false;
private bool hasCustomProfileName { get; set; } = false;
-
+
private bool hasOldRoomVersion { get; set; } = false;
private bool hasDangerousRoomVersion { get; set; } = false;
-
-
+
private static SemaphoreSlim _semaphoreSlim = new(128);
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
-
+
await _semaphoreSlim.WaitAsync();
var hs = RuntimeCache.CurrentHomeServer; //await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.UserId, RuntimeCache.CurrentHomeServer.AccessToken, RuntimeCache.CurrentHomeServer.HomeServerDomain).Configure();
-
- if (Room == null)
- {
- if (RoomId == null)
- {
+
+ if (Room == null) {
+ if (RoomId == null) {
throw new ArgumentNullException(nameof(RoomId));
}
Room = await hs.GetRoom(RoomId);
}
- else
- {
+ else {
RoomId = Room.RoomId;
}
- roomName = await Room.GetNameAsync() ?? "Unnamed room: " + RoomId;
+ RoomName ??= await Room.GetNameAsync() ?? "Unnamed room: " + RoomId;
var ce = await Room.GetCreateEventAsync();
- if (ce != null)
- {
- if (int.TryParse(ce.RoomVersion, out int rv) && rv < 10)
- {
+ if (ce != null) {
+ if (int.TryParse(ce.RoomVersion, out var rv) && rv < 10) {
hasOldRoomVersion = true;
}
- if (new[] { "1", "8" }.Contains(ce.RoomVersion))
- {
+ if (new[] { "1", "8" }.Contains(ce.RoomVersion)) {
hasDangerousRoomVersion = true;
- roomName = "Dangerous room: " + roomName;
+ RoomName = "Dangerous room: " + RoomName;
}
}
-
var state = await Room.GetStateAsync("m.room.avatar");
- if (state != null)
- {
- try
- {
+ if (state != null) {
+ try {
var url = state.Value.GetProperty("url").GetString();
- if (url != null)
- {
+ if (url != null) {
roomIcon = hs.ResolveMediaUri(url);
Console.WriteLine($"Got avatar for room {RoomId}: {roomIcon} ({url})");
}
}
- catch (InvalidOperationException e)
- {
+ catch (InvalidOperationException e) {
Console.WriteLine($"Failed to get avatar for room {RoomId}: {e.Message}\n{state.Value.ToJson()}");
}
}
- if (ShowOwnProfile)
- {
- var profile = await hs.GetProfile(hs.UserId, debounce: true);
+ if (ShowOwnProfile) {
+ var profile = await hs.GetProfile(hs.UserId, true);
var memberState = await Room.GetStateAsync("m.room.member", hs.UserId);
- if (memberState.HasValue)
- {
+ if (memberState.HasValue) {
memberState.Value.TryGetProperty("avatar_url", out var _avatar);
- if (_avatar.ValueKind == JsonValueKind.String)
- {
+ if (_avatar.ValueKind == JsonValueKind.String) {
hasCustomProfileAvatar = _avatar.GetString() != profile.AvatarUrl;
profileAvatar = hs.ResolveMediaUri(_avatar.GetString());
}
- else
- {
+ else {
profileAvatar = "/icon-192.png";
}
memberState.Value.TryGetProperty("displayname", out var _name);
- if (_name.ValueKind == JsonValueKind.String)
- {
+ if (_name.ValueKind == JsonValueKind.String) {
hasCustomProfileName = _name.GetString() != profile.DisplayName;
profileName = _name.GetString();
// Console.WriteLine($"{profile.DisplayName} - {_name.GetString()}: {hasCustomProfileName}");
}
- else
- {
+ else {
profileName = "Unnamed user";
}
}
@@ -139,9 +119,6 @@
await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage);
}
- private void Callback(ProgressEventArgs obj)
- {
- Console.WriteLine("prog: " + obj.ToJson(indent: false));
- }
+ private void Callback(ProgressEventArgs obj) => Console.WriteLine("prog: " + obj.ToJson(false));
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor.css b/MatrixRoomUtils.Web/Shared/RoomListItem.razor.css
index 8b9a9f6..da22d38 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListItem.razor.css
+++ b/MatrixRoomUtils.Web/Shared/RoomListItem.razor.css
@@ -5,6 +5,6 @@
/*}*/
.imageLoaded {
- opacity: 1;
- scale: 1;
- }
\ No newline at end of file
+ opacity: 1;
+ scale: 1;
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/SimpleComponents/DictionaryEditor.razor b/MatrixRoomUtils.Web/Shared/SimpleComponents/DictionaryEditor.razor
index 4d90c57..42a5f64 100644
--- a/MatrixRoomUtils.Web/Shared/SimpleComponents/DictionaryEditor.razor
+++ b/MatrixRoomUtils.Web/Shared/SimpleComponents/DictionaryEditor.razor
@@ -1,9 +1,8 @@
@using MatrixRoomUtils.Core.Extensions
<table>
- @foreach(var i in Items.Keys)
- {
+ @foreach (var i in Items.Keys) {
var key = i;
- <input value="@Items[key]" @oninput="(obj) => inputChanged(obj, key)">
+ <input value="@Items[key]" @oninput="obj => inputChanged(obj, key)">
<button @onclick="() => { Items.Remove(key); ItemsChanged.InvokeAsync(); }">Remove</button>
<br/>
}
@@ -11,28 +10,26 @@
<button @onclick="() => { Items.Add(string.Empty, default); ItemsChanged.InvokeAsync(); }">Add</button>
@code {
-
+
[Parameter]
public Dictionary<string, object> Items { get; set; } = new();
- [Parameter, EditorRequired]
+ [Parameter]
+ [EditorRequired]
public EventCallback ItemsChanged { get; set; }
[Parameter]
- public Func<string,string>? KeyFormatter { get; set; }
-
+ public Func<string, string>? KeyFormatter { get; set; }
+
[Parameter]
public Action? OnFocusLost { get; set; }
-
- protected override Task OnInitializedAsync()
- {
+ protected override Task OnInitializedAsync() {
Console.WriteLine($"DictionaryEditor initialized with {Items.Count} items: {Items.ToJson()}");
return base.OnInitializedAsync();
}
- private void inputChanged(ChangeEventArgs obj, string key)
- {
+ private void inputChanged(ChangeEventArgs obj, string key) {
Console.WriteLine($"StringListEditor inputChanged {key} {obj.Value}");
Items[key] = obj.Value.ToString();
ItemsChanged.InvokeAsync();
diff --git a/MatrixRoomUtils.Web/Shared/SimpleComponents/FancyTextBox.razor b/MatrixRoomUtils.Web/Shared/SimpleComponents/FancyTextBox.razor
index 702d41e..d17d0de 100644
--- a/MatrixRoomUtils.Web/Shared/SimpleComponents/FancyTextBox.razor
+++ b/MatrixRoomUtils.Web/Shared/SimpleComponents/FancyTextBox.razor
@@ -1,35 +1,29 @@
@inject IJSRuntime JsRuntime
-@if (isVisible)
-{
+@if (isVisible) {
<input autofocus type="@(IsPassword ? "password" : "text")" @bind="Value" @onfocusout="() => { isVisible = false; ValueChanged.InvokeAsync(Value); }" @ref="elementToFocus"/>
}
-else
-{
- <span tabindex="0" style="border-bottom: #ccc solid 1px; height: 1.4em; display: inline-block; @(string.IsNullOrEmpty(Value) ? "min-width: 50px;" : "")" @onfocusin="() => isVisible = true">@(Formatter?.Invoke(Value) ?? (IsPassword ? string.Join("", Value.Select(x=>'*')) : Value))</span>
+else {
+ <span tabindex="0" style="border-bottom: #ccc solid 1px; height: 1.4em; display: inline-block; @(string.IsNullOrEmpty(Value) ? "min-width: 50px;" : "")" @onfocusin="() => isVisible = true">@(Formatter?.Invoke(Value) ?? (IsPassword ? string.Join("", Value.Select(x => '*')) : Value))</span>
}
@code {
[Parameter]
public string Value { get; set; }
-
+
[Parameter]
public bool IsPassword { get; set; } = false;
-
+
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
-
+
[Parameter]
public Func<string?, string>? Formatter { get; set; }
-
private bool isVisible { get; set; } = false;
private ElementReference elementToFocus;
- protected override async Task OnAfterRenderAsync(bool firstRender)
- {
- await JsRuntime.InvokeVoidAsync("BlazorFocusElement", elementToFocus);
- }
+ protected override async Task OnAfterRenderAsync(bool firstRender) => await JsRuntime.InvokeVoidAsync("BlazorFocusElement", elementToFocus);
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/SimpleComponents/StringListEditor.razor b/MatrixRoomUtils.Web/Shared/SimpleComponents/StringListEditor.razor
index fe3a938..2bd6ed5 100644
--- a/MatrixRoomUtils.Web/Shared/SimpleComponents/StringListEditor.razor
+++ b/MatrixRoomUtils.Web/Shared/SimpleComponents/StringListEditor.razor
@@ -1,5 +1,4 @@
-@for (int i = 0; i < Items.Count; i++)
-{
+@for (var i = 0; i < Items.Count; i++) {
var self = i;
<button @onclick="() => { Items.RemoveAt(self); ItemsChanged.InvokeAsync(); }">Remove</button>
<FancyTextBox Value="@Items[self]" ValueChanged="@(obj => inputChanged(obj, self))"/>
@@ -10,19 +9,18 @@
@code {
[Parameter]
- public List<string> Items { get; set; } = new List<string>();
+ public List<string> Items { get; set; } = new();
- [Parameter, EditorRequired]
+ [Parameter]
+ [EditorRequired]
public EventCallback ItemsChanged { get; set; }
- protected override Task OnInitializedAsync()
- {
+ protected override Task OnInitializedAsync() {
Console.WriteLine($"StringListEditor initialized with {Items.Count} items: {string.Join(",", Items)}");
return base.OnInitializedAsync();
}
- private void inputChanged(string obj, int i)
- {
+ private void inputChanged(string obj, int i) {
Console.WriteLine($"StringListEditor inputChanged {i} {obj}");
Items[i] = obj;
ItemsChanged.InvokeAsync();
diff --git a/MatrixRoomUtils.Web/Shared/SimpleComponents/ToggleSlider.razor b/MatrixRoomUtils.Web/Shared/SimpleComponents/ToggleSlider.razor
index 49a363d..1a38e26 100644
--- a/MatrixRoomUtils.Web/Shared/SimpleComponents/ToggleSlider.razor
+++ b/MatrixRoomUtils.Web/Shared/SimpleComponents/ToggleSlider.razor
@@ -59,12 +59,14 @@
</style>
@code {
+
[Parameter]
public RenderFragment? ChildContent { get; set; }
-
+
[Parameter]
public bool Value { get; set; }
+
[Parameter]
public EventCallback<bool> ValueChanged { get; set; }
-
+
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor
index 3803d38..4fb5596 100644
--- a/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor
+++ b/MatrixRoomUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor
@@ -1,33 +1,25 @@
@using MatrixRoomUtils.Core.Extensions
-@if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "ban")
-{
+@if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "ban") {
<i>@Event.StateKey was banned</i>
}
-else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "invite")
-{
+else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "invite") {
<i>@Event.StateKey was invited</i>
}
-else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "join")
-{
- @if (Event.ReplacesState != null)
- {
+else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "join") {
+ @if (Event.ReplacesState != null) {
<i>@Event.StateKey changed their display name to @(Event.ContentAsJsonNode["displayname"]!.GetValue<string>())</i>
}
- else
- {
+ else {
<i><InlineUserItem UserId="@Event.StateKey"></InlineUserItem> joined</i>
}
}
-else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "leave")
-{
+else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "leave") {
<i>@Event.StateKey left</i>
}
-else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "knock")
-{
+else if (Event.ContentAsJsonNode["membership"]!.GetValue<string>() == "knock") {
<i>@Event.StateKey knocked</i>
}
-else
-{
+else {
<i>@Event.StateKey has an unknown state:</i>
<pre>
@Event.ToJson()
diff --git a/MatrixRoomUtils.Web/Shared/UserListItem.razor b/MatrixRoomUtils.Web/Shared/UserListItem.razor
index d357b0d..43c71ab 100644
--- a/MatrixRoomUtils.Web/Shared/UserListItem.razor
+++ b/MatrixRoomUtils.Web/Shared/UserListItem.razor
@@ -4,8 +4,7 @@
<span style="vertical-align: middle; margin-right: 8px; border-radius: 75px;">@profileName</span>
<div style="display: inline-block;">
- @if (ChildContent != null)
- {
+ @if (ChildContent != null) {
@ChildContent
}
</div>
@@ -26,11 +25,9 @@
private string? profileAvatar { get; set; } = "/icon-192.png";
private string? profileName { get; set; } = "Loading...";
-
private static SemaphoreSlim _semaphoreSlim = new(128);
- protected override async Task OnInitializedAsync()
- {
+ protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
@@ -38,19 +35,14 @@
var hs = await new AuthenticatedHomeServer(RuntimeCache.CurrentHomeServer.UserId, RuntimeCache.CurrentHomeServer.AccessToken, RuntimeCache.CurrentHomeServer.HomeServerDomain).Configure();
- if (User == null)
- {
- if (UserId == null)
- {
+ if (User == null) {
+ if (UserId == null) {
throw new ArgumentNullException(nameof(UserId));
}
User = await hs.GetProfile(UserId);
}
- else
- {
- // UserId = User.;
- }
-
+
+ // UserId = User.;
profileAvatar = RuntimeCache.CurrentHomeServer.ResolveMediaUri(User.AvatarUrl);
profileName = User.DisplayName;
diff --git a/MatrixRoomUtils.Web/_Imports.razor b/MatrixRoomUtils.Web/_Imports.razor
index a558b8f..837780e 100644
--- a/MatrixRoomUtils.Web/_Imports.razor
+++ b/MatrixRoomUtils.Web/_Imports.razor
@@ -17,14 +17,12 @@
@code
{
-
- protected override async Task OnInitializedAsync()
- {
- if (!RuntimeCache.WasLoaded)
- {
+
+ protected override async Task OnInitializedAsync() {
+ if (!RuntimeCache.WasLoaded) {
await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
Console.WriteLine("Loaded from local storage");
StateHasChanged();
}
}
-}
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/wwwroot/css/app.css b/MatrixRoomUtils.Web/wwwroot/css/app.css
index c6d71d1..245566a 100644
--- a/MatrixRoomUtils.Web/wwwroot/css/app.css
+++ b/MatrixRoomUtils.Web/wwwroot/css/app.css
@@ -15,7 +15,7 @@ html, body {
border-bottom: none;
}
-.table, .table-striped>tbody>tr:nth-of-type(odd), .table-hover>tbody>tr:hover {
+.table, .table-striped > tbody > tr:nth-of-type(odd), .table-hover > tbody > tr:hover {
color: unset;
}
@@ -34,7 +34,7 @@ a, .btn-link {
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
- box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
+ box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
@@ -65,12 +65,12 @@ a, .btn-link {
z-index: 1000;
}
- #blazor-error-ui .dismiss {
- cursor: pointer;
- position: absolute;
- right: 0.75rem;
- top: 0.5rem;
- }
+#blazor-error-ui .dismiss {
+ cursor: pointer;
+ position: absolute;
+ right: 0.75rem;
+ top: 0.5rem;
+}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
@@ -78,9 +78,9 @@ a, .btn-link {
color: white;
}
- .blazor-error-boundary::after {
- content: "An error has occurred."
- }
+.blazor-error-boundary::after {
+ content: "An error has occurred."
+}
.loading-progress {
position: relative;
@@ -90,19 +90,19 @@ a, .btn-link {
margin: 20vh auto 1rem auto;
}
- .loading-progress circle {
- fill: none;
- stroke: #e0e0e0;
- stroke-width: 0.6rem;
- transform-origin: 50% 50%;
- transform: rotate(-90deg);
- }
+.loading-progress circle {
+ fill: none;
+ stroke: #e0e0e0;
+ stroke-width: 0.6rem;
+ transform-origin: 50% 50%;
+ transform: rotate(-90deg);
+}
- .loading-progress circle:last-child {
- stroke: #1b6ec2;
- stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
- transition: stroke-dasharray 0.05s ease-in-out;
- }
+.loading-progress circle:last-child {
+ stroke: #1b6ec2;
+ stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
+ transition: stroke-dasharray 0.05s ease-in-out;
+}
.loading-progress-text {
position: absolute;
@@ -111,6 +111,6 @@ a, .btn-link {
inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}
- .loading-progress-text:after {
- content: var(--blazor-load-percentage-text, "Loading");
- }
+.loading-progress-text:after {
+ content: var(--blazor-load-percentage-text, "Loading");
+}
diff --git a/MatrixRoomUtils.Web/wwwroot/css/open-iconic/README.md b/MatrixRoomUtils.Web/wwwroot/css/open-iconic/README.md
index e34bd86..3ee3279 100644
--- a/MatrixRoomUtils.Web/wwwroot/css/open-iconic/README.md
+++ b/MatrixRoomUtils.Web/wwwroot/css/open-iconic/README.md
@@ -3,8 +3,6 @@
### Open Iconic is the open source sibling of [Iconic](https://github.com/iconic/open-iconic). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](https://github.com/iconic/open-iconic)
-
-
## What's in Open Iconic?
* 223 icons designed to be legible down to 8 pixels
@@ -14,7 +12,6 @@
* Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats
* PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px.
-
## Getting Started
#### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](https://github.com/iconic/open-iconic) and [Reference](https://github.com/iconic/open-iconic) sections.
@@ -33,7 +30,8 @@ We like SVGs and we think they're the way to display icons on the web. Since Ope
Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack.
-Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `<svg>` *tag and a unique class name for each different icon in the* `<use>` *tag.*
+Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `<svg>` *tag and a unique class name for
+each different icon in the* `<use>` *tag.*
```
<svg class="icon">
@@ -62,17 +60,14 @@ To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.c
#### Using Open Iconic's Icon Font...
-
##### …with Bootstrap
You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}`
-
```
<link href="/open-iconic/font/css/open-iconic-bootstrap.css" rel="stylesheet">
```
-
```
<span class="oi oi-icon-name" title="icon name" aria-hidden="true"></span>
```
@@ -85,7 +80,6 @@ You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css
<link href="/open-iconic/font/css/open-iconic-foundation.css" rel="stylesheet">
```
-
```
<span class="fi-icon-name" title="icon name" aria-hidden="true"></span>
```
@@ -102,7 +96,6 @@ You can find our default stylesheets in `font/css/open-iconic.{css, less, scss,
<span class="oi" data-glyph="icon-name" title="icon name" aria-hidden="true"></span>
```
-
## License
### Icons
diff --git a/MatrixRoomUtils.Web/wwwroot/css/open-iconic/font/fonts/open-iconic.svg b/MatrixRoomUtils.Web/wwwroot/css/open-iconic/font/fonts/open-iconic.svg
index 32b2c4e..9d1833e 100644
--- a/MatrixRoomUtils.Web/wwwroot/css/open-iconic/font/fonts/open-iconic.svg
+++ b/MatrixRoomUtils.Web/wwwroot/css/open-iconic/font/fonts/open-iconic.svg
@@ -4,540 +4,541 @@
2014-7-1: Created.
-->
<svg xmlns="http://www.w3.org/2000/svg">
-<metadata>
-Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014
- By P.J. Onori
-Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
-</metadata>
-<defs>
-<font id="open-iconic" horiz-adv-x="800" >
- <font-face
- font-family="Icons"
- font-weight="400"
- font-stretch="normal"
- units-per-em="800"
- panose-1="2 0 5 3 0 0 0 0 0 0"
- ascent="800"
- descent="0"
- bbox="-0.5 -101 802 800.126"
- underline-thickness="50"
- underline-position="-100"
- unicode-range="U+E000-E0DE"
- />
- <missing-glyph />
- <glyph glyph-name="" unicode=""
-d="M300 700h500v-700h-500v100h400v500h-400v100zM400 500l200 -150l-200 -150v100h-400v100h400v100z" />
- <glyph glyph-name="1" unicode=""
-d="M300 700h500v-700h-500v100h400v500h-400v100zM200 500v-100h400v-100h-400v-100l-200 150z" />
- <glyph glyph-name="2" unicode=""
-d="M350 700c193 0 350 -157 350 -350v-50h100l-200 -200l-200 200h100v50c0 138 -112 250 -250 250s-250 -112 -250 -250c0 193 157 350 350 350z" />
- <glyph glyph-name="3" unicode=""
-d="M450 700c193 0 350 -157 350 -350c0 138 -112 250 -250 250s-250 -112 -250 -250v-50h100l-200 -200l-200 200h100v50c0 193 157 350 350 350z" />
- <glyph glyph-name="4" unicode=""
-d="M0 700h800v-100h-800v100zM100 500h600v-100h-600v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
- <glyph glyph-name="5" unicode=""
-d="M0 700h800v-100h-800v100zM0 500h600v-100h-600v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
- <glyph glyph-name="6" unicode=""
-d="M0 700h800v-100h-800v100zM200 500h600v-100h-600v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
- <glyph glyph-name="7" unicode=""
-d="M400 700c75 0 146 -23 206 -59l-75 -225l-322 234c57 31 122 50 191 50zM125 588l191 -138l-310 -222c-4 24 -6 47 -6 72c0 114 49 215 125 288zM688 575c69 -72 112 -168 112 -275c0 -35 -8 -68 -16 -100h-218zM216 253l112 -347c-128 23 -232 109 -287 222zM372 100
-h372c-64 -109 -177 -185 -310 -197z" />
- <glyph glyph-name="8" unicode="" horiz-adv-x="600"
-d="M200 800h100v-500h200l-247 -300l-253 300h200v500z" />
- <glyph glyph-name="9" unicode=""
-d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 700v-300h-200l300 -300l300 300h-200v300h-200z" />
- <glyph glyph-name="a" unicode=""
-d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300l300 -300v200h300v200h-300v200z" />
- <glyph glyph-name="b" unicode=""
-d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700v-200h-300v-200h300v-200l300 300z" />
- <glyph glyph-name="c" unicode=""
-d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300h200v-300h200v300h200z" />
- <glyph glyph-name="d" unicode=""
-d="M300 600v-200h500v-100h-500v-200l-300 247z" />
- <glyph glyph-name="e" unicode=""
-d="M500 600l300 -247l-300 -253v200h-500v100h500v200z" />
- <glyph glyph-name="f" unicode="" horiz-adv-x="600"
-d="M200 800h200v-500h200l-297 -300l-303 300h200v500z" />
- <glyph glyph-name="10" unicode=""
-d="M300 700v-200h500v-200h-500v-200l-300 297z" />
- <glyph glyph-name="11" unicode=""
-d="M500 700l300 -297l-300 -303v200h-500v200h500v200z" />
- <glyph glyph-name="12" unicode="" horiz-adv-x="600"
-d="M297 800l303 -300h-200v-500h-200v500h-200z" />
- <glyph glyph-name="13" unicode="" horiz-adv-x="600"
-d="M247 800l253 -300h-200v-500h-100v500h-200z" />
- <glyph glyph-name="14" unicode=""
-d="M400 800h100v-800h-100v800zM200 700h100v-600h-100v600zM600 600h100v-400h-100v400zM0 500h100v-200h-100v200z" />
- <glyph glyph-name="15" unicode=""
-d="M116 600l72 -72c-54 -54 -88 -126 -88 -209s34 -159 88 -213l-72 -72c-72 72 -116 175 -116 285s44 209 116 281zM684 600c72 -72 116 -171 116 -281s-44 -213 -116 -285l-72 72c54 54 88 130 88 213s-34 155 -88 209zM259 460l69 -72c-18 -18 -28 -41 -28 -69
-s10 -54 28 -72l-69 -72c-36 36 -59 89 -59 144s23 105 59 141zM541 459c36 -36 59 -85 59 -140s-23 -108 -59 -144l-69 72c18 18 28 44 28 72s-10 51 -28 69z" />
- <glyph glyph-name="16" unicode="" horiz-adv-x="400"
-d="M200 800c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM100 319c31 -11 65 -19 100 -19s68 8 100 19v-319l-100 100l-100 -100v319z" />
- <glyph glyph-name="17" unicode=""
-d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300c0 -66 21 -126 56 -175l419 419c-49 35 -109 56 -175 56zM644 575l-419 -419c49 -35 109 -56 175 -56c166 0 300 134 300 300
-c0 66 -21 126 -56 175z" />
- <glyph glyph-name="18" unicode=""
-d="M0 700h100v-600h700v-100h-800v700zM500 700h200v-500h-200v500zM200 500h200v-300h-200v300z" />
- <glyph glyph-name="19" unicode=""
-d="M397 800c13 1 23 -4 34 -13c2 -2 214 -254 241 -287h128v-100h-100v-366c0 -18 -16 -34 -34 -34h-532c-18 0 -34 16 -34 34v366h-100v100h128l234 281c9 11 22 18 35 19zM400 672l-144 -172h288zM250 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50
-v100c0 28 -22 50 -50 50zM550 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50v100c0 28 -22 50 -50 50z" />
- <glyph glyph-name="1a" unicode=""
-d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9zM100 600v-400h500v400h-500z" />
- <glyph glyph-name="1b" unicode=""
-d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9z" />
- <glyph glyph-name="1c" unicode=""
-d="M92 650c0 23 19 50 45 50h3h5h5h500c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-141c9 -17 120 -231 166 -309c16 -26 34 -61 34 -106c0 -39 -15 -77 -41 -103h-3c-26 -25 -62 -41 -100 -41h-512c-39 0 -77 15 -103 41s-41 64 -41 103c0 46 18 80 34 106
-c46 78 157 292 166 309v141h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51zM500 600h-200v-162l-6 -10s-63 -123 -119 -228h450c-56 105 -119 228 -119 228l-6 10v162z" />
- <glyph glyph-name="1d" unicode=""
-d="M400 800c110 0 200 -90 200 -200c0 -104 52 -198 134 -266c41 -34 66 -82 66 -134h-800c0 52 25 100 66 134c82 68 134 162 134 266c0 110 90 200 200 200zM300 100h200c0 -55 -45 -100 -100 -100s-100 45 -100 100z" />
- <glyph glyph-name="1e" unicode="" horiz-adv-x="600"
-d="M150 800h50l350 -250l-225 -147l225 -153l-350 -250h-50v250l-75 -75l-75 75l150 150l-150 150l75 75l75 -75v250zM250 650v-200l150 100zM250 350v-200l150 100z" />
- <glyph glyph-name="1f" unicode=""
-d="M0 800h500c110 0 200 -90 200 -200c0 -47 -17 -91 -44 -125c85 -40 144 -125 144 -225c0 -138 -112 -250 -250 -250h-550v100c55 0 100 45 100 100v400c0 55 -45 100 -100 100v100zM300 700v-200h100c55 0 100 45 100 100s-45 100 -100 100h-100zM300 400v-300h150
-c83 0 150 67 150 150s-67 150 -150 150h-150z" />
- <glyph glyph-name="20" unicode="" horiz-adv-x="600"
-d="M300 800v-300h200l-300 -500v300h-200z" />
- <glyph glyph-name="21" unicode=""
-d="M100 800h300v-300l100 100l100 -100v300h50c28 0 50 -22 50 -50v-550h-550c-28 0 -50 -22 -50 -50s22 -50 50 -50h550v-100h-550c-83 0 -150 67 -150 150v550l3 19c8 39 39 70 78 78z" />
- <glyph glyph-name="22" unicode="" horiz-adv-x="400"
-d="M0 800h400v-800l-200 200l-200 -200v800z" />
- <glyph glyph-name="23" unicode=""
-d="M0 800h800v-100h-800v100zM0 600h300v-103h203v103h297v-591c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v591z" />
- <glyph glyph-name="24" unicode=""
-d="M300 800h200c55 0 100 -45 100 -100v-100h191c6 0 9 -3 9 -9v-241c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v241c0 6 3 9 9 9h191v100c0 55 45 100 100 100zM300 700v-100h200v100h-200zM0 209c16 -6 32 -9 50 -9h700c18 0 34 3 50 9v-200c0 -6 -3 -9 -9 -9h-782
-c-6 0 -9 3 -9 9v200z" />
- <glyph glyph-name="25" unicode="" horiz-adv-x="600"
-d="M300 800c58 0 110 -16 147 -53s53 -89 53 -147h-100c0 39 -11 61 -25 75s-36 25 -75 25c-35 0 -55 -10 -72 -31s-28 -55 -28 -94c0 -51 20 -107 28 -175h172v-100h-178c-14 -60 -49 -127 -113 -200h491v-100h-600v122l16 12c69 69 95 121 106 166h-122v100h125
-c-8 50 -25 106 -25 175c0 58 16 114 50 156c34 43 88 69 150 69z" />
- <glyph glyph-name="26" unicode=""
-d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-700c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v700v2c0 20 15 42 34 48zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50zM350 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h300c28 0 50 22 50 50
-s-22 50 -50 50h-300zM100 400v-400h600v400h-600z" />
- <glyph glyph-name="27" unicode=""
-d="M744 797l6 -3l44 -44c4 -4 3 -8 0 -12l-266 -375l-15 -13l-25 -12c-23 72 -78 127 -150 150l12 25l13 15l375 266zM266 400c74 0 134 -60 134 -134c0 -147 -119 -266 -266 -266c-48 0 -95 12 -134 34c80 46 134 133 134 232c0 74 58 134 132 134z" />
- <glyph glyph-name="28" unicode=""
-d="M9 451c0 23 19 50 46 50c8 0 19 -3 26 -7l131 -66l29 22c-79 81 -1 250 118 250s197 -167 119 -250l28 -22l131 66c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-115 -56c9 -16 19 -33 25 -50h68c28 0 50 -22 50 -50s-22 -50 -50 -50h-50
+ <metadata>
+ Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014
+ By P.J. Onori
+ Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
+ </metadata>
+ <defs>
+ <font id="open-iconic" horiz-adv-x="800">
+ <font-face
+ font-family="Icons"
+ font-weight="400"
+ font-stretch="normal"
+ units-per-em="800"
+ panose-1="2 0 5 3 0 0 0 0 0 0"
+ ascent="800"
+ descent="0"
+ bbox="-0.5 -101 802 800.126"
+ underline-thickness="50"
+ underline-position="-100"
+ unicode-range="U+E000-E0DE"
+ />
+ <missing-glyph/>
+ <glyph glyph-name="" unicode=""
+ d="M300 700h500v-700h-500v100h400v500h-400v100zM400 500l200 -150l-200 -150v100h-400v100h400v100z"/>
+ <glyph glyph-name="1" unicode=""
+ d="M300 700h500v-700h-500v100h400v500h-400v100zM200 500v-100h400v-100h-400v-100l-200 150z"/>
+ <glyph glyph-name="2" unicode=""
+ d="M350 700c193 0 350 -157 350 -350v-50h100l-200 -200l-200 200h100v50c0 138 -112 250 -250 250s-250 -112 -250 -250c0 193 157 350 350 350z"/>
+ <glyph glyph-name="3" unicode=""
+ d="M450 700c193 0 350 -157 350 -350c0 138 -112 250 -250 250s-250 -112 -250 -250v-50h100l-200 -200l-200 200h100v50c0 193 157 350 350 350z"/>
+ <glyph glyph-name="4" unicode=""
+ d="M0 700h800v-100h-800v100zM100 500h600v-100h-600v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z"/>
+ <glyph glyph-name="5" unicode=""
+ d="M0 700h800v-100h-800v100zM0 500h600v-100h-600v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z"/>
+ <glyph glyph-name="6" unicode=""
+ d="M0 700h800v-100h-800v100zM200 500h600v-100h-600v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z"/>
+ <glyph glyph-name="7" unicode=""
+ d="M400 700c75 0 146 -23 206 -59l-75 -225l-322 234c57 31 122 50 191 50zM125 588l191 -138l-310 -222c-4 24 -6 47 -6 72c0 114 49 215 125 288zM688 575c69 -72 112 -168 112 -275c0 -35 -8 -68 -16 -100h-218zM216 253l112 -347c-128 23 -232 109 -287 222zM372 100
+h372c-64 -109 -177 -185 -310 -197z"/>
+ <glyph glyph-name="8" unicode="" horiz-adv-x="600"
+ d="M200 800h100v-500h200l-247 -300l-253 300h200v500z"/>
+ <glyph glyph-name="9" unicode=""
+ d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 700v-300h-200l300 -300l300 300h-200v300h-200z"/>
+ <glyph glyph-name="a" unicode=""
+ d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300l300 -300v200h300v200h-300v200z"/>
+ <glyph glyph-name="b" unicode=""
+ d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700v-200h-300v-200h300v-200l300 300z"/>
+ <glyph glyph-name="c" unicode=""
+ d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300h200v-300h200v300h200z"/>
+ <glyph glyph-name="d" unicode=""
+ d="M300 600v-200h500v-100h-500v-200l-300 247z"/>
+ <glyph glyph-name="e" unicode=""
+ d="M500 600l300 -247l-300 -253v200h-500v100h500v200z"/>
+ <glyph glyph-name="f" unicode="" horiz-adv-x="600"
+ d="M200 800h200v-500h200l-297 -300l-303 300h200v500z"/>
+ <glyph glyph-name="10" unicode=""
+ d="M300 700v-200h500v-200h-500v-200l-300 297z"/>
+ <glyph glyph-name="11" unicode=""
+ d="M500 700l300 -297l-300 -303v200h-500v200h500v200z"/>
+ <glyph glyph-name="12" unicode="" horiz-adv-x="600"
+ d="M297 800l303 -300h-200v-500h-200v500h-200z"/>
+ <glyph glyph-name="13" unicode="" horiz-adv-x="600"
+ d="M247 800l253 -300h-200v-500h-100v500h-200z"/>
+ <glyph glyph-name="14" unicode=""
+ d="M400 800h100v-800h-100v800zM200 700h100v-600h-100v600zM600 600h100v-400h-100v400zM0 500h100v-200h-100v200z"/>
+ <glyph glyph-name="15" unicode=""
+ d="M116 600l72 -72c-54 -54 -88 -126 -88 -209s34 -159 88 -213l-72 -72c-72 72 -116 175 -116 285s44 209 116 281zM684 600c72 -72 116 -171 116 -281s-44 -213 -116 -285l-72 72c54 54 88 130 88 213s-34 155 -88 209zM259 460l69 -72c-18 -18 -28 -41 -28 -69
+s10 -54 28 -72l-69 -72c-36 36 -59 89 -59 144s23 105 59 141zM541 459c36 -36 59 -85 59 -140s-23 -108 -59 -144l-69 72c18 18 28 44 28 72s-10 51 -28 69z"/>
+ <glyph glyph-name="16" unicode="" horiz-adv-x="400"
+ d="M200 800c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM100 319c31 -11 65 -19 100 -19s68 8 100 19v-319l-100 100l-100 -100v319z"/>
+ <glyph glyph-name="17" unicode=""
+ d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300c0 -66 21 -126 56 -175l419 419c-49 35 -109 56 -175 56zM644 575l-419 -419c49 -35 109 -56 175 -56c166 0 300 134 300 300
+c0 66 -21 126 -56 175z"/>
+ <glyph glyph-name="18" unicode=""
+ d="M0 700h100v-600h700v-100h-800v700zM500 700h200v-500h-200v500zM200 500h200v-300h-200v300z"/>
+ <glyph glyph-name="19" unicode=""
+ d="M397 800c13 1 23 -4 34 -13c2 -2 214 -254 241 -287h128v-100h-100v-366c0 -18 -16 -34 -34 -34h-532c-18 0 -34 16 -34 34v366h-100v100h128l234 281c9 11 22 18 35 19zM400 672l-144 -172h288zM250 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50
+v100c0 28 -22 50 -50 50zM550 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50v100c0 28 -22 50 -50 50z"/>
+ <glyph glyph-name="1a" unicode=""
+ d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9zM100 600v-400h500v400h-500z"/>
+ <glyph glyph-name="1b" unicode=""
+ d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9z"/>
+ <glyph glyph-name="1c" unicode=""
+ d="M92 650c0 23 19 50 45 50h3h5h5h500c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-141c9 -17 120 -231 166 -309c16 -26 34 -61 34 -106c0 -39 -15 -77 -41 -103h-3c-26 -25 -62 -41 -100 -41h-512c-39 0 -77 15 -103 41s-41 64 -41 103c0 46 18 80 34 106
+c46 78 157 292 166 309v141h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51zM500 600h-200v-162l-6 -10s-63 -123 -119 -228h450c-56 105 -119 228 -119 228l-6 10v162z"/>
+ <glyph glyph-name="1d" unicode=""
+ d="M400 800c110 0 200 -90 200 -200c0 -104 52 -198 134 -266c41 -34 66 -82 66 -134h-800c0 52 25 100 66 134c82 68 134 162 134 266c0 110 90 200 200 200zM300 100h200c0 -55 -45 -100 -100 -100s-100 45 -100 100z"/>
+ <glyph glyph-name="1e" unicode="" horiz-adv-x="600"
+ d="M150 800h50l350 -250l-225 -147l225 -153l-350 -250h-50v250l-75 -75l-75 75l150 150l-150 150l75 75l75 -75v250zM250 650v-200l150 100zM250 350v-200l150 100z"/>
+ <glyph glyph-name="1f" unicode=""
+ d="M0 800h500c110 0 200 -90 200 -200c0 -47 -17 -91 -44 -125c85 -40 144 -125 144 -225c0 -138 -112 -250 -250 -250h-550v100c55 0 100 45 100 100v400c0 55 -45 100 -100 100v100zM300 700v-200h100c55 0 100 45 100 100s-45 100 -100 100h-100zM300 400v-300h150
+c83 0 150 67 150 150s-67 150 -150 150h-150z"/>
+ <glyph glyph-name="20" unicode="" horiz-adv-x="600"
+ d="M300 800v-300h200l-300 -500v300h-200z"/>
+ <glyph glyph-name="21" unicode=""
+ d="M100 800h300v-300l100 100l100 -100v300h50c28 0 50 -22 50 -50v-550h-550c-28 0 -50 -22 -50 -50s22 -50 50 -50h550v-100h-550c-83 0 -150 67 -150 150v550l3 19c8 39 39 70 78 78z"/>
+ <glyph glyph-name="22" unicode="" horiz-adv-x="400"
+ d="M0 800h400v-800l-200 200l-200 -200v800z"/>
+ <glyph glyph-name="23" unicode=""
+ d="M0 800h800v-100h-800v100zM0 600h300v-103h203v103h297v-591c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v591z"/>
+ <glyph glyph-name="24" unicode=""
+ d="M300 800h200c55 0 100 -45 100 -100v-100h191c6 0 9 -3 9 -9v-241c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v241c0 6 3 9 9 9h191v100c0 55 45 100 100 100zM300 700v-100h200v100h-200zM0 209c16 -6 32 -9 50 -9h700c18 0 34 3 50 9v-200c0 -6 -3 -9 -9 -9h-782
+c-6 0 -9 3 -9 9v200z"/>
+ <glyph glyph-name="25" unicode="" horiz-adv-x="600"
+ d="M300 800c58 0 110 -16 147 -53s53 -89 53 -147h-100c0 39 -11 61 -25 75s-36 25 -75 25c-35 0 -55 -10 -72 -31s-28 -55 -28 -94c0 -51 20 -107 28 -175h172v-100h-178c-14 -60 -49 -127 -113 -200h491v-100h-600v122l16 12c69 69 95 121 106 166h-122v100h125
+c-8 50 -25 106 -25 175c0 58 16 114 50 156c34 43 88 69 150 69z"/>
+ <glyph glyph-name="26" unicode=""
+ d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-700c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v700v2c0 20 15 42 34 48zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50zM350 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h300c28 0 50 22 50 50
+s-22 50 -50 50h-300zM100 400v-400h600v400h-600z"/>
+ <glyph glyph-name="27" unicode=""
+ d="M744 797l6 -3l44 -44c4 -4 3 -8 0 -12l-266 -375l-15 -13l-25 -12c-23 72 -78 127 -150 150l12 25l13 15l375 266zM266 400c74 0 134 -60 134 -134c0 -147 -119 -266 -266 -266c-48 0 -95 12 -134 34c80 46 134 133 134 232c0 74 58 134 132 134z"/>
+ <glyph glyph-name="28" unicode=""
+ d="M9 451c0 23 19 50 46 50c8 0 19 -3 26 -7l131 -66l29 22c-79 81 -1 250 118 250s197 -167 119 -250l28 -22l131 66c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-115 -56c9 -16 19 -33 25 -50h68c28 0 50 -22 50 -50s-22 -50 -50 -50h-50
c0 -23 -2 -45 -6 -66l78 -40c21 -5 37 -28 37 -49c0 -28 -22 -50 -50 -50c-10 0 -23 5 -31 11l-65 35c-24 -46 -62 -86 -103 -110c-35 19 -60 45 -60 72v135v4v5v6v5v5v87c0 28 -22 50 -50 50c-24 0 -45 -17 -50 -40c1 -3 1 -8 1 -11s0 -8 -1 -11v-82v-4v-5v-144
-c0 -28 -24 -53 -59 -72c-41 25 -79 64 -103 110l-66 -35c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49l78 40c-4 21 -6 43 -6 66h-50h-5c-28 0 -50 22 -50 50c0 26 22 50 50 50h5h69c6 17 16 34 25 50l-116 56c-16 7 -28 27 -28 45z" />
- <glyph glyph-name="29" unicode=""
-d="M600 700h91c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-91v600zM210 503l290 147v-500l-250 125v-3c-15 0 -25 -8 -28 -22l75 -178c11 -25 0 -58 -25 -69s-58 0 -69 25l-103 272h-91c-6 0 -9 3 -9 9v182c0 6 3 9 9 9h182z" />
- <glyph glyph-name="2a" unicode=""
-d="M9 800h682c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM100 700v-200h500v200h-500zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-300h100v300h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
- <glyph glyph-name="2b" unicode=""
-d="M0 800h700v-200h-700v200zM0 500h700v-491c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v491zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
- <glyph glyph-name="2c" unicode=""
-d="M409 800h182c6 0 10 -4 12 -9l94 -182c2 -5 6 -9 12 -9h82c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v441c0 83 67 150 150 150h141c6 0 10 4 12 9l94 182c2 5 6 9 12 9zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z
-M500 500c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM500 400c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
- <glyph glyph-name="2d" unicode=""
-d="M0 600h800l-400 -400z" />
- <glyph glyph-name="2e" unicode="" horiz-adv-x="400"
-d="M400 800v-800l-400 400z" />
- <glyph glyph-name="2f" unicode="" horiz-adv-x="400"
-d="M0 800l400 -400l-400 -400v800z" />
- <glyph glyph-name="30" unicode=""
-d="M400 600l400 -400h-800z" />
- <glyph glyph-name="31" unicode=""
-d="M0 550c0 23 20 50 46 50h3h5h4h200c17 0 37 -13 44 -28l38 -72h444c14 0 19 -12 15 -25l-81 -250c-4 -13 -21 -25 -35 -25h-350c-14 0 -30 12 -34 25c-27 83 -54 167 -81 250l-10 25h-150c-2 0 -5 -1 -7 -1c-28 0 -51 23 -51 51zM358 100c28 0 50 -22 50 -50
-s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM658 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
- <glyph glyph-name="32" unicode=""
-d="M0 700h500v-100h-300v-300h-100l-100 -100v500zM300 500h500v-500l-100 100h-400v400z" />
- <glyph glyph-name="33" unicode=""
-d="M641 700l143 -141l-493 -493c-71 76 -146 148 -219 222l-72 71l141 141c50 -51 101 -101 153 -150c116 117 234 231 347 350z" />
- <glyph glyph-name="34" unicode=""
-d="M150 600l250 -250l250 250l150 -150l-400 -400l-400 400z" />
- <glyph glyph-name="35" unicode="" horiz-adv-x="600"
-d="M400 800l150 -150l-250 -250l250 -250l-150 -150l-400 400z" />
- <glyph glyph-name="36" unicode="" horiz-adv-x="600"
-d="M150 800l400 -400l-400 -400l-150 150l250 250l-250 250z" />
- <glyph glyph-name="37" unicode=""
-d="M400 600l400 -400l-150 -150l-250 250l-250 -250l-150 150z" />
- <glyph glyph-name="38" unicode=""
-d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM600 622l-250 -250l-100 100l-72 -72l172 -172l322 322z" />
- <glyph glyph-name="39" unicode=""
-d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM250 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
- <glyph glyph-name="3a" unicode=""
-d="M350 800c28 0 50 -22 50 -50v-50h75c14 0 25 -11 25 -25v-75h-300v75c0 14 11 25 25 25h75v50c0 28 22 50 50 50zM25 700h75v-200h500v200h75c14 0 25 -11 25 -25v-650c0 -14 -11 -25 -25 -25h-650c-14 0 -25 11 -25 25v650c0 14 11 25 25 25z" />
- <glyph glyph-name="3b" unicode=""
-d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM350 600h100v-181c23 -24 47 -47 72 -69l-72 -72c-27 30 -55 59 -84 88l-16 12
-v222z" />
- <glyph glyph-name="3c" unicode=""
-d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -3 -34 -9 -50h-191v50c0 83 -67 150 -150 150s-150 -67 -150 -150v-50h-272c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM434 400h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1
-v-150h150l-200 -200l-200 200h150v150v2c0 20 15 42 34 48z" />
- <glyph glyph-name="3d" unicode=""
-d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -3 -34 -9 -50h-141l-200 200l-200 -200h-222c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM450 350l250 -250h-200v-50c0 -28 -22 -50 -50 -50s-50 22 -50 50v50h-200z" />
- <glyph glyph-name="3e" unicode=""
-d="M450 700c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200s90 200 200 200c23 114 129 200 250 200z" />
- <glyph glyph-name="3f" unicode=""
-d="M250 800c82 0 154 -40 200 -100c-143 0 -270 -85 -325 -209c-36 -10 -70 -25 -100 -47c-16 33 -25 67 -25 106c0 138 112 250 250 250zM450 600c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200
-s90 200 200 200c23 114 129 200 250 200z" />
- <glyph glyph-name="40" unicode=""
-d="M500 700h100l-300 -600h-100zM100 600h100l-100 -200l100 -200h-100l-100 200zM600 600h100l100 -200l-100 -200h-100l100 200z" />
- <glyph glyph-name="41" unicode=""
-d="M350 800h100l50 -119l28 -12l119 50l72 -72l-50 -119l12 -28l119 -50v-100l-119 -50l-12 -28l50 -119l-72 -72l-119 50l-28 -12l-50 -119h-100l-50 119l-28 12l-119 -50l-72 72l50 119l-12 28l-119 50v100l119 50l12 28l-50 119l72 72l119 -50l28 12zM400 550
-c-83 0 -150 -67 -150 -150s67 -150 150 -150s150 67 150 150s-67 150 -150 150z" />
- <glyph glyph-name="42" unicode=""
-d="M0 800h800v-200h-800v200zM200 500h400l-200 -200zM0 100h800v-100h-800v100z" />
- <glyph glyph-name="43" unicode=""
-d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM500 600v-400l-200 200z" />
- <glyph glyph-name="44" unicode=""
-d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM300 600l200 -200l-200 -200v400z" />
- <glyph glyph-name="45" unicode=""
-d="M0 800h800v-100h-800v100zM400 500l200 -200h-400zM0 200h800v-200h-800v200z" />
- <glyph glyph-name="46" unicode=""
-d="M150 700c83 0 150 -67 150 -150v-50h100v50c0 83 67 150 150 150s150 -67 150 -150s-67 -150 -150 -150h-50v-100h50c83 0 150 -67 150 -150s-67 -150 -150 -150s-150 67 -150 150v50h-100v-50c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150h50v100h-50
+c0 -28 -24 -53 -59 -72c-41 25 -79 64 -103 110l-66 -35c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49l78 40c-4 21 -6 43 -6 66h-50h-5c-28 0 -50 22 -50 50c0 26 22 50 50 50h5h69c6 17 16 34 25 50l-116 56c-16 7 -28 27 -28 45z"/>
+ <glyph glyph-name="29" unicode=""
+ d="M600 700h91c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-91v600zM210 503l290 147v-500l-250 125v-3c-15 0 -25 -8 -28 -22l75 -178c11 -25 0 -58 -25 -69s-58 0 -69 25l-103 272h-91c-6 0 -9 3 -9 9v182c0 6 3 9 9 9h182z"/>
+ <glyph glyph-name="2a" unicode=""
+ d="M9 800h682c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM100 700v-200h500v200h-500zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-300h100v300h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z"/>
+ <glyph glyph-name="2b" unicode=""
+ d="M0 800h700v-200h-700v200zM0 500h700v-491c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v491zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z"/>
+ <glyph glyph-name="2c" unicode=""
+ d="M409 800h182c6 0 10 -4 12 -9l94 -182c2 -5 6 -9 12 -9h82c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v441c0 83 67 150 150 150h141c6 0 10 4 12 9l94 182c2 5 6 9 12 9zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z
+M500 500c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM500 400c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z"/>
+ <glyph glyph-name="2d" unicode=""
+ d="M0 600h800l-400 -400z"/>
+ <glyph glyph-name="2e" unicode="" horiz-adv-x="400"
+ d="M400 800v-800l-400 400z"/>
+ <glyph glyph-name="2f" unicode="" horiz-adv-x="400"
+ d="M0 800l400 -400l-400 -400v800z"/>
+ <glyph glyph-name="30" unicode=""
+ d="M400 600l400 -400h-800z"/>
+ <glyph glyph-name="31" unicode=""
+ d="M0 550c0 23 20 50 46 50h3h5h4h200c17 0 37 -13 44 -28l38 -72h444c14 0 19 -12 15 -25l-81 -250c-4 -13 -21 -25 -35 -25h-350c-14 0 -30 12 -34 25c-27 83 -54 167 -81 250l-10 25h-150c-2 0 -5 -1 -7 -1c-28 0 -51 23 -51 51zM358 100c28 0 50 -22 50 -50
+s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM658 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z"/>
+ <glyph glyph-name="32" unicode=""
+ d="M0 700h500v-100h-300v-300h-100l-100 -100v500zM300 500h500v-500l-100 100h-400v400z"/>
+ <glyph glyph-name="33" unicode=""
+ d="M641 700l143 -141l-493 -493c-71 76 -146 148 -219 222l-72 71l141 141c50 -51 101 -101 153 -150c116 117 234 231 347 350z"/>
+ <glyph glyph-name="34" unicode=""
+ d="M150 600l250 -250l250 250l150 -150l-400 -400l-400 400z"/>
+ <glyph glyph-name="35" unicode="" horiz-adv-x="600"
+ d="M400 800l150 -150l-250 -250l250 -250l-150 -150l-400 400z"/>
+ <glyph glyph-name="36" unicode="" horiz-adv-x="600"
+ d="M150 800l400 -400l-400 -400l-150 150l250 250l-250 250z"/>
+ <glyph glyph-name="37" unicode=""
+ d="M400 600l400 -400l-150 -150l-250 250l-250 -250l-150 150z"/>
+ <glyph glyph-name="38" unicode=""
+ d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM600 622l-250 -250l-100 100l-72 -72l172 -172l322 322z"/>
+ <glyph glyph-name="39" unicode=""
+ d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM250 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z"/>
+ <glyph glyph-name="3a" unicode=""
+ d="M350 800c28 0 50 -22 50 -50v-50h75c14 0 25 -11 25 -25v-75h-300v75c0 14 11 25 25 25h75v50c0 28 22 50 50 50zM25 700h75v-200h500v200h75c14 0 25 -11 25 -25v-650c0 -14 -11 -25 -25 -25h-650c-14 0 -25 11 -25 25v650c0 14 11 25 25 25z"/>
+ <glyph glyph-name="3b" unicode=""
+ d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM350 600h100v-181c23 -24 47 -47 72 -69l-72 -72c-27 30 -55 59 -84 88l-16 12
+v222z"/>
+ <glyph glyph-name="3c" unicode=""
+ d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -3 -34 -9 -50h-191v50c0 83 -67 150 -150 150s-150 -67 -150 -150v-50h-272c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM434 400h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1
+v-150h150l-200 -200l-200 200h150v150v2c0 20 15 42 34 48z"/>
+ <glyph glyph-name="3d" unicode=""
+ d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -3 -34 -9 -50h-141l-200 200l-200 -200h-222c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM450 350l250 -250h-200v-50c0 -28 -22 -50 -50 -50s-50 22 -50 50v50h-200z"/>
+ <glyph glyph-name="3e" unicode=""
+ d="M450 700c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200s90 200 200 200c23 114 129 200 250 200z"/>
+ <glyph glyph-name="3f" unicode=""
+ d="M250 800c82 0 154 -40 200 -100c-143 0 -270 -85 -325 -209c-36 -10 -70 -25 -100 -47c-16 33 -25 67 -25 106c0 138 112 250 250 250zM450 600c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200
+s90 200 200 200c23 114 129 200 250 200z"/>
+ <glyph glyph-name="40" unicode=""
+ d="M500 700h100l-300 -600h-100zM100 600h100l-100 -200l100 -200h-100l-100 200zM600 600h100l100 -200l-100 -200h-100l100 200z"/>
+ <glyph glyph-name="41" unicode=""
+ d="M350 800h100l50 -119l28 -12l119 50l72 -72l-50 -119l12 -28l119 -50v-100l-119 -50l-12 -28l50 -119l-72 -72l-119 50l-28 -12l-50 -119h-100l-50 119l-28 12l-119 -50l-72 72l50 119l-12 28l-119 50v100l119 50l12 28l-50 119l72 72l119 -50l28 12zM400 550
+c-83 0 -150 -67 -150 -150s67 -150 150 -150s150 67 150 150s-67 150 -150 150z"/>
+ <glyph glyph-name="42" unicode=""
+ d="M0 800h800v-200h-800v200zM200 500h400l-200 -200zM0 100h800v-100h-800v100z"/>
+ <glyph glyph-name="43" unicode=""
+ d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM500 600v-400l-200 200z"/>
+ <glyph glyph-name="44" unicode=""
+ d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM300 600l200 -200l-200 -200v400z"/>
+ <glyph glyph-name="45" unicode=""
+ d="M0 800h800v-100h-800v100zM400 500l200 -200h-400zM0 200h800v-200h-800v200z"/>
+ <glyph glyph-name="46" unicode=""
+ d="M150 700c83 0 150 -67 150 -150v-50h100v50c0 83 67 150 150 150s150 -67 150 -150s-67 -150 -150 -150h-50v-100h50c83 0 150 -67 150 -150s-67 -150 -150 -150s-150 67 -150 150v50h-100v-50c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150h50v100h-50
c-83 0 -150 67 -150 150s67 150 150 150zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h50v50c0 28 -22 50 -50 50zM550 600c-28 0 -50 -22 -50 -50v-50h50c28 0 50 22 50 50s-22 50 -50 50zM300 400v-100h100v100h-100zM150 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
-s50 22 50 50v50h-50zM500 200v-50c0 -28 22 -50 50 -50s50 22 50 50s-22 50 -50 50h-50z" />
- <glyph glyph-name="47" unicode=""
-d="M0 791c0 5 4 9 9 9h782c6 0 9 -4 9 -10v-790l-200 200h-591c-6 0 -9 3 -9 9v582z" />
- <glyph glyph-name="48" unicode=""
-d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM600 600l-100 -300l-300 -100l100 300zM400 450c-28 0 -50 -22 -50 -50
-s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
- <glyph glyph-name="49" unicode=""
-d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700v-600c166 0 300 134 300 300s-134 300 -300 300z" />
- <glyph glyph-name="4a" unicode=""
-d="M0 800h800v-100h-800v100zM0 600h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100zM750 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
- <glyph glyph-name="4b" unicode=""
-d="M25 700h750c14 0 25 -11 25 -25v-75h-800v75c0 14 11 25 25 25zM0 500h800v-375c0 -14 -11 -25 -25 -25h-750c-14 0 -25 11 -25 25v375zM100 300v-100h100v100h-100zM300 300v-100h100v100h-100z" />
- <glyph glyph-name="4c" unicode=""
-d="M100 800h100v-100h450l100 100l50 -50l-100 -100v-450h100v-100h-100v-100h-100v100h-500v500h-100v100h100v100zM200 600v-350l350 350h-350zM600 550l-350 -350h350v350z" />
- <glyph glyph-name="4d" unicode=""
-d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z
-M200 452c0 20 15 42 34 48h3h3h8c12 0 28 -7 36 -16l91 -90l25 6c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100l6 25l-90 91c-9 8 -16 24 -16 36zM550 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
- <glyph glyph-name="4e" unicode=""
-d="M300 800h200v-300h200l-300 -300l-300 300h200v300zM0 100h800v-100h-800v100z" />
- <glyph glyph-name="4f" unicode=""
-d="M0 800h800v-100h-800v100zM400 600l300 -300h-200v-300h-200v300h-200z" />
- <glyph glyph-name="50" unicode=""
-d="M200 700h600v-600h-600l-200 300zM350 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
- <glyph glyph-name="51" unicode=""
-d="M400 700c220 0 400 -180 400 -400h-100c0 166 -134 300 -300 300s-300 -134 -300 -300h-100c0 220 180 400 400 400zM341 491l59 -88l59 88c81 -25 141 -101 141 -191c0 -110 -90 -200 -200 -200s-200 90 -200 200c0 90 60 166 141 191z" />
- <glyph glyph-name="52" unicode=""
-d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300zM100 600v-100h100v100h-100zM100 400v-100h100v100h-100zM100 200v-100h400v100h-400z" />
- <glyph glyph-name="53" unicode="" horiz-adv-x="600"
-d="M200 700h100v-100h75c30 0 58 -6 81 -22s44 -44 44 -78v-100h-100v94c-4 3 -13 6 -25 6h-250c-14 0 -25 -11 -25 -25v-50c0 -15 20 -40 34 -44l257 -65c66 -16 109 -73 109 -141v-50c0 -68 -57 -125 -125 -125h-75v-100h-100v100h-75c-30 0 -58 6 -81 22s-44 44 -44 78
-v100h100v-94c4 -3 13 -6 25 -6h250c14 0 25 11 25 25v50c0 15 -20 40 -34 44l-257 65c-66 16 -109 73 -109 141v50c0 68 57 125 125 125h75v100z" />
- <glyph glyph-name="54" unicode=""
-d="M0 700h300v-300l-300 -300v600zM500 700h300v-300l-300 -300v600z" />
- <glyph glyph-name="55" unicode=""
-d="M300 700v-600h-300v300zM800 700v-600h-300v300z" />
- <glyph glyph-name="56" unicode=""
-d="M300 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300zM800 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300z" />
- <glyph glyph-name="57" unicode=""
-d="M0 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300zM500 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300z" />
- <glyph glyph-name="58" unicode="" horiz-adv-x="600"
-d="M300 800l34 -34c11 -11 266 -270 266 -488c0 -165 -135 -300 -300 -300s-300 135 -300 300c0 218 255 477 266 488zM150 328c-28 0 -50 -22 -50 -50c0 -110 90 -200 200 -200c28 0 50 22 50 50s-22 50 -50 50c-55 0 -100 45 -100 100c0 28 -22 50 -50 50z" />
- <glyph glyph-name="59" unicode=""
-d="M400 800l400 -500h-800zM0 200h800v-200h-800v200z" />
- <glyph glyph-name="5a" unicode="" horiz-adv-x="600"
-d="M300 800l300 -300h-600zM0 300h600l-300 -300z" />
- <glyph glyph-name="5b" unicode=""
-d="M0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200z" />
- <glyph glyph-name="5c" unicode=""
-d="M0 700h800v-100l-400 -200l-400 200v100zM0 500l400 -200l400 200v-400h-800v400z" />
- <glyph glyph-name="5d" unicode=""
-d="M400 800l400 -200v-600h-800v600zM400 688l-300 -150v-188l300 -150l300 150v188zM200 500h400v-100l-200 -100l-200 100v100z" />
- <glyph glyph-name="5e" unicode=""
-d="M600 700c69 0 134 -19 191 -50l-16 -106c-49 35 -109 56 -175 56c-131 0 -240 -84 -281 -200h331l-16 -100h-334c0 -36 8 -68 19 -100h297l-16 -100h-222c55 -61 133 -100 222 -100c78 0 147 30 200 78v-122c-59 -35 -127 -56 -200 -56c-147 0 -274 82 -344 200h-256
-l19 100h197c-8 32 -16 66 -16 100h-200l25 100h191c45 172 198 300 384 300z" />
- <glyph glyph-name="5f" unicode=""
-d="M0 700h700v-100h-700v100zM0 500h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100z" />
- <glyph glyph-name="60" unicode=""
-d="M0 800h800v-100h-800v100zM200 600h400l-200 -200zM0 200h800v-200h-800v200z" />
- <glyph glyph-name="61" unicode=""
-d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM200 600l200 -200l-200 -200v400z" />
- <glyph glyph-name="62" unicode=""
-d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM600 600v-400l-200 200z" />
- <glyph glyph-name="63" unicode=""
-d="M0 800h800v-200h-800v200zM400 400l200 -200h-400zM0 100h800v-100h-800v100z" />
- <glyph glyph-name="64" unicode=""
-d="M0 800h200v-100h-100v-600h600v100h100v-200h-800v800zM400 800h400v-400l-150 150l-250 -250l-100 100l250 250z" />
- <glyph glyph-name="65" unicode=""
-d="M403 700c247 0 397 -300 397 -300s-150 -300 -397 -300c-253 0 -403 300 -403 300s150 300 403 300zM400 600c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM400 500c10 0 19 -3 28 -6c-16 -8 -28 -24 -28 -44c0 -28 22 -50 50 -50
-c20 0 36 12 44 28c3 -9 6 -18 6 -28c0 -55 -45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
- <glyph glyph-name="66" unicode="" horiz-adv-x="900"
-d="M331 700h3h3c3 1 7 1 10 1c12 0 29 -8 37 -17l94 -93l66 65c57 57 155 57 212 0c58 -58 58 -154 0 -212l-65 -66l93 -94c10 -8 18 -25 18 -38c0 -28 -22 -50 -50 -50c-13 0 -32 9 -40 20l-62 65l-381 -381h-269v272l375 381l-63 63c-9 8 -16 24 -16 36c0 20 16 42 35 48z
-M447 481l-313 -315l128 -132l316 316z" />
- <glyph glyph-name="67" unicode=""
-d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300z" />
- <glyph glyph-name="68" unicode=""
-d="M200 800c0 0 200 -100 200 -300s-298 -302 -200 -500c0 0 -200 100 -200 300s300 300 200 500zM500 500c0 0 200 -100 200 -300c0 -150 -60 -200 -100 -200h-300c0 200 300 300 200 500z" />
- <glyph glyph-name="69" unicode=""
-d="M0 800h100v-800h-100v800zM200 800h300v-100h300l-200 -203l200 -197h-400v100h-200v400z" />
- <glyph glyph-name="6a" unicode="" horiz-adv-x="400"
-d="M150 800h150l-100 -200h200l-150 -300h150l-300 -300l-100 300h134l66 200h-200z" />
- <glyph glyph-name="6b" unicode=""
-d="M0 800h300v-100h500v-100h-800v200zM0 500h800v-450c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v450z" />
- <glyph glyph-name="6c" unicode=""
-d="M150 800c83 0 150 -67 150 -150c0 -66 -41 -121 -100 -141v-118c15 5 33 9 50 9h200c28 0 50 22 50 50v59c-59 20 -100 75 -100 141c0 83 67 150 150 150s150 -67 150 -150c0 -66 -41 -121 -100 -141v-59c0 -82 -68 -150 -150 -150h-200c-14 0 -25 -7 -34 -16
-c50 -24 84 -74 84 -134c0 -83 -67 -150 -150 -150s-150 67 -150 150c0 66 41 121 100 141v218c-59 20 -100 75 -100 141c0 83 67 150 150 150z" />
- <glyph glyph-name="6d" unicode=""
-d="M0 800h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400zM500 400l150 -150l150 150v-400h-400l150 150l-150 150z" />
- <glyph glyph-name="6e" unicode=""
-d="M100 800l150 -150l150 150v-400h-400l150 150l-150 150zM400 400h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400z" />
- <glyph glyph-name="6f" unicode=""
-d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700c-56 0 -108 -17 -153 -44l22 -19c33 -18 13 -48 -13 -59c-30 -13 -77 10 -65 -41c13 -55 -27 -3 -47 -15c-42 -26 49 -152 31 -156l-59 34c-8 0 -13 -5 -16 -10
+s50 22 50 50v50h-50zM500 200v-50c0 -28 22 -50 50 -50s50 22 50 50s-22 50 -50 50h-50z"/>
+ <glyph glyph-name="47" unicode=""
+ d="M0 791c0 5 4 9 9 9h782c6 0 9 -4 9 -10v-790l-200 200h-591c-6 0 -9 3 -9 9v582z"/>
+ <glyph glyph-name="48" unicode=""
+ d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM600 600l-100 -300l-300 -100l100 300zM400 450c-28 0 -50 -22 -50 -50
+s22 -50 50 -50s50 22 50 50s-22 50 -50 50z"/>
+ <glyph glyph-name="49" unicode=""
+ d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700v-600c166 0 300 134 300 300s-134 300 -300 300z"/>
+ <glyph glyph-name="4a" unicode=""
+ d="M0 800h800v-100h-800v100zM0 600h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100zM750 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z"/>
+ <glyph glyph-name="4b" unicode=""
+ d="M25 700h750c14 0 25 -11 25 -25v-75h-800v75c0 14 11 25 25 25zM0 500h800v-375c0 -14 -11 -25 -25 -25h-750c-14 0 -25 11 -25 25v375zM100 300v-100h100v100h-100zM300 300v-100h100v100h-100z"/>
+ <glyph glyph-name="4c" unicode=""
+ d="M100 800h100v-100h450l100 100l50 -50l-100 -100v-450h100v-100h-100v-100h-100v100h-500v500h-100v100h100v100zM200 600v-350l350 350h-350zM600 550l-350 -350h350v350z"/>
+ <glyph glyph-name="4d" unicode=""
+ d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z
+M200 452c0 20 15 42 34 48h3h3h8c12 0 28 -7 36 -16l91 -90l25 6c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100l6 25l-90 91c-9 8 -16 24 -16 36zM550 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z"/>
+ <glyph glyph-name="4e" unicode=""
+ d="M300 800h200v-300h200l-300 -300l-300 300h200v300zM0 100h800v-100h-800v100z"/>
+ <glyph glyph-name="4f" unicode=""
+ d="M0 800h800v-100h-800v100zM400 600l300 -300h-200v-300h-200v300h-200z"/>
+ <glyph glyph-name="50" unicode=""
+ d="M200 700h600v-600h-600l-200 300zM350 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z"/>
+ <glyph glyph-name="51" unicode=""
+ d="M400 700c220 0 400 -180 400 -400h-100c0 166 -134 300 -300 300s-300 -134 -300 -300h-100c0 220 180 400 400 400zM341 491l59 -88l59 88c81 -25 141 -101 141 -191c0 -110 -90 -200 -200 -200s-200 90 -200 200c0 90 60 166 141 191z"/>
+ <glyph glyph-name="52" unicode=""
+ d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300zM100 600v-100h100v100h-100zM100 400v-100h100v100h-100zM100 200v-100h400v100h-400z"/>
+ <glyph glyph-name="53" unicode="" horiz-adv-x="600"
+ d="M200 700h100v-100h75c30 0 58 -6 81 -22s44 -44 44 -78v-100h-100v94c-4 3 -13 6 -25 6h-250c-14 0 -25 -11 -25 -25v-50c0 -15 20 -40 34 -44l257 -65c66 -16 109 -73 109 -141v-50c0 -68 -57 -125 -125 -125h-75v-100h-100v100h-75c-30 0 -58 6 -81 22s-44 44 -44 78
+v100h100v-94c4 -3 13 -6 25 -6h250c14 0 25 11 25 25v50c0 15 -20 40 -34 44l-257 65c-66 16 -109 73 -109 141v50c0 68 57 125 125 125h75v100z"/>
+ <glyph glyph-name="54" unicode=""
+ d="M0 700h300v-300l-300 -300v600zM500 700h300v-300l-300 -300v600z"/>
+ <glyph glyph-name="55" unicode=""
+ d="M300 700v-600h-300v300zM800 700v-600h-300v300z"/>
+ <glyph glyph-name="56" unicode=""
+ d="M300 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300zM800 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300z"/>
+ <glyph glyph-name="57" unicode=""
+ d="M0 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300zM500 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300z"/>
+ <glyph glyph-name="58" unicode="" horiz-adv-x="600"
+ d="M300 800l34 -34c11 -11 266 -270 266 -488c0 -165 -135 -300 -300 -300s-300 135 -300 300c0 218 255 477 266 488zM150 328c-28 0 -50 -22 -50 -50c0 -110 90 -200 200 -200c28 0 50 22 50 50s-22 50 -50 50c-55 0 -100 45 -100 100c0 28 -22 50 -50 50z"/>
+ <glyph glyph-name="59" unicode=""
+ d="M400 800l400 -500h-800zM0 200h800v-200h-800v200z"/>
+ <glyph glyph-name="5a" unicode="" horiz-adv-x="600"
+ d="M300 800l300 -300h-600zM0 300h600l-300 -300z"/>
+ <glyph glyph-name="5b" unicode=""
+ d="M0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200z"/>
+ <glyph glyph-name="5c" unicode=""
+ d="M0 700h800v-100l-400 -200l-400 200v100zM0 500l400 -200l400 200v-400h-800v400z"/>
+ <glyph glyph-name="5d" unicode=""
+ d="M400 800l400 -200v-600h-800v600zM400 688l-300 -150v-188l300 -150l300 150v188zM200 500h400v-100l-200 -100l-200 100v100z"/>
+ <glyph glyph-name="5e" unicode=""
+ d="M600 700c69 0 134 -19 191 -50l-16 -106c-49 35 -109 56 -175 56c-131 0 -240 -84 -281 -200h331l-16 -100h-334c0 -36 8 -68 19 -100h297l-16 -100h-222c55 -61 133 -100 222 -100c78 0 147 30 200 78v-122c-59 -35 -127 -56 -200 -56c-147 0 -274 82 -344 200h-256
+l19 100h197c-8 32 -16 66 -16 100h-200l25 100h191c45 172 198 300 384 300z"/>
+ <glyph glyph-name="5f" unicode=""
+ d="M0 700h700v-100h-700v100zM0 500h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100z"/>
+ <glyph glyph-name="60" unicode=""
+ d="M0 800h800v-100h-800v100zM200 600h400l-200 -200zM0 200h800v-200h-800v200z"/>
+ <glyph glyph-name="61" unicode=""
+ d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM200 600l200 -200l-200 -200v400z"/>
+ <glyph glyph-name="62" unicode=""
+ d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM600 600v-400l-200 200z"/>
+ <glyph glyph-name="63" unicode=""
+ d="M0 800h800v-200h-800v200zM400 400l200 -200h-400zM0 100h800v-100h-800v100z"/>
+ <glyph glyph-name="64" unicode=""
+ d="M0 800h200v-100h-100v-600h600v100h100v-200h-800v800zM400 800h400v-400l-150 150l-250 -250l-100 100l250 250z"/>
+ <glyph glyph-name="65" unicode=""
+ d="M403 700c247 0 397 -300 397 -300s-150 -300 -397 -300c-253 0 -403 300 -403 300s150 300 403 300zM400 600c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM400 500c10 0 19 -3 28 -6c-16 -8 -28 -24 -28 -44c0 -28 22 -50 50 -50
+c20 0 36 12 44 28c3 -9 6 -18 6 -28c0 -55 -45 -100 -100 -100s-100 45 -100 100s45 100 100 100z"/>
+ <glyph glyph-name="66" unicode="" horiz-adv-x="900"
+ d="M331 700h3h3c3 1 7 1 10 1c12 0 29 -8 37 -17l94 -93l66 65c57 57 155 57 212 0c58 -58 58 -154 0 -212l-65 -66l93 -94c10 -8 18 -25 18 -38c0 -28 -22 -50 -50 -50c-13 0 -32 9 -40 20l-62 65l-381 -381h-269v272l375 381l-63 63c-9 8 -16 24 -16 36c0 20 16 42 35 48z
+M447 481l-313 -315l128 -132l316 316z"/>
+ <glyph glyph-name="67" unicode=""
+ d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300z"/>
+ <glyph glyph-name="68" unicode=""
+ d="M200 800c0 0 200 -100 200 -300s-298 -302 -200 -500c0 0 -200 100 -200 300s300 300 200 500zM500 500c0 0 200 -100 200 -300c0 -150 -60 -200 -100 -200h-300c0 200 300 300 200 500z"/>
+ <glyph glyph-name="69" unicode=""
+ d="M0 800h100v-800h-100v800zM200 800h300v-100h300l-200 -203l200 -197h-400v100h-200v400z"/>
+ <glyph glyph-name="6a" unicode="" horiz-adv-x="400"
+ d="M150 800h150l-100 -200h200l-150 -300h150l-300 -300l-100 300h134l66 200h-200z"/>
+ <glyph glyph-name="6b" unicode=""
+ d="M0 800h300v-100h500v-100h-800v200zM0 500h800v-450c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v450z"/>
+ <glyph glyph-name="6c" unicode=""
+ d="M150 800c83 0 150 -67 150 -150c0 -66 -41 -121 -100 -141v-118c15 5 33 9 50 9h200c28 0 50 22 50 50v59c-59 20 -100 75 -100 141c0 83 67 150 150 150s150 -67 150 -150c0 -66 -41 -121 -100 -141v-59c0 -82 -68 -150 -150 -150h-200c-14 0 -25 -7 -34 -16
+c50 -24 84 -74 84 -134c0 -83 -67 -150 -150 -150s-150 67 -150 150c0 66 41 121 100 141v218c-59 20 -100 75 -100 141c0 83 67 150 150 150z"/>
+ <glyph glyph-name="6d" unicode=""
+ d="M0 800h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400zM500 400l150 -150l150 150v-400h-400l150 150l-150 150z"/>
+ <glyph glyph-name="6e" unicode=""
+ d="M100 800l150 -150l150 150v-400h-400l150 150l-150 150zM400 400h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400z"/>
+ <glyph glyph-name="6f" unicode=""
+ d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700c-56 0 -108 -17 -153 -44l22 -19c33 -18 13 -48 -13 -59c-30 -13 -77 10 -65 -41c13 -55 -27 -3 -47 -15c-42 -26 49 -152 31 -156l-59 34c-8 0 -13 -5 -16 -10
c1 -30 10 -57 19 -84c28 -11 77 -2 100 -25c47 -28 97 -115 75 -159c34 -13 68 -22 106 -22c101 0 193 48 247 125c3 24 -8 44 -50 44c-69 0 -156 13 -153 97c2 46 101 108 66 143c-30 30 12 39 12 66c0 37 -65 32 -69 50s20 36 41 56c-30 10 -60 19 -94 19zM631 591
-c-38 -11 -94 -35 -87 -53c6 -15 52 -1 65 -13c11 -10 16 -59 44 -31l22 22v3c-11 26 -26 50 -44 72z" />
- <glyph glyph-name="70" unicode=""
-d="M703 800l97 -100l-400 -400l-100 100l-200 -203l-100 100l300 303l100 -100zM0 100h800v-100h-800v100z" />
- <glyph glyph-name="71" unicode=""
-d="M0 700h100v-100h-100v100zM200 700h100v-100h-100v100zM400 700h100v-100h-100v100zM600 700h100v-100h-100v100zM0 500h100v-100h-100v100zM200 500h100v-100h-100v100zM400 500h100v-100h-100v100zM600 500h100v-100h-100v100zM0 300h100v-100h-100v100zM200 300h100
-v-100h-100v100zM400 300h100v-100h-100v100zM600 300h100v-100h-100v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100zM600 100h100v-100h-100v100z" />
- <glyph glyph-name="72" unicode=""
-d="M0 800h200v-200h-200v200zM300 800h200v-200h-200v200zM600 800h200v-200h-200v200zM0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200zM0 200h200v-200h-200v200zM300 200h200v-200h-200v200zM600 200h200v-200h-200v200z" />
- <glyph glyph-name="73" unicode=""
-d="M0 800h300v-300h-300v300zM500 800h300v-300h-300v300zM0 300h300v-300h-300v300zM500 300h300v-300h-300v300z" />
- <glyph glyph-name="74" unicode=""
-d="M19 800h662c11 0 19 -8 19 -19v-331c0 -28 -22 -50 -50 -50h-600c-28 0 -50 22 -50 50v331c0 11 8 19 19 19zM0 309c16 -6 32 -9 50 -9h600c18 0 34 3 50 9v-290c0 -11 -8 -19 -19 -19h-662c-11 0 -19 8 -19 19v290zM550 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
-s50 22 50 50s-22 50 -50 50z" />
- <glyph glyph-name="75" unicode=""
-d="M0 700h300v-100h-50c-28 0 -50 -22 -50 -50v-150h300v150c0 28 -22 50 -50 50h-50v100h300v-100h-50c-28 0 -50 -22 -50 -50v-400c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50v150h-300v-150c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50
-v400c0 28 -22 50 -50 50h-50v100z" />
- <glyph glyph-name="76" unicode=""
-d="M400 700c165 0 300 -135 300 -300v-100h50c28 0 50 -22 50 -50v-200c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v350c0 111 -89 200 -200 200s-200 -89 -200 -200v-350c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v200c0 28 22 50 50 50h50v100
-c0 165 135 300 300 300z" />
- <glyph glyph-name="77" unicode=""
-d="M0 500c0 109 91 200 200 200s200 -91 200 -200c0 109 91 200 200 200s200 -91 200 -200c0 -55 -23 -105 -59 -141l-341 -340l-341 340c-36 36 -59 86 -59 141z" />
- <glyph glyph-name="78" unicode=""
-d="M400 700l400 -300l-100 3v-403h-200v200h-200v-200h-200v400h-100z" />
- <glyph glyph-name="79" unicode=""
-d="M0 800h800v-800h-800v800zM100 700v-300l100 100l400 -400h100v100l-200 200l100 100l100 -100v300h-600z" />
- <glyph glyph-name="7a" unicode=""
-d="M19 800h762c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-762c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 600v-300h100l100 -100h200l100 100h100v300h-600z" />
- <glyph glyph-name="7b" unicode=""
-d="M200 600c80 0 142 -56 200 -122c58 66 119 122 200 122c131 0 200 -101 200 -200s-69 -200 -200 -200c-81 0 -142 56 -200 122c-58 -66 -121 -122 -200 -122c-131 0 -200 101 -200 200s69 200 200 200zM200 500c-74 0 -100 -54 -100 -100s26 -100 100 -100
-c42 0 88 47 134 100c-46 53 -92 100 -134 100zM600 500c-43 0 -88 -47 -134 -100c46 -53 91 -100 134 -100c74 0 100 54 100 100s-26 100 -100 100z" />
- <glyph glyph-name="7c" unicode="" horiz-adv-x="400"
-d="M300 800c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100zM150 550c83 0 150 -69 150 -150c0 -66 -100 -214 -100 -250c0 -28 22 -50 50 -50s50 22 50 50h100c0 -83 -67 -150 -150 -150s-150 64 -150 150s100 222 100 250s-22 50 -50 50
-s-50 -22 -50 -50h-100c0 83 67 150 150 150z" />
- <glyph glyph-name="7d" unicode=""
-d="M200 800h500v-100h-122c-77 -197 -156 -392 -234 -588l-6 -12h162v-100h-500v100h122c77 197 156 392 234 588l7 12h-163v100z" />
- <glyph glyph-name="7e" unicode=""
-d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
- <glyph glyph-name="7f" unicode=""
-d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
- <glyph glyph-name="80" unicode=""
-d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
- <glyph glyph-name="81" unicode=""
-d="M550 800c138 0 250 -112 250 -250s-112 -250 -250 -250c-16 0 -32 0 -47 3l-3 -3v-100h-200v-200h-300v200l303 303c-3 15 -3 31 -3 47c0 138 112 250 250 250zM600 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
- <glyph glyph-name="82" unicode=""
-d="M134 600h3h4h4h5h500c28 0 50 -22 50 -50v-350h100v-150c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v150h100v350v2c0 20 15 42 34 48zM200 500v-300h100v-100h200v100h100v300h-400z" />
- <glyph glyph-name="83" unicode=""
-d="M0 800h400v-400h-400v400zM500 600h100v-400h-400v100h300v300zM700 400h100v-400h-400v100h300v300z" />
- <glyph glyph-name="84" unicode="" horiz-adv-x="600"
-d="M337 694c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-300 -150c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49zM437 544c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-400 -200c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50
+c-38 -11 -94 -35 -87 -53c6 -15 52 -1 65 -13c11 -10 16 -59 44 -31l22 22v3c-11 26 -26 50 -44 72z"/>
+ <glyph glyph-name="70" unicode=""
+ d="M703 800l97 -100l-400 -400l-100 100l-200 -203l-100 100l300 303l100 -100zM0 100h800v-100h-800v100z"/>
+ <glyph glyph-name="71" unicode=""
+ d="M0 700h100v-100h-100v100zM200 700h100v-100h-100v100zM400 700h100v-100h-100v100zM600 700h100v-100h-100v100zM0 500h100v-100h-100v100zM200 500h100v-100h-100v100zM400 500h100v-100h-100v100zM600 500h100v-100h-100v100zM0 300h100v-100h-100v100zM200 300h100
+v-100h-100v100zM400 300h100v-100h-100v100zM600 300h100v-100h-100v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100zM600 100h100v-100h-100v100z"/>
+ <glyph glyph-name="72" unicode=""
+ d="M0 800h200v-200h-200v200zM300 800h200v-200h-200v200zM600 800h200v-200h-200v200zM0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200zM0 200h200v-200h-200v200zM300 200h200v-200h-200v200zM600 200h200v-200h-200v200z"/>
+ <glyph glyph-name="73" unicode=""
+ d="M0 800h300v-300h-300v300zM500 800h300v-300h-300v300zM0 300h300v-300h-300v300zM500 300h300v-300h-300v300z"/>
+ <glyph glyph-name="74" unicode=""
+ d="M19 800h662c11 0 19 -8 19 -19v-331c0 -28 -22 -50 -50 -50h-600c-28 0 -50 22 -50 50v331c0 11 8 19 19 19zM0 309c16 -6 32 -9 50 -9h600c18 0 34 3 50 9v-290c0 -11 -8 -19 -19 -19h-662c-11 0 -19 8 -19 19v290zM550 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
+s50 22 50 50s-22 50 -50 50z"/>
+ <glyph glyph-name="75" unicode=""
+ d="M0 700h300v-100h-50c-28 0 -50 -22 -50 -50v-150h300v150c0 28 -22 50 -50 50h-50v100h300v-100h-50c-28 0 -50 -22 -50 -50v-400c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50v150h-300v-150c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50
+v400c0 28 -22 50 -50 50h-50v100z"/>
+ <glyph glyph-name="76" unicode=""
+ d="M400 700c165 0 300 -135 300 -300v-100h50c28 0 50 -22 50 -50v-200c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v350c0 111 -89 200 -200 200s-200 -89 -200 -200v-350c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v200c0 28 22 50 50 50h50v100
+c0 165 135 300 300 300z"/>
+ <glyph glyph-name="77" unicode=""
+ d="M0 500c0 109 91 200 200 200s200 -91 200 -200c0 109 91 200 200 200s200 -91 200 -200c0 -55 -23 -105 -59 -141l-341 -340l-341 340c-36 36 -59 86 -59 141z"/>
+ <glyph glyph-name="78" unicode=""
+ d="M400 700l400 -300l-100 3v-403h-200v200h-200v-200h-200v400h-100z"/>
+ <glyph glyph-name="79" unicode=""
+ d="M0 800h800v-800h-800v800zM100 700v-300l100 100l400 -400h100v100l-200 200l100 100l100 -100v300h-600z"/>
+ <glyph glyph-name="7a" unicode=""
+ d="M19 800h762c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-762c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 600v-300h100l100 -100h200l100 100h100v300h-600z"/>
+ <glyph glyph-name="7b" unicode=""
+ d="M200 600c80 0 142 -56 200 -122c58 66 119 122 200 122c131 0 200 -101 200 -200s-69 -200 -200 -200c-81 0 -142 56 -200 122c-58 -66 -121 -122 -200 -122c-131 0 -200 101 -200 200s69 200 200 200zM200 500c-74 0 -100 -54 -100 -100s26 -100 100 -100
+c42 0 88 47 134 100c-46 53 -92 100 -134 100zM600 500c-43 0 -88 -47 -134 -100c46 -53 91 -100 134 -100c74 0 100 54 100 100s-26 100 -100 100z"/>
+ <glyph glyph-name="7c" unicode="" horiz-adv-x="400"
+ d="M300 800c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100zM150 550c83 0 150 -69 150 -150c0 -66 -100 -214 -100 -250c0 -28 22 -50 50 -50s50 22 50 50h100c0 -83 -67 -150 -150 -150s-150 64 -150 150s100 222 100 250s-22 50 -50 50
+s-50 -22 -50 -50h-100c0 83 67 150 150 150z"/>
+ <glyph glyph-name="7d" unicode=""
+ d="M200 800h500v-100h-122c-77 -197 -156 -392 -234 -588l-6 -12h162v-100h-500v100h122c77 197 156 392 234 588l7 12h-163v100z"/>
+ <glyph glyph-name="7e" unicode=""
+ d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z"/>
+ <glyph glyph-name="7f" unicode=""
+ d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z"/>
+ <glyph glyph-name="80" unicode=""
+ d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z"/>
+ <glyph glyph-name="81" unicode=""
+ d="M550 800c138 0 250 -112 250 -250s-112 -250 -250 -250c-16 0 -32 0 -47 3l-3 -3v-100h-200v-200h-300v200l303 303c-3 15 -3 31 -3 47c0 138 112 250 250 250zM600 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z"/>
+ <glyph glyph-name="82" unicode=""
+ d="M134 600h3h4h4h5h500c28 0 50 -22 50 -50v-350h100v-150c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v150h100v350v2c0 20 15 42 34 48zM200 500v-300h100v-100h200v100h100v300h-400z"/>
+ <glyph glyph-name="83" unicode=""
+ d="M0 800h400v-400h-400v400zM500 600h100v-400h-400v100h300v300zM700 400h100v-400h-400v100h300v300z"/>
+ <glyph glyph-name="84" unicode="" horiz-adv-x="600"
+ d="M337 694c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-300 -150c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49zM437 544c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-400 -200c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50
c0 21 16 44 37 49zM437 344c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-106 -56c24 -4 43 -26 43 -50c0 -28 -23 -51 -51 -51c-2 0 -6 1 -8 1h-200c-26 1 -48 24 -48 50c0 16 12 36 26 44zM151 -50c0 23 20 50 46 50h3h4h5h100c28 0 50 -22 50 -50
-s-22 -50 -50 -50h-100c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
- <glyph glyph-name="85" unicode=""
-d="M199 800h100v-200h-200v100h100v100zM586 797h1c18 1 38 1 56 -3c36 -8 69 -26 97 -54c78 -78 78 -203 0 -281l-150 -150c-8 -13 -28 -24 -43 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43l150 150c40 40 39 105 0 144c-41 41 -110 34 -144 0l-44 -44
+s-22 -50 -50 -50h-100c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z"/>
+ <glyph glyph-name="85" unicode=""
+ d="M199 800h100v-200h-200v100h100v100zM586 797h1c18 1 38 1 56 -3c36 -8 69 -26 97 -54c78 -78 78 -203 0 -281l-150 -150c-8 -13 -28 -24 -43 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43l150 150c40 40 39 105 0 144c-41 41 -110 34 -144 0l-44 -44
c-8 -13 -27 -24 -42 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43l43 44c32 33 72 53 128 56zM208 490c4 5 14 16 22 16h3c2 0 6 1 8 1c28 0 50 -22 50 -50c0 -11 -6 -27 -14 -35l-150 -150c-40 -40 -39 -105 0 -144c41 -41 110 -34 144 0l44 44c8 13 27 24 42 24
-c28 0 50 -22 50 -50c0 -15 -11 -35 -24 -43l-43 -44c-22 -22 -48 -37 -75 -47c-70 -25 -151 -9 -207 47c-78 78 -78 203 0 281zM499 200h200v-100h-100v-100h-100v200z" />
- <glyph glyph-name="86" unicode=""
-d="M586 797c18 1 39 1 57 -3c36 -8 69 -26 97 -54c78 -78 78 -203 0 -281l-150 -150c-62 -62 -132 -81 -182 -78s-69 17 -84 25s-26 27 -26 44c0 28 22 51 50 51c8 0 19 -3 26 -7c0 0 15 -11 41 -13s62 3 106 47l150 150c40 40 39 105 0 144c-41 41 -110 34 -144 0
+c28 0 50 -22 50 -50c0 -15 -11 -35 -24 -43l-43 -44c-22 -22 -48 -37 -75 -47c-70 -25 -151 -9 -207 47c-78 78 -78 203 0 281zM499 200h200v-100h-100v-100h-100v200z"/>
+ <glyph glyph-name="86" unicode=""
+ d="M586 797c18 1 39 1 57 -3c36 -8 69 -26 97 -54c78 -78 78 -203 0 -281l-150 -150c-62 -62 -132 -81 -182 -78s-69 17 -84 25s-26 27 -26 44c0 28 22 51 50 51c8 0 19 -3 26 -7c0 0 15 -11 41 -13s62 3 106 47l150 150c40 40 39 105 0 144c-41 41 -110 34 -144 0
c-8 -13 -28 -24 -43 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43c32 33 72 53 128 56zM386 566c50 -2 64 -17 85 -22s37 -28 37 -49c0 -28 -22 -50 -50 -50c-10 0 -23 5 -31 11c0 0 -19 9 -47 10s-63 -4 -103 -44l-150 -150c-40 -40 -39 -105 0 -144c41 -41 110 -34 144 0
-c8 13 27 24 42 24c28 0 50 -22 50 -50c0 -15 -10 -35 -23 -43c-22 -22 -48 -37 -75 -47c-70 -25 -151 -9 -207 47c-78 78 -78 203 0 281l150 150c60 60 128 78 178 76z" />
- <glyph glyph-name="87" unicode=""
-d="M0 700h300v-300h-300v300zM400 700h400v-100h-400v100zM400 500h300v-100h-300v100zM0 300h300v-300h-300v300zM400 300h400v-100h-400v100zM400 100h300v-100h-300v100z" />
- <glyph glyph-name="88" unicode=""
-d="M50 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 700h600v-100h-600v100zM50 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 500h600v-100h-600v100zM50 300c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
-s22 50 50 50zM200 300h600v-100h-600v100zM50 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 100h600v-100h-600v100z" />
- <glyph glyph-name="89" unicode=""
-d="M800 800l-400 -800l-100 300l-300 100z" />
- <glyph glyph-name="8a" unicode="" horiz-adv-x="600"
-d="M300 700c110 0 200 -90 200 -200v-100h100v-400h-600v400h100v100c0 110 90 200 200 200zM300 600c-56 0 -100 -44 -100 -100v-100h200v100c0 56 -44 100 -100 100z" />
- <glyph glyph-name="8b" unicode="" horiz-adv-x="600"
-d="M300 800c110 0 200 -90 200 -200v-200h100v-400h-600v400h400v200c0 56 -44 100 -100 100s-100 -44 -100 -100h-100c0 110 90 200 200 200z" />
- <glyph glyph-name="8c" unicode=""
-d="M400 700v-100c-111 0 -200 -89 -200 -200h100l-150 -200l-150 200h100c0 165 135 300 300 300zM650 600l150 -200h-100c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-100z" />
- <glyph glyph-name="8d" unicode=""
-d="M100 800h600v-300h100l-150 -250l-150 250h100v200h-400v-100h-100v200zM150 550l150 -250h-100v-200h400v100h100v-200h-600v300h-100z" />
- <glyph glyph-name="8e" unicode=""
-d="M600 700l200 -150l-200 -150v100h-500v-100h-100v100c0 55 45 100 100 100h500v100zM200 300v-100h500v100h100v-100c0 -55 -45 -100 -100 -100h-500v-100l-200 150z" />
- <glyph glyph-name="8f" unicode="" horiz-adv-x="900"
-d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c5 -3 12 -8 16 -12l100 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 100c-4 3 -9 9 -12 13c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 200
-c142 0 250 108 250 250c0 139 -111 250 -250 250s-250 -111 -250 -250s111 -250 250 -250z" />
- <glyph glyph-name="90" unicode="" horiz-adv-x="600"
-d="M300 800c166 0 300 -134 300 -300c0 -200 -300 -500 -300 -500s-300 300 -300 500c0 166 134 300 300 300zM300 700c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200z" />
- <glyph glyph-name="91" unicode="" horiz-adv-x="900"
-d="M0 800h800v-541c1 -3 1 -8 1 -11s0 -7 -1 -10v-238h-800v800zM495 250c0 26 22 50 50 50h5h150v400h-600v-600h600v100h-150h-5c-28 0 -50 22 -50 50zM350 600c83 0 150 -67 150 -150c0 -100 -150 -250 -150 -250s-150 150 -150 250c0 83 67 150 150 150zM350 500
-c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
- <glyph glyph-name="92" unicode="" horiz-adv-x="600"
-d="M0 700h200v-600h-200v600zM400 700h200v-600h-200v600z" />
- <glyph glyph-name="93" unicode="" horiz-adv-x="600"
-d="M0 700l600 -300l-600 -300v600z" />
- <glyph glyph-name="94" unicode="" horiz-adv-x="600"
-d="M300 700c166 0 300 -134 300 -300s-134 -300 -300 -300s-300 134 -300 300s134 300 300 300z" />
- <glyph glyph-name="95" unicode=""
-d="M400 700v-600l-400 300zM400 400l400 300v-600z" />
- <glyph glyph-name="96" unicode=""
-d="M0 700l400 -300l-400 -300v600zM400 100v600l400 -300z" />
- <glyph glyph-name="97" unicode=""
-d="M0 700h200v-600h-200v600zM200 400l500 300v-600z" />
- <glyph glyph-name="98" unicode=""
-d="M0 700l500 -300l-500 -300v600zM500 100v600h200v-600h-200z" />
- <glyph glyph-name="99" unicode="" horiz-adv-x="600"
-d="M0 700h600v-600h-600v600z" />
- <glyph glyph-name="9a" unicode=""
-d="M200 800h400v-200h200v-400h-200v-200h-400v200h-200v400h200v200z" />
- <glyph glyph-name="9b" unicode=""
-d="M0 700h800v-100h-800v100zM0 403h800v-100h-800v100zM0 103h800v-100h-800v100z" />
- <glyph glyph-name="9c" unicode="" horiz-adv-x="600"
-d="M278 700c7 2 13 4 22 4c55 0 100 -45 100 -100v-4v-200c0 -55 -45 -100 -100 -100s-100 45 -100 100v200v2c0 44 35 88 78 98zM34 500h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-50c0 -111 89 -200 200 -200s200 89 200 200v50c0 28 22 50 50 50s50 -22 50 -50v-50
-c0 -148 -109 -270 -250 -294v-106h50c55 0 100 -45 100 -100h-400c0 55 45 100 100 100h50v106c-141 24 -250 146 -250 294v50v2c0 20 15 42 34 48z" />
- <glyph glyph-name="9d" unicode=""
-d="M0 500h800v-200h-800v200z" />
- <glyph glyph-name="9e" unicode=""
-d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-500c0 -28 -22 -50 -50 -50h-250v-100h100c55 0 100 -45 100 -100h-600c0 55 45 100 100 100h100v100h-250c-28 0 -50 22 -50 50v500v2c0 20 15 42 34 48zM100 600v-400h600v400h-600z" />
- <glyph glyph-name="9f" unicode=""
-d="M272 700c-14 -40 -22 -83 -22 -128c0 -221 179 -400 400 -400c45 0 88 8 128 22c-53 -158 -202 -272 -378 -272c-221 0 -400 179 -400 400c0 176 114 325 272 378z" />
- <glyph glyph-name="a0" unicode=""
-d="M350 700l150 -150h-100v-150h150v100l150 -150l-150 -150v100h-150v-150h100l-150 -150l-150 150h100v150h-150v-100l-150 150l150 150v-100h150v150h-100z" />
- <glyph glyph-name="a1" unicode=""
-d="M800 800v-550c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v206c-201 -6 -327 -27 -400 -50v-397c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v409s100 100 600 100z" />
- <glyph glyph-name="a2" unicode="" horiz-adv-x="700"
-d="M499 700c51 0 102 -20 141 -59c78 -78 78 -203 0 -281l-250 -244c-48 -48 -127 -48 -175 0s-48 127 0 175l96 97l69 -69l-90 -94l-7 -3c-10 -10 -10 -28 0 -38s28 -10 38 0l250 247c37 40 39 102 0 141s-104 40 -144 0l-278 -275c-66 -69 -68 -179 0 -247
-c69 -69 181 -69 250 0l9 12l116 113l69 -69l-125 -125c-107 -107 -281 -107 -388 0s-107 281 0 388l278 272c39 39 90 59 141 59z" />
- <glyph glyph-name="a3" unicode=""
-d="M600 800l200 -200l-100 -100l-200 200zM400 600l200 -200l-400 -400h-200v200z" />
- <glyph glyph-name="a4" unicode=""
-d="M550 800c83 0 150 -90 150 -200s-67 -200 -150 -200c-22 0 -40 8 -59 19c6 26 9 52 9 81c0 84 -27 158 -72 212c27 52 71 88 122 88zM250 700c83 0 150 -90 150 -200s-67 -200 -150 -200s-150 90 -150 200s67 200 150 200zM725 384c44 -22 75 -66 75 -118v-166h-200v66
-c0 50 -17 96 -44 134c66 2 126 33 169 84zM75 284c45 -53 106 -84 175 -84s130 31 175 84c44 -22 75 -66 75 -118v-166h-500v166c0 52 31 96 75 118z" />
- <glyph glyph-name="a5" unicode=""
-d="M400 800c110 0 200 -112 200 -250s-90 -250 -200 -250s-200 112 -200 250s90 250 200 250zM191 300c54 -61 128 -100 209 -100s155 39 209 100c106 -5 191 -92 191 -200v-100h-800v100c0 108 85 195 191 200z" />
- <glyph glyph-name="a6" unicode="" horiz-adv-x="600"
-d="M19 800h462c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-462c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 700v-500h300v500h-300zM250 150c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
- <glyph glyph-name="a7" unicode=""
-d="M350 800c17 0 34 -1 50 -3v-397l-297 297c63 64 150 103 247 103zM500 694c169 -25 300 -168 300 -344c0 -193 -157 -350 -350 -350c-85 0 -161 31 -222 81l272 272v341zM91 562l237 -234l-212 -212c-70 55 -116 138 -116 234c0 84 35 158 91 212z" />
- <glyph glyph-name="a8" unicode=""
-d="M92 650c0 23 20 50 46 50h3h4h5h400c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-200h100c55 0 100 -45 100 -100h-300v-300l-56 -100l-44 100v300h-300c0 55 45 100 100 100h100v200h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
- <glyph glyph-name="a9" unicode=""
-d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 600v-400l300 200z" />
- <glyph glyph-name="aa" unicode=""
-d="M300 800h200v-300h300v-200h-300v-300h-200v300h-300v200h300v300z" />
- <glyph glyph-name="ab" unicode=""
-d="M300 800h100v-400h-100v400zM172 656l62 -78l-40 -31c-58 -46 -94 -117 -94 -197c0 -139 111 -250 250 -250s250 111 250 250c0 80 -39 151 -97 197l-37 31l62 78l38 -31c82 -64 134 -164 134 -275c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 111 53 211 134 275z
-" />
- <glyph glyph-name="ac" unicode=""
-d="M200 800h400v-200h-400v200zM9 500h782c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-91v200h-600v-200h-91c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM200 300h400v-300h-400v300z" />
- <glyph glyph-name="ad" unicode=""
-d="M0 700h100v-700h-100v700zM700 700h100v-700h-100v700zM200 600h200v-100h-200v100zM300 400h200v-100h-200v100zM400 200h200v-100h-200v100z" />
- <glyph glyph-name="ae" unicode=""
-d="M325 700c42 -141 87 -280 131 -419c29 74 59 148 88 222c30 -57 58 -114 87 -172h169v-100h-231l-13 28c-37 -92 -74 -184 -112 -275c-38 129 -79 257 -119 385c-42 -133 -83 -267 -125 -400c-28 88 -56 175 -84 262h-116v100h188l9 -34l3 -6c42 137 83 273 125 409z" />
- <glyph glyph-name="af" unicode=""
-d="M200 600c0 57 43 100 100 100s100 -43 100 -100c0 -28 -18 -48 -28 -72c-3 -6 -3 -16 -3 -28h231v-231c12 0 22 0 28 3c24 10 44 28 72 28c57 0 100 -43 100 -100s-43 -100 -100 -100c-28 0 -48 18 -72 28c-6 3 -16 3 -28 3v-231h-231c0 12 0 22 3 28c10 24 28 44 28 72
-c0 57 -43 100 -100 100s-100 -43 -100 -100c0 -28 18 -48 28 -72c3 -6 3 -16 3 -28h-231v600h231c0 12 0 22 -3 28c-10 24 -28 44 -28 72z" />
- <glyph glyph-name="b0" unicode="" horiz-adv-x="500"
-d="M247 700c84 0 148 -20 191 -59s59 -93 59 -141c0 -117 -69 -181 -119 -225s-81 -67 -81 -150v-25h-100v25c0 117 65 181 115 225s85 67 85 150c0 25 -8 48 -28 66s-56 34 -122 34s-97 -18 -116 -37s-27 -43 -31 -69l-100 12c5 38 19 88 59 128s103 66 188 66zM197 0h100
-v-100h-100v100z" />
- <glyph glyph-name="b1" unicode=""
-d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -69 -48 -127 -112 -144c-22 55 -75 94 -138 94c-20 0 -39 -5 -56 -12c-17 64 -75 112 -144 112s-127 -48 -144 -112c-17 7 -36 12 -56 12c-37 0 -71 -12 -97 -34c-33 36 -53 82 -53 134
+c8 13 27 24 42 24c28 0 50 -22 50 -50c0 -15 -10 -35 -23 -43c-22 -22 -48 -37 -75 -47c-70 -25 -151 -9 -207 47c-78 78 -78 203 0 281l150 150c60 60 128 78 178 76z"/>
+ <glyph glyph-name="87" unicode=""
+ d="M0 700h300v-300h-300v300zM400 700h400v-100h-400v100zM400 500h300v-100h-300v100zM0 300h300v-300h-300v300zM400 300h400v-100h-400v100zM400 100h300v-100h-300v100z"/>
+ <glyph glyph-name="88" unicode=""
+ d="M50 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 700h600v-100h-600v100zM50 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 500h600v-100h-600v100zM50 300c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
+s22 50 50 50zM200 300h600v-100h-600v100zM50 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 100h600v-100h-600v100z"/>
+ <glyph glyph-name="89" unicode=""
+ d="M800 800l-400 -800l-100 300l-300 100z"/>
+ <glyph glyph-name="8a" unicode="" horiz-adv-x="600"
+ d="M300 700c110 0 200 -90 200 -200v-100h100v-400h-600v400h100v100c0 110 90 200 200 200zM300 600c-56 0 -100 -44 -100 -100v-100h200v100c0 56 -44 100 -100 100z"/>
+ <glyph glyph-name="8b" unicode="" horiz-adv-x="600"
+ d="M300 800c110 0 200 -90 200 -200v-200h100v-400h-600v400h400v200c0 56 -44 100 -100 100s-100 -44 -100 -100h-100c0 110 90 200 200 200z"/>
+ <glyph glyph-name="8c" unicode=""
+ d="M400 700v-100c-111 0 -200 -89 -200 -200h100l-150 -200l-150 200h100c0 165 135 300 300 300zM650 600l150 -200h-100c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-100z"/>
+ <glyph glyph-name="8d" unicode=""
+ d="M100 800h600v-300h100l-150 -250l-150 250h100v200h-400v-100h-100v200zM150 550l150 -250h-100v-200h400v100h100v-200h-600v300h-100z"/>
+ <glyph glyph-name="8e" unicode=""
+ d="M600 700l200 -150l-200 -150v100h-500v-100h-100v100c0 55 45 100 100 100h500v100zM200 300v-100h500v100h100v-100c0 -55 -45 -100 -100 -100h-500v-100l-200 150z"/>
+ <glyph glyph-name="8f" unicode="" horiz-adv-x="900"
+ d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c5 -3 12 -8 16 -12l100 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 100c-4 3 -9 9 -12 13c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 200
+c142 0 250 108 250 250c0 139 -111 250 -250 250s-250 -111 -250 -250s111 -250 250 -250z"/>
+ <glyph glyph-name="90" unicode="" horiz-adv-x="600"
+ d="M300 800c166 0 300 -134 300 -300c0 -200 -300 -500 -300 -500s-300 300 -300 500c0 166 134 300 300 300zM300 700c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200z"/>
+ <glyph glyph-name="91" unicode="" horiz-adv-x="900"
+ d="M0 800h800v-541c1 -3 1 -8 1 -11s0 -7 -1 -10v-238h-800v800zM495 250c0 26 22 50 50 50h5h150v400h-600v-600h600v100h-150h-5c-28 0 -50 22 -50 50zM350 600c83 0 150 -67 150 -150c0 -100 -150 -250 -150 -250s-150 150 -150 250c0 83 67 150 150 150zM350 500
+c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z"/>
+ <glyph glyph-name="92" unicode="" horiz-adv-x="600"
+ d="M0 700h200v-600h-200v600zM400 700h200v-600h-200v600z"/>
+ <glyph glyph-name="93" unicode="" horiz-adv-x="600"
+ d="M0 700l600 -300l-600 -300v600z"/>
+ <glyph glyph-name="94" unicode="" horiz-adv-x="600"
+ d="M300 700c166 0 300 -134 300 -300s-134 -300 -300 -300s-300 134 -300 300s134 300 300 300z"/>
+ <glyph glyph-name="95" unicode=""
+ d="M400 700v-600l-400 300zM400 400l400 300v-600z"/>
+ <glyph glyph-name="96" unicode=""
+ d="M0 700l400 -300l-400 -300v600zM400 100v600l400 -300z"/>
+ <glyph glyph-name="97" unicode=""
+ d="M0 700h200v-600h-200v600zM200 400l500 300v-600z"/>
+ <glyph glyph-name="98" unicode=""
+ d="M0 700l500 -300l-500 -300v600zM500 100v600h200v-600h-200z"/>
+ <glyph glyph-name="99" unicode="" horiz-adv-x="600"
+ d="M0 700h600v-600h-600v600z"/>
+ <glyph glyph-name="9a" unicode=""
+ d="M200 800h400v-200h200v-400h-200v-200h-400v200h-200v400h200v200z"/>
+ <glyph glyph-name="9b" unicode=""
+ d="M0 700h800v-100h-800v100zM0 403h800v-100h-800v100zM0 103h800v-100h-800v100z"/>
+ <glyph glyph-name="9c" unicode="" horiz-adv-x="600"
+ d="M278 700c7 2 13 4 22 4c55 0 100 -45 100 -100v-4v-200c0 -55 -45 -100 -100 -100s-100 45 -100 100v200v2c0 44 35 88 78 98zM34 500h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-50c0 -111 89 -200 200 -200s200 89 200 200v50c0 28 22 50 50 50s50 -22 50 -50v-50
+c0 -148 -109 -270 -250 -294v-106h50c55 0 100 -45 100 -100h-400c0 55 45 100 100 100h50v106c-141 24 -250 146 -250 294v50v2c0 20 15 42 34 48z"/>
+ <glyph glyph-name="9d" unicode=""
+ d="M0 500h800v-200h-800v200z"/>
+ <glyph glyph-name="9e" unicode=""
+ d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-500c0 -28 -22 -50 -50 -50h-250v-100h100c55 0 100 -45 100 -100h-600c0 55 45 100 100 100h100v100h-250c-28 0 -50 22 -50 50v500v2c0 20 15 42 34 48zM100 600v-400h600v400h-600z"/>
+ <glyph glyph-name="9f" unicode=""
+ d="M272 700c-14 -40 -22 -83 -22 -128c0 -221 179 -400 400 -400c45 0 88 8 128 22c-53 -158 -202 -272 -378 -272c-221 0 -400 179 -400 400c0 176 114 325 272 378z"/>
+ <glyph glyph-name="a0" unicode=""
+ d="M350 700l150 -150h-100v-150h150v100l150 -150l-150 -150v100h-150v-150h100l-150 -150l-150 150h100v150h-150v-100l-150 150l150 150v-100h150v150h-100z"/>
+ <glyph glyph-name="a1" unicode=""
+ d="M800 800v-550c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v206c-201 -6 -327 -27 -400 -50v-397c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v409s100 100 600 100z"/>
+ <glyph glyph-name="a2" unicode="" horiz-adv-x="700"
+ d="M499 700c51 0 102 -20 141 -59c78 -78 78 -203 0 -281l-250 -244c-48 -48 -127 -48 -175 0s-48 127 0 175l96 97l69 -69l-90 -94l-7 -3c-10 -10 -10 -28 0 -38s28 -10 38 0l250 247c37 40 39 102 0 141s-104 40 -144 0l-278 -275c-66 -69 -68 -179 0 -247
+c69 -69 181 -69 250 0l9 12l116 113l69 -69l-125 -125c-107 -107 -281 -107 -388 0s-107 281 0 388l278 272c39 39 90 59 141 59z"/>
+ <glyph glyph-name="a3" unicode=""
+ d="M600 800l200 -200l-100 -100l-200 200zM400 600l200 -200l-400 -400h-200v200z"/>
+ <glyph glyph-name="a4" unicode=""
+ d="M550 800c83 0 150 -90 150 -200s-67 -200 -150 -200c-22 0 -40 8 -59 19c6 26 9 52 9 81c0 84 -27 158 -72 212c27 52 71 88 122 88zM250 700c83 0 150 -90 150 -200s-67 -200 -150 -200s-150 90 -150 200s67 200 150 200zM725 384c44 -22 75 -66 75 -118v-166h-200v66
+c0 50 -17 96 -44 134c66 2 126 33 169 84zM75 284c45 -53 106 -84 175 -84s130 31 175 84c44 -22 75 -66 75 -118v-166h-500v166c0 52 31 96 75 118z"/>
+ <glyph glyph-name="a5" unicode=""
+ d="M400 800c110 0 200 -112 200 -250s-90 -250 -200 -250s-200 112 -200 250s90 250 200 250zM191 300c54 -61 128 -100 209 -100s155 39 209 100c106 -5 191 -92 191 -200v-100h-800v100c0 108 85 195 191 200z"/>
+ <glyph glyph-name="a6" unicode="" horiz-adv-x="600"
+ d="M19 800h462c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-462c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 700v-500h300v500h-300zM250 150c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z"/>
+ <glyph glyph-name="a7" unicode=""
+ d="M350 800c17 0 34 -1 50 -3v-397l-297 297c63 64 150 103 247 103zM500 694c169 -25 300 -168 300 -344c0 -193 -157 -350 -350 -350c-85 0 -161 31 -222 81l272 272v341zM91 562l237 -234l-212 -212c-70 55 -116 138 -116 234c0 84 35 158 91 212z"/>
+ <glyph glyph-name="a8" unicode=""
+ d="M92 650c0 23 20 50 46 50h3h4h5h400c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-200h100c55 0 100 -45 100 -100h-300v-300l-56 -100l-44 100v300h-300c0 55 45 100 100 100h100v200h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z"/>
+ <glyph glyph-name="a9" unicode=""
+ d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 600v-400l300 200z"/>
+ <glyph glyph-name="aa" unicode=""
+ d="M300 800h200v-300h300v-200h-300v-300h-200v300h-300v200h300v300z"/>
+ <glyph glyph-name="ab" unicode=""
+ d="M300 800h100v-400h-100v400zM172 656l62 -78l-40 -31c-58 -46 -94 -117 -94 -197c0 -139 111 -250 250 -250s250 111 250 250c0 80 -39 151 -97 197l-37 31l62 78l38 -31c82 -64 134 -164 134 -275c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 111 53 211 134 275z
+"/>
+ <glyph glyph-name="ac" unicode=""
+ d="M200 800h400v-200h-400v200zM9 500h782c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-91v200h-600v-200h-91c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM200 300h400v-300h-400v300z"/>
+ <glyph glyph-name="ad" unicode=""
+ d="M0 700h100v-700h-100v700zM700 700h100v-700h-100v700zM200 600h200v-100h-200v100zM300 400h200v-100h-200v100zM400 200h200v-100h-200v100z"/>
+ <glyph glyph-name="ae" unicode=""
+ d="M325 700c42 -141 87 -280 131 -419c29 74 59 148 88 222c30 -57 58 -114 87 -172h169v-100h-231l-13 28c-37 -92 -74 -184 -112 -275c-38 129 -79 257 -119 385c-42 -133 -83 -267 -125 -400c-28 88 -56 175 -84 262h-116v100h188l9 -34l3 -6c42 137 83 273 125 409z"/>
+ <glyph glyph-name="af" unicode=""
+ d="M200 600c0 57 43 100 100 100s100 -43 100 -100c0 -28 -18 -48 -28 -72c-3 -6 -3 -16 -3 -28h231v-231c12 0 22 0 28 3c24 10 44 28 72 28c57 0 100 -43 100 -100s-43 -100 -100 -100c-28 0 -48 18 -72 28c-6 3 -16 3 -28 3v-231h-231c0 12 0 22 3 28c10 24 28 44 28 72
+c0 57 -43 100 -100 100s-100 -43 -100 -100c0 -28 18 -48 28 -72c3 -6 3 -16 3 -28h-231v600h231c0 12 0 22 -3 28c-10 24 -28 44 -28 72z"/>
+ <glyph glyph-name="b0" unicode="" horiz-adv-x="500"
+ d="M247 700c84 0 148 -20 191 -59s59 -93 59 -141c0 -117 -69 -181 -119 -225s-81 -67 -81 -150v-25h-100v25c0 117 65 181 115 225s85 67 85 150c0 25 -8 48 -28 66s-56 34 -122 34s-97 -18 -116 -37s-27 -43 -31 -69l-100 12c5 38 19 88 59 128s103 66 188 66zM197 0h100
+v-100h-100v100z"/>
+ <glyph glyph-name="b1" unicode=""
+ d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -69 -48 -127 -112 -144c-22 55 -75 94 -138 94c-20 0 -39 -5 -56 -12c-17 64 -75 112 -144 112s-127 -48 -144 -112c-17 7 -36 12 -56 12c-37 0 -71 -12 -97 -34c-33 36 -53 82 -53 134
c0 110 90 200 200 200c23 114 129 200 250 200zM334 300h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-200c0 -28 -22 -50 -50 -50s-50 22 -50 50v200v2c0 20 15 42 34 48zM134 200h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2
-c0 20 15 42 34 48zM534 200h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2c0 20 15 42 34 48z" />
- <glyph glyph-name="b2" unicode=""
-d="M600 800l200 -150l-200 -150v100h-50l-153 -191l175 -206l6 -3h22v100l200 -150l-200 -150v100h-25c-35 0 -56 12 -78 38l-166 190l-153 -190c-22 -27 -43 -38 -78 -38h-100v100h100l166 206l-163 191l-3 3h-100v100h100c34 0 56 -12 78 -38l153 -178l141 178
-c22 27 43 38 78 38h50v100z" />
- <glyph glyph-name="b3" unicode=""
-d="M400 800c110 0 209 -47 281 -119l119 119v-300h-300l109 109c-54 55 -126 91 -209 91c-166 0 -300 -134 -300 -300s134 -300 300 -300c83 0 158 34 212 88l72 -72c-72 -72 -174 -116 -284 -116c-220 0 -400 180 -400 400s180 400 400 400z" />
- <glyph glyph-name="b4" unicode=""
-d="M400 800h400v-400l-166 166l-400 -400l166 -166h-400v400l166 -166l400 400z" />
- <glyph glyph-name="b5" unicode="" horiz-adv-x="600"
-d="M250 800l250 -300h-200v-200h200l-250 -300l-250 300h200v200h-200z" />
- <glyph glyph-name="b6" unicode=""
-d="M300 600v-200h200v200l300 -250l-300 -250v200h-200v-200l-300 250z" />
- <glyph glyph-name="b7" unicode=""
-d="M0 800c441 0 800 -359 800 -800h-200c0 333 -267 600 -600 600v200zM0 500c275 0 500 -225 500 -500h-200c0 167 -133 300 -300 300v200zM0 200c110 0 200 -90 200 -200h-200v200z" />
- <glyph glyph-name="b8" unicode=""
-d="M100 800c386 0 700 -314 700 -700h-100c0 332 -268 600 -600 600v100zM100 600c276 0 500 -224 500 -500h-100c0 222 -178 400 -400 400v100zM100 400c165 0 300 -135 300 -300h-100c0 111 -89 200 -200 200v100zM100 200c55 0 100 -45 100 -100s-45 -100 -100 -100
-s-100 45 -100 100s45 100 100 100z" />
- <glyph glyph-name="b9" unicode=""
-d="M300 800h400c55 0 100 -45 100 -100v-200h-400v150c0 28 -22 50 -50 50s-50 -22 -50 -50v-250h400v-300c0 -55 -45 -100 -100 -100h-500c-55 0 -100 45 -100 100v200h100v-150c0 -28 22 -50 50 -50s50 22 50 50v550c0 55 45 100 100 100z" />
- <glyph glyph-name="ba" unicode=""
-d="M75 700h225v-100h-200v-500h400v100h100v-125c0 -41 -34 -75 -75 -75h-450c-41 0 -75 34 -75 75v550c0 41 34 75 75 75zM600 700l200 -200l-200 -200v100h-200c-94 0 -173 -65 -194 -153c23 199 189 353 394 353v100z" />
- <glyph glyph-name="bb" unicode=""
-d="M500 700l300 -284l-300 -316v200h-100c-200 0 -348 -102 -400 -300c0 295 100 500 500 500v200z" />
- <glyph glyph-name="bc" unicode=""
-d="M381 791l19 9l19 -9c127 -53 253 -108 381 -160v-31c0 -166 -67 -313 -147 -419c-40 -53 -83 -97 -125 -128s-82 -53 -128 -53s-86 22 -128 53s-85 75 -125 128c-80 107 -147 253 -147 419v31c128 52 254 107 381 160zM400 100v591l-294 -122c8 -126 58 -243 122 -328
-c35 -46 73 -86 106 -110s62 -31 66 -31z" />
- <glyph glyph-name="bd" unicode=""
-d="M600 800h100v-800h-100v800zM400 700h100v-700h-100v700zM200 500h100v-500h-100v500zM0 300h100v-300h-100v300z" />
- <glyph glyph-name="be" unicode=""
-d="M300 800h100v-200h200l100 -100l-100 -100h-200v-400h-100v500h-200l-100 100l100 100h200v100z" />
- <glyph glyph-name="bf" unicode=""
-d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h200v-100h-200v100zM400 600h300v-100h-300v100zM400 400h400v-100h-400v100z" />
- <glyph glyph-name="c0" unicode=""
-d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h400v-100h-400v100zM400 600h300v-100h-300v100zM400 400h200v-100h-200v100z" />
- <glyph glyph-name="c1" unicode=""
-d="M75 700h650c41 0 75 -34 75 -75v-550c0 -41 -34 -75 -75 -75h-650c-41 0 -75 34 -75 75v550c0 41 34 75 75 75zM100 600v-100h100v100h-100zM300 600v-100h400v100h-400zM100 400v-100h100v100h-100zM300 400v-100h400v100h-400zM100 200v-100h100v100h-100zM300 200
-v-100h400v100h-400z" />
- <glyph glyph-name="c2" unicode=""
-d="M400 800l100 -300h300l-250 -200l100 -300l-250 200l-250 -200l100 300l-250 200h300z" />
- <glyph glyph-name="c3" unicode=""
-d="M400 800c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM650 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 600c110 0 200 -90 200 -200
+c0 20 15 42 34 48zM534 200h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2c0 20 15 42 34 48z"/>
+ <glyph glyph-name="b2" unicode=""
+ d="M600 800l200 -150l-200 -150v100h-50l-153 -191l175 -206l6 -3h22v100l200 -150l-200 -150v100h-25c-35 0 -56 12 -78 38l-166 190l-153 -190c-22 -27 -43 -38 -78 -38h-100v100h100l166 206l-163 191l-3 3h-100v100h100c34 0 56 -12 78 -38l153 -178l141 178
+c22 27 43 38 78 38h50v100z"/>
+ <glyph glyph-name="b3" unicode=""
+ d="M400 800c110 0 209 -47 281 -119l119 119v-300h-300l109 109c-54 55 -126 91 -209 91c-166 0 -300 -134 -300 -300s134 -300 300 -300c83 0 158 34 212 88l72 -72c-72 -72 -174 -116 -284 -116c-220 0 -400 180 -400 400s180 400 400 400z"/>
+ <glyph glyph-name="b4" unicode=""
+ d="M400 800h400v-400l-166 166l-400 -400l166 -166h-400v400l166 -166l400 400z"/>
+ <glyph glyph-name="b5" unicode="" horiz-adv-x="600"
+ d="M250 800l250 -300h-200v-200h200l-250 -300l-250 300h200v200h-200z"/>
+ <glyph glyph-name="b6" unicode=""
+ d="M300 600v-200h200v200l300 -250l-300 -250v200h-200v-200l-300 250z"/>
+ <glyph glyph-name="b7" unicode=""
+ d="M0 800c441 0 800 -359 800 -800h-200c0 333 -267 600 -600 600v200zM0 500c275 0 500 -225 500 -500h-200c0 167 -133 300 -300 300v200zM0 200c110 0 200 -90 200 -200h-200v200z"/>
+ <glyph glyph-name="b8" unicode=""
+ d="M100 800c386 0 700 -314 700 -700h-100c0 332 -268 600 -600 600v100zM100 600c276 0 500 -224 500 -500h-100c0 222 -178 400 -400 400v100zM100 400c165 0 300 -135 300 -300h-100c0 111 -89 200 -200 200v100zM100 200c55 0 100 -45 100 -100s-45 -100 -100 -100
+s-100 45 -100 100s45 100 100 100z"/>
+ <glyph glyph-name="b9" unicode=""
+ d="M300 800h400c55 0 100 -45 100 -100v-200h-400v150c0 28 -22 50 -50 50s-50 -22 -50 -50v-250h400v-300c0 -55 -45 -100 -100 -100h-500c-55 0 -100 45 -100 100v200h100v-150c0 -28 22 -50 50 -50s50 22 50 50v550c0 55 45 100 100 100z"/>
+ <glyph glyph-name="ba" unicode=""
+ d="M75 700h225v-100h-200v-500h400v100h100v-125c0 -41 -34 -75 -75 -75h-450c-41 0 -75 34 -75 75v550c0 41 34 75 75 75zM600 700l200 -200l-200 -200v100h-200c-94 0 -173 -65 -194 -153c23 199 189 353 394 353v100z"/>
+ <glyph glyph-name="bb" unicode=""
+ d="M500 700l300 -284l-300 -316v200h-100c-200 0 -348 -102 -400 -300c0 295 100 500 500 500v200z"/>
+ <glyph glyph-name="bc" unicode=""
+ d="M381 791l19 9l19 -9c127 -53 253 -108 381 -160v-31c0 -166 -67 -313 -147 -419c-40 -53 -83 -97 -125 -128s-82 -53 -128 -53s-86 22 -128 53s-85 75 -125 128c-80 107 -147 253 -147 419v31c128 52 254 107 381 160zM400 100v591l-294 -122c8 -126 58 -243 122 -328
+c35 -46 73 -86 106 -110s62 -31 66 -31z"/>
+ <glyph glyph-name="bd" unicode=""
+ d="M600 800h100v-800h-100v800zM400 700h100v-700h-100v700zM200 500h100v-500h-100v500zM0 300h100v-300h-100v300z"/>
+ <glyph glyph-name="be" unicode=""
+ d="M300 800h100v-200h200l100 -100l-100 -100h-200v-400h-100v500h-200l-100 100l100 100h200v100z"/>
+ <glyph glyph-name="bf" unicode=""
+ d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h200v-100h-200v100zM400 600h300v-100h-300v100zM400 400h400v-100h-400v100z"/>
+ <glyph glyph-name="c0" unicode=""
+ d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h400v-100h-400v100zM400 600h300v-100h-300v100zM400 400h200v-100h-200v100z"/>
+ <glyph glyph-name="c1" unicode=""
+ d="M75 700h650c41 0 75 -34 75 -75v-550c0 -41 -34 -75 -75 -75h-650c-41 0 -75 34 -75 75v550c0 41 34 75 75 75zM100 600v-100h100v100h-100zM300 600v-100h400v100h-400zM100 400v-100h100v100h-100zM300 400v-100h400v100h-400zM100 200v-100h100v100h-100zM300 200
+v-100h400v100h-400z"/>
+ <glyph glyph-name="c2" unicode=""
+ d="M400 800l100 -300h300l-250 -200l100 -300l-250 200l-250 -200l100 300l-250 200h300z"/>
+ <glyph glyph-name="c3" unicode=""
+ d="M400 800c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM650 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 600c110 0 200 -90 200 -200
s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM50 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM750 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
-s22 50 50 50zM650 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
- <glyph glyph-name="c4" unicode=""
-d="M34 800h632c18 0 34 -16 34 -34v-732c0 -18 -16 -34 -34 -34h-632c-18 0 -34 16 -34 34v732c0 18 16 34 34 34zM100 700v-500h500v500h-500zM350 150c-38 0 -63 -42 -44 -75s69 -33 88 0s-6 75 -44 75z" />
- <glyph glyph-name="c5" unicode=""
-d="M0 800h300l500 -500l-300 -300l-500 500v300zM200 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
- <glyph glyph-name="c6" unicode=""
-d="M0 600h200l300 -300l-200 -200l-300 300v200zM340 600h160l300 -300l-200 -200l-78 78l119 122zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
- <glyph glyph-name="c7" unicode=""
-d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200
-s90 200 200 200zM400 500c-56 0 -100 -44 -100 -100s44 -100 100 -100s100 44 100 100s-44 100 -100 100z" />
- <glyph glyph-name="c8" unicode=""
-d="M0 700h559l-100 -100h-359v-500h500v159l100 100v-359h-700v700zM700 700l100 -100l-400 -400l-200 200l100 100l100 -100z" />
- <glyph glyph-name="c9" unicode=""
-d="M9 800h782c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM150 722l-72 -72l100 -100l-100 -100l72 -72l172 172zM400 500v-100h300v100h-300z" />
- <glyph glyph-name="ca" unicode=""
-d="M0 800h800v-200h-50c0 55 -45 100 -100 100h-150v-550c0 -28 22 -50 50 -50h50v-100h-400v100h50c28 0 50 22 50 50v550h-150c-55 0 -100 -45 -100 -100h-50v200z" />
- <glyph glyph-name="cb" unicode=""
-d="M0 700h100v-400h-100v400zM200 700h350c21 0 39 -13 47 -31c0 0 103 -291 103 -319s-22 -50 -50 -50h-150c-28 0 -50 -25 -50 -50s39 -158 47 -184s-5 -55 -31 -63s-52 5 -66 31s-109 219 -128 238s-44 28 -72 28v400z" />
- <glyph glyph-name="cc" unicode=""
-d="M400 666c10 19 28 32 47 34l19 -3c26 -8 39 -37 31 -63s-47 -159 -47 -184s22 -50 50 -50h150c28 0 50 -22 50 -50s-103 -319 -103 -319c-8 -18 -26 -31 -47 -31h-350v400c28 0 53 9 72 28s114 212 128 238zM0 400h100v-400h-100v400z" />
- <glyph glyph-name="cd" unicode=""
-d="M200 700h300v-100h-100v-6c25 -4 50 -8 72 -16l-34 -94c-28 11 -58 16 -88 16c-139 0 -250 -111 -250 -250s111 -250 250 -250s250 111 250 250c0 31 -5 60 -16 88l91 37c14 -38 25 -81 25 -125c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 176 130 323 300 347v3
-h-100v100zM700 584c0 0 -296 -348 -316 -368s-48 -20 -68 0s-20 48 0 68s384 300 384 300z" />
- <glyph glyph-name="ce" unicode=""
-d="M600 700l200 -150l-200 -150v100h-600v100h600v100zM200 300v-100h600v-100h-600v-100l-200 150z" />
- <glyph glyph-name="cf" unicode=""
-d="M300 800h100c55 0 100 -45 100 -100h100c55 0 100 -45 100 -100h-700c0 55 45 100 100 100h100c0 55 45 100 100 100zM100 500h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-481c0 -11 -8 -19 -19 -19h-462
-c-11 0 -19 8 -19 19v481z" />
- <glyph glyph-name="d0" unicode=""
-d="M100 800h200v-400c0 -55 45 -100 100 -100s100 45 100 100v400h100v-400c0 -110 -90 -200 -200 -200h-50c-138 0 -250 90 -250 200v400zM0 100h700v-100h-700v100z" />
- <glyph glyph-name="d1" unicode=""
-d="M9 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM609 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282
-c0 6 3 9 9 9zM0 100h800v-100h-800v100z" />
- <glyph glyph-name="d2" unicode=""
-d="M10 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 4 9 10 9zM610 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 5 9 10 9zM310 600h181c6 0 9 -3 9 -9v-91h-200v91c0 6 4 9 10 9zM0 400h800v-100h-800v100zM0 200h200v-191c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v191zM300 200
-h200v-91c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v91zM600 200h200v-191c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v191z" />
- <glyph glyph-name="d3" unicode=""
-d="M0 700h800v-100h-800v100zM9 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM609 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182
-c-6 0 -9 3 -9 9v482c0 6 3 9 9 9z" />
- <glyph glyph-name="d4" unicode=""
-d="M50 600h500c28 0 50 -22 50 -50v-150l100 100h100v-300h-100l-100 100v-150c0 -28 -22 -50 -50 -50h-500c-28 0 -50 22 -50 50v400c0 28 22 50 50 50z" />
- <glyph glyph-name="d5" unicode=""
-d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 600v100c26 0 52 -4 75 -10c130 -33 225 -150 225 -290s-95 -258 -225 -291h-3c-23 -6 -47 -9 -72 -9v100c17 0 34 2 50 6c86 22 150 100 150 194s-64 172 -150 194c-16 4 -33 6 -50 6zM500 500l25 -3
-c44 -11 75 -51 75 -97s-32 -86 -75 -97l-25 -3v200z" />
- <glyph glyph-name="d6" unicode="" horiz-adv-x="600"
-d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 500l25 -3c44 -11 75 -51 75 -97s-32 -86 -75 -97l-25 -3v200z" />
- <glyph glyph-name="d7" unicode="" horiz-adv-x="400"
-d="M334 800h66v-800h-66l-134 200h-200v400h200z" />
- <glyph glyph-name="d8" unicode=""
-d="M309 800h82c6 0 10 -4 12 -9l294 -682l3 -19v-81c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v81l3 19l294 682c2 5 6 9 12 9zM300 500v-200h100v200h-100zM300 200v-100h100v100h-100z" />
- <glyph glyph-name="d9" unicode=""
-d="M375 800c138 0 269 -39 378 -109l-53 -82c-93 60 -205 91 -325 91c-119 0 -229 -32 -322 -91l-53 82c109 70 237 109 375 109zM375 500c78 0 154 -23 216 -62l-53 -85c-46 30 -104 47 -163 47c-60 0 -112 -17 -159 -47l-54 85c62 40 134 62 213 62zM375 200
-c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
- <glyph glyph-name="da" unicode="" horiz-adv-x="900"
-d="M551 800c16 0 32 0 47 -3l-97 -97v-200h200l97 97c3 -15 3 -31 3 -47c0 -138 -112 -250 -250 -250c-32 0 -62 8 -90 19l-288 -291c-20 -20 -46 -28 -72 -28s-52 8 -72 28c-39 39 -39 105 0 144l291 287c-11 28 -19 59 -19 91c0 138 112 250 250 250zM101 150
-c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
- <glyph glyph-name="db" unicode=""
-d="M141 700c84 -84 169 -167 253 -250c82 83 167 165 247 250l143 -141l-253 -253c84 -82 167 -166 253 -247l-143 -143c-81 86 -165 169 -247 253l-253 -253l-141 143c85 80 167 164 250 247c-83 84 -166 169 -250 253z" />
- <glyph glyph-name="dc" unicode=""
-d="M0 800h100l231 -300h38l231 300h100l-225 -300h225v-100h-300v-100h300v-100h-300v-200h-100v200h-300v100h300v100h-300v100h225z" />
- <glyph glyph-name="dd" unicode="" horiz-adv-x="900"
-d="M350 800c193 0 350 -157 350 -350c0 -61 -17 -119 -44 -169c4 -2 10 -6 13 -9l103 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 103c-3 3 -7 9 -9 13c-50 -28 -108 -44 -169 -44c-193 0 -350 157 -350 350s157 350 350 350zM350 700
-c-139 0 -250 -111 -250 -250s111 -250 250 -250c62 0 119 23 163 60c7 11 19 25 31 31l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM300 600h100v-100h100v-100h-100v-100h-100v100h-100v100h100v100z" />
- <glyph glyph-name="de" unicode="" horiz-adv-x="900"
-d="M350 800c193 0 350 -157 350 -350c0 -61 -17 -119 -44 -169c4 -2 10 -6 13 -9l103 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 103c-3 3 -7 9 -9 13c-50 -28 -108 -44 -169 -44c-193 0 -350 157 -350 350s157 350 350 350zM350 700
-c-139 0 -250 -111 -250 -250s111 -250 250 -250c62 0 119 23 163 60c7 11 19 25 31 31l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM200 500h300v-100h-300v100z" />
- </font>
-</defs></svg>
+s22 50 50 50zM650 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z"/>
+ <glyph glyph-name="c4" unicode=""
+ d="M34 800h632c18 0 34 -16 34 -34v-732c0 -18 -16 -34 -34 -34h-632c-18 0 -34 16 -34 34v732c0 18 16 34 34 34zM100 700v-500h500v500h-500zM350 150c-38 0 -63 -42 -44 -75s69 -33 88 0s-6 75 -44 75z"/>
+ <glyph glyph-name="c5" unicode=""
+ d="M0 800h300l500 -500l-300 -300l-500 500v300zM200 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z"/>
+ <glyph glyph-name="c6" unicode=""
+ d="M0 600h200l300 -300l-200 -200l-300 300v200zM340 600h160l300 -300l-200 -200l-78 78l119 122zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z"/>
+ <glyph glyph-name="c7" unicode=""
+ d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200
+s90 200 200 200zM400 500c-56 0 -100 -44 -100 -100s44 -100 100 -100s100 44 100 100s-44 100 -100 100z"/>
+ <glyph glyph-name="c8" unicode=""
+ d="M0 700h559l-100 -100h-359v-500h500v159l100 100v-359h-700v700zM700 700l100 -100l-400 -400l-200 200l100 100l100 -100z"/>
+ <glyph glyph-name="c9" unicode=""
+ d="M9 800h782c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM150 722l-72 -72l100 -100l-100 -100l72 -72l172 172zM400 500v-100h300v100h-300z"/>
+ <glyph glyph-name="ca" unicode=""
+ d="M0 800h800v-200h-50c0 55 -45 100 -100 100h-150v-550c0 -28 22 -50 50 -50h50v-100h-400v100h50c28 0 50 22 50 50v550h-150c-55 0 -100 -45 -100 -100h-50v200z"/>
+ <glyph glyph-name="cb" unicode=""
+ d="M0 700h100v-400h-100v400zM200 700h350c21 0 39 -13 47 -31c0 0 103 -291 103 -319s-22 -50 -50 -50h-150c-28 0 -50 -25 -50 -50s39 -158 47 -184s-5 -55 -31 -63s-52 5 -66 31s-109 219 -128 238s-44 28 -72 28v400z"/>
+ <glyph glyph-name="cc" unicode=""
+ d="M400 666c10 19 28 32 47 34l19 -3c26 -8 39 -37 31 -63s-47 -159 -47 -184s22 -50 50 -50h150c28 0 50 -22 50 -50s-103 -319 -103 -319c-8 -18 -26 -31 -47 -31h-350v400c28 0 53 9 72 28s114 212 128 238zM0 400h100v-400h-100v400z"/>
+ <glyph glyph-name="cd" unicode=""
+ d="M200 700h300v-100h-100v-6c25 -4 50 -8 72 -16l-34 -94c-28 11 -58 16 -88 16c-139 0 -250 -111 -250 -250s111 -250 250 -250s250 111 250 250c0 31 -5 60 -16 88l91 37c14 -38 25 -81 25 -125c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 176 130 323 300 347v3
+h-100v100zM700 584c0 0 -296 -348 -316 -368s-48 -20 -68 0s-20 48 0 68s384 300 384 300z"/>
+ <glyph glyph-name="ce" unicode=""
+ d="M600 700l200 -150l-200 -150v100h-600v100h600v100zM200 300v-100h600v-100h-600v-100l-200 150z"/>
+ <glyph glyph-name="cf" unicode=""
+ d="M300 800h100c55 0 100 -45 100 -100h100c55 0 100 -45 100 -100h-700c0 55 45 100 100 100h100c0 55 45 100 100 100zM100 500h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-481c0 -11 -8 -19 -19 -19h-462
+c-11 0 -19 8 -19 19v481z"/>
+ <glyph glyph-name="d0" unicode=""
+ d="M100 800h200v-400c0 -55 45 -100 100 -100s100 45 100 100v400h100v-400c0 -110 -90 -200 -200 -200h-50c-138 0 -250 90 -250 200v400zM0 100h700v-100h-700v100z"/>
+ <glyph glyph-name="d1" unicode=""
+ d="M9 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM609 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282
+c0 6 3 9 9 9zM0 100h800v-100h-800v100z"/>
+ <glyph glyph-name="d2" unicode=""
+ d="M10 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 4 9 10 9zM610 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 5 9 10 9zM310 600h181c6 0 9 -3 9 -9v-91h-200v91c0 6 4 9 10 9zM0 400h800v-100h-800v100zM0 200h200v-191c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v191zM300 200
+h200v-91c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v91zM600 200h200v-191c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v191z"/>
+ <glyph glyph-name="d3" unicode=""
+ d="M0 700h800v-100h-800v100zM9 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM609 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182
+c-6 0 -9 3 -9 9v482c0 6 3 9 9 9z"/>
+ <glyph glyph-name="d4" unicode=""
+ d="M50 600h500c28 0 50 -22 50 -50v-150l100 100h100v-300h-100l-100 100v-150c0 -28 -22 -50 -50 -50h-500c-28 0 -50 22 -50 50v400c0 28 22 50 50 50z"/>
+ <glyph glyph-name="d5" unicode=""
+ d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 600v100c26 0 52 -4 75 -10c130 -33 225 -150 225 -290s-95 -258 -225 -291h-3c-23 -6 -47 -9 -72 -9v100c17 0 34 2 50 6c86 22 150 100 150 194s-64 172 -150 194c-16 4 -33 6 -50 6zM500 500l25 -3
+c44 -11 75 -51 75 -97s-32 -86 -75 -97l-25 -3v200z"/>
+ <glyph glyph-name="d6" unicode="" horiz-adv-x="600"
+ d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 500l25 -3c44 -11 75 -51 75 -97s-32 -86 -75 -97l-25 -3v200z"/>
+ <glyph glyph-name="d7" unicode="" horiz-adv-x="400"
+ d="M334 800h66v-800h-66l-134 200h-200v400h200z"/>
+ <glyph glyph-name="d8" unicode=""
+ d="M309 800h82c6 0 10 -4 12 -9l294 -682l3 -19v-81c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v81l3 19l294 682c2 5 6 9 12 9zM300 500v-200h100v200h-100zM300 200v-100h100v100h-100z"/>
+ <glyph glyph-name="d9" unicode=""
+ d="M375 800c138 0 269 -39 378 -109l-53 -82c-93 60 -205 91 -325 91c-119 0 -229 -32 -322 -91l-53 82c109 70 237 109 375 109zM375 500c78 0 154 -23 216 -62l-53 -85c-46 30 -104 47 -163 47c-60 0 -112 -17 -159 -47l-54 85c62 40 134 62 213 62zM375 200
+c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z"/>
+ <glyph glyph-name="da" unicode="" horiz-adv-x="900"
+ d="M551 800c16 0 32 0 47 -3l-97 -97v-200h200l97 97c3 -15 3 -31 3 -47c0 -138 -112 -250 -250 -250c-32 0 -62 8 -90 19l-288 -291c-20 -20 -46 -28 -72 -28s-52 8 -72 28c-39 39 -39 105 0 144l291 287c-11 28 -19 59 -19 91c0 138 112 250 250 250zM101 150
+c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z"/>
+ <glyph glyph-name="db" unicode=""
+ d="M141 700c84 -84 169 -167 253 -250c82 83 167 165 247 250l143 -141l-253 -253c84 -82 167 -166 253 -247l-143 -143c-81 86 -165 169 -247 253l-253 -253l-141 143c85 80 167 164 250 247c-83 84 -166 169 -250 253z"/>
+ <glyph glyph-name="dc" unicode=""
+ d="M0 800h100l231 -300h38l231 300h100l-225 -300h225v-100h-300v-100h300v-100h-300v-200h-100v200h-300v100h300v100h-300v100h225z"/>
+ <glyph glyph-name="dd" unicode="" horiz-adv-x="900"
+ d="M350 800c193 0 350 -157 350 -350c0 -61 -17 -119 -44 -169c4 -2 10 -6 13 -9l103 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 103c-3 3 -7 9 -9 13c-50 -28 -108 -44 -169 -44c-193 0 -350 157 -350 350s157 350 350 350zM350 700
+c-139 0 -250 -111 -250 -250s111 -250 250 -250c62 0 119 23 163 60c7 11 19 25 31 31l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM300 600h100v-100h100v-100h-100v-100h-100v100h-100v100h100v100z"/>
+ <glyph glyph-name="de" unicode="" horiz-adv-x="900"
+ d="M350 800c193 0 350 -157 350 -350c0 -61 -17 -119 -44 -169c4 -2 10 -6 13 -9l103 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 103c-3 3 -7 9 -9 13c-50 -28 -108 -44 -169 -44c-193 0 -350 157 -350 350s157 350 350 350zM350 700
+c-139 0 -250 -111 -250 -250s111 -250 250 -250c62 0 119 23 163 60c7 11 19 25 31 31l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM200 500h300v-100h-300v100z"/>
+ </font>
+ </defs>
+</svg>
diff --git a/MatrixRoomUtils.Web/wwwroot/index.html b/MatrixRoomUtils.Web/wwwroot/index.html
index eeac69b..f268ac4 100644
--- a/MatrixRoomUtils.Web/wwwroot/index.html
+++ b/MatrixRoomUtils.Web/wwwroot/index.html
@@ -3,27 +3,27 @@
<head>
<meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
+ <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<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="favicon.png" rel="icon" type="image/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%"/>
+ <circle cx="50%" cy="50%" r="40%"/>
+ <circle cx="50%" cy="50%" r="40%"/>
</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="reload" href="">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script>
diff --git a/MatrixRoomUtils.sln.DotSettings b/MatrixRoomUtils.sln.DotSettings
new file mode 100644
index 0000000..4ac5b79
--- /dev/null
+++ b/MatrixRoomUtils.sln.DotSettings
@@ -0,0 +1,2 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:String x:Key="/Default/CustomTools/CustomToolsData/@EntryValue"></s:String></wpf:ResourceDictionary>
\ No newline at end of file
|