using QtCNETAPI.Dtos.User; using QtCNETAPI.Services.ApiService; using QtCNETAPI.Services.GatewayService; using QtCNETAPI.Events; using QtCNETAPI.Models; using qtc_net_client_2.Forms; using qtc_net_client_2.Services; using qtc_net_client_2.ClientModel; using System.Threading.Tasks; using QtCNETAPI.Schema; using QtCNETAPI.Services; namespace qtc_net_client_2 { public partial class Main : Form { private IApiService _apiService; private IGatewayService _gatewayService; private Config _config; private ServerConfig _serverConfig; private AudioService AudioService = new(); private LoggingService LoggingService; public List RoomList = []; public List UserDirectory = []; public List Contacts = []; private bool FirstMinimize = true; public Main(IApiService apiService, IGatewayService gatewayService, Config config, LoggingService loggingService) { _apiService = apiService; _gatewayService = gatewayService; _config = config; LoggingService = loggingService; InitializeComponent(); } private async void frmMain_Load(object sender, EventArgs e) { LoggingService.LogString("Main Form Loaded"); // start request notif blink task Thread blinkThread = new Thread(async () => await StartRequestNotifBlankLoop(lblRequestNotif)); blinkThread.Start(); // check if client is already logged in if (_apiService.CurrentUser == null) { // not logged in, load the login form llblForgotPassword frmLogin = new llblForgotPassword(_apiService); var result = frmLogin.ShowDialog(); if (result == DialogResult.OK) await OnSuccessfulLogin(); } } private async void llblSignIn_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { // just reshow the login dialog lol llblForgotPassword frmLogin = new llblForgotPassword(_apiService); var result = frmLogin.ShowDialog(); if (result == DialogResult.OK) await OnSuccessfulLogin(); } private async void lbRooms_DoubleClick(object sender, EventArgs e) { if (lbRooms.SelectedItems.Count > 0) { string? selectedRoom = (string?)lbRooms.SelectedItems[lbRooms.SelectedItems.Count - 1]; if (selectedRoom != null) { if (selectedRoom == "Lobby") { // join lobby if (!_gatewayService.InLobby) await _gatewayService.JoinLobbyAsync(); ChatRoom frmChat = new ChatRoom(_gatewayService, _apiService); frmChat.Show(); LoggingService.LogString("User Has Joined Lobby Room"); return; } // join the room Room? room = RoomList.FirstOrDefault(e => e.Name == selectedRoom); if (room != null) { if (_gatewayService.CurrentRoom != room) await _gatewayService.JoinRoomAsync(room); ChatRoom frmChat = new ChatRoom(_gatewayService, _apiService); frmChat.Show(); LoggingService.LogString($"User Has Joined {room.Name}"); } } } } private void pbUserPfp_Click(object sender, EventArgs e) { var args = (MouseEventArgs)e; if (args.Button == MouseButtons.Right) return; Thread t = new Thread(new ThreadStart(async () => { using (OpenFileDialog openFileDialog = new OpenFileDialog()) { openFileDialog.InitialDirectory = Environment.CurrentDirectory; openFileDialog.Title = "Select New Profile Picture"; openFileDialog.Filter = "Image Files (*.png, *.jpg)|*.png;*.jpg"; if (openFileDialog.ShowDialog() == DialogResult.OK) { // upload to server with api endpoint var res = await _apiService.UpdateUserProfilePic(openFileDialog.FileName); if (res.Success) { // update profile pic in ui var pfpRes = await _apiService.GetUserProfilePic(_apiService.CurrentUser.Id); if (pfpRes.Success && pfpRes.Data != null) { using (var ms = new MemoryStream(pfpRes.Data)) { pbUserPfp.Image = Image.FromStream(ms); ms.Dispose(); } } else LoggingService.LogString($"User Has No Profile Picture Or It Could Not Be Loaded.\n{pfpRes.Message}"); } } } })); t.SetApartmentState(ApartmentState.STA); t.Start(); } private void llblSignOut_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { if (MessageBox.Show("Are You Sure You Want To Sign Out?\nThis Deletes Your session.token, Requiring You To Sign In Again", "are you sure...?", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { File.Delete("./session.token"); Environment.Exit(0); } } private void llblEditProfile_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { ProfileEdit frmProfileEdit = new ProfileEdit(_apiService); frmProfileEdit.ShowDialog(); } private async void lvContacts_DoubleClick(object sender, EventArgs e) { if (lvContacts.SelectedItems.Count > 0) { string? selectedUser = (string?)lvContacts.SelectedItems[lvContacts.SelectedItems.Count - 1].Text; if (selectedUser != null) { // split from [ if it exists if (selectedUser.Contains('[')) selectedUser = selectedUser.Split('[', options: StringSplitOptions.TrimEntries)[0]; // get user info and open profile dialog var user = UserDirectory.FirstOrDefault(e => e.Username == selectedUser); var res = await _apiService.GetUserInformationAsync(user!.Id); var pfpRes = await _apiService.GetUserProfilePic(user!.Id); // get cosmetic byte[]? cosmeticData = null; if (user.ProfileCosmeticId != 0) { var storeRes = await _apiService.GetStoreItem(user.ProfileCosmeticId); if (storeRes != null && storeRes.Success && storeRes.Data != null) { using var client = new HttpClient(); using var response = await client.GetAsync(storeRes.Data.AssetUrl); if (response.IsSuccessStatusCode) { cosmeticData = await response.Content.ReadAsByteArrayAsync(); } else LoggingService.LogString($"Could Not Get User Cosmetic.\nStatus Code: {response.StatusCode}"); } } if (pfpRes != null && !pfpRes.Success) LoggingService.LogString($"User Has No Profile Picture Or It Could Not Be Loaded.\n{pfpRes.Message}"); if (res.Data != null && res.Success) { LoggingService.LogString($"Opening Profile For User '{res.Data.Username}'"); Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService, cosmeticData); frmProfile.Show(); } } } } private void frmMain_Resize(object sender, EventArgs e) { if (WindowState == FormWindowState.Minimized && _config.MinimizeToTray) { Hide(); niMain.Visible = true; if (FirstMinimize) { niMain.ShowBalloonTip(10, "I'm over here!", "QtC.NET Mimimizes To Tray By Default. To Change This Behaviour, Refer To config.json", ToolTipIcon.Info); FirstMinimize = false; } } } private void niMain_DoubleClick(object sender, EventArgs e) { Show(); WindowState = FormWindowState.Normal; niMain.Visible = false; } private async void frmMain_FormClosed(object sender, FormClosedEventArgs e) { DialogResult = DialogResult.OK; // ensure the gateway stops the connection and disposes properly await _gatewayService.StopAsync(); await _gatewayService.DisposeAsync(); } private async void ctxmChangeStatus_ItemClicked(object sender, ToolStripItemClickedEventArgs e) { if (e.ClickedItem != null) { UserStatus userStatus = UserStatus.Unknown; switch (e.ClickedItem.Name) { case "onlineToolStripMenuItem": userStatus = UserStatus.Online; onlineToolStripMenuItem.Checked = true; awayToolStripMenuItem.Checked = false; doNotDisturbToolStripMenuItem.Checked = false; invisibleToolStripMenuItem.Checked = false; break; case "awayToolStripMenuItem": userStatus = UserStatus.Away; onlineToolStripMenuItem.Checked = false; awayToolStripMenuItem.Checked = true; doNotDisturbToolStripMenuItem.Checked = false; invisibleToolStripMenuItem.Checked = false; break; case "doNotDisturbToolStripMenuItem": userStatus = UserStatus.DoNotDisturb; onlineToolStripMenuItem.Checked = false; awayToolStripMenuItem.Checked = false; doNotDisturbToolStripMenuItem.Checked = true; invisibleToolStripMenuItem.Checked = false; break; case "invisibleToolStripMenuItem": userStatus = UserStatus.Offline; onlineToolStripMenuItem.Checked = false; awayToolStripMenuItem.Checked = false; doNotDisturbToolStripMenuItem.Checked = false; invisibleToolStripMenuItem.Checked = true; break; } // set it await _gatewayService.UpdateStatus((int)userStatus); } } private void btnAddRoom_Click(object sender, EventArgs e) { CreateRoom createRoom = new CreateRoom(_apiService); createRoom.ShowDialog(); } private async void llblClaimSpin_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { CurrencyJackpotSpinner tokenJackpotSpinner = new CurrencyJackpotSpinner(); var frmResult = tokenJackpotSpinner.ShowDialog(); if (frmResult == DialogResult.OK && tokenJackpotSpinner.TokensWon > 0) { // claim var result = await _apiService.AddCurrencyToCurrentUser(tokenJackpotSpinner.TokensWon, true); if (result.Success) { lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0"); llblClaimSpin.Visible = false; } else MessageBox.Show("We Were Unable To Claim Your Prize At This Time. Please Try Again Later.", "Uh Oh.", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private async void lbUserDirectory_DoubleClick(object sender, EventArgs e) { if (lvUserDirectory.SelectedItems.Count > 0) { string? selectedUser = (string?)lvUserDirectory.SelectedItems[lvUserDirectory.SelectedItems.Count - 1].Text; if (selectedUser != null) { // get user info and open profile dialog var user = UserDirectory.FirstOrDefault(e => e.Username == selectedUser); var res = await _apiService.GetUserInformationAsync(user!.Id); var pfpRes = await _apiService.GetUserProfilePic(user!.Id); // get cosmetic byte[]? cosmeticData = null; if (user.ProfileCosmeticId != 0) { var storeRes = await _apiService.GetStoreItem(user.ProfileCosmeticId); if (storeRes != null && storeRes.Success && storeRes.Data != null) { using var client = new HttpClient(); using var response = await client.GetAsync(storeRes.Data.AssetUrl); if (response.IsSuccessStatusCode) { cosmeticData = await response.Content.ReadAsByteArrayAsync(); } else LoggingService.LogString($"Could Not Get User Cosmetic.\nStatus Code: {response.StatusCode}"); } } if (pfpRes != null && !pfpRes.Success) LoggingService.LogString($"User Has No Profile Picture Or It Could Not Be Loaded.\n{pfpRes.Message}"); if (res.Data != null && res.Success) { LoggingService.LogString($"Opening Profile For User '{res.Data.Username}'"); Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService, cosmeticData); frmProfile.Show(); } } } } private void lvGames_DoubleClick(object sender, EventArgs e) { if (lvGames.SelectedItems.Count > 0) { string? gameSelected = (string?)lvGames.SelectedItems[lvGames.SelectedItems.Count - 1].Tag; switch (gameSelected) { case "StockMarketGame": StockMarketGame stockMarketGame = new StockMarketGame(_apiService); stockMarketGame.Show(); break; case "GuessTheNumberGame": GuessTheNumber guessTheNumber = new GuessTheNumber(_apiService); guessTheNumber.Show(); break; case "TicTacToeGame": TicTacToeGame ticTacToeGame = new TicTacToeGame(_apiService, _config); ticTacToeGame.Show(); break; } } } private async void refreshToolStripMenuItem_Click(object sender, EventArgs e) { if (tbcMain.SelectedIndex == 0) await RefreshContactsList(); if (tbcMain.SelectedIndex == 1) await RefreshRoomsList(); if (tbcMain.SelectedIndex == 2) await RefreshUsers(); } private void pbDonate_Click(object sender, EventArgs e) { DonationWindow donationWindow = new DonationWindow(); donationWindow.Show(); } private async void tbcMain_SelectedIndexChanged(object sender, EventArgs e) { if (tbcMain.SelectedIndex == 4) { // get store items var storeItems = await _apiService.GetStoreItems(); if (storeItems != null && storeItems.Success && storeItems.Data != null) { if (lvStoreItems.Items.Count == storeItems.Data.Count) return; ilStoreThumbnails.Images.Clear(); foreach (var item in storeItems.Data) { await GetAndAddStoreThumbnail(item); var lvitem = lvStoreItems.Items.Add(new ListViewItem { Text = item.Name, Name = item.Id.ToString() }); lvitem.ImageKey = item.Id.ToString(); } } } } private async void lvStoreItems_DoubleClick(object sender, EventArgs e) { if (lvStoreItems.SelectedItems.Count > 0) { string? itemSelected = (string?)lvStoreItems.SelectedItems[lvStoreItems.SelectedItems.Count - 1].Name; if (itemSelected != null) { // get item var item = await _apiService.GetStoreItem(int.Parse(itemSelected)); if (item != null && item.Success && item.Data != null) { StoreItemDisplay storeItemDisplay = new StoreItemDisplay(item.Data, LoggingService, _apiService); storeItemDisplay.ShowDialog(); } } } } private void addRoomToolStripMenuItem_Click(object sender, EventArgs e) { CreateRoom createRoom = new CreateRoom(_apiService); createRoom.ShowDialog(); } private void ctxmAdminRoomList_Opening(object sender, System.ComponentModel.CancelEventArgs e) { if (lbRooms.SelectedItem == null) deleteRoomToolStripMenuItem.Enabled = false; else deleteRoomToolStripMenuItem.Enabled = true; } private void ctxmAdminUserList_Opening(object sender, System.ComponentModel.CancelEventArgs e) { if (lvUserDirectory.SelectedItems.Count > 0) { string? lvUserDirectoryItemSelected = (string?)lvUserDirectory.SelectedItems[lvUserDirectory.SelectedItems.Count - 1].Text; if (lvUserDirectoryItemSelected != null && lvUserDirectoryItemSelected == _apiService.CurrentUser.Username) { deleteUserToolStripMenuItem.Enabled = false; adminDirectMessageToolStripMenuItem.Enabled = false; copyUserIDToClipboardToolStripMenuItem.Enabled = true; return; } } else { deleteUserToolStripMenuItem.Enabled = false; adminDirectMessageToolStripMenuItem.Enabled = false; copyUserIDToClipboardToolStripMenuItem.Enabled = false; return; } deleteUserToolStripMenuItem.Enabled = true; adminDirectMessageToolStripMenuItem.Enabled = true; copyUserIDToClipboardToolStripMenuItem.Enabled = true; } private void copyUserIDToClipboardToolStripMenuItem_Click(object sender, EventArgs e) { if (lvUserDirectory.SelectedItems.Count > 0) { string? itemSelected = (string?)lvUserDirectory.SelectedItems[lvUserDirectory.SelectedItems.Count - 1].Text; if (itemSelected != null) { // get user var user = UserDirectory.FirstOrDefault(e => e.Username == itemSelected); if (user != null) { // clipboard requires STA apartment state Thread thread = new(delegate () { Clipboard.SetText(user.Id); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); MessageBox.Show("Copied!"); } } } } private async void deleteRoomToolStripMenuItem_Click(object sender, EventArgs e) { if (lbRooms.SelectedItems.Count > 0) { string? itemSelected = (string?)lbRooms.SelectedItems[lbRooms.SelectedItems.Count - 1]; if (itemSelected != null) { var dialogResult = MessageBox.Show("Are You Sure You Want To Delete This Room?\nThis will kick everyone currently in the room out.", "are you sure..?", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (dialogResult == DialogResult.Yes) { // get the room var room = RoomList.FirstOrDefault(e => e.Name == itemSelected); if (room != null) { var apiResult = await _apiService.DeleteRoomAsync(room.Id); if (apiResult != null && apiResult.Success) MessageBox.Show("Deleted!"); else MessageBox.Show("There was an error deleting the room. Try Again?", "", MessageBoxButtons.OK, MessageBoxIcon.Error); } else MessageBox.Show("This room is unknown. It may have already been deleted."); } } } } private async void deleteUserToolStripMenuItem_Click(object sender, EventArgs e) { if (lvUserDirectory.SelectedItems.Count > 0) { string? itemSelected = (string?)lvUserDirectory.SelectedItems[lvUserDirectory.SelectedItems.Count - 1].Text; if (itemSelected != null) { var dialogResult = MessageBox.Show("Are You Sure You Want To Delete This User?\n" + "This should only be done as a last resort. You should take the proper percautions to ensure the user cannot make a new account.\n" + "Proper moderation tools are coming soon.\n" + "Doing this will also log out the user if they are logged in.", "are you sure...?", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (dialogResult == DialogResult.Yes) { // get the user var user = UserDirectory.FirstOrDefault(e => e.Username == itemSelected); if (user != null) { var apiResult = await _apiService.DeleteUserById(user.Id); if (apiResult != null && apiResult.Success) MessageBox.Show("User Deleted!"); else MessageBox.Show("There was an error deleting the user. Try Again?", "", MessageBoxButtons.OK, MessageBoxIcon.Error); } else MessageBox.Show("This user is unknown. They may have already been deleted."); } } } } private async void adminDirectMessageToolStripMenuItem_Click(object sender, EventArgs e) { if (lvUserDirectory.SelectedItems.Count > 0) { string? itemSelected = (string?)lvUserDirectory.SelectedItems[lvUserDirectory.SelectedItems.Count - 1].Text; if (itemSelected != null) { // get the user var user = UserDirectory.FirstOrDefault(e => e.Username == itemSelected); if (user != null) { var userDto = await _apiService.GetUserInformationAsync(user.Id); if(userDto != null && userDto.Success && userDto.Data != null) { // immediately start a dm chat with the user, the user will be informed immediately if the user is an admin DirectMessage directMessage = new DirectMessage(_gatewayService, _apiService, userDto.Data, null); directMessage.Show(); } } } } } private async Task OnSuccessfulLogin() { // double check if (_apiService.CurrentUser != null && _apiService.SessionToken != null) { LoggingService.LogString($"Logged In As '{_apiService.CurrentUser.Username}'"); LoggingService.LogString("Starting SignalR Connection..."); // start gateway connection await _gatewayService.StartAsync(); // subscribe to gateway events _gatewayService.OnServerReconnecting += _gatewayService_OnServerReconnecting; _gatewayService.OnServerReconnected += _gatewayService_OnServerReconnected; _gatewayService.OnServerDisconnect += _gatewayService_OnServerDisconnect; _gatewayService.OnDirectMessageReceived += _gatewayService_OnDirectMessageReceived; _gatewayService.OnRefreshUserListsReceived += _gatewayService_OnRefreshUserListReceived; _gatewayService.OnRefreshRoomListReceived += _gatewayService_OnRefreshRoomListReceived; _gatewayService.OnRefreshContactsListReceived += _gatewayService_OnRefreshContactsListReceived; _gatewayService.OnServerConfigReceived += _gatewayService_OnServerConfigReceived; _gatewayService.OnUserForceLogout += _gatewayService_OnUserForceLogout; _apiService.OnCurrentUserUpdate += _apiService_OnCurrentUserUpdate; if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected) { LoggingService.LogString("Connected To SignalR Succesfully."); LoggingService.LogString("Building UI..."); // we are fully logged in, get current user profile pic and set up ui llblSignIn.Visible = false; lblWelcome.Text = $"Welcome, {_apiService.CurrentUser.Username}"; lblCurrencyAmount.Visible = true; pbCurrencyIcon.Visible = true; lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0"); llblSignOut.Visible = true; llblEditProfile.Visible = true; tbcMain.Enabled = true; var pfpRes = await _apiService.GetUserProfilePic(_apiService.CurrentUser.Id); if (pfpRes.Success && pfpRes.Data != null) { using (var ms = new MemoryStream(pfpRes.Data)) { pbUserPfp.Image = Image.FromStream(ms); ms.Dispose(); } } if (lvUserDirectory.Items.Count <= 0) await RefreshUsers(); // prevent edge case where the refresh event never gets sent to connecting client await RefreshContactsList(); await RefreshRoomsList(); // set status context menu checked // TODO - figure out more efficient way to do this UserStatus cuStatus = (UserStatus)_apiService.CurrentUser.Status; switch (cuStatus) { case UserStatus.Online: onlineToolStripMenuItem.Checked = true; awayToolStripMenuItem.Checked = false; doNotDisturbToolStripMenuItem.Checked = false; invisibleToolStripMenuItem.Checked = false; break; case UserStatus.Away: onlineToolStripMenuItem.Checked = false; awayToolStripMenuItem.Checked = true; doNotDisturbToolStripMenuItem.Checked = false; invisibleToolStripMenuItem.Checked = false; break; case UserStatus.DoNotDisturb: onlineToolStripMenuItem.Checked = false; awayToolStripMenuItem.Checked = false; doNotDisturbToolStripMenuItem.Checked = true; invisibleToolStripMenuItem.Checked = false; break; case UserStatus.Offline: onlineToolStripMenuItem.Checked = false; awayToolStripMenuItem.Checked = false; doNotDisturbToolStripMenuItem.Checked = false; invisibleToolStripMenuItem.Checked = true; break; } var current = DateTime.UtcNow; if (current > _apiService.CurrentUser.LastCurrencySpin.ToUniversalTime() || _apiService.CurrentUser.LastCurrencySpin == new DateTime()) llblClaimSpin.Visible = true; else llblClaimSpin.Visible = false; if (_config.StartMinimized) WindowState = FormWindowState.Minimized; if (_apiService.CurrentUser.Role == "Admin") { LoggingService.LogString("Current User Is An Admin. Using Admin Context Menu Strips..."); lvUserDirectory.ContextMenuStrip = ctxmAdminUserList; lbRooms.ContextMenuStrip = ctxmAdminRoomList; } LoggingService.LogString("Client Ready"); } } } private async Task RefreshUsers() { LoggingService.LogString("Refreshing All Users List..."); if (IsHandleCreated && !IsDisposed) { await Invoke(async delegate () { // get all users on server var userList = await _apiService.GetAllUsersAsync(); if (userList != null && userList.Success && userList.Data != null) { // clear the list lvUserDirectory.Items.Clear(); UserDirectory.Clear(); // populate list foreach (var user in userList.Data) { lvUserDirectory.Items.Add(user.Username, user.Status); UserDirectory.Add(user); } if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs) LoggingService.LogModel(userList.Data); } }); } } private async Task RefreshRoomsList() { LoggingService.LogString("Refreshing Rooms List..."); if (IsHandleCreated && !IsDisposed) { await Invoke(async delegate () { lbRooms.Items.Clear(); var roomsRes = await _apiService.GetAllRoomsAsync(); if (roomsRes.Success && roomsRes.Data != null) { foreach (var room in roomsRes.Data) { lbRooms.Items.Add(room.Name); } RoomList = roomsRes.Data; if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs) LoggingService.LogModel(roomsRes.Data); } // always add lobby room to rooms list lbRooms.Items.Add("Lobby"); }); } } private async Task RefreshContactsList() { LoggingService.LogString("Refreshing Contacts List..."); if (IsHandleCreated && !IsDisposed) { await Invoke(async delegate () { lvContacts.Items.Clear(); Contacts.Clear(); lblRequestNotif.Visible = false; var contactsRes = await _apiService.GetCurrentUserContacts(); if (contactsRes.Success && contactsRes.Data != null) { if (contactsRes.Data.Where(e => e.UserId == _apiService.CurrentUser!.Id && e.UserStatus == Contact.ContactStatus.AwaitingApprovalFromSelf).Count() >= 1) lblRequestNotif.Visible = true; else lblRequestNotif.Visible = false; foreach (var contact in contactsRes.Data) { ServiceResponse user = null!; if (contact.OwnerId == _apiService.CurrentUser!.Id) user = await _apiService.GetUserInformationAsync(contact.UserId); else if (contact.UserId == _apiService.CurrentUser!.Id) user = await _apiService.GetUserInformationAsync(contact.OwnerId); if (user.Data != null) { Contacts.Add(contact); if (contact.OwnerId == _apiService.CurrentUser!.Id) { switch (contact.OwnerStatus) { case Contact.ContactStatus.AwaitingApprovalFromOther: var lvi = lvContacts.Items.Add($"{user.Data.Username} [Request Sent]"); await AddProfilePicToList(user.Data.Id); if (ilProfilePics.Images.ContainsKey(user.Data.Id)) lvi.ImageKey = user.Data.Id; else lvi.ImageKey = "DEFAULT"; break; case Contact.ContactStatus.Accepted: var lvi2 = lvContacts.Items.Add($"{user.Data.Username}"); await AddProfilePicToList(user.Data.Id); if (ilProfilePics.Images.ContainsKey(user.Data.Id)) lvi2.ImageKey = user.Data.Id; else lvi2.ImageKey = "DEFAULT"; break; } } else if (contact.UserId == _apiService.CurrentUser!.Id) { switch (contact.UserStatus) { case Contact.ContactStatus.AwaitingApprovalFromSelf: var lvi = lvContacts.Items.Add($"{user.Data.Username} [Contact Request]"); await AddProfilePicToList(user.Data.Id); if (ilProfilePics.Images.ContainsKey(user.Data.Id)) lvi.ImageKey = user.Data.Id; else lvi.ImageKey = "DEFAULT"; AudioService.PlaySoundEffect("sndContactRequest"); break; case Contact.ContactStatus.Accepted: var lvi2 = lvContacts.Items.Add($"{user.Data.Username}"); await AddProfilePicToList(user.Data.Id); if (ilProfilePics.Images.ContainsKey(user.Data.Id)) lvi2.ImageKey = user.Data.Id; else lvi2.ImageKey = "DEFAULT"; break; } } } } if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs) LoggingService.LogModel(contactsRes.Data); } }); } } private async Task StartRequestNotifBlankLoop(Label label) { while (true) { if(IsHandleCreated && !IsDisposed) { await Invoke(async delegate () { label.ForeColor = Color.Red; await Task.Delay(500); label.ForeColor = Color.Blue; await Task.Delay(500); }); } } } private async Task AddProfilePicToList(string userId) { var result = await _apiService.GetUserProfilePic(userId); if (result != null && result.Success && result.Data != null) { using (var ms = new MemoryStream(result.Data)) { ilProfilePics.Images.Add(userId, Image.FromStream(ms)); ms.Dispose(); } } else if (result != null) LoggingService.LogString($"User Has No Profile Picture Or It Could Not Be Loaded.\n{result.Message}"); } private async Task GetAndAddStoreThumbnail(StoreItem item) { try { using HttpClient client = new(); var response = await client.GetAsync(item.ThumbnailUrl); if (response != null && response.IsSuccessStatusCode) { using var stream = await response.Content.ReadAsStreamAsync(); Image image = Image.FromStream(stream); stream.Dispose(); ilStoreThumbnails.Images.Add(item.Id.ToString(), image); } else if (response != null) LoggingService.LogString($"Store Item Thumbnail Could Not Be Loaded Due To Status Code {response.StatusCode}"); else LoggingService.LogString("Store Item Thumbnail Could Not Be Loaded"); client.Dispose(); } catch (Exception ex) { LoggingService.LogString("Store Item Thumbnail Could Not Be Loaded\n" + ex.Message); } } public void RefreshCurrencyCounter() { if (IsHandleCreated && !IsDisposed) { Invoke(delegate () { lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0"); }); } } private async void _gatewayService_OnServerDisconnect(object? sender, EventArgs e) { LoggingService.LogString("Disconnected From SignalR"); if (DialogResult == DialogResult.OK) return; var args = (ServerConnectionClosedEventArgs)e; string? error = string.Empty; if (args.Error != null) { error = args.Error.Message; LoggingService.LogString($"{args.Error.Message}\n{args.Error.StackTrace}"); } // disconnect can sometimes be caused by an expired JWT token, attempt connection restart await _gatewayService.StopAsync(); await _gatewayService.StartAsync(); if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected) { BeginInvoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; }); return; } ConnectionClosed frmConnectionClosed = new ConnectionClosed(); var result = frmConnectionClosed.ShowDialog(); if (result == DialogResult.OK) { // tell the gateway service to attempt reconnection Reconnect: if (_gatewayService.HubConnection != null) { await _gatewayService.StopAsync(); await _gatewayService.StartAsync(); if (_gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected) { Invoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; }); } else { // reshow the dialog frmConnectionClosed.StatusLabel.Text = "Couldn't Reconnect. The Server Could Be Down."; var result1 = frmConnectionClosed.ShowDialog(); if (result1 == DialogResult.OK) goto Reconnect; else Environment.Exit(0); } } } else Environment.Exit(0); } private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e) { var args = (ServerConnectionReconnectingEventArgs)e; if (args.Error == null) LoggingService.LogString("Server Requested Reconnect. Reconnecting..."); else LoggingService.LogString($"SignalR Reconnecting Due To An Error.\n{args.Error.Message}\n{args.Error.StackTrace}"); if (IsHandleCreated && !IsDisposed) { Invoke(delegate () { tbcMain.Enabled = false; lblConnectionLost.Visible = true; }); } } private async void _gatewayService_OnServerReconnected(object? sender, EventArgs e) { LoggingService.LogString("SignalR Reconnected"); if (IsHandleCreated && !IsDisposed) { Invoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; }); } // ensure status is set to whatever the current user was set to try { await _gatewayService.UpdateStatus(_apiService.CurrentUser.Status); } catch (InvalidOperationException) { LoggingService.LogString("Could Not Set Status Back To Online"); } } private async void _gatewayService_OnServerConfigReceived(object? sender, EventArgs e) { var args = (ServerConfigEventArgs)e; LoggingService.LogString($"Server Config Received"); LoggingService.LogModel(args.ServerConfig); if (_serverConfig != null) return; // only set server config upon client restart, not during reconnect (preventing log spam) if (args.ServerConfig != null) { _serverConfig = args.ServerConfig; if (args.ServerConfig.IsDown) { LoggingService.LogString("Server Is Marked As Down"); MessageBox.Show($"Sorry, This Server Is Currently Down.\nMessage: {args.ServerConfig.IsDownMessage}\n\nPlease Try Again Later"); if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected) { await _gatewayService.StopAsync(); await _gatewayService.DisposeAsync(); } Environment.Exit(0); } } } private void _gatewayService_OnDirectMessageReceived(object? sender, EventArgs e) { var args = (DirectMessageEventArgs)e; LoggingService.LogString($"Direct Message Received From '{args.User.Username}'"); DirectMessage? existingForm = (DirectMessage?)Application.OpenForms.Cast
().FirstOrDefault(e => e.Name == "DirectMessage"); if (existingForm != null && existingForm.User.Id == args.User.Id) { // we want to just add to its text box existingForm.Messages.Add($"[{args.User.Username}] {args.Message.Content}\n"); } else { // start a new form DirectMessage frmDirectMessage = new DirectMessage(_gatewayService, _apiService, args.User, args.Message); Task.Run(frmDirectMessage.ShowDialog); } } private async void _gatewayService_OnUserForceLogout(object? sender, EventArgs e) { MessageBox.Show("Connection To Server Lost. You Were Logged Out.\nTry Signing In Again.\nThe Application Will Now Close.", "oops.", MessageBoxButtons.OK, MessageBoxIcon.Error); await _gatewayService.StopAsync(); if (File.Exists("./session.token")) File.Delete("./session.token"); Environment.Exit(0); // dont want the user to try and make a new account right away } private void _apiService_OnCurrentUserUpdate(object? sender, EventArgs e) { lblWelcome.Text = $"Welcome, {_apiService.CurrentUser.Username}"; RefreshCurrencyCounter(); } private async void _gatewayService_OnRefreshContactsListReceived(object? sender, EventArgs e) => await RefreshContactsList(); private async void _gatewayService_OnRefreshRoomListReceived(object? sender, EventArgs e) => await RefreshRoomsList(); private async void _gatewayService_OnRefreshUserListReceived(object? sender, EventArgs e) => await RefreshUsers(); } }