diff options
75 files changed, 1753 insertions, 2154 deletions
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 |