about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTheArcaneBrony <myrainbowdash949@gmail.com>2023-07-17 00:21:24 +0200
committerTheArcaneBrony <myrainbowdash949@gmail.com>2023-07-17 00:21:24 +0200
commit1beca653b772cf10586c417b2c25df03a67df8a2 (patch)
treed539b5f329cb62f253e1bc8142c3a313719657b0
parentChanges (diff)
downloadMatrixUtils-1beca653b772cf10586c417b2c25df03a67df8a2.tar.xz
Handle external logouts
-rw-r--r--MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs3
-rw-r--r--MatrixRoomUtils.Core/MatrixException.cs4
-rw-r--r--MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs40
-rw-r--r--MatrixRoomUtils.Web/Pages/About.razor4
-rw-r--r--MatrixRoomUtils.Web/Pages/Index.razor18
-rw-r--r--MatrixRoomUtils.Web/Pages/InvalidSession.razor97
-rw-r--r--MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor10
-rw-r--r--MatrixRoomUtils.Web/Shared/InlineUserItem.razor9
-rw-r--r--MatrixRoomUtils.Web/Shared/ModalWindow.razor22
-rw-r--r--MatrixRoomUtils.Web/Shared/ModalWindow.razor.css4
-rw-r--r--MatrixRoomUtils.Web/Shared/RoomList.razor15
-rw-r--r--MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor14
-rw-r--r--MatrixRoomUtils.Web/_Imports.razor3
-rw-r--r--MatrixRoomUtils.Web/wwwroot/css/app.css2
-rw-r--r--MatrixRoomUtils.Web/wwwroot/index.html13
15 files changed, 212 insertions, 46 deletions
diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
index 060867d..695e8e3 100644
--- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
@@ -40,6 +40,7 @@ public class MatrixHttpClient : HttpClient {
             var content = await a.Content.ReadAsStringAsync(cancellationToken);
             if (content.StartsWith('{')) {
                 var ex = JsonSerializer.Deserialize<MatrixException>(content);
+                ex.RawContent = content;
             Console.WriteLine($"Failed to send request: {ex}");
                 if (ex?.RetryAfterMs is not null) {
                     await Task.Delay(ex.RetryAfterMs.Value, cancellationToken);
@@ -61,4 +62,4 @@ public class MatrixHttpClient : HttpClient {
         await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
         return await JsonSerializer.DeserializeAsync<T>(responseStream, cancellationToken: cancellationToken);
     }
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Core/MatrixException.cs b/MatrixRoomUtils.Core/MatrixException.cs
index 4795d6d..a469a62 100644
--- a/MatrixRoomUtils.Core/MatrixException.cs
+++ b/MatrixRoomUtils.Core/MatrixException.cs
@@ -16,6 +16,8 @@ public class MatrixException : Exception {
     [JsonPropertyName("retry_after_ms")]
     public int? RetryAfterMs { get; set; }
 
+    public string RawContent { get; set; }
+
     public override string Message =>
         $"{ErrorCode}: {ErrorCode switch {
             // common
@@ -54,4 +56,4 @@ public class MatrixException : Exception {
             "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => $"Cannot leave server notice room: {Error}",
             _ => $"Unknown error: {new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)}"
         }}";
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs
index 41f604d..d4b256b 100644
--- a/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs
+++ b/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs
@@ -21,8 +21,10 @@ public class MRUStorageWrapper {
     }
 
     public async Task<List<LoginResponse>?> GetAllTokens() {
-        return await _storageService.DataStorageProvider.LoadObjectAsync<List<LoginResponse>>("mru.tokens") ?? new List<LoginResponse>();
+        return await _storageService.DataStorageProvider.LoadObjectAsync<List<LoginResponse>>("mru.tokens") ??
+               new List<LoginResponse>();
     }
+
     public async Task<LoginResponse?> GetCurrentToken() {
         var currentToken = await _storageService.DataStorageProvider.LoadObjectAsync<LoginResponse>("token");
         var allTokens = await GetAllTokens();
@@ -30,12 +32,15 @@ public class MRUStorageWrapper {
             await SetCurrentToken(null);
             return null;
         }
+
         if (currentToken is null) {
             await SetCurrentToken(currentToken = allTokens[0]);
         }
-        if(!allTokens.Any(x=>x.AccessToken == currentToken.AccessToken)) {
+
+        if (!allTokens.Any(x => x.AccessToken == currentToken.AccessToken)) {
             await SetCurrentToken(currentToken = allTokens[0]);
         }
+
         return currentToken;
     }
 
@@ -49,22 +54,39 @@ public class MRUStorageWrapper {
         await _storageService.DataStorageProvider.SaveObjectAsync("mru.tokens", tokens);
     }
 
-    public async Task<AuthenticatedHomeServer?> GetCurrentSession() {
+    private async Task<AuthenticatedHomeServer?> GetCurrentSession() {
         var token = await GetCurrentToken();
         if (token == null) {
             return null;
         }
-        
-        return await _homeserverProviderService.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken); 
+
+        return await _homeserverProviderService.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken);
     }
 
     public async Task<AuthenticatedHomeServer?> GetCurrentSessionOrNavigate() {
-        var session = await GetCurrentSession();
-        if (session == null) {
+        AuthenticatedHomeServer? session = null;
+
+        try {
+            //catch if the token is invalid
+            session = await GetCurrentSession();
+        }
+        catch (MatrixException e) {
+            if (e.ErrorCode == "M_UNKNOWN_TOKEN") {
+                var token = await GetCurrentToken();
+                _navigationManager.NavigateTo("/InvalidSession?ctx=" + token.AccessToken);
+                return null;
+            }
+
+            throw;
+        }
+
+        if (session is null) {
             _navigationManager.NavigateTo("/Login");
         }
+
         return session;
     }
+
     public class Settings {
         public DeveloperSettings DeveloperSettings { get; set; } = new();
     }
@@ -81,11 +103,11 @@ public class MRUStorageWrapper {
             return;
         }
 
-        tokens.RemoveAll(x=>x.AccessToken == auth.AccessToken);
+        tokens.RemoveAll(x => x.AccessToken == auth.AccessToken);
         await _storageService.DataStorageProvider.SaveObjectAsync("mru.tokens", tokens);
     }
 
     public async Task SetCurrentToken(LoginResponse? auth) {
         _storageService.DataStorageProvider.SaveObjectAsync("token", auth);
     }
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor
index b8d9c4a..971bd9b 100644
--- a/MatrixRoomUtils.Web/Pages/About.razor
+++ b/MatrixRoomUtils.Web/Pages/About.razor
@@ -1,4 +1,4 @@
-@page "/About"
+@page "/About"
 @using System.Net
 @using System.Net.Sockets
 @inject NavigationManager NavigationManager
@@ -56,7 +56,7 @@
         var message = "Hello, World!\nThis is a terminal emulator!\n\nYou can type stuff here, and it will be sent to the server!\n\nThis is a test of the emergency broadcast system.\n\nThis is only a t";
         _terminal.Options.RendererType = RendererType.Dom;
         _terminal.Options.ScreenReaderMode = true;
-        TcpClient.
+//        TcpClient.
         for (var i = 0; i < message.Length; i++) {
             await _terminal.Write(message[i].ToString());
 
diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor
index 16a6cee..01e2be4 100644
--- a/MatrixRoomUtils.Web/Pages/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Index.razor
@@ -21,10 +21,10 @@ Small collection of tools to do not-so-everyday things.
                 <input type="radio" name="csa" checked="@(_currentSession.AccessToken == _auth.AccessToken)" @onclick="@(()=>SwitchSession(_auth))" style="text-decoration-line: unset;"/>
                 <b>@_user.DisplayName</b> on <b>@_auth.Homeserver</b>
                 <a role="button" @onclick="@(() => RemoveUser(_auth))">Remove</a>
-                
+
             </p>
             <p style="margin-top: -1.5em; margin-left: 4em;">Member of @_user.RoomCount rooms</p>
-        
+
         </div>
     }
 </form>
@@ -39,7 +39,17 @@ Small collection of tools to do not-so-everyday things.
         var tokens = await MRUStorage.GetAllTokens();
         var profileTasks = tokens.Select(async token => {
             UserInfo userInfo = new();
-            var hs = await HomeserverProvider.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken);
+            AuthenticatedHomeServer hs;
+            try {
+                hs = await HomeserverProvider.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken);
+            }
+            catch (MatrixException e) {
+                if (e.ErrorCode == "M_UNKNOWN_TOKEN") {
+                    NavigationManager.NavigateTo("/InvalidSession?ctx="+token.AccessToken);
+                    return;
+                }
+                throw;
+            }
             var roomCountTask = hs.GetJoinedRooms();
             var profile = await hs.GetProfile(hs.WhoAmI.UserId);
             userInfo.DisplayName = profile.DisplayName ?? hs.WhoAmI.UserId;
@@ -75,4 +85,4 @@ Small collection of tools to do not-so-everyday things.
         await MRUStorage.SetCurrentToken(auth);
         await OnInitializedAsync();
     }
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Pages/InvalidSession.razor b/MatrixRoomUtils.Web/Pages/InvalidSession.razor
new file mode 100644
index 0000000..3bcd797
--- /dev/null
+++ b/MatrixRoomUtils.Web/Pages/InvalidSession.razor
@@ -0,0 +1,97 @@
+@page "/InvalidSession"
+@using MatrixRoomUtils.Core.Helpers
+@using MatrixRoomUtils.Core.Responses
+@using MatrixRoomUtils.Web.Shared.SimpleComponents
+
+<PageTitle>Invalid session</PageTitle>
+
+<h3>Rory&::MatrixUtils - Invalid session encountered</h3>
+<p>A session was encountered that is no longer valid. This can happen if you have logged out of the account on another device, or if the access token has expired.</p>
+
+@if (_login is not null) {
+    <p>It appears that the affected user is @_login.UserId (@_login.DeviceId) on @_login.Homeserver!</p>
+    <LinkButton OnClick="@(OpenRefreshDialog)">Refresh token</LinkButton>
+    <LinkButton OnClick="@(RemoveUser)">Remove</LinkButton>
+
+    @if (_showRefreshDialog) {
+        <ModalWindow MinWidth="300" X="275" Y="300" Title="@($"Password for {_login.UserId}")">
+            <FancyTextBox IsPassword="true" @bind-Value="@_password"></FancyTextBox><br/>
+            <LinkButton OnClick="TryLogin">Log in</LinkButton>
+            @if (_loginException is not null) {
+                <pre style="color: red;">@_loginException.RawContent</pre>
+            }
+        </ModalWindow>
+    }
+}
+
+@code
+{
+    [Parameter]
+    [SupplyParameterFromQuery(Name = "ctx")]
+    public string Context { get; set; }
+
+    private LoginResponse _login { get; set; }
+
+    private bool _showRefreshDialog { get; set; } = false;
+
+    private string _password { get; set; } = "";
+
+    private MatrixException _loginException { get; set; }
+
+    protected override async Task OnInitializedAsync() {
+        var tokens = await MRUStorage.GetAllTokens();
+        if (tokens is null || tokens.Count == 0) {
+            NavigationManager.NavigateTo("/Login");
+            return;
+        }
+
+        _login = tokens.FirstOrDefault(x => x.AccessToken == Context);
+
+        if (_login is null) {
+            Console.WriteLine($"Could not find {_login} in stored tokens!");
+        }
+
+        await base.OnInitializedAsync();
+    }
+
+    private async Task RemoveUser() {
+        await MRUStorage.RemoveToken(_login);
+        if ((await MRUStorage.GetCurrentToken()).AccessToken == _login.AccessToken)
+            MRUStorage.SetCurrentToken((await MRUStorage.GetAllTokens()).FirstOrDefault());
+        await OnInitializedAsync();
+    }
+
+    private async Task OpenRefreshDialog() {
+        _showRefreshDialog = true;
+        StateHasChanged();
+    }
+
+    private async Task SwitchSession(LoginResponse auth) {
+        Console.WriteLine($"Switching to {auth.Homeserver} {auth.AccessToken} {auth.UserId}");
+        await MRUStorage.SetCurrentToken(auth);
+        await OnInitializedAsync();
+    }
+
+    private async Task TryLogin() {
+        try {
+            var result = await HomeserverProvider.Login(_login.Homeserver, _login.UserId, _password);
+            if (result is null) {
+                Console.WriteLine($"Failed to login to {_login.Homeserver} as {_login.UserId}!");
+                return;
+            }
+            Console.WriteLine($"Obtained access token for {result.UserId}!");
+
+            await RemoveUser();
+            await MRUStorage.AddToken(result);
+            if (result.UserId == (await MRUStorage.GetCurrentToken())?.UserId)
+                await MRUStorage.SetCurrentToken(result);
+            NavigationManager.NavigateTo("/");
+        }
+        catch (MatrixException e) {
+            Console.WriteLine($"Failed to login to {_login.Homeserver} as {_login.UserId}!");
+            Console.WriteLine(e);
+            _loginException = e;
+            StateHasChanged();
+        }
+    }
+}
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
index 4cb16b8..cd4788b 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
@@ -207,7 +207,9 @@ else {
     }
 
     private async Task LoadStatesAsync() {
-        var hs = await MRUStorage.GetCurrentSession();
+        var hs = await MRUStorage.GetCurrentSessionOrNavigate();
+        if (hs is null) return;
+
         var room = await hs.GetRoom(RoomId);
 
         var states = room.GetFullStateAsync();
@@ -215,8 +217,8 @@ else {
             if (!state.Type.StartsWith("m.policy.rule")) continue;
             PolicyEvents.Add(state);
         }
-        
-        
+
+
         // var stateEventsQuery = await room.GetStateAsync("");
         // var stateEvents = stateEventsQuery.Value.Deserialize<List<StateEventResponse>>();
         // PolicyEvents = stateEvents.Where(x => x.Type.StartsWith("m.policy.rule"))
@@ -247,4 +249,4 @@ else {
         StateHasChanged();
     }
 
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
index ffccc25..f9cef91 100644
--- a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
+++ b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor
@@ -35,14 +35,15 @@
 
     protected override async Task OnInitializedAsync() {
         await base.OnInitializedAsync();
-        var hs = await MRUStorage.GetCurrentSession();
-    
+        var hs = await MRUStorage.GetCurrentSessionOrNavigate();
+        if(hs is null) return;
+
         await _semaphoreSlim.WaitAsync();
 
         if (User == null && UserId == null)
             throw new ArgumentNullException(nameof(UserId));
         User ??= await hs.GetProfile(UserId);
- 
+
 
         ProfileAvatar ??= MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, User.AvatarUrl);
         ProfileName ??= User.DisplayName;
@@ -50,4 +51,4 @@
         _semaphoreSlim.Release();
     }
 
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Shared/ModalWindow.razor b/MatrixRoomUtils.Web/Shared/ModalWindow.razor
index 216f1f3..b40d246 100644
--- a/MatrixRoomUtils.Web/Shared/ModalWindow.razor
+++ b/MatrixRoomUtils.Web/Shared/ModalWindow.razor
@@ -1,12 +1,12 @@
 <div class="r-modal" style="top: @(_y)px; left: @(_x)px;">
     <div class="titlebar" @onmousedown="MouseDown" @onmouseup="MouseUp" @onmousemove="MouseMove" @onmouseleave="MouseMove">
-        <b class="title">@Title</b>
+        <b class="title" @ref="_titleRef">@Title</b>
         <button class="btnclose" @onclick="OnCloseClicked">X</button>
         <button class="btncollapse" @onclick="@(() => Collapsed = !Collapsed)">_</button>
     </div>
-        <div class="content" style="@(Collapsed ? "height: 0px;" : "")">
-            @ChildContent
-        </div>
+    <div class="r-modal-content" style="@((Collapsed ? "height: 0px;" : "") + $"min-width: {MinWidth}px;")">
+        @ChildContent
+    </div>
 </div>
 
 @code {
@@ -24,17 +24,29 @@
     public double Y { get; set; } = 60;
 
     [Parameter]
+    public double MinWidth { get; set; } = 100;
+
+    [Parameter]
     public Action OnCloseClicked { get; set; }
 
     [Parameter]
     public bool Collapsed { get; set; } = false;
 
+    private ElementReference _titleRef;
+
     private double _x = 60;
     private double _y = 60;
 
-    protected override void OnInitialized() {
+    protected override async Task OnInitializedAsync() {
         _x = X;
         _y = Y;
+        await base.OnInitializedAsync();
+    }
+
+    protected override async Task OnAfterRenderAsync(bool firstRender) {
+    //set minwidth to title width
+        MinWidth = await JSRuntime.InvokeAsync<int>("getWidth", _titleRef) + 75;
+        await base.OnAfterRenderAsync(firstRender);
     }
 
     private void WindowDrag(DragEventArgs obj) {
diff --git a/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css b/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css
index b25ab0e..6d08114 100644
--- a/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css
+++ b/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css
@@ -24,6 +24,7 @@
     top: 0;
     left: 0;
     width: fit-content;
+    text-wrap: nowrap;
     height: 100%;
     line-height: 25px;
     padding-left: 10px;
@@ -55,14 +56,13 @@
     cursor: pointer;
 }
 
-.r-modal > .content {
+.r-modal > .r-modal-content {
     position: relative;
     top: 25px;
     left: 0;
     width: fit-content;
     height: fit-content;
     min-width: 150px;
-    min-height: 5px;
     max-width: 75vw;
     max-height: 75vh;
     overflow: auto;
diff --git a/MatrixRoomUtils.Web/Shared/RoomList.razor b/MatrixRoomUtils.Web/Shared/RoomList.razor
index fadec1c..e3894a6 100644
--- a/MatrixRoomUtils.Web/Shared/RoomList.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomList.razor
@@ -22,9 +22,12 @@ else {
     public ProfileResponseEventData? GlobalProfile { get; set; }
 
     Dictionary<string, List<RoomInfo>> RoomsWithTypes = new();
-    
+
     protected override async Task OnInitializedAsync() {
-        GlobalProfile ??= await (await MRUStorage.GetCurrentSession())!.GetProfile((await MRUStorage.GetCurrentSession())!.WhoAmI.UserId);
+        var hs = await MRUStorage.GetCurrentSessionOrNavigate();
+        if (hs is null) return;
+
+        GlobalProfile ??= await hs.GetProfile(hs.WhoAmI.UserId);
         if (RoomsWithTypes.Any()) return;
 
         var tasks = Rooms.Select(ProcessRoom);
@@ -41,7 +44,7 @@ else {
         _ => roomType
         };
 
-    
+
     private static SemaphoreSlim _semaphoreSlim = new(8, 8);
     private async Task ProcessRoom(RoomInfo room) {
         await _semaphoreSlim.WaitAsync();
@@ -59,14 +62,14 @@ else {
         catch (MatrixException e) {
             roomType = $"Error: {e.ErrorCode}";
         }
-        
+
         if (!RoomsWithTypes.ContainsKey(roomType)) {
             RoomsWithTypes.Add(roomType, new List<RoomInfo>());
         }
         RoomsWithTypes[roomType].Add(room);
-        
+
             StateHasChanged();
         _semaphoreSlim.Release();
     }
 
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor b/MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor
index 09b5c32..b800989 100644
--- a/MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor
+++ b/MatrixRoomUtils.Web/Shared/SimpleComponents/LinkButton.razor
@@ -1,16 +1,20 @@
-<a href="@href" class="btn btn-primary" style="background-color: @(Color ?? "#1b6ec2");">
+<a href="@href" class="btn btn-primary" @onclick="@(() => OnClick?.Invoke())"
+   style="background-color: @(Color ?? "#1b6ec2");">
     @ChildContent
 </a>
 
 @code {
-    
+
     [Parameter]
-    public string href { get; set; }
-    
+    public string? href { get; set; }
+
     [Parameter]
     public RenderFragment ChildContent { get; set; }
 
     [Parameter]
     public string? Color { get; set; }
 
-}
\ No newline at end of file
+    [Parameter]
+    public Func<Task>? OnClick { get; set; }
+
+}
diff --git a/MatrixRoomUtils.Web/_Imports.razor b/MatrixRoomUtils.Web/_Imports.razor
index 85c015e..b1d9212 100644
--- a/MatrixRoomUtils.Web/_Imports.razor
+++ b/MatrixRoomUtils.Web/_Imports.razor
@@ -21,4 +21,5 @@
 @inject MRUStorageWrapper MRUStorage
 @inject HomeserverProviderService HomeserverProvider
 @inject TieredStorageService TieredStorage
-@inject HomeserverResolverService HomeserverResolver
\ No newline at end of file
+@inject HomeserverResolverService HomeserverResolver
+@inject IJSRuntime JSRuntime
diff --git a/MatrixRoomUtils.Web/wwwroot/css/app.css b/MatrixRoomUtils.Web/wwwroot/css/app.css
index b3a8cf3..bcd8f5d 100644
--- a/MatrixRoomUtils.Web/wwwroot/css/app.css
+++ b/MatrixRoomUtils.Web/wwwroot/css/app.css
@@ -118,4 +118,4 @@ a, .btn-link {
 
 pre {
     font-family: JetBrainsMono, var(--bs-font-monospace);
-}
\ No newline at end of file
+}
diff --git a/MatrixRoomUtils.Web/wwwroot/index.html b/MatrixRoomUtils.Web/wwwroot/index.html
index 0598c4d..9a85530 100644
--- a/MatrixRoomUtils.Web/wwwroot/index.html
+++ b/MatrixRoomUtils.Web/wwwroot/index.html
@@ -34,10 +34,21 @@
         if (element instanceof HTMLElement) {
             console.log(element);
             element.focus();
-        } else if (element.__internalId) {
+        } else if (element.hasOwnProperty("__internalId")) {
             console.log("Element is not an HTMLElement", element);
         }
     }
+    function getWidth(element) {
+        console.log("getWidth", element);
+        if (element == null) return 0;
+        if (element instanceof HTMLElement) {
+            return element.offsetWidth
+        } else if (element.hasOwnProperty("__internalId")) {
+            console.log("Element is not an HTMLElement", element);
+            return 0;
+        }
+        return 0;
+    }
     function getWindowDimensions() {
         return {
             width: window.innerWidth,