From 2bfbf7b204e29e23a30c1ea15594e1c9de2e29a9 Mon Sep 17 00:00:00 2001 From: Moonbase Date: Thu, 11 Dec 2025 16:04:17 -0800 Subject: [PATCH] Fix User Status Being Lagged Behind The Server (requires server update) --- .../Events/UserStatusUpdatedEventArgs.cs | 14 +++++ QtCNETAPI/Services/ApiService/ApiService.cs | 25 +-------- .../Services/GatewayService/GatewayService.cs | 7 +-- .../CurrentProfileControl.Designer.cs | 54 ++++++++++++++++++- .../Controls/CurrentProfileControl.cs | 46 ++++++++++++++-- .../Controls/CurrentProfileControl.resx | 3 ++ qtcnet-client/Forms/MainForm.cs | 52 +++++++++++++++--- qtcnet-client/Forms/ProfileForm.Designer.cs | 15 +++--- qtcnet-client/Forms/ProfileForm.cs | 2 +- 9 files changed, 168 insertions(+), 50 deletions(-) create mode 100644 QtCNETAPI/Events/UserStatusUpdatedEventArgs.cs diff --git a/QtCNETAPI/Events/UserStatusUpdatedEventArgs.cs b/QtCNETAPI/Events/UserStatusUpdatedEventArgs.cs new file mode 100644 index 0000000..3343d5f --- /dev/null +++ b/QtCNETAPI/Events/UserStatusUpdatedEventArgs.cs @@ -0,0 +1,14 @@ +using QtCNETAPI.Dtos.User; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace QtCNETAPI.Events +{ + public class UserStatusUpdatedEventArgs : EventArgs + { + public UserStatusDto? StatusDto { get; set; } + } +} diff --git a/QtCNETAPI/Services/ApiService/ApiService.cs b/QtCNETAPI/Services/ApiService/ApiService.cs index 165357f..12b9f4e 100644 --- a/QtCNETAPI/Services/ApiService/ApiService.cs +++ b/QtCNETAPI/Services/ApiService/ApiService.cs @@ -159,10 +159,6 @@ namespace QtCNETAPI.Services.ApiService { serviceResponse.Success = true; serviceResponse.Data = response.Data; - - // anything that changes the user should tell the api service to set it again - await SetCurrentUser(); - OnCurrentUserUpdate?.Invoke(this, EventArgs.Empty); } else { serviceResponse.Success = false; @@ -188,10 +184,6 @@ namespace QtCNETAPI.Services.ApiService { serviceResponse.Success = true; serviceResponse.Data = response.Data; - - // anything that changes the user should tell the api service to set it again - await SetCurrentUser(); - OnCurrentUserUpdate?.Invoke(this, EventArgs.Empty); } else { @@ -370,6 +362,7 @@ namespace QtCNETAPI.Services.ApiService _loggingService.LogString($"Current User's Status Is {userResponse.Data.Status}"); + OnCurrentUserUpdate?.Invoke(this, EventArgs.Empty); return userResponse.Data; } else { @@ -680,10 +673,6 @@ namespace QtCNETAPI.Services.ApiService { serviceResponse.Success = true; serviceResponse.Data = response.Data; - - // anything that changes the user should tell the api service to set it again - await SetCurrentUser(); - OnCurrentUserUpdate?.Invoke(this, EventArgs.Empty); } return serviceResponse; @@ -731,10 +720,6 @@ namespace QtCNETAPI.Services.ApiService { serviceResponse.Success = true; serviceResponse.Data = response.Data; - - // anything that changes the user should tell the api service to set it again - await SetCurrentUser(); - OnCurrentUserUpdate?.Invoke(this, EventArgs.Empty); } return serviceResponse; @@ -759,10 +744,6 @@ namespace QtCNETAPI.Services.ApiService { serviceResponse.Success = true; serviceResponse.Data = response.Data; - - // anything that changes the user should tell the api service to set it again - await SetCurrentUser(); - OnCurrentUserUpdate?.Invoke(this, EventArgs.Empty); } return serviceResponse; @@ -882,10 +863,6 @@ namespace QtCNETAPI.Services.ApiService { serviceResponse.Success = true; serviceResponse.Data = response.Data; - - // anything that changes the user should tell the api service to set it again - await SetCurrentUser(); - OnCurrentUserUpdate?.Invoke(this, EventArgs.Empty); } return serviceResponse; diff --git a/QtCNETAPI/Services/GatewayService/GatewayService.cs b/QtCNETAPI/Services/GatewayService/GatewayService.cs index 3f440d4..70d0b0a 100644 --- a/QtCNETAPI/Services/GatewayService/GatewayService.cs +++ b/QtCNETAPI/Services/GatewayService/GatewayService.cs @@ -74,6 +74,7 @@ namespace QtCNETAPI.Services.GatewayService HubConnection.On("GuestJoin", (username) => OnGuestUserJoin?.Invoke(this, new GuestUserJoinEventArgs { Username = username })); HubConnection.On("RoomDeleted", () => OnRoomDeleted?.Invoke(this, EventArgs.Empty)); HubConnection.On("ForceSignOut", () => OnUserForceLogout?.Invoke(this, EventArgs.Empty)); + HubConnection.On("UpdateCurrentUser", async () => await _apiService.SetCurrentUser()); HubConnection.Closed += HubConnection_Closed; HubConnection.Reconnecting += HubConnection_Reconnecting; @@ -89,9 +90,6 @@ namespace QtCNETAPI.Services.GatewayService _loggingService.LogString($"Unable To Connect To SignalR.\n{ex.Message}\n{ex.StackTrace}"); return; } - - // ensure current user is up to date (particularly status (its still busted lol)) - await _apiService.SetCurrentUser(); } public async Task StopAsync() @@ -172,9 +170,6 @@ namespace QtCNETAPI.Services.GatewayService if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made."); await HubConnection.SendAsync("UpdateStatus", _apiService.CurrentUser, status); - - // anything that changes the user should tell the api service to set it again - await _apiService.SetCurrentUser(); } diff --git a/qtcnet-client/Controls/CurrentProfileControl.Designer.cs b/qtcnet-client/Controls/CurrentProfileControl.Designer.cs index 0da8149..0e377a5 100644 --- a/qtcnet-client/Controls/CurrentProfileControl.Designer.cs +++ b/qtcnet-client/Controls/CurrentProfileControl.Designer.cs @@ -28,19 +28,26 @@ /// private void InitializeComponent() { + components = new System.ComponentModel.Container(); pbCurrentProfilePic = new PictureBox(); + ctxStatus = new ContextMenuStrip(components); + tsiStatusOnline = new ToolStripMenuItem(); + tsiStatusAway = new ToolStripMenuItem(); + tsiStatusDND = new ToolStripMenuItem(); + tsiStatusOffline = new ToolStripMenuItem(); llblSignOut = new LinkLabel(); llblEditProfile = new LinkLabel(); pictureBox1 = new PictureBox(); lblUsername = new Label(); lblCurrencyAmount = new Label(); ((System.ComponentModel.ISupportInitialize)pbCurrentProfilePic).BeginInit(); + ctxStatus.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit(); SuspendLayout(); // // pbCurrentProfilePic // - pbCurrentProfilePic.Cursor = Cursors.Hand; + pbCurrentProfilePic.ContextMenuStrip = ctxStatus; pbCurrentProfilePic.Image = Properties.Resources.DefaultPfp; pbCurrentProfilePic.Location = new Point(3, 3); pbCurrentProfilePic.Name = "pbCurrentProfilePic"; @@ -49,6 +56,45 @@ pbCurrentProfilePic.TabIndex = 0; pbCurrentProfilePic.TabStop = false; // + // ctxStatus + // + ctxStatus.Items.AddRange(new ToolStripItem[] { tsiStatusOnline, tsiStatusAway, tsiStatusDND, tsiStatusOffline }); + ctxStatus.Name = "ctxStatus"; + ctxStatus.Size = new Size(181, 114); + ctxStatus.ItemClicked += ctxStatus_ItemClicked; + // + // tsiStatusOnline + // + tsiStatusOnline.Image = Properties.Resources.OnlineIcon; + tsiStatusOnline.Name = "tsiStatusOnline"; + tsiStatusOnline.Size = new Size(180, 22); + tsiStatusOnline.Tag = "Online"; + tsiStatusOnline.Text = "Online"; + // + // tsiStatusAway + // + tsiStatusAway.Image = Properties.Resources.AwayIcon; + tsiStatusAway.Name = "tsiStatusAway"; + tsiStatusAway.Size = new Size(180, 22); + tsiStatusAway.Tag = "Away"; + tsiStatusAway.Text = "Away"; + // + // tsiStatusDND + // + tsiStatusDND.Image = Properties.Resources.DNDIcon; + tsiStatusDND.Name = "tsiStatusDND"; + tsiStatusDND.Size = new Size(180, 22); + tsiStatusDND.Tag = "DoNotDisturb"; + tsiStatusDND.Text = "Do Not Disturb"; + // + // tsiStatusOffline + // + tsiStatusOffline.Image = Properties.Resources.OfflineIcon; + tsiStatusOffline.Name = "tsiStatusOffline"; + tsiStatusOffline.Size = new Size(180, 22); + tsiStatusOffline.Tag = "Invisible"; + tsiStatusOffline.Text = "Invisible"; + // // llblSignOut // llblSignOut.AutoSize = true; @@ -119,6 +165,7 @@ Size = new Size(197, 73); Load += CurrentProfileControl_Load; ((System.ComponentModel.ISupportInitialize)pbCurrentProfilePic).EndInit(); + ctxStatus.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit(); ResumeLayout(false); PerformLayout(); @@ -132,5 +179,10 @@ private PictureBox pictureBox1; private Label lblUsername; private Label lblCurrencyAmount; + private ContextMenuStrip ctxStatus; + private ToolStripMenuItem tsiStatusOnline; + private ToolStripMenuItem tsiStatusAway; + private ToolStripMenuItem tsiStatusDND; + private ToolStripMenuItem tsiStatusOffline; } } diff --git a/qtcnet-client/Controls/CurrentProfileControl.cs b/qtcnet-client/Controls/CurrentProfileControl.cs index e8227db..1b7d19d 100644 --- a/qtcnet-client/Controls/CurrentProfileControl.cs +++ b/qtcnet-client/Controls/CurrentProfileControl.cs @@ -17,9 +17,12 @@ namespace qtcnet_client.Controls public int CurrencyCount { get; set; } = 0; [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public Image ProfileImage { get; set; } = Resources.DefaultPfp; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int SelectedStatus { get; set; } = 1; public event EventHandler? OnEditProfileClicked; public event EventHandler? OnSignOutClicked; + public event EventHandler? OnStatusChanged; public CurrentProfileControl() { @@ -35,13 +38,50 @@ namespace qtcnet_client.Controls public void RefreshInfo() { - lblUsername.Text = $"Welcome, {Username}!"; - lblCurrencyAmount.Text = CurrencyCount.ToString(); - pbCurrentProfilePic.Image = ProfileImage; + if (InvokeRequired) + { + Invoke(() => + { + lblUsername.Text = $"Welcome, {Username}!"; + lblCurrencyAmount.Text = CurrencyCount.ToString(); + pbCurrentProfilePic.Image = ProfileImage; + }); + } else + { + lblUsername.Text = $"Welcome, {Username}!"; + lblCurrencyAmount.Text = CurrencyCount.ToString(); + pbCurrentProfilePic.Image = ProfileImage; + } } private void llblEditProfile_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) => OnEditProfileClicked?.Invoke(this, EventArgs.Empty); private void llblSignOut_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) => OnSignOutClicked?.Invoke(this, EventArgs.Empty); + + private async void ctxStatus_ItemClicked(object sender, ToolStripItemClickedEventArgs e) + { + int status = 0; + if(e.ClickedItem?.Tag is string _statusTag) + { + switch(_statusTag) + { + case "Invisible": + status = 0; + break; + case "Online": + status = 1; + break; + case "Away": + status = 2; + break; + case "DoNotDisturb": + status = 3; + break; + } + + SelectedStatus = status; + OnStatusChanged?.Invoke(this, EventArgs.Empty); + } + } } } diff --git a/qtcnet-client/Controls/CurrentProfileControl.resx b/qtcnet-client/Controls/CurrentProfileControl.resx index 8b2ff64..9e4bd9b 100644 --- a/qtcnet-client/Controls/CurrentProfileControl.resx +++ b/qtcnet-client/Controls/CurrentProfileControl.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/qtcnet-client/Forms/MainForm.cs b/qtcnet-client/Forms/MainForm.cs index 0762514..7c98f53 100644 --- a/qtcnet-client/Forms/MainForm.cs +++ b/qtcnet-client/Forms/MainForm.cs @@ -12,6 +12,7 @@ using QtCNETAPI.Services; using QtCNETAPI.Services.ApiService; using QtCNETAPI.Services.GatewayService; using System.Drawing.Drawing2D; +using System.Threading.Tasks; namespace qtcnet_client { @@ -492,15 +493,37 @@ namespace qtcnet_client private void _gatewayService_OnServerReconnected(object? sender, EventArgs e) { // reenable interactive controls - CurrentProfileControl?.Enabled = true; - MainTabControl?.Enabled = true; + if (InvokeRequired) + { + Invoke(() => + { + CurrentProfileControl?.Enabled = true; + MainTabControl?.Enabled = true; + }); + } + else + { + CurrentProfileControl?.Enabled = true; + MainTabControl?.Enabled = true; + } } private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e) { // disable interactive controls - CurrentProfileControl?.Enabled = false; - MainTabControl?.Enabled = false; + if(InvokeRequired) + { + Invoke(() => + { + CurrentProfileControl?.Enabled = false; + MainTabControl?.Enabled = false; + }); + } + else + { + CurrentProfileControl?.Enabled = false; + MainTabControl?.Enabled = false; + } } private void _gatewayService_OnDirectMessageReceived(object? sender, EventArgs e) @@ -583,9 +606,6 @@ namespace qtcnet_client if (_res) { - if (_apiService.CurrentUser.Status == 0) - await _gatewayService.UpdateStatus(1); - // setup current profile control based on current user CurrentProfileControl = new() { @@ -596,6 +616,7 @@ namespace qtcnet_client CurrentProfileControl.OnEditProfileClicked += CurrentProfileControl_OnEditProfileClicked; CurrentProfileControl.OnSignOutClicked += CurrentProfileControl_OnSignOutClicked; + CurrentProfileControl.OnStatusChanged += CurrentProfileControl_OnStatusChanged; // get profile image for the current user CurrentProfileControl.ProfileImage = await _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser.Id, _apiService.CurrentUser.Status, _apiService.CurrentUser.ActiveProfileCosmetic); @@ -703,6 +724,23 @@ namespace qtcnet_client } } + private async void _gatewayService_OnUserStatusUpdated(object? sender, EventArgs e) + { + if(e is UserStatusUpdatedEventArgs _args) + { + CurrentProfileControl?.ProfileImage = await _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser!.Id, _args.StatusDto!.Status, _apiService.CurrentUser!.ActiveProfileCosmetic); + } + } + + private async void CurrentProfileControl_OnStatusChanged(object? sender, EventArgs e) + { + if(sender is CurrentProfileControl _ctrl) + { + // set status based on selected + await _gatewayService.UpdateStatus(_ctrl.SelectedStatus); + } + } + private void MainTabControl_OnGameItemDoubleClicked(object? sender, EventArgs e) { if(sender is MainTabControl _ctrl) diff --git a/qtcnet-client/Forms/ProfileForm.Designer.cs b/qtcnet-client/Forms/ProfileForm.Designer.cs index df81cdd..e23129d 100644 --- a/qtcnet-client/Forms/ProfileForm.Designer.cs +++ b/qtcnet-client/Forms/ProfileForm.Designer.cs @@ -30,6 +30,7 @@ { components = new System.ComponentModel.Container(); pbProfileImage = new PictureBox(); + ctxBoughtItems = new ContextMenuStrip(components); lblStatus = new Label(); rtxtBio = new RichTextBox(); tlpActionButtons = new TableLayoutPanel(); @@ -39,7 +40,6 @@ lblUsername = new Label(); tlpTagIcons = new TableLayoutPanel(); btnSaveProfile = new Button(); - ctxBoughtItems = new ContextMenuStrip(components); ((System.ComponentModel.ISupportInitialize)pbProfileImage).BeginInit(); tlpActionButtons.SuspendLayout(); tlpUsernameTags.SuspendLayout(); @@ -47,7 +47,6 @@ // // pbProfileImage // - pbProfileImage.ContextMenuStrip = ctxBoughtItems; pbProfileImage.Image = Properties.Resources.DefaultPfp; pbProfileImage.Location = new Point(12, 12); pbProfileImage.Name = "pbProfileImage"; @@ -55,10 +54,15 @@ pbProfileImage.SizeMode = PictureBoxSizeMode.Zoom; pbProfileImage.TabIndex = 0; pbProfileImage.TabStop = false; - pbProfileImage.Click += pbProfileImage_Click; + pbProfileImage.MouseClick += pbProfileImage_Click; pbProfileImage.MouseEnter += pbProfileImage_MouseEnter; pbProfileImage.MouseLeave += pbProfileImage_MouseLeave; // + // ctxBoughtItems + // + ctxBoughtItems.Name = "ctxBoughtItems"; + ctxBoughtItems.Size = new Size(61, 4); + // // lblStatus // lblStatus.AutoEllipsis = true; @@ -175,11 +179,6 @@ btnSaveProfile.Visible = false; btnSaveProfile.Click += btnSaveProfile_Click; // - // ctxBoughtItems - // - ctxBoughtItems.Name = "ctxBoughtItems"; - ctxBoughtItems.Size = new Size(61, 4); - // // ProfileForm // AutoScaleDimensions = new SizeF(7F, 15F); diff --git a/qtcnet-client/Forms/ProfileForm.cs b/qtcnet-client/Forms/ProfileForm.cs index ff6f124..2a74de2 100644 --- a/qtcnet-client/Forms/ProfileForm.cs +++ b/qtcnet-client/Forms/ProfileForm.cs @@ -281,7 +281,7 @@ namespace qtcnet_client.Forms KryptonMessageBox.Show("Profile Update Failed. Please Try Again Later.", "Oops."); } - private void pbProfileImage_Click(object sender, EventArgs e) + private void pbProfileImage_Click(object sender, MouseEventArgs e) { if(UserId == _apiService.CurrentUser?.Id) {