Compare commits

...

39 Commits
5.2 ... master

Author SHA1 Message Date
a95d012d69 Update Packages That Could Be Updated
Rework Missed Refresh Token Code
Minor Version Bump
2025-09-21 14:16:41 -07:00
d1d3af2ec9 Rework Refresh Token Storage Solution To Use Windows Credentials API
Minor Version Bump
2025-09-21 13:49:11 -07:00
4e61a1d7b9 Version Bump 2025-08-03 15:00:37 -07:00
446745d4df Implement CreateProfileImage To Combine Precense Icon, Profile Image, and Cosmetic To Make Final Profile Image 2025-08-03 15:00:09 -07:00
00df7505a7 Move Cosmetic Download To Main Form Thread 2025-08-03 14:27:00 -07:00
b17d391406 Minor Version Bump 2025-07-31 15:21:50 -07:00
2092d2c6d3 Keep Default KA Values 2025-07-31 15:05:03 -07:00
fd743dcb42 Do Not Set Server Timeout (it was the default value anyways) 2025-07-31 15:00:37 -07:00
9214460927 Server Timeout = 30s
Keep-Alive Interval  = 1m
2025-07-31 14:59:13 -07:00
1b30636937 Configure TimeSpans For ServerTimeout And KeepAliveInterval 2025-07-31 10:18:57 -07:00
72d9b99c4c Refresh Session Everytime Gateway Connection Is Used To Prevent The Server From Aborting Requests 2025-07-31 10:06:12 -07:00
c7137e4c7e Version Bump 2025-07-27 13:56:55 -07:00
4ee524778d Merge pull request 'Email Features' (#5) from email-features into master
Reviewed-on: #5
2025-07-27 13:55:57 -07:00
cfcc5ad4c1 Message Box On Register To Inform User Of Email Verification 2025-07-27 13:54:17 -07:00
b81b059177 Initial Implementation Of Email Features 2025-07-27 13:51:44 -07:00
5d553cdf58 Change Default Pfp 2025-07-19 13:54:14 -07:00
8cb59e1b21 Work Around Weird Offline Behaviour 2025-07-19 13:31:54 -07:00
6acb72975e Optimizations To Main Form 2025-07-17 15:33:17 -07:00
098a59f555 Avoid Updating Store Items ListView If Count Is Still Equal (No New Items)
Fix Stock Market Game UI Positioning
2025-07-17 15:27:16 -07:00
e6c842cb19 Remove Uneeded Log Entry 2025-07-17 15:20:53 -07:00
758a5a96dc Add Additional Logging/Diagnostics For Weird Status Behaviours 2025-07-17 14:36:56 -07:00
6bf77aa12e Add enableDebugLogs Config Property 2025-07-15 14:15:31 -07:00
f98e5a490c Implement Extra Logging To Diagnose Status Issues
Better Connection Error Handling
2025-07-15 14:04:46 -07:00
75b61c0a8c Move LoggingService to API library
Add Additional Logging For SignalR
2025-07-14 19:07:54 -07:00
0432fec3cd Finish Initial Implementation Of Admin Menus
Implement Logout Event
Fix Own Direct Messages Not New Lining The Chat
Version Bump
2025-07-13 17:27:16 -07:00
de2a81d485 Initial WIP Implementation Of Admin Menus 2025-07-12 14:23:15 -07:00
fca34d5479 Version Bump 2025-07-12 11:14:40 -07:00
932ee5fe62 Implement OnCurrentUserUpdate Event 2025-07-12 11:14:22 -07:00
34f478213e Version Bump 2025-07-11 12:29:06 -07:00
ffa44ff036 More Profile Image Sizing Fixes 2025-07-11 12:28:22 -07:00
217e3f301a Fix Some Profile Images Not Showing As A Square 2025-07-11 12:15:53 -07:00
28358f44f8 Version Bump 2025-07-11 11:07:10 -07:00
eb9fd6c8cf Fix Order Of Operations In StoreItemDisplay 2025-07-11 11:03:21 -07:00
a325a622a5 Check If User Already Owns The Item Requested 2025-07-11 11:02:10 -07:00
0c76a206f7 Refresh Currency Counter When Buying From Store 2025-07-11 10:58:28 -07:00
502dda6436 Profile Editing - Reworked Cosmetic Selection 2025-07-11 10:43:40 -07:00
868f6fa067 Positioning And Size Fixes 2025-07-11 10:18:37 -07:00
003d01fe4e Implement Store Frontend
Implement Profile Cosmetics
2025-07-10 17:18:54 -07:00
0d93557959 Implement Store API Functions
Fix CurrentUser Not Being Up To Date When Updating User API Side
2025-07-10 10:34:09 -07:00
46 changed files with 2702 additions and 766 deletions

View File

@ -11,5 +11,6 @@
public DateTime CreatedAt { get; set; } = new DateTime();
public int Status { get; set; } = 0;
public int CurrencyAmount { get; set; } = 0;
public int ProfileCosmeticId { get; set; } = 0;
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Dtos.User
{
public class UserPasswordResetDto
{
public string Token { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
}

View File

@ -6,5 +6,6 @@
public string Username { get; set; } = string.Empty;
public string Bio { get; set; } = string.Empty;
public DateTime DateOfBirth { get; set; } = new DateTime();
public int ProfileCosmeticId { get; set; } = 0;
}
}

View File

@ -0,0 +1,8 @@
namespace QtCNETAPI.Enums
{
public enum StoreItemType
{
ProfileCosmetic = 1,
ClientCosmetic = 2
}
}

View File

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace QtCNETAPI.Models
{
public class OwnedStoreItem
{
[Key]
public int Id { get; set; }
public string UserId { get; set; } = string.Empty;
public int StoreItemId { get; set; }
public virtual User? User { get; }
}
}

View File

@ -15,9 +15,11 @@
public int CurrencyAmount { get; set; } = 0;
public int StockAmount { get; set; } = 0;
public DateTime LastCurrencySpin { get; set; }
public int ActiveProfileCosmetic { get; set; } = 0;
public virtual IEnumerable<RefreshToken>? RefreshTokens { get; }
public virtual IEnumerable<Contact>? ContactsMade { get; }
public virtual IEnumerable<Contact>? ContactsList { get; }
public virtual IEnumerable<OwnedStoreItem>? OwnedStoreItems { get; }
}
}

View File

@ -7,10 +7,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Framework.Win32.CredentialManager" Version="1.7.6" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.9" />
<PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.10.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,23 @@
using QtCNETAPI.Enums;
using System.Text.Json.Serialization;
namespace QtCNETAPI.Schema
{
public class StoreItem
{
[JsonPropertyName("Id")]
public int Id { get; set; }
[JsonPropertyName("Type")]
public StoreItemType Type { get; set; }
[JsonPropertyName("Price")]
public int Price { get; set; }
[JsonPropertyName("Name")]
public string Name { get; set; } = string.Empty;
[JsonPropertyName("Description")]
public string Description { get; set; } = string.Empty;
[JsonPropertyName("AssetUrl")]
public string AssetUrl { get; set; } = string.Empty;
[JsonPropertyName("ThumbnailUrl")]
public string ThumbnailUrl { get; set; } = string.Empty;
}
}

View File

@ -2,9 +2,9 @@
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Enums;
using QtCNETAPI.Models;
using QtCNETAPI.Schema;
using RestSharp;
using System.IdentityModel.Tokens.Jwt;
using System.Resources;
using System.Text.Json;
namespace QtCNETAPI.Services.ApiService
@ -13,10 +13,15 @@ namespace QtCNETAPI.Services.ApiService
{
private User? user;
private RestClient _client;
private LoggingService _loggingService;
private CredentialService _credService = new();
internal string? sessionToken;
internal string apiUri;
public event EventHandler? OnCurrentUserUpdate;
public string? SessionToken
{
get { return sessionToken; }
@ -28,9 +33,10 @@ namespace QtCNETAPI.Services.ApiService
get { return user; }
}
public ApiService(string apiUrl)
public ApiService(string apiUrl, LoggingService loggingService)
{
apiUri = apiUrl;
_loggingService = loggingService;
_client = new RestClient(apiUri);
}
@ -151,10 +157,9 @@ namespace QtCNETAPI.Services.ApiService
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
// update currentuser model
CurrentUser!.Username = response.Data!.Username;
CurrentUser.Bio = response.Data.Bio;
CurrentUser.DateOfBirth = response.Data.DateOfBirth;
// 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;
@ -180,6 +185,10 @@ 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
{
@ -228,9 +237,9 @@ namespace QtCNETAPI.Services.ApiService
}
}
public async Task<ServiceResponse<User>> LoginAsync(UserLoginDto userLoginDto)
public async Task<ServiceResponse<string>> LoginAsync(UserLoginDto userLoginDto)
{
var serviceResponse = new ServiceResponse<User>();
var serviceResponse = new ServiceResponse<string>();
try
{
@ -250,13 +259,11 @@ namespace QtCNETAPI.Services.ApiService
{
SessionToken = response.Data!;
await File.WriteAllTextAsync("./session.token", response.Message);
var user = await SetCurrentUser();
serviceResponse.Success = true;
if (response.Message != null) serviceResponse.Message = response.Message;
serviceResponse.Data = user;
serviceResponse.Data = response.Message;
}
else
{
@ -273,16 +280,85 @@ namespace QtCNETAPI.Services.ApiService
return serviceResponse;
}
private async Task<User> SetCurrentUser()
public async Task<ServiceResponse<bool>> ResendVerificationEmail(string email)
{
var serviceResponse = new ServiceResponse<bool>();
var restRequest = new RestRequest($"auth/resend-verification-email")
.AddQueryParameter("email", email);
var response = await _client.PostAsync<ServiceResponse<bool>>(restRequest);
if (response != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded.";
}
return serviceResponse;
}
public async Task<ServiceResponse<bool>> SendPasswordResetEmail(string email)
{
var serviceResponse = new ServiceResponse<bool>();
var restRequest = new RestRequest($"auth/request-password-reset")
.AddQueryParameter("email", email);
var response = await _client.PostAsync<ServiceResponse<bool>>(restRequest);
if (response != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded.";
}
return serviceResponse;
}
public async Task<ServiceResponse<bool>> ResetPassword(UserPasswordResetDto request)
{
var serviceResponse = new ServiceResponse<bool>();
var restRequest = new RestRequest($"auth/reset-password")
.AddJsonBody(request);
var response = await _client.PostAsync<ServiceResponse<bool>>(restRequest);
if (response != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded.";
}
return serviceResponse;
}
public async Task<User> SetCurrentUser()
{
var userRequest = new RestRequest("users/user-authorized")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var userResponse = await _client.GetAsync<ServiceResponse<User>>(userRequest);
if (userResponse != null || userResponse!.Data != null)
if (userResponse != null && userResponse.Success && userResponse.Data != null)
{
user = userResponse.Data;
return userResponse.Data!;
_loggingService.LogString($"Current User's Status Is {userResponse.Data.Status}");
return userResponse.Data;
} else
{
throw new NullReferenceException("Current User could not be set.");
@ -337,21 +413,23 @@ namespace QtCNETAPI.Services.ApiService
public async Task<ServiceResponse<string>> RefreshSessionIfInvalid()
{
var tokenHandler = new JwtSecurityTokenHandler();
var refToken = await File.ReadAllTextAsync("./session.token");
var refToken = _credService.GetAccessToken(); // fuck CA1416, if this is being ran on linux it should just crash (theoretically)
if (refToken == null)
{
// treat as session expired
return new ServiceResponse<string> { Success = false, Message = "Refresh Token Not Found. Session Expired." };
}
JwtSecurityToken token = tokenHandler.ReadJwtToken(SessionToken);
if(DateTime.Compare(DateTime.UtcNow, token.ValidTo) > 0)
{
if (!File.Exists("./session.token")) { return new ServiceResponse<string> { Success = false, Message = "Session File Not Found. Session Expired." }; }
var result = await RefreshLogin(refToken);
if (result == null || result.Success == false)
{
File.Delete("./session.token");
return new ServiceResponse<string> { Success = false, Message = "Session Expired." };
return new ServiceResponse<string> { Success = false, Message = "Session Expired." }; // logging in again should overwrite old token
} else return new ServiceResponse<string> { Success = true, Data = refToken };
} else return new ServiceResponse<string> { Success = true, Data = refToken };
}
@ -590,6 +668,10 @@ 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;
@ -637,6 +719,10 @@ 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;
@ -661,6 +747,10 @@ 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;
@ -713,5 +803,151 @@ namespace QtCNETAPI.Services.ApiService
return serviceResponse;
}
public async Task<ServiceResponse<List<StoreItem>>> GetStoreItems()
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<List<StoreItem>>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("store/all-items")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.GetAsync<ServiceResponse<List<StoreItem>>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
public async Task<ServiceResponse<StoreItem>> GetStoreItem(int id)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<StoreItem>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("store/item")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("id", id);
var response = await _client.GetAsync<ServiceResponse<StoreItem>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
public async Task<ServiceResponse<bool>> BuyStoreItem(int id)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<bool>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("store/buy-item")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("id", id);
var response = await _client.PostAsync<ServiceResponse<bool>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
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;
}
public async Task<ServiceResponse<List<OwnedStoreItem>>> GetOwnedStoreItems()
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<List<OwnedStoreItem>>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("store/bought-items")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.GetAsync<ServiceResponse<List<OwnedStoreItem>>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
public async Task<ServiceResponse<OwnedStoreItem>> GetOwnedStoreItem(int id)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<OwnedStoreItem>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("store/bought-item")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("id", id);
var response = await _client.GetAsync<ServiceResponse<OwnedStoreItem>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
public async Task<ServiceResponse<User>> DeleteUserById(string id)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<User>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("users/delete-user")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("id", id);
var response = await _client.DeleteAsync<ServiceResponse<User>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
}
}

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using QtCNETAPI.Enums;
using QtCNETAPI.Schema;
namespace QtCNETAPI.Services.ApiService
{
@ -16,12 +17,19 @@ namespace QtCNETAPI.Services.ApiService
public string? SessionToken { get; set; }
public User CurrentUser { get; }
public event EventHandler? OnCurrentUserUpdate;
public Task<ServiceResponse<string>> PingServerAsync();
public Task<ServiceResponse<List<UserInformationDto>>> GetOnlineUsersAsync();
public Task<ServiceResponse<List<UserInformationDto>>> GetAllUsersAsync();
public Task<ServiceResponse<User>> LoginAsync(UserLoginDto userLoginDto);
public Task<ServiceResponse<User>> DeleteUserById(string id);
public Task<ServiceResponse<string>> LoginAsync(UserLoginDto userLoginDto);
public Task<ServiceResponse<bool>> ResendVerificationEmail(string email);
public Task<ServiceResponse<bool>> SendPasswordResetEmail(string email);
public Task<ServiceResponse<bool>> ResetPassword(UserPasswordResetDto request);
public Task<ServiceResponse<User>> RefreshLogin(string refreshToken);
public Task<ServiceResponse<string>> RefreshSessionIfInvalid();
public Task<User> SetCurrentUser();
public Task<ServiceResponse<User>> RegisterAsync(UserDto userDto);
public Task<ServiceResponse<UserInformationDto>> GetUserInformationAsync(string id);
public Task<ServiceResponse<UserInformationDto>> UpdateUserInformationAsync(UserUpdateInformationDto request);
@ -40,5 +48,10 @@ namespace QtCNETAPI.Services.ApiService
public Task<ServiceResponse<UserStockActionResultDto>> SellStock(int amount);
public Task<ServiceResponse<int>> GetRandomNumber();
public Task<ServiceResponse<NumberGuessResult>> GuessRandomNumber(int original, int guess);
public Task<ServiceResponse<List<StoreItem>>> GetStoreItems();
public Task<ServiceResponse<StoreItem>> GetStoreItem(int id);
public Task<ServiceResponse<bool>> BuyStoreItem(int id);
public Task<ServiceResponse<List<OwnedStoreItem>>> GetOwnedStoreItems();
public Task<ServiceResponse<OwnedStoreItem>> GetOwnedStoreItem(int id);
}
}

View File

@ -0,0 +1,26 @@
using Meziantou.Framework.Win32;
namespace QtCNETAPI.Services
{
public class CredentialService()
{
public void SaveAccessToken(string username, string accessToken)
{
string applicationName = "QtC.NET";
if (System.Diagnostics.Debugger.IsAttached) applicationName = "QtC.NET.Development";
CredentialManager.WriteCredential(applicationName, username, accessToken, $"Access Token For User {username} On QtC.NET", CredentialPersistence.LocalMachine);
}
public string? GetAccessToken()
{
string applicationName = "QtC.NET";
if (System.Diagnostics.Debugger.IsAttached) applicationName = "QtC.NET.Development";
var credential = CredentialManager.ReadCredential(applicationName);
if (credential == null) return null;
return credential.Password;
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Events;
using QtCNETAPI.Models;
@ -6,48 +7,55 @@ using QtCNETAPI.Services.ApiService;
namespace QtCNETAPI.Services.GatewayService
{
public class GatewayService : IGatewayService, IAsyncDisposable
public class GatewayService(string GWUrl, IApiService apiService, LoggingService loggingService) : IGatewayService, IAsyncDisposable
{
internal string gwBaseUri = "127.0.0.1";
internal string gwBaseUri = GWUrl;
public Room? CurrentRoom { get; private set; }
public bool InLobby { get; private set; }
public HubConnection? HubConnection { get; private set; }
public event EventHandler OnRoomMessageReceived;
public event EventHandler OnRoomUserListReceived;
public event EventHandler OnGuestUserJoin;
public event EventHandler OnRefreshUserListsReceived;
public event EventHandler OnRefreshRoomListReceived;
public event EventHandler OnRefreshContactsListReceived;
public event EventHandler OnClientFunctionReceived;
public event EventHandler OnDirectMessageReceived;
public event EventHandler OnServerConfigReceived;
public event EventHandler OnServerDisconnect;
public event EventHandler OnServerReconnecting;
public event EventHandler OnServerReconnected;
public event EventHandler? OnRoomMessageReceived;
public event EventHandler? OnRoomUserListReceived;
public event EventHandler? OnGuestUserJoin;
public event EventHandler? OnRefreshUserListsReceived;
public event EventHandler? OnRefreshRoomListReceived;
public event EventHandler? OnRoomDeleted;
public event EventHandler? OnRefreshContactsListReceived;
public event EventHandler? OnClientFunctionReceived;
public event EventHandler? OnDirectMessageReceived;
public event EventHandler? OnServerConfigReceived;
public event EventHandler? OnServerDisconnect;
public event EventHandler? OnServerReconnecting;
public event EventHandler? OnServerReconnected;
public event EventHandler? OnUserForceLogout;
private IApiService _apiService;
public GatewayService(string GWUrl, IApiService apiService)
{
gwBaseUri = GWUrl;
_apiService = apiService;
}
private IApiService _apiService = apiService;
private LoggingService _loggingService = loggingService;
public async Task StartAsync()
{
// just to be safe (it doesn't load the server since it shouldn't request a new one unless its actually expired)
await _apiService.RefreshSessionIfInvalid();
// build connection
var gwConBuilder = new HubConnectionBuilder()
.WithAutomaticReconnect()
.ConfigureLogging((builder) =>
{
builder.AddProvider(new LoggingServiceProvider(_loggingService));
if (System.Diagnostics.Debugger.IsAttached) builder.SetMinimumLevel(LogLevel.Debug);
else builder.SetMinimumLevel(LogLevel.Error);
})
.WithUrl(gwBaseUri, options =>
{
options.AccessTokenProvider = () => Task.FromResult(_apiService.SessionToken);
});
options.AccessTokenProvider = async () =>
{
// this should hopefully refresh the session every time the gateway connection is used to prevent connection aborts
await _apiService.RefreshSessionIfInvalid();
return _apiService.SessionToken;
};
})
.WithStatefulReconnect();
HubConnection = gwConBuilder.Build();
// register events
@ -60,14 +68,27 @@ namespace QtCNETAPI.Services.GatewayService
HubConnection.On<ServerConfig>("ReceiveServerConfig", (serverConfig) => OnServerConfigReceived?.Invoke(this, new ServerConfigEventArgs { ServerConfig = serverConfig }));
HubConnection.On<List<User>>("RoomUserList", (userList) => OnRoomUserListReceived?.Invoke(this, new RoomListEventArgs { UserList = userList }));
HubConnection.On<string>("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.Closed += HubConnection_Closed;
HubConnection.Reconnecting += HubConnection_Reconnecting;
HubConnection.Reconnected += HubConnection_Reconnected;
// start connection
try
{
await HubConnection.StartAsync();
}
catch (HttpRequestException ex)
{
_loggingService.LogString($"Unable To Connect To SignalR.\n{ex.Message}\n{ex.StackTrace}");
return;
}
// ensure current user is up to date (particularly status)
await _apiService.SetCurrentUser();
}
public async Task StopAsync()
{
@ -101,8 +122,6 @@ namespace QtCNETAPI.Services.GatewayService
public async Task JoinLobbyAsync()
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("JoinLobby", _apiService.CurrentUser);
@ -112,8 +131,6 @@ namespace QtCNETAPI.Services.GatewayService
public async Task JoinRoomAsync(Room room)
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
if (InLobby == true)
@ -132,8 +149,6 @@ namespace QtCNETAPI.Services.GatewayService
public async Task LeaveRoomAsync()
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
if (InLobby)
@ -150,8 +165,6 @@ namespace QtCNETAPI.Services.GatewayService
public async Task PostMessageAsync(Message message)
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("SendMessage", _apiService.CurrentUser, message, InLobby, CurrentRoom);
@ -168,11 +181,12 @@ namespace QtCNETAPI.Services.GatewayService
public async Task UpdateStatus(int status)
{
await _apiService.RefreshSessionIfInvalid();
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();
}

View File

@ -104,6 +104,11 @@ namespace QtCNETAPI.Services.GatewayService
/// </summary>
public event EventHandler OnRoomUserListReceived;
/// <summary>
/// Fires When The Room The User Is In Gets Deleted By An Admin
/// </summary>
public event EventHandler OnRoomDeleted;
/// <summary>
/// Fires When A Guest User Joins Your Room
/// </summary>
@ -153,5 +158,10 @@ namespace QtCNETAPI.Services.GatewayService
/// When the Connection Reconnects, This Event Fires
/// </summary>
public event EventHandler OnServerReconnected;
/// <summary>
/// Fires When The Current User Is Signed Out By The Server
/// </summary>
public event EventHandler OnUserForceLogout;
}
}

View File

@ -5,10 +5,12 @@ using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Abstractions;
namespace qtc_net_client_2.Services
namespace QtCNETAPI.Services
{
public class LoggingService : IDisposable
public class LoggingService : IDisposable, ILogger
{
private DateTime LogDate { get; set; }
private string LogFilePath { get; set; }
@ -16,7 +18,7 @@ namespace qtc_net_client_2.Services
public LoggingService()
{
LogDate = DateTime.Now;
LogFilePath = $"./Logs/QtCClientLog_{LogDate.ToString("ddMMyyy-hhmm")}.log";
LogFilePath = $"./Logs/QtCClientLog_{LogDate:ddMMyyy-hhmm}.log";
// create log file
@ -30,8 +32,8 @@ namespace qtc_net_client_2.Services
{
try
{
Debug.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {message}");
LogFile.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {message}");
Debug.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {message}");
LogFile.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {message}");
} catch (ObjectDisposedException)
{
}
@ -45,8 +47,8 @@ namespace qtc_net_client_2.Services
string modelSerialized = JsonSerializer.Serialize(model, options: new JsonSerializerOptions { WriteIndented = true });
// log it
Debug.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {modelSerialized}");
LogFile.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {modelSerialized}");
Debug.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {modelSerialized}");
LogFile.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {modelSerialized}");
} catch (ObjectDisposedException)
{
}
@ -58,5 +60,34 @@ namespace qtc_net_client_2.Services
LogFile.Close();
LogFile.Dispose();
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
try
{
// format message
string message = $"({DateTime.Now.ToLocalTime():hh:mm}) [{logLevel}] {formatter(state, exception)}";
// log it
Debug.WriteLine(message);
LogFile.WriteLine(message);
} catch (ObjectDisposedException)
{
}
}
public bool IsEnabled(LogLevel logLevel) => true;
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default!;
}
public class LoggingServiceProvider(LoggingService? loggingService = null) : ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
if (loggingService != null) return loggingService;
else return new LoggingService();
}
public void Dispose() { }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace qtc_net_client_2.ClientModel
{
public class ComboBoxItem
{
public string? Name { get; set; }
public object? Value { get; set; }
public override string ToString()
{
return Name ?? string.Empty;
}
}
}

View File

@ -15,6 +15,9 @@ namespace qtc_net_client_2.ClientModel
[JsonPropertyName("minimizeToTray")]
[JsonRequired]
public bool MinimizeToTray { get; set; } = true;
[JsonPropertyName("enableDebugLogs")]
[JsonRequired]
public bool EnableDebugLogs { get; set; } = false;
[JsonPropertyName("serverUrl")]
[JsonRequired]

View File

@ -1,17 +1,7 @@
using qtc_net_client_2.Services;
using QtCNETAPI.Events;
using QtCNETAPI.Models;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace qtc_net_client_2.Forms
{
@ -33,6 +23,7 @@ namespace qtc_net_client_2.Forms
// subscribe to server message event
_gatewayService.OnRoomMessageReceived += _gatewayService_OnServerMessageReceived;
_gatewayService.OnRoomUserListReceived += _gatewayService_OnRoomUserListReceived;
_gatewayService.OnRoomDeleted += _gatewayService_OnRoomDeleted;
_gatewayService.OnGuestUserJoin += _gatewayService_OnGuestUserJoin;
if (_gatewayService.CurrentRoom != null) { Text = $"QtC.NET Client - Chat Room - {_gatewayService.CurrentRoom.Name}"; lblRoomName.Text = _gatewayService.CurrentRoom.Name; }
@ -94,8 +85,10 @@ namespace qtc_net_client_2.Forms
{
// get user info and open profile dialog
var user = mainForm.UserDirectory.FirstOrDefault(e => e.Username == selectedUser);
var res = await _apiService.GetUserInformationAsync(user!.Id);
var pfpRes = await _apiService.GetUserProfilePic(user!.Id);
if (user != null)
{
var res = await _apiService.GetUserInformationAsync(user.Id);
var pfpRes = await _apiService.GetUserProfilePic(user.Id);
if (res.Data != null && res.Success)
{
@ -106,6 +99,7 @@ namespace qtc_net_client_2.Forms
}
}
}
}
private void _gatewayService_OnServerMessageReceived(object? sender, EventArgs e)
{
@ -119,12 +113,16 @@ namespace qtc_net_client_2.Forms
{
var args = (RoomListEventArgs)e;
if (!IsHandleCreated) return;
lvUserList.BeginInvoke(lvUserList.Clear);
if (IsHandleCreated && !IsDisposed)
{
Invoke(delegate ()
{
lvUserList.Clear();
foreach (var user in args.UserList)
{
lvUserList.BeginInvoke(delegate () { lvUserList.Items.Add(user.Username, user.Status); });
lvUserList.Items.Add(user.Username, user.Status);
}
});
}
}
private void _gatewayService_OnGuestUserJoin(object? sender, EventArgs e)
@ -133,12 +131,32 @@ namespace qtc_net_client_2.Forms
AddMessage($"[SERVER] Guest User {args.Username} Has Joined {_gatewayService.CurrentRoom?.Name}");
}
private void _gatewayService_OnRoomDeleted(object? sender, EventArgs e)
{
if(IsHandleCreated && !IsDisposed)
{
Invoke(delegate ()
{
AddMessage($"[SERVER] This Room Was Deleted By An Admin.");
lvUserList.Clear();
lvUserList.Enabled = false;
rtxtChatbox.Enabled = false;
btnSend.Enabled = false;
});
}
}
private void AddMessage(string message)
{
if (InvokeRequired)
Invoke(delegate { rtxtChat.AppendText(message + Environment.NewLine); });
else rtxtChat.AppendText(message + Environment.NewLine);
if(IsHandleCreated && !IsDisposed)
{
Invoke(delegate ()
{
rtxtChat.AppendText(message + Environment.NewLine);
});
}
}
}
}

View File

@ -31,18 +31,17 @@
lblHead = new Label();
btnReconnect = new Button();
btnQuit = new Button();
lblReason = new Label();
SuspendLayout();
//
// lblHead
//
lblHead.AutoSize = true;
lblHead.Font = new Font("Segoe UI", 11F, FontStyle.Bold);
lblHead.Location = new Point(12, 9);
lblHead.Name = "lblHead";
lblHead.Size = new Size(444, 20);
lblHead.TabIndex = 0;
lblHead.Text = "Your Connection To The QtC.NET Server Was Lost. What Now?";
lblHead.TextAlign = ContentAlignment.MiddleCenter;
//
// btnReconnect
//
@ -66,23 +65,12 @@
btnQuit.UseVisualStyleBackColor = true;
btnQuit.Click += btnQuit_Click;
//
// lblReason
//
lblReason.AutoSize = true;
lblReason.Font = new Font("Segoe UI", 5F, FontStyle.Bold);
lblReason.Location = new Point(88, 40);
lblReason.Name = "lblReason";
lblReason.Size = new Size(72, 10);
lblReason.TabIndex = 3;
lblReason.Text = "Reason: ${REASON}";
//
// frmConnectionClosed
// ConnectionClosed
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(463, 65);
Controls.Add(lblReason);
Controls.Add(btnQuit);
Controls.Add(btnReconnect);
Controls.Add(lblHead);
@ -92,12 +80,10 @@
Margin = new Padding(4, 3, 4, 3);
MaximizeBox = false;
MinimizeBox = false;
Name = "frmConnectionClosed";
Name = "ConnectionClosed";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client - Connection Lost";
Load += frmConnectionClosed_Load;
ResumeLayout(false);
PerformLayout();
}
#endregion
@ -105,6 +91,5 @@
private Label lblHead;
private Button btnReconnect;
private Button btnQuit;
private Label lblReason;
}
}

View File

@ -12,19 +12,12 @@ namespace qtc_net_client_2.Forms
{
public partial class ConnectionClosed : Form
{
public string? Reason { get; set; } = string.Empty;
public ConnectionClosed(string? reason = "")
public Label StatusLabel { get { return lblHead; } }
public ConnectionClosed()
{
Reason = reason;
InitializeComponent();
}
private void frmConnectionClosed_Load(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(Reason)) lblReason.Visible = false;
else lblReason.Text = $"Reason: {Reason}";
}
private void btnReconnect_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;

View File

@ -42,6 +42,11 @@ namespace qtc_net_client_2.Forms
Text = $"QtC.NET Client - Direct Message With {User.Username}";
Messages.CollectionChanged += Messages_CollectionChanged;
if (User.Role == "Admin")
{
Messages.Add($"[SERVER] This User Is A Server Admin. You should comply with anything this user asks. however admins should not ask for personal information.\n");
}
if (InitMessage != null)
{
Messages.Add($"[{User.Username}] {InitMessage.Content}\n");
@ -58,7 +63,7 @@ namespace qtc_net_client_2.Forms
if (!string.IsNullOrEmpty(rtxtChatbox.Text))
{
await _gatewayService.SendDirectMessageAsync(User, new QtCNETAPI.Models.Message { Content = rtxtChatbox.Text, AuthorId = _apiService.CurrentUser.Id });
Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}");
Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}\n");
rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick");
}
@ -69,7 +74,7 @@ namespace qtc_net_client_2.Forms
if (!string.IsNullOrEmpty(rtxtChatbox.Text))
{
await _gatewayService.SendDirectMessageAsync(User, new QtCNETAPI.Models.Message { Content = rtxtChatbox.Text, AuthorId = _apiService.CurrentUser.Id });
Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}");
Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}\n");
rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick");
}

View File

@ -32,11 +32,13 @@
pbLoginBanner = new PictureBox();
tbEmail = new TextBox();
lblEmail = new Label();
label1 = new Label();
lblPassword = new Label();
tbPassword = new TextBox();
btnLogin = new Button();
llblRegister = new LinkLabel();
cbRememberMe = new CheckBox();
llblResendEmail = new LinkLabel();
llblForgotPasswor = new LinkLabel();
((System.ComponentModel.ISupportInitialize)pbLoginBanner).BeginInit();
SuspendLayout();
//
@ -68,16 +70,16 @@
lblEmail.TabIndex = 2;
lblEmail.Text = "Email";
//
// label1
// lblPassword
//
label1.AutoSize = true;
label1.Font = new Font("Segoe UI Light", 9F);
label1.ForeColor = SystemColors.ControlLight;
label1.Location = new Point(11, 138);
label1.Name = "label1";
label1.Size = new Size(55, 15);
label1.TabIndex = 4;
label1.Text = "Password";
lblPassword.AutoSize = true;
lblPassword.Font = new Font("Segoe UI Light", 9F);
lblPassword.ForeColor = SystemColors.ControlLight;
lblPassword.Location = new Point(11, 138);
lblPassword.Name = "lblPassword";
lblPassword.Size = new Size(55, 15);
lblPassword.TabIndex = 4;
lblPassword.Text = "Password";
//
// tbPassword
//
@ -122,16 +124,44 @@
cbRememberMe.Text = "Remember Me For 7 Days";
cbRememberMe.UseVisualStyleBackColor = true;
//
// frmLogin
// llblResendEmail
//
llblResendEmail.AutoSize = true;
llblResendEmail.Font = new Font("Segoe UI Light", 9F);
llblResendEmail.LinkColor = SystemColors.ControlLight;
llblResendEmail.Location = new Point(369, 164);
llblResendEmail.Name = "llblResendEmail";
llblResendEmail.Size = new Size(129, 15);
llblResendEmail.TabIndex = 8;
llblResendEmail.TabStop = true;
llblResendEmail.Text = "Resend Verification Email";
llblResendEmail.LinkClicked += llblResendEmail_LinkClicked;
//
// llblForgotPasswor
//
llblForgotPasswor.AutoSize = true;
llblForgotPasswor.Font = new Font("Segoe UI Light", 9F);
llblForgotPasswor.LinkColor = SystemColors.ControlLight;
llblForgotPasswor.Location = new Point(401, 181);
llblForgotPasswor.Name = "llblForgotPasswor";
llblForgotPasswor.Size = new Size(98, 15);
llblForgotPasswor.TabIndex = 9;
llblForgotPasswor.TabStop = true;
llblForgotPasswor.Text = "Forgot Password?";
llblForgotPasswor.LinkClicked += llblForgotPasswor_LinkClicked;
//
// llblForgotPassword
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(515, 203);
Controls.Add(llblForgotPasswor);
Controls.Add(llblResendEmail);
Controls.Add(cbRememberMe);
Controls.Add(llblRegister);
Controls.Add(btnLogin);
Controls.Add(label1);
Controls.Add(lblPassword);
Controls.Add(tbPassword);
Controls.Add(lblEmail);
Controls.Add(tbEmail);
@ -139,7 +169,7 @@
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
Name = "frmLogin";
Name = "llblForgotPassword";
StartPosition = FormStartPosition.CenterParent;
Text = "QtC.NET Client - Login";
Load += frmLogin_Load;
@ -153,10 +183,12 @@
private PictureBox pbLoginBanner;
private TextBox tbEmail;
private Label lblEmail;
private Label label1;
private Label lblPassword;
private TextBox tbPassword;
private Button btnLogin;
private LinkLabel llblRegister;
private CheckBox cbRememberMe;
private LinkLabel llblResendEmail;
private LinkLabel llblForgotPasswor;
}
}

View File

@ -1,4 +1,5 @@
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services;
using QtCNETAPI.Dtos.User;
using System;
using System.Collections.Generic;
@ -9,12 +10,14 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using qtc_net_client_2.Services;
namespace qtc_net_client_2.Forms
{
public partial class Login : Form
{
private IApiService _apiService;
private CredentialService _credService = new();
public Login(IApiService apiService)
{
_apiService = apiService;
@ -24,14 +27,14 @@ namespace qtc_net_client_2.Forms
private async void frmLogin_Load(object sender, EventArgs e)
{
if (File.Exists("./session.token"))
string? accessToken = _credService.GetAccessToken();
if (accessToken != null)
{
ToggleControls(false, false);
// try logging in with the token in the file
string token = File.ReadAllText("./session.token");
var result = await _apiService.RefreshLogin(token);
// try logging in with the token in cred storage
var result = await _apiService.RefreshLogin(accessToken);
if (result.Success)
{
DialogResult = DialogResult.OK;
@ -55,8 +58,9 @@ namespace qtc_net_client_2.Forms
RememberMe = cbRememberMe.Checked
});
if (result.Success)
if (result.Success && result.Data != null)
{
_credService.SaveAccessToken(_apiService.CurrentUser.Username, result.Data);
DialogResult = DialogResult.OK;
Close();
}
@ -68,6 +72,24 @@ namespace qtc_net_client_2.Forms
}
}
private void llblRegister_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Register frmRegister = new Register(_apiService);
frmRegister.ShowDialog();
}
private void llblResendEmail_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ResendVerificationEmail resendVerificationEmail = new ResendVerificationEmail(_apiService);
resendVerificationEmail.ShowDialog();
}
private void llblForgotPasswor_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ResetPassword resetPassword = new ResetPassword(_apiService);
resetPassword.ShowDialog();
}
private void ToggleControls(bool enable, bool clearText)
{
tbEmail.Enabled = enable;
@ -81,11 +103,5 @@ namespace qtc_net_client_2.Forms
tbPassword.Text = string.Empty;
}
}
private void llblRegister_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Register frmRegister = new Register(_apiService);
frmRegister.ShowDialog();
}
}
}

View File

@ -40,7 +40,6 @@
refreshToolStripMenuItem = new ToolStripMenuItem();
ilProfilePics = new ImageList(components);
tbpRooms = new TabPage();
btnAddRoom = new Button();
lbRooms = new ListBox();
tbpUsers = new TabPage();
lvUserDirectory = new ListView();
@ -48,6 +47,9 @@
tbpGames = new TabPage();
lvGames = new ListView();
ilGames = new ImageList(components);
tbpStore = new TabPage();
lvStoreItems = new ListView();
ilStoreThumbnails = new ImageList(components);
ilTabIcons = new ImageList(components);
ctxmChangeStatus = new ContextMenuStrip(components);
onlineToolStripMenuItem = new ToolStripMenuItem();
@ -67,18 +69,33 @@
lblWelcome = new Label();
pbUserPfp = new PictureBox();
pbDonate = new PictureBox();
ctxmAdminUserList = new ContextMenuStrip(components);
refreshToolStripMenuItem1 = new ToolStripMenuItem();
toolStripSeparator1 = new ToolStripSeparator();
copyUserIDToClipboardToolStripMenuItem = new ToolStripMenuItem();
deleteUserToolStripMenuItem = new ToolStripMenuItem();
adminDirectMessageToolStripMenuItem = new ToolStripMenuItem();
ctxmAdminRoomList = new ContextMenuStrip(components);
toolStripMenuItem1 = new ToolStripMenuItem();
toolStripSeparator2 = new ToolStripSeparator();
addRoomToolStripMenuItem = new ToolStripMenuItem();
deleteRoomToolStripMenuItem = new ToolStripMenuItem();
lblConnectionLost = new Label();
tbcMain.SuspendLayout();
tbpContacts.SuspendLayout();
ctxmRefresh.SuspendLayout();
tbpRooms.SuspendLayout();
tbpUsers.SuspendLayout();
tbpGames.SuspendLayout();
tbpStore.SuspendLayout();
ctxmChangeStatus.SuspendLayout();
pCurrencyArea.SuspendLayout();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).BeginInit();
pCurrentUser.SuspendLayout();
((System.ComponentModel.ISupportInitialize)pbUserPfp).BeginInit();
((System.ComponentModel.ISupportInitialize)pbDonate).BeginInit();
ctxmAdminUserList.SuspendLayout();
ctxmAdminRoomList.SuspendLayout();
SuspendLayout();
//
// tbcMain
@ -88,13 +105,15 @@
tbcMain.Controls.Add(tbpRooms);
tbcMain.Controls.Add(tbpUsers);
tbcMain.Controls.Add(tbpGames);
tbcMain.Controls.Add(tbpStore);
tbcMain.Enabled = false;
tbcMain.ImageList = ilTabIcons;
tbcMain.Location = new Point(12, 83);
tbcMain.Name = "tbcMain";
tbcMain.SelectedIndex = 0;
tbcMain.Size = new Size(305, 499);
tbcMain.Size = new Size(352, 499);
tbcMain.TabIndex = 0;
tbcMain.SelectedIndexChanged += tbcMain_SelectedIndexChanged;
//
// tbpContacts
//
@ -103,7 +122,7 @@
tbpContacts.Location = new Point(4, 24);
tbpContacts.Name = "tbpContacts";
tbpContacts.Padding = new Padding(3);
tbpContacts.Size = new Size(297, 471);
tbpContacts.Size = new Size(344, 471);
tbpContacts.TabIndex = 0;
tbpContacts.Text = "Contacts";
tbpContacts.UseVisualStyleBackColor = true;
@ -117,7 +136,7 @@
lvContacts.Location = new Point(0, 0);
lvContacts.MultiSelect = false;
lvContacts.Name = "lvContacts";
lvContacts.Size = new Size(297, 478);
lvContacts.Size = new Size(344, 478);
lvContacts.SmallImageList = ilProfilePics;
lvContacts.TabIndex = 1;
lvContacts.UseCompatibleStateImageBehavior = false;
@ -146,28 +165,16 @@
//
// tbpRooms
//
tbpRooms.Controls.Add(btnAddRoom);
tbpRooms.Controls.Add(lbRooms);
tbpRooms.ImageKey = "RoomsChatIcon.png";
tbpRooms.Location = new Point(4, 24);
tbpRooms.Name = "tbpRooms";
tbpRooms.Padding = new Padding(3);
tbpRooms.Size = new Size(297, 471);
tbpRooms.Size = new Size(344, 471);
tbpRooms.TabIndex = 1;
tbpRooms.Text = "Rooms";
tbpRooms.UseVisualStyleBackColor = true;
//
// btnAddRoom
//
btnAddRoom.FlatAppearance.BorderSize = 0;
btnAddRoom.FlatStyle = FlatStyle.Flat;
btnAddRoom.Location = new Point(342, 485);
btnAddRoom.Name = "btnAddRoom";
btnAddRoom.Size = new Size(20, 22);
btnAddRoom.TabIndex = 7;
btnAddRoom.UseVisualStyleBackColor = true;
btnAddRoom.Click += btnAddRoom_Click;
//
// lbRooms
//
lbRooms.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
@ -176,7 +183,7 @@
lbRooms.ItemHeight = 15;
lbRooms.Location = new Point(0, 0);
lbRooms.Name = "lbRooms";
lbRooms.Size = new Size(297, 514);
lbRooms.Size = new Size(344, 484);
lbRooms.TabIndex = 0;
lbRooms.DoubleClick += lbRooms_DoubleClick;
//
@ -186,7 +193,7 @@
tbpUsers.ImageIndex = 1;
tbpUsers.Location = new Point(4, 24);
tbpUsers.Name = "tbpUsers";
tbpUsers.Size = new Size(297, 471);
tbpUsers.Size = new Size(344, 471);
tbpUsers.TabIndex = 3;
tbpUsers.Text = "Users";
tbpUsers.UseVisualStyleBackColor = true;
@ -200,7 +207,7 @@
lvUserDirectory.MultiSelect = false;
lvUserDirectory.Name = "lvUserDirectory";
lvUserDirectory.RightToLeft = RightToLeft.Yes;
lvUserDirectory.Size = new Size(297, 514);
lvUserDirectory.Size = new Size(344, 484);
lvUserDirectory.SmallImageList = ilStatusIcons;
lvUserDirectory.TabIndex = 0;
lvUserDirectory.UseCompatibleStateImageBehavior = false;
@ -223,7 +230,7 @@
tbpGames.ImageIndex = 3;
tbpGames.Location = new Point(4, 24);
tbpGames.Name = "tbpGames";
tbpGames.Size = new Size(297, 471);
tbpGames.Size = new Size(344, 471);
tbpGames.TabIndex = 4;
tbpGames.Text = "Games";
tbpGames.UseVisualStyleBackColor = true;
@ -239,7 +246,7 @@
lvGames.Location = new Point(0, 0);
lvGames.MultiSelect = false;
lvGames.Name = "lvGames";
lvGames.Size = new Size(297, 533);
lvGames.Size = new Size(344, 484);
lvGames.SmallImageList = ilGames;
lvGames.TabIndex = 2;
lvGames.UseCompatibleStateImageBehavior = false;
@ -254,6 +261,35 @@
ilGames.Images.SetKeyName(1, "NumberGuessGameIcon.png");
ilGames.Images.SetKeyName(2, "Tic-tac-toe.png");
//
// tbpStore
//
tbpStore.Controls.Add(lvStoreItems);
tbpStore.ImageIndex = 3;
tbpStore.Location = new Point(4, 24);
tbpStore.Name = "tbpStore";
tbpStore.Padding = new Padding(3);
tbpStore.Size = new Size(344, 471);
tbpStore.TabIndex = 5;
tbpStore.Text = "Store";
tbpStore.UseVisualStyleBackColor = true;
//
// lvStoreItems
//
lvStoreItems.LargeImageList = ilStoreThumbnails;
lvStoreItems.Location = new Point(0, 0);
lvStoreItems.Name = "lvStoreItems";
lvStoreItems.Size = new Size(344, 484);
lvStoreItems.SmallImageList = ilStoreThumbnails;
lvStoreItems.TabIndex = 0;
lvStoreItems.UseCompatibleStateImageBehavior = false;
lvStoreItems.DoubleClick += lvStoreItems_DoubleClick;
//
// ilStoreThumbnails
//
ilStoreThumbnails.ColorDepth = ColorDepth.Depth32Bit;
ilStoreThumbnails.ImageSize = new Size(64, 64);
ilStoreThumbnails.TransparentColor = Color.Transparent;
//
// ilTabIcons
//
ilTabIcons.ColorDepth = ColorDepth.Depth32Bit;
@ -301,7 +337,7 @@
//
// lblRequestNotif
//
lblRequestNotif.Anchor = AnchorStyles.Top | AnchorStyles.Right;
lblRequestNotif.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
lblRequestNotif.AutoSize = true;
lblRequestNotif.Font = new Font("Segoe UI", 6F, FontStyle.Bold);
lblRequestNotif.Location = new Point(12, 67);
@ -337,7 +373,7 @@
pCurrencyArea.Controls.Add(llblClaimSpin);
pCurrencyArea.Controls.Add(pbCurrencyIcon);
pCurrencyArea.Controls.Add(lblCurrencyAmount);
pCurrencyArea.Location = new Point(223, 5);
pCurrencyArea.Location = new Point(270, 5);
pCurrencyArea.Name = "pCurrencyArea";
pCurrencyArea.Size = new Size(95, 46);
pCurrencyArea.TabIndex = 12;
@ -447,9 +483,10 @@
//
// pbDonate
//
pbDonate.Anchor = AnchorStyles.Top | AnchorStyles.Right;
pbDonate.Cursor = Cursors.Hand;
pbDonate.Image = Properties.Resources.donatebtn;
pbDonate.Location = new Point(241, 56);
pbDonate.Location = new Point(288, 56);
pbDonate.Name = "pbDonate";
pbDonate.Size = new Size(77, 22);
pbDonate.SizeMode = PictureBoxSizeMode.Zoom;
@ -457,17 +494,103 @@
pbDonate.TabStop = false;
pbDonate.Click += pbDonate_Click;
//
// ctxmAdminUserList
//
ctxmAdminUserList.Items.AddRange(new ToolStripItem[] { refreshToolStripMenuItem1, toolStripSeparator1, copyUserIDToClipboardToolStripMenuItem, deleteUserToolStripMenuItem, adminDirectMessageToolStripMenuItem });
ctxmAdminUserList.Name = "contextMenuStrip1";
ctxmAdminUserList.Size = new Size(213, 98);
ctxmAdminUserList.Opening += ctxmAdminUserList_Opening;
//
// refreshToolStripMenuItem1
//
refreshToolStripMenuItem1.Name = "refreshToolStripMenuItem1";
refreshToolStripMenuItem1.Size = new Size(212, 22);
refreshToolStripMenuItem1.Text = "Refresh";
refreshToolStripMenuItem1.Click += refreshToolStripMenuItem_Click;
//
// toolStripSeparator1
//
toolStripSeparator1.Name = "toolStripSeparator1";
toolStripSeparator1.Size = new Size(209, 6);
//
// copyUserIDToClipboardToolStripMenuItem
//
copyUserIDToClipboardToolStripMenuItem.Name = "copyUserIDToClipboardToolStripMenuItem";
copyUserIDToClipboardToolStripMenuItem.Size = new Size(212, 22);
copyUserIDToClipboardToolStripMenuItem.Text = "Copy User ID To Clipboard";
copyUserIDToClipboardToolStripMenuItem.Click += copyUserIDToClipboardToolStripMenuItem_Click;
//
// deleteUserToolStripMenuItem
//
deleteUserToolStripMenuItem.Name = "deleteUserToolStripMenuItem";
deleteUserToolStripMenuItem.Size = new Size(212, 22);
deleteUserToolStripMenuItem.Text = "Delete User";
deleteUserToolStripMenuItem.Click += deleteUserToolStripMenuItem_Click;
//
// adminDirectMessageToolStripMenuItem
//
adminDirectMessageToolStripMenuItem.Name = "adminDirectMessageToolStripMenuItem";
adminDirectMessageToolStripMenuItem.Size = new Size(212, 22);
adminDirectMessageToolStripMenuItem.Text = "Admin Direct Message";
adminDirectMessageToolStripMenuItem.Click += adminDirectMessageToolStripMenuItem_Click;
//
// ctxmAdminRoomList
//
ctxmAdminRoomList.Items.AddRange(new ToolStripItem[] { toolStripMenuItem1, toolStripSeparator2, addRoomToolStripMenuItem, deleteRoomToolStripMenuItem });
ctxmAdminRoomList.Name = "contextMenuStrip1";
ctxmAdminRoomList.Size = new Size(143, 76);
ctxmAdminRoomList.Opening += ctxmAdminRoomList_Opening;
//
// toolStripMenuItem1
//
toolStripMenuItem1.Name = "toolStripMenuItem1";
toolStripMenuItem1.Size = new Size(142, 22);
toolStripMenuItem1.Text = "Refresh";
toolStripMenuItem1.Click += refreshToolStripMenuItem_Click;
//
// toolStripSeparator2
//
toolStripSeparator2.Name = "toolStripSeparator2";
toolStripSeparator2.Size = new Size(139, 6);
//
// addRoomToolStripMenuItem
//
addRoomToolStripMenuItem.Name = "addRoomToolStripMenuItem";
addRoomToolStripMenuItem.Size = new Size(142, 22);
addRoomToolStripMenuItem.Text = "Add Room";
addRoomToolStripMenuItem.Click += addRoomToolStripMenuItem_Click;
//
// deleteRoomToolStripMenuItem
//
deleteRoomToolStripMenuItem.Name = "deleteRoomToolStripMenuItem";
deleteRoomToolStripMenuItem.Size = new Size(142, 22);
deleteRoomToolStripMenuItem.Text = "Delete Room";
deleteRoomToolStripMenuItem.Click += deleteRoomToolStripMenuItem_Click;
//
// lblConnectionLost
//
lblConnectionLost.AutoSize = true;
lblConnectionLost.Font = new Font("Segoe UI", 6F, FontStyle.Bold);
lblConnectionLost.ForeColor = Color.Red;
lblConnectionLost.Location = new Point(12, 67);
lblConnectionLost.Name = "lblConnectionLost";
lblConnectionLost.Size = new Size(175, 11);
lblConnectionLost.TabIndex = 15;
lblConnectionLost.Text = "Server Connection Lost. Trying To Reconnect...";
lblConnectionLost.Visible = false;
//
// Main
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(329, 594);
ClientSize = new Size(376, 594);
Controls.Add(pbDonate);
Controls.Add(pCurrentUser);
Controls.Add(pCurrencyArea);
Controls.Add(lblRequestNotif);
Controls.Add(tbcMain);
Controls.Add(lblConnectionLost);
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
@ -483,6 +606,7 @@
tbpRooms.ResumeLayout(false);
tbpUsers.ResumeLayout(false);
tbpGames.ResumeLayout(false);
tbpStore.ResumeLayout(false);
ctxmChangeStatus.ResumeLayout(false);
pCurrencyArea.ResumeLayout(false);
pCurrencyArea.PerformLayout();
@ -491,6 +615,8 @@
pCurrentUser.PerformLayout();
((System.ComponentModel.ISupportInitialize)pbUserPfp).EndInit();
((System.ComponentModel.ISupportInitialize)pbDonate).EndInit();
ctxmAdminUserList.ResumeLayout(false);
ctxmAdminRoomList.ResumeLayout(false);
ResumeLayout(false);
PerformLayout();
}
@ -511,7 +637,6 @@
private ToolStripMenuItem awayToolStripMenuItem;
private ToolStripMenuItem doNotDisturbToolStripMenuItem;
private ToolStripMenuItem invisibleToolStripMenuItem;
private Button btnAddRoom;
private LinkLabel llblClaimSpin;
private Panel pCurrencyArea;
private Panel pCurrentUser;
@ -531,5 +656,20 @@
private TabPage tbpUsers;
private ListView lvUserDirectory;
private PictureBox pbDonate;
private TabPage tbpStore;
private ListView lvStoreItems;
private ImageList ilStoreThumbnails;
private ContextMenuStrip ctxmAdminUserList;
private ToolStripMenuItem refreshToolStripMenuItem1;
private ToolStripSeparator toolStripSeparator1;
private ToolStripMenuItem copyUserIDToClipboardToolStripMenuItem;
private ToolStripMenuItem deleteUserToolStripMenuItem;
private ToolStripMenuItem adminDirectMessageToolStripMenuItem;
private ContextMenuStrip ctxmAdminRoomList;
private ToolStripMenuItem toolStripMenuItem1;
private ToolStripSeparator toolStripSeparator2;
private ToolStripMenuItem addRoomToolStripMenuItem;
private ToolStripMenuItem deleteRoomToolStripMenuItem;
private Label lblConnectionLost;
}
}

View File

@ -6,6 +6,9 @@ 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
{
@ -13,7 +16,9 @@ namespace qtc_net_client_2
{
private IApiService _apiService;
private IGatewayService _gatewayService;
private Config _config;
private ServerConfig _serverConfig;
private AudioService AudioService = new();
private LoggingService LoggingService;
@ -148,11 +153,7 @@ namespace qtc_net_client_2
private void llblEditProfile_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProfileEdit frmProfileEdit = new ProfileEdit(_apiService);
var dialogResult = frmProfileEdit.ShowDialog();
if (dialogResult == DialogResult.OK)
{
MessageBox.Show("If you updated your username, hit the refresh button to see it update on your lists.\nThe top username will not update until you restart your client.");
}
frmProfileEdit.ShowDialog();
}
private async void lvContacts_DoubleClick(object sender, EventArgs e)
@ -171,12 +172,29 @@ namespace qtc_net_client_2
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);
Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService, cosmeticData);
frmProfile.Show();
}
}
@ -275,7 +293,7 @@ namespace qtc_net_client_2
var result = await _apiService.AddCurrencyToCurrentUser(tokenJackpotSpinner.TokensWon, true);
if (result.Success)
{
lblCurrencyAmount.Text = (_apiService.CurrentUser.CurrencyAmount + tokenJackpotSpinner.TokensWon).ToString("N0");
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);
@ -295,12 +313,29 @@ namespace qtc_net_client_2
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);
Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService, cosmeticData);
frmProfile.Show();
}
}
@ -333,10 +368,9 @@ namespace qtc_net_client_2
private async void refreshToolStripMenuItem_Click(object sender, EventArgs e)
{
// refresh all controls (TODO - probably restrict this to currently visible control)
await RefreshContactsList();
await RefreshRoomsList();
await RefreshUsers();
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)
@ -345,6 +379,192 @@ namespace qtc_net_client_2
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
@ -365,10 +585,14 @@ namespace qtc_net_client_2
_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;
@ -390,14 +614,14 @@ namespace qtc_net_client_2
}
}
if (lvUserDirectory.Items.Count <= 0)
await RefreshUsers(); // prevent edge case where the refresh event never gets sent to connecting client
await RefreshContactsList();
await RefreshRoomsList();
await RefreshUsers();
// TODO - figure out server side why online status is invisible on login
_apiService.CurrentUser.Status = 1;
// set status context menu checked
// TODO - figure out more efficient way to do this
UserStatus cuStatus = (UserStatus)_apiService.CurrentUser.Status;
switch (cuStatus)
{
@ -427,15 +651,19 @@ namespace qtc_net_client_2
break;
}
if (_apiService.CurrentUser.Role == "Admin") btnAddRoom.Enabled = true;
else btnAddRoom.Enabled = false;
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");
}
}
@ -445,29 +673,30 @@ namespace qtc_net_client_2
{
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
if (lvUserDirectory.InvokeRequired) lvUserDirectory.BeginInvoke(lvUserDirectory.Items.Clear);
else lvUserDirectory.Items.Clear();
lvUserDirectory.Items.Clear();
UserDirectory.Clear();
// populate list
foreach (var user in userList.Data)
{
if (lvUserDirectory.InvokeRequired)
{
lvUserDirectory.BeginInvoke(delegate () { lvUserDirectory.Items.Add(user.Username, user.Status); });
}
else
{
lvUserDirectory.Items.Add(user.Username, user.Status);
}
UserDirectory.Add(user);
}
if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs)
LoggingService.LogModel(userList.Data);
}
});
}
}
@ -475,7 +704,7 @@ namespace qtc_net_client_2
{
LoggingService.LogString("Refreshing Rooms List...");
if (InvokeRequired)
if (IsHandleCreated && !IsDisposed)
{
await Invoke(async delegate ()
{
@ -488,35 +717,21 @@ namespace qtc_net_client_2
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");
});
return;
}
lbRooms.Items.Clear();
var roomsRes = await _apiService.GetAllRoomsAsync();
if (roomsRes.Success && roomsRes.Data != null)
{
lbRooms.Items.Clear();
foreach (var room in roomsRes.Data)
{
lbRooms.Items.Add(room.Name);
}
RoomList = roomsRes.Data;
}
// always add lobby room to rooms list
lbRooms.Items.Add("Lobby");
}
private async Task RefreshContactsList()
{
LoggingService.LogString("Refreshing Contacts List...");
if (InvokeRequired)
if (IsHandleCreated && !IsDisposed)
{
await Invoke(async delegate ()
{
@ -585,76 +800,11 @@ namespace qtc_net_client_2
}
}
}
if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs)
LoggingService.LogModel(contactsRes.Data);
}
});
return;
}
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<UserInformationDto> 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;
}
}
}
}
}
}
@ -662,11 +812,9 @@ namespace qtc_net_client_2
{
while (true)
{
if (!IsHandleCreated) { LoggingService.LogString("Form Handle Wasn't Created"); return; }
if (InvokeRequired && !IsDisposed)
if(IsHandleCreated && !IsDisposed)
{
BeginInvoke(async delegate ()
await Invoke(async delegate ()
{
label.ForeColor = Color.Red;
await Task.Delay(500);
@ -674,11 +822,6 @@ namespace qtc_net_client_2
await Task.Delay(500);
});
}
label.ForeColor = Color.Red;
await Task.Delay(500);
label.ForeColor = Color.Blue;
await Task.Delay(500);
}
}
@ -696,18 +839,34 @@ namespace qtc_net_client_2
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 (lblCurrencyAmount.InvokeRequired)
if (IsHandleCreated && !IsDisposed)
{
lblCurrencyAmount.BeginInvoke(delegate ()
{
lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0");
});
}
else
{
lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0");
Invoke(delegate () { lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0"); });
}
}
@ -726,9 +885,9 @@ namespace qtc_net_client_2
await _gatewayService.StartAsync();
if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
{ BeginInvoke(delegate () { Enabled = true; }); return; }
{ BeginInvoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; }); return; }
ConnectionClosed frmConnectionClosed = new ConnectionClosed(error);
ConnectionClosed frmConnectionClosed = new ConnectionClosed();
var result = frmConnectionClosed.ShowDialog();
if (result == DialogResult.OK)
@ -736,19 +895,20 @@ namespace qtc_net_client_2
// tell the gateway service to attempt reconnection
Reconnect:
if (_gatewayService.HubConnection != null)
{
try
{
await _gatewayService.StopAsync();
await _gatewayService.StartAsync();
if (_gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
BeginInvoke(delegate () { Enabled = true; });
{
Invoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; });
}
catch (HttpRequestException ex)
else
{
// reshow the dialog
frmConnectionClosed.Reason = ex.Message;
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);
}
@ -757,8 +917,47 @@ namespace qtc_net_client_2
else Environment.Exit(0);
}
private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e) => BeginInvoke(delegate () { Enabled = false; });
private void _gatewayService_OnServerReconnected(object? sender, EventArgs e) => BeginInvoke(delegate () { Enabled = true; });
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)
{
@ -767,8 +966,11 @@ namespace qtc_net_client_2
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");
@ -804,6 +1006,23 @@ namespace qtc_net_client_2
}
}
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();

View File

@ -127,66 +127,66 @@
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAzg0AAAJNU0Z0AUkBTAMBAgAB
AQEAAQEBIAEAASABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAGAAwABIAMAAQEBAAEgBgABQBIAA60B
/wO6Af8DugH/A7kB/wO6Af8DugH/A9oF/wP8Af8D/g3/A/0B/wP8Ef8D/AH/A/4N/wP9Af8D2QH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH//wCBAAOfAf8DugH/A7oB/wO6Af8DugH/A7oB/wPaCf8D/QH/A/4N
/wP9Af8D/RH/A/wB/wP+Df8D2gH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH//wCBAAOiAf8DugH/A7oB
/wO6Af8DugH/A7oB/wPaDf8D/AH/A/4l/wP8Df8D2gH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH//wCB
AAOlAf8DugH/A7oB/wO6Af8DugH/A7oB/wPZEf8D/hX/A/0d/wPaAf8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af//AIEAA6QB/wO5Af8DugH/A7oB/wO6Af8DugH/A9kB/wP8Ff8D/g3/A/0B/wP9Ef8D/gH/A/4B
/wPaAf8DugH/A7oB/wO5Af8DugH/A7oB/wO6Af//AIEAA6EB/wO6Af8DugH/A7oB/wO6Af8DugH/A9oB
/wP+Af8D/RH/A/wB/wP+Df8D/QH/A/0R/wP8Af8D2QH/A7oB/wO6Af8DugH/A7kB/wO6Af8DugH//wCB
AAOfAf8DugH/A7oB/wO6Af8DugH/A7oB/wPaBf8D/QH/A/0R/wP9Af8D/g3/A/0B/wP9Ef8D2QH/A7oB
/wO6Af8DugH/A7oB/wO5Af8DugH//wCBAAOxAf8DugH/A7oB/wO6Af8DugH/A7oB/wPTCf8D/hX/A/wB
/wP+If8D0wH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH//wCBAAO6Af8DugH/A7oB/wO6Af8DugH/A7kB
/wPAAf8D/Q3/A/4R/wP+Ff8D/QX/A/0B/wPAAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wPaDf8D/gH/A/0V/wP+Df8D/QH/A/wB/wPaAf8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH//wCBAAO6Af8DuQH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A90N
/wP9Af8D/RH/A/0B/wP+Df8D3AH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB
/wO6Af8DuQH/A7kB/wO6Af8DugH/A7oB/wO6Af8DuQH/A8kB/wPsAf8D/gX/A/4B/wP8Ef8D/QH/A/0B
/wPsAf8DygH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH//wCBAAO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO5Af8DugH/A8EB/wPSAf8D3wH/A+YB/wPqAf8D6wH/A+cB
/wPfAf8D0gH/A8AB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH//wCB
AAO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A70B
/wPHAf8DxgH/A70B/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH//wCBAAO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wPCAf8D5wH/A/0F/wP+Af8D+gH/A+YB/wPCAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DyAH/A/kB/wP9Df8D/QH/A/0B/wP6Af8DyAH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DuQH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO+Af8D+AX/A/wB/wP+Df8D/QH/A/wB/wP4Af8DvgH/A7oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB
/wO6Af8DuQH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO5Af8DugH/A9sN
/wP9Af8D/Q3/A/0B/wP+Af8D2wH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO5Af8DugH/A7oB/wO6Af8DugH/A/AR/wP8Af8D/hH/A/AB
/wO6Af8DugH/A7oB/wO6Af8DuQH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wP4Af8D/SX/A/gB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wP0Af8D/QH/A/wR/wP+Af8D/gn/A/UB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wPlBf8D/gH/A/wR
/wP8Af8D/QX/A+UB/wO6Af8DuQH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7kB/wPIAf8D/gX/A/4B/wP8Ef8D/QH/A/0B
/wPIAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO5Af8DugH//wCBAAO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A90J/wP+Ff8D3AH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A9kB/wP8Cf8D/gX/A/wB/wPZAf8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wPAAf8D1gH/A+IB/wPhAf8D1QH/A8AB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO5Af8DugH/A7oB/wO6Af8DugH/A7oB/wO5Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DuQH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DuQH/A7kB
/wO6Af//AIEAA7oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB/wO6Af8DuQH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DuQH/A7oB
/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO5Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB
/wO6Af8DugH/A7oB/wO5Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAAUIB
TQE+BwABPgMAASgDAAGAAwABIAMAAQEBAAEBBgABAhYAA///AP8AAwAL
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA0A0AAAJNU0Z0AUkBTAMBAQAB
mAEBAZgBAQEgAQABIAEABP8BIQEACP8BQgFNATYHAAE2AwABKAMAAYADAAEgAwABAQEAASAGAAFAEgAD
rQH/A7oB/wO6Af8DuQH/A7oB/wO6Af8D2gX/A/wB/wP+Df8D/QH/A/wR/wP8Af8D/g3/A/0B/wPZAf8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA58B/wO6Af8DugH/A7oB/wO6Af8DugH/A9oJ/wP9Af8D
/g3/A/0B/wP9Ef8D/AH/A/4N/wPaAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA6IB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A9oN/wP8Af8D/iX/A/wN/wPaAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//
AIEAA6UB/wO6Af8DugH/A7oB/wO6Af8DugH/A9kR/wP+Ff8D/R3/A9oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB//8AgQADpAH/A7kB/wO6Af8DugH/A7oB/wO6Af8D2QH/A/wV/wP+Df8D/QH/A/0R/wP+Af8D
/gH/A9oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB//8AgQADoQH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
2gH/A/4B/wP9Ef8D/AH/A/4N/wP9Af8D/RH/A/wB/wPZAf8DugH/A7oB/wO6Af8DuQH/A7oB/wO6Af//
AIEAA58B/wO6Af8DugH/A7oB/wO6Af8DugH/A9oF/wP9Af8D/RH/A/0B/wP+Df8D/QH/A/0R/wPZAf8D
ugH/A7oB/wO6Af8DugH/A7kB/wO6Af//AIEAA7EB/wO6Af8DugH/A7oB/wO6Af8DugH/A9MJ/wP+Ff8D
/AH/A/4h/wPTAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8D
uQH/A8AB/wP9Df8D/hH/A/4V/wP9Bf8D/QH/A8AB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQAD
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A9oN/wP+Af8D/RX/A/4N/wP9Af8D/AH/A9oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO5Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
3Q3/A/0B/wP9Ef8D/QH/A/4N/wPcAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQAD
ugH/A7oB/wO5Af8DuQH/A7oB/wO6Af8DugH/A7oB/wO5Af8DyQH/A+wB/wP+Bf8D/gH/A/wR/wP9Af8D
/QH/A+wB/wPKAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7kB/wO6Af8DwQH/A9IB/wPfAf8D5gH/A+oB/wPrAf8D
5wH/A98B/wPSAf8DwAH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//
AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
vQH/A8cB/wPGAf8DvQH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af//AIEAA7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A8IB/wPnAf8D/QX/A/4B/wP6Af8D5gH/A8IB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wPIAf8D+QH/A/0N/wP9Af8D/QH/A/oB/wPIAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO5Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A74B/wP4Bf8D/AH/A/4N/wP9Af8D/AH/A/gB/wO+Af8DugH/A7oB/wO6Af8DuQH/A7oB/wO6Af8D
ugH/A7oB/wO5Af8DugH//wCBAAO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7kB/wO6Af8D
2w3/A/0B/wP9Df8D/QH/A/4B/wPbAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH//wCBAAO6Af8DugH/A7oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB/wO6Af8D8BH/A/wB/wP+Ef8D
8AH/A7oB/wO6Af8DugH/A7oB/wO5Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A/gB/wP9Jf8D+AH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A/QB/wP9Af8D/BH/A/4B/wP+Cf8D9QH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A+UF/wP+Af8D
/BH/A/wB/wP9Bf8D5QH/A7oB/wO5Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQAD
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DuQH/A8gB/wP+Bf8D/gH/A/wR/wP9Af8D
/QH/A8gB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7kB/wO6Af//AIEAA7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D3Qn/A/4V/wPcAf8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D2QH/A/wJ/wP+Bf8D/AH/A9kB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A8AB/wPWAf8D4gH/A+EB/wPVAf8DwAH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQAD
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO5Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO5Af8D
uQH/A7oB//8AgQADugH/A7oB/wO6Af8DuQH/A7oB/wO6Af8DugH/A7oB/wO5Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO5Af8D
ugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DuQH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQADugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8D
ugH/A7oB/wO6Af8DugH/A7kB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB//8AgQAB
QgFNAT4HAAE+AwABKAMAAYADAAEgAwABAQEAAQEGAAECFgAD//8A/wADAAs=
</value>
</data>
<metadata name="ilStatusIcons.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@ -196,93 +196,91 @@
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA+hMAAAJNU0Z0AUkBTAIBAQQB
AAG4AQABuAEAARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAASADAAEBAQABIAYAASD/
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAohMAAAJNU0Z0AUkBTAIBAQQB
AAFQAQEBUAEBARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAASADAAEBAQABIAYAASD/
AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AC4AAwYBBwM0AVQDUQGiA14B0gNaAekDYAHoA10B
0QNQAZ8DMQFNAwUBBhgAAwYBBwM0AVQDUQGiA14B0gNaAekDYAHoA10B0QNQAZ8DMQFNAwUBBhgAAwYB
BwM0AVQDUQGiA14B0gNaAekDYAHoA10B0QNQAZ8DMQFNAwUBBhgAAwYBBwM0AVQDUQGiA14B0gNaAekD
YAHoA10B0QNQAZ8DMQFNAwUBBhQAAyABLQNUAasDWwHkA1MB9QMkAfsDOwH+AzsB/gMkAfsDUwH0A2IB
4QNRAaEDHgEqEAADIAEtA1QBqwNbAeQBSgFaAUoB9QEhAVcBIQH7ARcBVwEXAf4BFwFXARcB/gEhAVcB
IQH7A1MB9ANiAeEDUQGhAx4BKhAAAyABLQNUAasDWwHkAUoCWgH1ASECVwH7ARcCVwH+ARcCVwH+ASEC
VwH7A1MB9ANiAeEDUQGhAx4BKhAAAyABLQNUAasDWwHkAkoBWgH1AiEBVwH7AhcBVwH+AhcBVwH+AiEB
VwH7A1MB9ANiAeEDUQGhAx4BKgwAAxsBJQNYAb0DWgHyAz8B/gMwAf8DOQH/AzwB/wM2Af8DKgH/AyQB
/wNAAf0DXQHwA1YBsgMaASMIAAMbASUDWAG9A1oB8gEXAV8BFwH+AQABVwEAAf8BAAFnAQAB/wEAAWwB
AAH/AQABYQEAAf8BAAFMAQAB/wEAAUABAAH/AS4BQAEuAf0BWQFeAVkB8ANWAbIBGQEaARkBIwgAAxsB
JQNYAb0DWgHyARcCXwH+AQACVwH/AQACZwH/AQACbAH/AQACYQH/AQACTAH/AQACQAH/AS4CQAH9AVkC
XgHwA1YBsgEZAhoBIwgAAxsBJQNYAb0DWgHyAhcBXwH+AgABVwH/AgABZwH/AgABbAH/AgABYQH/AgAB
TAH/AgABQAH/Ai4BQAH9AlkBXgHwA1YBsgIZARoBIwQAAwMBBANSAaUDYAHzA0kB/wNVAf8DZQH/A3EB
/wN1Af8DcQH/A2QB/wNMAf8DMQH/AzsB/gNhAe4DUAGaAwMBBAMDAQQBUgFTAVIBpQFVAW8BVQHzAQAB
ggEAAf8BAAGZAQAB/wEAAbYBAAH/AQABzAEAAf8BAAHTAQAB/wEAAcsBAAH/AQABswEAAf8BAAGIAQAB
/wEAAVcBAAH/ARcBVwEXAf4BXgFiAV4B7gNQAZoDAwEEAwMBBAFSAlMBpQFVAm8B8wEAAoIB/wEAApkB
/wEAArYB/wEAAswB/wEAAtMB/wEAAssB/wEAArMB/wEAAogB/wEAAlcB/wEXAlcB/gFeAmIB7gNQAZoD
AwEEAwMBBAJSAVMBpQJVAW8B8wIAAYIB/wIAAZkB/wIAAbYB/wIAAcwB/wIAAdMB/wIAAcsB/wIAAbMB
/wIAAYgB/wIAAVcB/wIXAVcB/gJeAWIB7gNQAZoDAwEEAy0BRANgAegDegH+A24B/wN7Af8DhQH/A4oB
/wOMAf8DigH/A4UB/wN2Af8DVwH/AzIB/wNAAf0DXgHdAyoBPwMtAUQBYAFpAWAB6AEXAYYBFwH+AQAB
xgEAAf8BAAHcAQAB/wEAAe4BAAH/AQAB+AEAAf8BAAH7AQAB/wEAAfkBAAH/AQAB7wEAAf8BAAHUAQAB
/wEAAZwBAAH/AQABWgEAAf8BLgFAAS4B/QNeAd0DKgE/Ay0BRAFgAmkB6AEXAoYB/gEAAsYB/wEAAtwB
/wEAAu4B/wEAAvgB/wEAAvsB/wEAAvkB/wEAAu8B/wEAAtQB/wEAApwB/wEAAloB/wEuAkAB/QNeAd0D
KgE/Ay0BRAJgAWkB6AIXAYYB/gIAAcYB/wIAAdwB/wIAAe4B/wIAAfgB/wIAAfsB/wIAAfkB/wIAAe8B
/wIAAdQB/wIAAZwB/wIAAVoB/wIuAUAB/QNeAd0DKgE/A04BlQN3AfgDfwH/A4UB/wOKAf8DjQH/A44B
/wOOAf8DjgH/A40B/wOJAf8DdwH/A00B/wMlAf8DWgHyA0oBiwNOAZUBQQGEAUEB+AEAAeUBAAH/AQAB
7wEAAf8BAAH4AQAB/wEAAf0BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/gEAAf8BAAH2AQAB
/wEAAdUBAAH/AQABiwEAAf8BAAFBAQAB/wNaAfIDSgGLA04BlQFBAoQB+AEAAuUB/wEAAu8B/wEAAvgB
/wEAAv0B/wEAA/8BAAP/AQAD/wEAAv4B/wEAAvYB/wEAAtUB/wEAAosB/wEAAkEB/wNaAfIDSgGLA04B
lQJBAYQB+AIAAeUB/wIAAe8B/wIAAfgB/wIAAf0B/wIAAv8CAAL/AgAC/wIAAf4B/wIAAfYB/wIAAdUB
/wIAAYsB/wIAAUEB/wNaAfIDSgGLA18B0wN+AfwDkwH/A44B/wONAf8DjgH/A44B/wOOAf8DjgH/A44B
/wONAf8DhQH/A2cB/wM0Af8DQQH5A1oBxAFbAV8BWwHTASsBsgErAfwBDgH7AQ4B/wEDAf0BAwH/AQAB
/gEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/QEAAf8BAAHvAQAB
/wEAAbkBAAH/AQABXQEAAf8DQQH5A1oBxAFbAl8B0wErArIB/AEOAvsB/wEDAv0B/wEAAv4B/wEAA/8B
AAP/AQAD/wEAA/8BAAP/AQAC/QH/AQAC7wH/AQACuQH/AQACXQH/A0EB+QNaAcQCWwFfAdMCKwGyAfwC
DgH7Af8CAwH9Af8CAAH+Af8CAAL/AgAC/wIAAv8CAAL/AgAC/wIAAf0B/wIAAe8B/wIAAbkB/wIAAV0B
/wNBAfkDWgHEA24B9QOAAf4DnwH/A5MB/wOPAf8DjgH/A44B/wOOAf8DjgH/A44B/wOOAf8DiwH/A3cB
/wNIAf8DQAH9A2IB4QFRAXkBUQH1ATsB0QE7Af4BJwH/AScB/wELAf8BCwH/AQEB/wEBAf8BAAH/AQAB
/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH5AQAB/wEAAdYBAAH/AQAB
gQEAAf8BLgFAAS4B/QNiAeEBUQJ5AfUBOwLRAf4BJwP/AQsD/wEBA/8BAAP/AQAD/wEAA/8BAAP/AQAD
/wEAA/8BAAL5Af8BAALWAf8BAAKBAf8BLgJAAf0DYgHhAlEBeQH1AjsB0QH+AicC/wILAv8CAQL/AgAC
/wIAAv8CAAL/AgAC/wIAAv8CAAL/AgAB+QH/AgAB1gH/AgABgQH/Ai4BQAH9A2IB4QNvAfYDgAH+A6sB
/wOZAf8DkAH/A44B/wOOAf8DjgH/A44B/wOOAf8DjgH/A40B/wN/Af8DVQH/A0AB/QNeAeIBSAF7AUgB
9gFeAdEBXgH+AUIB/wFCAf8BGQH/ARkB/wEEAf8BBAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/QEAAf8BAAHkAQAB/wEAAZgBAAH/AS4BQAEuAf0DXgHiAUgC
ewH2AV4C0QH+AUID/wEZA/8BBAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAC/QH/AQAC5AH/AQAC
mAH/AS4CQAH9A14B4gJIAXsB9gJeAdEB/gJCAv8CGQL/AgQC/wIAAv8CAAL/AgAC/wIAAv8CAAL/AgAC
/wIAAf0B/wIAAeQB/wIAAZgB/wIuAUAB/QNeAeIDYQHWA34B/AO4Af8DowH/A5MB/wOOAf8DjgH/A44B
/wOOAf8DjgH/A44B/wONAf8DggH/A1wB/wNNAfoDWgHHAVwBYQFcAdYBZAG2AWQB/AFfAf8BXwH/AS8B
/wEvAf8BDAH/AQwB/wEBAf8BAQH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEAAf4BAAH/AQAB6gEAAf8BAAGlAQAB/wErAU0BKwH6A1oBxwFcAmEB1gFkArYB/AFfA/8BLwP/AQwD
/wEBA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAAv4B/wEAAuoB/wEAAqUB/wErAk0B+gNaAccCXAFhAdYC
ZAG2AfwCXwL/Ai8C/wIMAv8CAQL/AgAC/wIAAv8CAAL/AgAC/wIAAv8CAAH+Af8CAAHqAf8CAAGlAf8C
KwFNAfoDWgHHA1ABmgOJAfkDxQH/A7IB/wOcAf8DkQH/A44B/wOOAf8DjgH/A44B/wOPAf8DjgH/A4MB
/wNgAf8DWgHyA0wBkANQAZoBagGSAWoB+QF8Af8BfAH/AVEB/wFRAf8BHwH/AR8B/wEHAf8BBwH/AQEB
/wEBAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAgH/AQIB/wECAf4BAgH/AQAB6wEAAf8BAAGtAQAB
/wFaAWsBWgHyA0wBkANQAZoBagKSAfkBfAP/AVED/wEfA/8BBwP/AQED/wEAA/8BAAP/AQAD/wECA/8B
AgL+Af8BAALrAf8BAAKtAf8BWgJrAfIDTAGQA1ABmgJqAZIB+QJ8Av8CUQL/Ah8C/wIHAv8CAQL/AgAC
/wIAAv8CAAL/AgIC/wICAf4B/wIAAesB/wIAAa0B/wJaAWsB8gNMAZADLwFJA2wB6wOiAf4DxgH/A64B
/wOcAf8DkwH/A5AB/wOPAf8DkAH/A5MB/wOTAf8DhQH/A08B/QNgAeADLQFFAy8BSQNsAesBgAHRAYAB
/gF/Af8BfwH/AUkB/wFJAf8BHwH/AR8B/wEMAf8BDAH/AQUB/wEFAf8BAwH/AQMB/wEFAf8BBQH/AQoB
/wEKAf8BCgH+AQoB/wEBAe0BAQH/AS4BtgEuAf0BYAFmAWAB4AMtAUUDLwFJA2wB6wGAAtEB/gF/A/8B
SQP/AR8D/wEMA/8BBQP/AQMD/wEFA/8BCgP/AQoC/gH/AQEC7QH/AS4CtgH9AWACZgHgAy0BRQMvAUkD
bAHrAoAB0QH+An8C/wJJAv8CHwL/AgwC/wIFAv8CAwL/AgUC/wIKAv8CCgH+Af8CAQHtAf8CLgG2Af0C
YAFmAeADLQFFAwMBBANWAa4DcQH1A9kB/wPLAf8DtwH/A6cB/wOdAf8DmgH/A5wB/wOfAf8DmwH/A4kB
/wNoAfADUgGjAwMBBAMDAQQDVgGuAW4BeQFuAfUBqAH/AagB/wGJAf8BiQH/AVwB/wFcAf8BNwH/ATcB
/wEiAf8BIgH/ARsB/wEbAf8BHwH/AR8B/wEmAf8BJgH/AR0B/wEdAf8BBQHzAQUB/wFZAWgBWQHwA1IB
owMDAQQDAwEEA1YBrgFuAnkB9QGoA/8BiQP/AVwD/wE3A/8BIgP/ARsD/wEfA/8BJgP/AR0D/wEFAvMB
/wFZAmgB8ANSAaMDAwEEAwMBBANWAa4CbgF5AfUCqAL/AokC/wJcAv8CNwL/AiIC/wIbAv8CHwL/AiYC
/wIdAv8CBQHzAf8CWQFoAfADUgGjAwMBBAQAAxwBJwNdAccDeAH2A7EB/gPXAf8DzAH/A8IB/wO7Af8D
twH/A7EB/wOAAf4DaAH0A1kBvAMbASYIAAMcAScDXQHHAXMBewFzAfYBiQHRAYkB/gGlAf8BpQH/AYsB
/wGLAf8BdAH/AXQB/wFmAf8BZgH/AVwB/wFcAf8BTgH/AU4B/wFHAdEBRwH+AVMBeAFTAfQBVwFZAVcB
vAMbASYIAAMcAScDXQHHAXMCewH2AYkC0QH+AaUD/wGLA/8BdAP/AWYD/wFcA/8BTgP/AUcC0QH+AVMC
eAH0AVcCWQG8AxsBJggAAxwBJwNdAccCcwF7AfYCiQHRAf4CpQL/AosC/wJ0Av8CZgL/AlwC/wJOAv8C
RwHRAf4CUwF4AfQCVwFZAbwDGwEmDAADIQEwA1kBtgNoAe4DlgH6A74B/QPUAf8DzAH/A74B/QOFAfkD
bAHrA1UBrAMfASwQAAMhATADWQG2AWYBagFmAe4BfQGhAX0B+gGuAb4BrgH9AZ8B/wGfAf8BjAH/AYwB
/wFfAb4BXwH9AWgBkgFoAfkBYQFsAWEB6wNVAawDHwEsEAADIQEwA1kBtgFmAmoB7gF9AqEB+gGuAr4B
/QGfA/8BjAP/AV8CvgH9AWgCkgH5AWECbAHrA1UBrAMfASwQAAMhATADWQG2AmYBagHuAn0BoQH6Aq4B
vgH9Ap8C/wKMAv8CXwG+Af0CaAGSAfkCYQFsAesDVQGsAx8BLBQAAwYBBwM2AVgDVQGsA2YB5QOjAfwD
lAH7A2UB4gNTAacDMwFRAwYBBxgAAwYBBwM2AVgDVQGsA2YB5QF+AbYBfgH8AW8BogFvAfsDZQHiA1MB
pwMzAVEDBgEHGAADBgEHAzYBWANVAawDZgHlAX4CtgH8AW8CogH7A2UB4gNTAacDMwFRAwYBBxgAAwYB
BwM2AVgDVQGsA2YB5QJ+AbYB/AJvAaIB+wNlAeIDUwGnAzMBUQMGAQcMAAFCAU0BPgcAAT4DAAEoAwAB
QAMAASADAAEBAQABAQYAAQEWAAP/gQAB4AEHAeABBwHgAQcB4AEHAcABAwHAAQMBwAEDAcABAwGAAQEB
gAEBAYABAQGAAQFQAAGAAQEBgAEBAYABAQGAAQEBwAEDAcABAwHAAQMBwAEDAeABBwHgAQcB4AEHAeAB
Bws=
YAHoA10B0QNQAZ8DMQFNAwUBBhQAAyABLQNUAasDWwHkA1oB9QMkAfsDTgH+A04B/gMkAfsDUwH0A2IB
4QNRAaEDHgEqEAADIAEtA1QBqwNbAeQDWgH1ASEBXgEhAfsBKgFqASoB/gEqAWoBKgH+ASEBXgEhAfsD
UwH0A2IB4QNRAaEDHgEqEAADIAEtA1QBqwNbAeQDWgH1ASECXgH7ASoCagH+ASoCagH+ASECXgH7A1MB
9ANiAeEDUQGhAx4BKhAAAyABLQNUAasDWwHkA1oB9QIhAV4B+wIqAWoB/gIqAWoB/gIhAV4B+wNTAfQD
YgHhA1EBoQMeASoMAAMbASUDWAG9A1oB8gNSAf4DMAH/AzkB/wM8Af8DNgH/AyoB/wMkAf8DQAH9A14B
8ANWAbIDGgEjCAADGwElA1gBvQNaAfIBKgFyASoB/gEAAVcBAAH/AQABZwEAAf8BAAFsAQAB/wEAAWEB
AAH/AQABTAEAAf8BAAFAAQAB/wNAAf0DXgHwA1YBsgEZARoBGQEjCAADGwElA1gBvQNaAfIBKgJyAf4B
AAJXAf8BAAJnAf8BAAJsAf8BAAJhAf8BAAJMAf8BAAJAAf8DQAH9A14B8ANWAbIBGQIaASMIAAMbASUD
WAG9A1oB8gIqAXIB/gIAAVcB/wIAAWcB/wIAAWwB/wIAAWEB/wIAAUwB/wIAAUAB/wNAAf0DXgHwA1YB
sgIZARoBIwQAAwMBBANSAaUDYAHzA0kB/wNVAf8DZQH/A3EB/wN1Af8DcQH/A2QB/wNMAf8DMQH/A04B
/gNiAe4DUAGaAwMBBAMDAQQBUgFTAVIBpQFgAW8BYAHzAQABggEAAf8BAAGZAQAB/wEAAbYBAAH/AQAB
zAEAAf8BAAHTAQAB/wEAAcsBAAH/AQABswEAAf8BAAGIAQAB/wEAAVcBAAH/ASoBagEqAf4DYgHuA1AB
mgMDAQQDAwEEAVICUwGlAWACbwHzAQACggH/AQACmQH/AQACtgH/AQACzAH/AQAC0wH/AQACywH/AQAC
swH/AQACiAH/AQACVwH/ASoCagH+A2IB7gNQAZoDAwEEAwMBBAJSAVMBpQJgAW8B8wIAAYIB/wIAAZkB
/wIAAbYB/wIAAcwB/wIAAdMB/wIAAcsB/wIAAbMB/wIAAYgB/wIAAVcB/wIqAWoB/gNiAe4DUAGaAwMB
BAMtAUQDYAHoA4AB/gNuAf8DewH/A4UB/wOKAf8DjAH/A4oB/wOFAf8DdgH/A1cB/wMyAf8DQAH9A14B
3QMqAT8DLQFEAWABaQFgAegBKgGAASoB/gEAAcYBAAH/AQAB3AEAAf8BAAHuAQAB/wEAAfgBAAH/AQAB
+wEAAf8BAAH5AQAB/wEAAe8BAAH/AQAB1AEAAf8BAAGcAQAB/wEAAVoBAAH/A0AB/QNeAd0DKgE/Ay0B
RAFgAmkB6AEqAoAB/gEAAsYB/wEAAtwB/wEAAu4B/wEAAvgB/wEAAvsB/wEAAvkB/wEAAu8B/wEAAtQB
/wEAApwB/wEAAloB/wNAAf0DXgHdAyoBPwMtAUQCYAFpAegCKgGAAf4CAAHGAf8CAAHcAf8CAAHuAf8C
AAH4Af8CAAH7Af8CAAH5Af8CAAHvAf8CAAHUAf8CAAGcAf8CAAFaAf8DQAH9A14B3QMqAT8DTgGVA3cB
+AN/Af8DhQH/A4oB/wONAf8DjgH/A44B/wOOAf8DjQH/A4kB/wN3Af8DTQH/AyUB/wNaAfIDSgGLA04B
lQFcAXwBXAH4AQAB5QEAAf8BAAHvAQAB/wEAAfgBAAH/AQAB/QEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEAAf8BAAH+AQAB/wEAAfYBAAH/AQAB1QEAAf8BAAGLAQAB/wEAAUEBAAH/A1oB8gNKAYsDTgGVAVwC
fAH4AQAC5QH/AQAC7wH/AQAC+AH/AQAC/QH/AQAD/wEAA/8BAAP/AQAC/gH/AQAC9gH/AQAC1QH/AQAC
iwH/AQACQQH/A1oB8gNKAYsDTgGVAlwBfAH4AgAB5QH/AgAB7wH/AgAB+AH/AgAB/QH/AgAC/wIAAv8C
AAL/AgAB/gH/AgAB9gH/AgAB1QH/AgABiwH/AgABQQH/A1oB8gNKAYsDXwHTA34B/AOTAf8DjgH/A40B
/wOOAf8DjgH/A44B/wOOAf8DjgH/A40B/wOFAf8DZwH/AzQB/wNBAfkDWgHEAVsBXwFbAdMBKwF+ASsB
/AEOAfsBDgH/AQMB/QEDAf8BAAH+AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEAAf8BAAH9AQAB/wEAAe8BAAH/AQABuQEAAf8BAAFdAQAB/wNBAfkDWgHEAVsCXwHTASsCfgH8AQ4C
+wH/AQMC/QH/AQAC/gH/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL9Af8BAALvAf8BAAK5Af8BAAJdAf8D
QQH5A1oBxAJbAV8B0wIrAX4B/AIOAfsB/wIDAf0B/wIAAf4B/wIAAv8CAAL/AgAC/wIAAv8CAAL/AgAB
/QH/AgAB7wH/AgABuQH/AgABXQH/A0EB+QNaAcQDbgH1A4AB/gOfAf8DkwH/A48B/wOOAf8DjgH/A44B
/wOOAf8DjgH/A44B/wOLAf8DdwH/A0gB/wNAAf0DYgHhAVoBbgFaAfUBTgGrAU4B/gEnAf8BJwH/AQsB
/wELAf8BAQH/AQEB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEAAfkBAAH/AQAB1gEAAf8BAAGBAQAB/wNAAf0DYgHhAVoCbgH1AU4CqwH+AScD/wELA/8BAQP/AQAD
/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAC+QH/AQAC1gH/AQACgQH/A0AB/QNiAeECWgFuAfUCTgGrAf4C
JwL/AgsC/wIBAv8CAAL/AgAC/wIAAv8CAAL/AgAC/wIAAv8CAAH5Af8CAAHWAf8CAAGBAf8DQAH9A2IB
4QNjAfYDgAH+A6sB/wOZAf8DkAH/A44B/wOOAf8DjgH/A44B/wOOAf8DjgH/A40B/wN/Af8DVQH/A0AB
/QNeAeIBSAFjAUgB9gFxAasBcQH+AUIB/wFCAf8BGQH/ARkB/wEEAf8BBAH/AQAB/wEAAf8BAAH/AQAB
/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/QEAAf8BAAHkAQAB/wEAAZgBAAH/A0AB
/QNeAeIBSAJjAfYBcQKrAf4BQgP/ARkD/wEEA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL9Af8B
AALkAf8BAAKYAf8DQAH9A14B4gJIAWMB9gJxAasB/gJCAv8CGQL/AgQC/wIAAv8CAAL/AgAC/wIAAv8C
AAL/AgAC/wIAAf0B/wIAAeQB/wIAAZgB/wNAAf0DXgHiA2EB1gN+AfwDuAH/A6MB/wOTAf8DjgH/A44B
/wOOAf8DjgH/A44B/wOOAf8DjQH/A4IB/wNcAf8DTQH6A1oBxwFcAWEBXAHWAWQBgwFkAfwBXwH/AV8B
/wEvAf8BLwH/AQwB/wEMAf8BAQH/AQEB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEAAf8BAAH+AQAB/wEAAeoBAAH/AQABpQEAAf8BSAFNAUgB+gNaAccBXAJhAdYBZAKDAfwBXwP/AS8D
/wEMA/8BAQP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL+Af8BAALqAf8BAAKlAf8BSAJNAfoDWgHHAlwB
YQHWAmQBgwH8Al8C/wIvAv8CDAL/AgEC/wIAAv8CAAL/AgAC/wIAAv8CAAL/AgAB/gH/AgAB6gH/AgAB
pQH/AkgBTQH6A1oBxwNQAZoDbQH5A8UB/wOyAf8DnAH/A5EB/wOOAf8DjgH/A44B/wOOAf8DjwH/A44B
/wODAf8DYAH/A1oB8gNMAZADUAGaAWoBfwFqAfkBfAH/AXwB/wFRAf8BUQH/AR8B/wEfAf8BBwH/AQcB
/wEBAf8BAQH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQIB/wECAf8BAgH+AQIB/wEAAesBAAH/AQAB
rQEAAf8BWgFrAVoB8gNMAZADUAGaAWoCfwH5AXwD/wFRA/8BHwP/AQcD/wEBA/8BAAP/AQAD/wEAA/8B
AgP/AQIC/gH/AQAC6wH/AQACrQH/AVoCawHyA0wBkANQAZoCagF/AfkCfAL/AlEC/wIfAv8CBwL/AgEC
/wIAAv8CAAL/AgAC/wICAv8CAgH+Af8CAAHrAf8CAAGtAf8CWgFrAfIDTAGQAy8BSQNsAesDgAH+A8YB
/wOuAf8DnAH/A5MB/wOQAf8DjwH/A5AB/wOTAf8DkwH/A4UB/wNAAf0DYAHgAy0BRQMvAUkDbAHrAYAB
qwGAAf4BfwH/AX8B/wFJAf8BSQH/AR8B/wEfAf8BDAH/AQwB/wEFAf8BBQH/AQMB/wEDAf8BBQH/AQUB
/wEKAf8BCgH/AQoB/gEKAf8BAQHtAQEB/wFAAbYBQAH9AWABZgFgAeADLQFFAy8BSQNsAesBgAKrAf4B
fwP/AUkD/wEfA/8BDAP/AQUD/wEDA/8BBQP/AQoD/wEKAv4B/wEBAu0B/wFAArYB/QFgAmYB4AMtAUUD
LwFJA2wB6wKAAasB/gJ/Av8CSQL/Ah8C/wIMAv8CBQL/AgMC/wIFAv8CCgL/AgoB/gH/AgEB7QH/AkAB
tgH9AmABZgHgAy0BRQMDAQQDVgGuA24B9QPZAf8DywH/A7cB/wOnAf8DnQH/A5oB/wOcAf8DnwH/A5sB
/wOJAf8DaAHwA1IBowMDAQQDAwEEA1YBrgNuAfUBqAH/AagB/wGJAf8BiQH/AVwB/wFcAf8BNwH/ATcB
/wEiAf8BIgH/ARsB/wEbAf8BHwH/AR8B/wEmAf8BJgH/AR0B/wEdAf8BBQHzAQUB/wFeAWgBXgHwA1IB
owMDAQQDAwEEA1YBrgNuAfUBqAP/AYkD/wFcA/8BNwP/ASID/wEbA/8BHwP/ASYD/wEdA/8BBQLzAf8B
XgJoAfADUgGjAwMBBAMDAQQDVgGuA24B9QKoAv8CiQL/AlwC/wI3Av8CIgL/AhsC/wIfAv8CJgL/Ah0C
/wIFAfMB/wJeAWgB8ANSAaMDAwEEBAADHAEnA10BxwNjAfYDiwH+A9cB/wPMAf8DwgH/A7sB/wO3Af8D
sQH/A4AB/gNoAfQDWQG8AxsBJggAAxwBJwNdAccDYwH2AYABqwGAAf4BpQH/AaUB/wGLAf8BiwH/AXQB
/wF0Af8BZgH/AWYB/wFcAf8BXAH/AU4B/wFOAf8BWgGrAVoB/gFTAWgBUwH0AVcBWQFXAbwDGwEmCAAD
HAEnA10BxwNjAfYBgAKrAf4BpQP/AYsD/wF0A/8BZgP/AVwD/wFOA/8BWgKrAf4BUwJoAfQBVwJZAbwD
GwEmCAADHAEnA10BxwNjAfYCgAGrAf4CpQL/AosC/wJ0Av8CZgL/AlwC/wJOAv8CWgGrAf4CUwFoAfQC
VwFZAbwDGwEmDAADIQEwA1kBtgNiAe4DfQH6A74B/QPUAf8DzAH/A74B/QNqAfkDbAHrA1UBrAMfASwQ
AAMhATADWQG2A2IB7gN9AfoBrgG+Aa4B/QGfAf8BnwH/AYwB/wGMAf8BTAG+AUwB/QFoAX8BaAH5AWEB
bAFhAesDVQGsAx8BLBAAAyEBMANZAbYDYgHuA30B+gGuAr4B/QGfA/8BjAP/AUwCvgH9AWgCfwH5AWEC
bAHrA1UBrAMfASwQAAMhATADWQG2A2IB7gN9AfoCrgG+Af0CnwL/AowC/wJMAb4B/QJoAX8B+QJhAWwB
6wNVAawDHwEsFAADBgEHAzYBWANVAawDZgHlA34B/AOBAfsDZQHiA1MBpwMzAVEDBgEHGAADBgEHAzYB
WANVAawDZgHlAX4BgwF+AfwBXwGMAV8B+wNlAeIDUwGnAzMBUQMGAQcYAAMGAQcDNgFYA1UBrANmAeUB
fgKDAfwBXwKMAfsDZQHiA1MBpwMzAVEDBgEHGAADBgEHAzYBWANVAawDZgHlAn4BgwH8Al8BjAH7A2UB
4gNTAacDMwFRAwYBBwwAAUIBTQE+BwABPgMAASgDAAFAAwABIAMAAQEBAAEBBgABARYAA/+BAAHgAQcB
4AEHAeABBwHgAQcBwAEDAcABAwHAAQMBwAEDAYABAQGAAQEBgAEBAYABAVAAAYABAQGAAQEBgAEBAYAB
AQHAAQMBwAEDAcABAwHAAQMB4AEHAeABBwHgAQcB4AEHCw==
</value>
</data>
<metadata name="ilGames.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@ -292,168 +290,171 @@
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAMiUAAAJNU0Z0AUkBTAIBAQMB
AAF4AQEBeAEBASABAAEgAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABgAMAASADAAEBAQABIAYAAUB6
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAKCUAAAJNU0Z0AUkBTAIBAQMB
AAEQAQIBEAECASABAAEgAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABgAMAASADAAEBAQABIAYAAUB6
AANcAecBCAEKAQAB/wMqAUAMAAMqAUADNgFXAz8BbAM/AWwDPwFsAz8BbAM/AWwDPwFsAz8BbAM/AWwD
PwFsAz8BbAM/AWwDPwFsAz8BbAM/AWwDPwFsAz8BbAI7AToBYgMzAVEDGAEhVAADUwGiA1sBwCAAA1QB
pgNZAbzwAAMhATADRgGAFAABFQEfAQQB/wENARUBAAH/AQgBCgEAAf8BCAEKAQAB/wwAA0QBegNTAacC
YQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8C
YQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYAFdAc4DWQG7AlABTwGbAyoBQFQAA2MB0ANvAe4g
AANlAdQDbQHp5AADWgG/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoB
YQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYAFdAc4DWQG7AlABTwGbAyoBQFQAA18B0ANiAe4g
AANhAdQDYwHp5AADWgG/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoB
AAH/AQgBCgEAAf8DWgG/A1oBvwFDAXQBBwH/AUcBdwEMAf8BJQFHAQAB/wEOARkBAAH/DAACSwFKAYoC
WgFYAb0CZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoC
ZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCYwFaAekCXwFbAdMDVQGvAy4BSFQAA2MB
0ANvAe4gAANlAdQDbQHp4AABCAEKAQAB/wEQAR4BAAH/ASEBQQEAAf8BJwFLAQAB/wEmAUoBAAH/ASEB
ZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCYwFaAekCXwFbAdMDVQGvAy4BSFQAA18B
0ANiAe4gAANhAdQDYwHp4AABCAEKAQAB/wEQAR4BAAH/ASEBQQEAAf8BJwFLAQAB/wEmAUoBAAH/ASEB
QQEAAf8BEAEeAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEpAU4BAAH/AVoBlgEQAf8BWgGcARAB
/wFzAbYBJgH/ARgBLgEAAf8MAANJAYYCWgFYAbcCZQFgAeMCZQFgAeMCZQFgAeMCZQFgAeMCagFhAeYC
agFeAe0BawFoAVMB9AGhAXcBKwH8Af8BeAEAAv8BeAEAAf8CbgFXAfUCagFhAeYCZQFgAeMCZQFgAeMC
ZQFgAeMCZQFeAeICXgFbAc0CVQFTAaoCLgEtAUZUAANjAdADbwHuIAADZQHUA20B6dwAAQgBCgEAAf8B
PQFxAQIB/wFMAYYBBwH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB/wFMAX8B
DwH/ATsBZwEIAf8BIQE0AQgB/wFTAY4BCwH/AVoBnAEQAf8BcgGzAScB/wFTAYEBGwH/EAADKwFBAzYB
WQJAAT8BbgJAAT8BbgJAAT8BbgJAAT8BbgNEAXsCUQFQAZ8CXQFbAcUDYgHuAf8BeAEAAv8BeAEAAf8C
XwFdAckDRQF8AkABPwFuAkABPwFuAkABPwFuAz8BbQM7AWMDMwFSAxkBIlQAA2MB0ANvAe4gAANlAdQD
bQHp2AABFQEpAQAB/wFKAYQBBAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB
/wFSAYwBEAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVYBkAEMAf8BWgGaARAB/wFaAZwBEAH/AXIB
qQEwAf8BCAEKAQAB/wEIAQoBAAH/DAADDAEQAxEBFgMVARwDFQEcAxUBHAMVARwDIQEwAj4BPQFpAlMB
UgGlAmYBXwHlAf8BeAEAAv8BeAEAAf8CVQFTAaoDIgExAxUBHAMVARwDFQEcAxQBGwMTARkDEAEVAwcB
CVQAA2MB0ANvAe4gAANlAdQDbQHp1AABFgEnAQIB/wFJAYABBwH/AVIBjAEIAf8BUgGMAQgB/wFSAYwB
CAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCwH/AVIBjAEQAf8BUgGMARAB/wFYAZIBEAH/AVoBlAEQAf8B
WgGbARAB/wFaAZwBEAH/AVwBngESAf8BOwFtAQMB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wgAAwUE
BgQIAQoDCAEKAwgBCgMIAQoDFwEfAjkBOAFdA1EBngJlAWAB4wH/AXgBAAL/AXgBAAH/A1IBowMYASED
CAEKAwgBCgMIAQoDCAEKAwcBCQMGAQcDAgEDVAADYwHQA28B7iAAA2UB1ANtAenQAANdAd8BRwGAAQMB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBEAH/AVIB
jAEQAf8BUgGMARAB/wFaAZQBEAH/AVoBlAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB
/wFaAZgBEAH/ARYBKgEAAf8BCAEKAQAB/wEIAQoBAAH/HAADEgEXAzUBVgNQAZoDYgHhAf8BeAEAAv8B
eAEAAf8CUQFQAZ8DEgEYcAADYwHQA28B7iAAA2UB1ANtAenQAAEsAVABAAH/AUoBjAEAAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEOAf8BVQGSARAB/wFaAZwBEAH/AVoB
nAEQAf8BWgGUARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGUARgB
/wFYAZIBFgH/ARABHgEAAf8BCAEKAQAB/xwAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEB
UAGfAxIBGHAAA2MB0ANvAe4gAANlAdQDbQHp1AADSwGPATMBXgEAAf8BUgGMAQgB/wFSAYwBCAH/AVIB
jAEIAf8BUgGMARAB/wF/AcABMwH/AV8BngEXAf8BIAE/AQAB/wEIAQoBAAH/AVIBjAEQAf8BWgGcARAB
/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZUBFwH/AVoBlAEYAf8BWgGUARgB/wFaAZQBGAH/AQgB
CgEAAf8BCAEKAQAB/xgAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQABAED
AwEEAwYEBwEJAwcBCQMEAQUEAgQBFAADEQEWAyEBLwMhAS8DIQEvAyEBLwMhAS8DIQEvAyEBLwMhAS8D
IQEvA2QB2QN0AfEDIQEvAyEBLwMhAS8DIQEvAyEBLwMhAS8DIQEvAyEBLwNoAdwDcQHtAyEBLwMhAS8D
IQEvAyEBLwMhAS8DIQEvAyEBLwMhAS8DIQEvAw0BEbgAAUIBewEAAf8BUgGMAQgB/wF7AcYBKQH/AzMB
UAQAAwwBEAE5AWsBAAH/AVoBnAEQAf8BWgGcARAB/wFdAZcBEwH/AVYBlAEMAf8BWgGcARAB/wFaAZQB
GAH/AVoBlAEYAf8BYwGcARgB/wFjAaUBFwH/ASkBUAEAAf8BCAEKAQAB/xgAAxIBFwM1AVYDUAGaA2IB
4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQABAIDCQEMAw8BFAMTARoDEgEYAwoBDQMEAQUEARQAA28B
6AN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B
/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B
/wNjAc+8AAEIAQoBAAH/DAABEwEjAQAB/wFaAZQBEAH/AVoBnAEQAf8BlAHWAUoB/wENAREBAgH/ATEB
WgEAAf8BWgGUARIB/wFaAZQBGAH/AVoBlAEYAf8BYwGcARgB/wFjAaUBFwH/AWsBpQEhAf8BEAEeAQAB
/xgAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwUBBgMdASkDMAFKAjsB
PAFlAjsBPAFkAygBPAIVARYBHQMGAQgUAAM8AWQDTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPA0wB
jwNMAY8DbgHqA3wB+ANMAY8DTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPA3EB7ANxAfUDTAGPA0wB
jwNMAY8DTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DNQFWqAADXQHfAQgBCgEAAf8BCAEKAQAB/wEIAQoB
AAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AzMBUAM6AWABSwGFAQcB/wFaAZQBEAH/AVoBnAEQAf8B
OQFrAQAB/wMJAQwBEAEeAQAB/wFUAY4BEgH/AVoBlAEYAf8BYwGcARgB/wFjAZwBGAH/AWMBpQEXAf8B
cwG0ASkB/wEQAR4BAAH/GAADEgEXAzUBVgNQAZoDYgHhAf8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYFAAD
CQEMAzMBUAJMAU0BkQJdAV8ByQJbAV0BygNEAXoDKQE9AwwBEDwAA2MB0ANvAe4gAANlAdQDbQHpzAAB
CAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoB
AAH/AQgBCgEAAf8BMwFfAQIB/wFaAZQBEAH/AVoBnAEQAf8BrQHnAWMB/wEQAR4BAAH/AQgBCgEAAf8B
FAEmAQAB/wFUAY4BEgH/AWIBmwEYAf8BYwGcARgB/wFjAaUBFwH/AWMBpQEXAf8BYwGcASEB/wEYAS4B
AAH/GAADEgEXAzUBVgNQAZoDYgHhAf8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYFAADCQELAy4BRwNIAYQC
WQFcAcMCXAFfAcsDSQGFAy4BRwMPARM8AANjAdADbwHuIAADZQHUA20B6cgAAQgBCgEAAf8BKwFSAQAB
/wFBAXIBBQH/AUoBhAEGAf8BSgGEAQYB/wFKAYQBBgH/AUIBcwEGAf8BKQFQAQAB/wEQAR4BAAH/AR8B
OgEAAf8BVQGPAQsB/wFaAZUBEAH/AVoBnAEQAf8B1gH/AYwB/wEQAR4BAAH/ARgBLgEAAf8BQgF4AQMB
/wFaAZQBGAH/AWIBmwEYAf8BYwGcARgB/wFjAaUBFwH/AWMBpQEXAf8BcwG0ASkB/wEQAR4BAAH/GAAD
EgEXAzUBVgNQAZoDYgHhAf8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYFAADBgEIAyQBNAM8AWYDUwGnA1gB
uAJHAUgBgwMxAU0DDwEUPAADYwHQA28B7iAAA2UB1ANtAenEAAEoAU0BAAH/AU4BiAEHAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB/wFSAYwBEAH/AVIB
jAEQAf8BWgGUARAB/wFaAZwBEAH/AWMBpQEXAf8BQgF7AQAB/wFKAYQBBgH/AVIBjAEIAf8BWgGYARQB
/wFaAZQBGAH/AWMBnAEYAf8BYwGlARcB/wFjAaUBFwH/AWMBpQEXAf8BlAHJAU0B/wQAAxABFQMaASQD
IQEvAxEBFgQCBAEDEgEXAzUBVgNQAZoDYgHhAf8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYFAADAwEEAxMB
GQMlATYCOwE8AWQCQQFCAXIDMwFRAyEBLwMJAQw8AANjAdADbwHuIAADZQHUA20B6cAAASkBTgECAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwB
EAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoBlQEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8B
WgGcARAB/wFaAZQBGAH/AVoBlAEYAf8BYwGcARgB/wFjAZwBGAH/AWMBpQEXAf8BYwGlARcB/wFjAaUB
FwH/AWUBoAEhAf8EAAMeASsCMQEwAUwDOwFjAyIBMQMHAQkDBAEFAxIBGAM2AVcDUAGaA2IB4QH/AXgB
AAL/AXgBAAH/AlEBUAGfAxIBGBwAAwcBCQMcAScDIwEyAhkBGgEjAw8BFAMEAQU8AANjAdADbwHuIAAD
ZQHUA20B6bwAA0YBgAFOAYgBBwH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB/wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFaAZcBEAH/AVoB
nAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGUARgB/wFaAZQBGAH/AWMBnAEYAf8BYwGhARgB
/wFjAaUBFwH/AWMBpQEXAf8BeAG6ASwB/wEIAQoBAAH/BAADJAE0A0UBfAJaAVgBtwNOAZQDPwFtAygB
OwMfASwDOQFeA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVABnwMSARgcAAMSARcDPAFmA0oBiQNAAW8D
LgFHAw4BEjwAA2MB0ANvAe4gAANlAdQDbQHpvAABNQFeAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB/wFSAYwBEAH/AVoB
lAEQAf8BWgGUARAB/wFaAZsBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlgEWAf8BWgGUARgB
/wFdAZcBGAH/AWMBnAEYAf8BYwGlARcB/wFjAaUBFwH/AXABswEgAf8BKwFQAQQB/wgAAycBOgJTAVEB
ogGWAV8BIQH7AmUBXAHnAlwBWgHEAkMBQgF1AzEBTQJAAT8BbgJRAVABnwJlAWAB4wH/AXgBAAL/AXgB
AAH/AlEBUAGfAxIBGBwAAxkBIgNOAZUCWwFeAc0CVgFXAbIDRAF6Ax0BKQMGAQgEAgQBMAADYwHQA28B
7iAAA2UB1ANtAem8AAFKAYQBBgH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB
/wFnAakBFwH/AVIBjAEIAf8BSgGEAQYB/wFKAYQBBgH/AVIBjAEQAf8BWgGUARAB/wFaAZcBEAH/AVoB
nAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGUARgB/wFaAZQBGAH/AV8BmAEYAf8BYwGeARgB
/wFjAaUBFwH/AWsBqQEdAf8BKQFQAQAB/wwAAyIBMQNOAZgCbwFgAfMCaAFeAfACagFhAeYCWgFXAcIC
VQFTAaoDUwGnAlkBVwG8AmwBYQHrAf8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYHAADFAEbAkMBRAF3AlYB
WQG2AlsBXgHNAlYBWAGzAz8BbAMmATgDDAEPAwIBAwgAAwIBAwMNAREDDQERAw0BEQMNAREDDQERAw0B
EQMNAREDDQERAw0BEQNiAdMDcQHvAw0BEQMNAREDDQERAw0BEQMNAREDDQERAw0BEQMNAREDZQHXA28B
6gMNAREDDQERAw0BEQMNAREDDQERAw0BEQMNAREDDQERAw0BEQMEAQWUAAFKAYQBBgH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BaQGkAR4B/wEsAVABAwH/AQgBCgEAAf8BNwFhAQcB/wFSAYwB
DgH/AVIBjAEQAf8BfwHBATIB/wFbAV4BWwHTA1oBvwEvAVkBAAH/AUQBegEFAf8BUAGKAQgB/wFaAZwB
DgH/AVoBnAEQAf8BWgGcARAB/wFPAYYBEAH/AUQBdAEMAf8DWgG/EAACGgEZASMDRAF7Al0BWwHKAmMB
XwHaAmkBYAHoAmcBWQHvAmUBXQHsAmEBXAHWAl8BWwHYAWsBaAFTAfQB/wF4AQAC/wF4AQAB/wJRAVAB
nwMSARgcAAMPARMDNAFTA00BkgJdAWEBzwJdAWEB1AJSAVQBqAJBAUIBcgMmATgDFAEbAwYBCAMCAQMD
XQHCA38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8D
agFeAe0CaAFTAfQBfgF3ASsB/AH/AXgBAAL/AXgBAAH/Am4BWgH1AmoBYQHmAmUBYAHjAmUBYAHjAmUB
YAHjAmUBXgHiAl4BWwHNAlUBUwGqAi4BLQFGVAADXwHQA2IB7iAAA2EB1ANjAencAAEIAQoBAAH/AT0B
cQECAf8BTAGGAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BTAF/AQ8B
/wE7AWcBCAH/ASEBNAEIAf8BUwGOAQsB/wFaAZwBEAH/AXIBswEnAf8BUwGBARsB/xAAAysBQQM2AVkC
QAE/AW4CQAE/AW4CQAE/AW4CQAE/AW4DRAF7AlEBUAGfAl0BWwHFA2IB7gH/AXgBAAL/AXgBAAH/Al8B
XQHJA0UBfAJAAT8BbgJAAT8BbgJAAT8BbgM/AW0DOwFjAzMBUgMZASJUAANfAdADYgHuIAADYQHUA2MB
6dgAARUBKQEAAf8BSgGEAQQB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
UgGMARAB/wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFWAZABDAH/AVoBmgEQAf8BWgGcARAB/wFyAakB
MAH/AQgBCgEAAf8BCAEKAQAB/wwAAwwBEAMRARYDFQEcAxUBHAMVARwDFQEcAyEBMAI+AT0BaQJTAVIB
pQJmAV8B5QH/AXgBAAL/AXgBAAH/AlUBUwGqAyIBMQMVARwDFQEcAxUBHAMUARsDEwEZAxABFQMHAQlU
AANfAdADYgHuIAADYQHUA2MB6dQAARYBJwECAf8BSQGAAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQsB/wFSAYwBEAH/AVIBjAEQAf8BWAGSARAB/wFaAZQBEAH/AVoB
mwEQAf8BWgGcARAB/wFcAZ4BEgH/ATsBbQEDAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8IAAMFBAYE
CAEKAwgBCgMIAQoDCAEKAxcBHwI5ATgBXQNRAZ4CZQFgAeMB/wF4AQAC/wF4AQAB/wNSAaMDGAEhAwgB
CgMIAQoDCAEKAwgBCgMHAQkDBgEHAwIBA1QAA18B0ANiAe4gAANhAdQDYwHp0AADXQHfAUcBgAEDAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB/wFSAYwB
EAH/AVIBjAEQAf8BWgGUARAB/wFaAZQBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8B
WgGYARAB/wEWASoBAAH/AQgBCgEAAf8BCAEKAQAB/xwAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgB
AAH/AlEBUAGfAxIBGHAAA18B0ANiAe4gAANhAdQDYwHp0AABLAFQAQAB/wFKAYwBAAH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBDgH/AVUBkgEQAf8BWgGcARAB/wFaAZwB
EAH/AVoBlAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8B
WAGSARYB/wEQAR4BAAH/AQgBCgEAAf8cAAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVAB
nwMSARhwAANfAdADYgHuIAADYQHUA2MB6dQAA0sBjwEzAV4BAAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwB
CAH/AVIBjAEQAf8BfwHAATMB/wFfAZ4BFwH/ASABPwEAAf8BCAEKAQAB/wFSAYwBEAH/AVoBnAEQAf8B
WgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGVARcB/wFaAZQBGAH/AVoBlAEYAf8BWgGUARgB/wEIAQoB
AAH/AQgBCgEAAf8YAAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVABnwMSARgUAAQBAwMB
BAMGBAcBCQMHAQkDBAEFBAIEARQAAxEBFgMhAS8DIQEvAyEBLwMhAS8DIQEvAyEBLwMhAS8DIQEvAyEB
LwNhAdkDcAHxAyEBLwMhAS8DIQEvAyEBLwMhAS8DIQEvAyEBLwMhAS8DYgHcA2oB7QMhAS8DIQEvAyEB
LwMhAS8DIQEvAyEBLwMhAS8DIQEvAyEBLwMNARG4AAFCAXsBAAH/AVIBjAEIAf8BewHGASkB/wMzAVAE
AAMMARABOQFrAQAB/wFaAZwBEAH/AVoBnAEQAf8BXQGXARMB/wFWAZQBDAH/AVoBnAEQAf8BWgGUARgB
/wFaAZQBGAH/AWMBnAEYAf8BYwGlARcB/wEpAVABAAH/AQgBCgEAAf8YAAMSARcDNQFWA1ABmgNiAeEB
/wF4AQAC/wF4AQAB/wJRAVABnwMSARgUAAQCAwkBDAMPARQDEwEaAxIBGAMKAQ0DBAEFBAEUAANpAegD
fwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8D
fwH/A2gB3JQAAUoBjAEAAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFnAZ0BJwH/AQgB
CgEAAf8BCgEPAQAB/wFCAXcBAwH/AVIBjAEQAf8BWgGcARAB/wElAUEBBQH/BAADOgFgAQgBCgEAAf8B
CAEKAQAB/wQAA0YBgANGAYADRgGAHAADCgENAyIBMQM0AVQDRAF4AlEBUAGfAmEBXQHPAmoBXgHtAmUB
XQHsAmgBXgHwAZYBXwEhAfsB/wF4AQAC/wF4AQAB/wJRAVABnwMSARgcAAMGAQgDGAEhAzEBTQJMAU0B
kQJYAVoBvQJdAWEB0QJZAVwBwwJRAVMBogM9AWcCGQEaASMDDAEPA0ABbwNVAa0DVQGtA1UBrQNVAa0D
VQGtA1UBrQNVAa0DVQGtA1UBrQNwAfADfQH6A1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNVAa0D
dAHxA3wB+ANVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNHAYGUAAE7AW0BAgH/AVIB
jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUwGOARAB/wEPARsBAAH/ASMBQwEAAf8BUgGMARAB
/wFSAYwBEAH/AXsBvQEwAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgB
CgEAAf8BCAEKAQAB/ygAAwMBBAMJAQsDIgExAzgBWwJPAU4BlwJfAVwByAJmAV8B5QGBAW8BUQH3Ab4B
WAFAAf0B/wF4AQAC/wF4AQAB/wJRAVABnwMSARgcAAQBAwIBAwMVARwDNQFWAksBTAGPAlwBXwHIAl0B
YwHfAmABZQHjAlIBUwGlAzQBUwMcAScoAANjAdADbwHuIAADZQHUA20B6bwAAQgBCgEAAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AU4BggEPAf8BSgGEAQYB/wFSAYwBEAH/AWMB
pQEXAf8BCAEKAQAB/wEIAQoBAAH/ARMBIwEAAf8BQgFzAQYB/wEgAT8BAAH/AQgBCgEAAf8BCAEKAQAB
/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/yAABAEDAwEEAwwBEAMYASADKQE9Az4BagJXAVYBtQJmAV8B
5QGBAW8BUQH3Af8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYIAAEAQMHAQkDFAEbAyoBQANDAXYDVQGvAloB
YwHpAlsBXwHQAlABUQGfAzMBUSgAA2MB0ANvAe4gAANlAdQDbQHpwAABQgFzAQYB/wFSAYwBCAH/AVIB
jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB/wFSAYwBEAH/AUoBhAEGAf8BEAEeAQAB
/wExAVoBAAH/AU0BhAENAf8BWgGcARAB/wFaAZwBEAH/AVIBjAEIAf8BEAEeAQAB/wEIAQoBAAH/AQgB
CgEAAf8DRgGALAADAgEDAwsBDgMgAS4DQwF2AlcBVQGxAmYBYAHgAXIBYwFIAfYBiQFqAUEB+QJRAVAB
nAMSARcMAAQBAwwBEAMXAR8DHQEoAx0BKAMMAQ8IAAMPARQCJgEnATkCRwFIAYMDYgHhAlwBZQHnA14B
0gI/AUABbigAA2MB0ANvAe4gAANlAdQDbQHpxAABSQGJAQAB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFaAZwBEAH/AVoB
nAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8BQgFzAQYB/wEKAQ4BAAH/MAAEAQMDAQQDDAEPAx4B
KgM6AWECWQFXAbkCYwFbAeQDYgHuAk8BTgGXAxIBFwwAAwIBAwMhAS8DNgFZAkIBQwF1AkEBQgFzAx4B
KwgAAwQBBQMMAQ8DOAFcAl0BYAHOAl0BZQHsAlkBZwHvA0YBfygAA2MB0ANvAe4gAANlAdQDbQHpyAAB
OgFrAQAB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFaAZQB
EAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGUARgB/wFaAZQBGAH/AWMBpQEXAf8B
GQEsAQMB/zgAAwIBAwMIAQoDIAEtAkEBQAFxAk0BTAGRA1ABmgI7AToBYgMMAQ8MAAMDAQQDKwFBA0QB
egNTAacDVQGtAzcBWgMYASADCQELAwYBBwMNAREDOQFfAl0BYQHRAlkBZwHvAmABbwHzAkYBRwGBKAAD
YwHQA28B7iAAA2UB1ANtAenMAAEsAVUBAAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBEAH/AVIBjAEQAf8B
WgGUARAB/wFaAZQBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGUARgB/wFaAZQB
GAH/ASEBQQEAAf9AAAQCAwcBCQMQBBUBHAIWARUBHQMPARMDAgEDDAADAwEEAzADSwFMAY8CWwFdAcoC
WwFhAd4DTgGUAzQBUwMVARwDDQERAx4BKwJDAUQBdwJdAWMB3wJlAXAB8QJaAWMB6QNEAXsoAANjAdAD
bwHuIAADZQHUA20B6cgAARIBIgEAAf8BTAGGAQYB/wFSAYwBCAH/AVoBnAEQAf8BUAGKAQgB/wFSAYwB
EAH/AVoBlAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEQH/AWkBqwEfAf8B
CAEKAQAB/2wAAwIBAwMhAS8DOgFhA1UBrQJfAWMB2gNaAb8CUAFRAZ8DRAF6AkEBQgFyA0kBhQJWAVcB
sgJeAWoB7QJgAWQB2wJVAVcBsQM4AVsoAANjAdADbwHuIAADZQHUA20B6cgAARABHgEAAf8BSAF9AQcB
/wFZAZMBEwH/AVUBiAEYAf8IAAEhAUEBAAH/AUIBewEAAf8BUgGMAQgB/wFVAZEBCAH/ATkBZgEEAf8D
WQHveAADBgEIAxcBHwNEAXsCWQFcAcYCWwFjAeQCUwFrAfQDYgHuA2IB7gJlAXAB8QJIAXIB9gIrAaEB
/AJZAVwBvgM+AWsDJAE0KAADYwHQA28B7iAAA2UB1ANtAenQAAEeAToBAAH/nAAEAgMJAQwDKgM/AUAB
bgNOAZYCVgFYAbMCWQFcAcECWgFdAccCWgFdAccCVwFZAbkCUQFSAaQCQAFBAXEDJQE2AxMBGSgAA2MB
0ANvAe4gAANlAdQDbQHp/wB5AAQBAwsBDgMmATgDOQFeA0YBfQJKAUsBiwNKAYkDPgFrAyoBPwMXAR8D
AgEDLAADUwGiA1wBvyAAA1QBpgNaAbuoAAFCAU0BPgcAAT4DAAEoAwABgAMAASADAAEBAQABAQYAAQIW
AAP/AQAD/wHHAgABBwL/Ac8B8wH/BAAC/wHPAYcCAAEHAv8BzwHzAf8EAAH/Af4BAAEHAgABBwL/Ac8B
8wH/BAAB/wH8AQABBwIAAQcC/wHPAfMB/wQAAf8B+AEAAQ8CAAEHAv8BzwHzAf8EAAH/AfABAAEHAgAB
BwL/Ac8B8wH/BAAB/wHgAQABAwIAAQcC/wHPAfMB/wQAAf8BwAEAAQEB/AEDA/8BzwHzAf8EAAH/AcAB
AAEBAfwBAwP/Ac8B8wH/BAAB/wHgAgAB/AEDAeABHwgAAf8B/AEgAQAB/AEDAeABHwgAAf8B/gHgAQAB
/AEDAeABHwgAAf8BwAIAAfwBAwHgAR8B/wHPAfMB/wQAAf8BgAIAAfwBAwHgAR8B/wHPAfMB/wQAAf8D
AAH8AQMB4AEfAf8BzwHzAf8EAAH+AgABAQEAAQMB4AEfAf8BzwHzAf8EAAH8AgABAQEAAQMB+AEfAf8B
zwHzAf8EAAH4AgABAQEAAQMB+AEfAf8BzwHzAf8EAAH4AgABAwEAAQMB+AEDAf8BzwHzAf8EAAH4AgAB
BwEAAQMB+AEDCAAB+AIAAQ8BAAEDAfgJAAH4AQABRAF/AQABAwH4CQAB+AEAAQEB/wGAAQMB+AEAAf8B
zwHzAf8EAAH4AgABfwGAAQMB/AEAAf8BzwHzAf8EAAH8AgABfwHwAQMBgQGAAf8BzwHzAf8EAAH+AgAB
/wHwAQMBgQGAAf8BzwHzAf8EAAH/AgAB/wH8AQMBgAEAAf8BzwHzAf8EAAH/AYABAQH/Af4BAwGAAQAB
/wHPAfMB/wQAAf8BAAEDA/8BgAEAAf8BzwHzAf8EAAH/AQwBDwP/AcABAAH/Ac8B8wH/BAAB/wHfBP8B
wAEAAf8BzwHzAf8EAAb/AfABAQH/Ac8B8wH/BAAL
fwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8D
YQHPvAABCAEKAQAB/wwAARMBIwEAAf8BWgGUARAB/wFaAZwBEAH/AZQB1gFKAf8BDQERAQIB/wExAVoB
AAH/AVoBlAESAf8BWgGUARgB/wFaAZQBGAH/AWMBnAEYAf8BYwGlARcB/wFrAaUBIQH/ARABHgEAAf8Y
AAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVABnwMSARgUAAMFAQYDHQEpAzABSgI7ATwB
ZQI7ATwBZAMoATwCFQEWAR0DBgEIFAADPAFkA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8D
TAGPA2cB6gN8AfgDTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPA0wBjwNlAewDbgH1A0wBjwNMAY8D
TAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPAzUBVqgAA10B3wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB
/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wMzAVADOgFgAUsBhQEHAf8BWgGUARAB/wFaAZwBEAH/ATkB
awEAAf8DCQEMARABHgEAAf8BVAGOARIB/wFaAZQBGAH/AWMBnAEYAf8BYwGcARgB/wFjAaUBFwH/AXMB
tAEpAf8BEAEeAQAB/xgAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwkB
DAMzAVACTAFNAZECXQFfAckCWwFdAcoDRAF6AykBPQMMARA8AANfAdADYgHuIAADYQHUA2MB6cwAAQgB
CgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB
/wEIAQoBAAH/ATMBXwECAf8BWgGUARAB/wFaAZwBEAH/Aa0B5wFjAf8BEAEeAQAB/wEIAQoBAAH/ARQB
JgEAAf8BVAGOARIB/wFiAZsBGAH/AWMBnAEYAf8BYwGlARcB/wFjAaUBFwH/AWMBnAEhAf8BGAEuAQAB
/xgAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwkBCwMuAUcDSAGEAlkB
XAHDAlwBXwHLA0kBhQMuAUcDDwETPAADXwHQA2IB7iAAA2EB1ANjAenIAAEIAQoBAAH/ASsBUgEAAf8B
QQFyAQUB/wFKAYQBBgH/AUoBhAEGAf8BSgGEAQYB/wFCAXMBBgH/ASkBUAEAAf8BEAEeAQAB/wEfAToB
AAH/AVUBjwELAf8BWgGVARAB/wFaAZwBEAH/AdYB/wGMAf8BEAEeAQAB/wEYAS4BAAH/AUIBeAEDAf8B
WgGUARgB/wFiAZsBGAH/AWMBnAEYAf8BYwGlARcB/wFjAaUBFwH/AXMBtAEpAf8BEAEeAQAB/xgAAxIB
FwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwYBCAMkATQDPAFmA1MBpwNYAbgC
RwFIAYMDMQFNAw8BFDwAA18B0ANiAe4gAANhAdQDYwHpxAABKAFNAQAB/wFOAYgBBwH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFSAYwB
EAH/AVoBlAEQAf8BWgGcARAB/wFjAaUBFwH/AUIBewEAAf8BSgGEAQYB/wFSAYwBCAH/AVoBmAEUAf8B
WgGUARgB/wFjAZwBGAH/AWMBpQEXAf8BYwGlARcB/wFjAaUBFwH/AZQByQFNAf8EAAMQARUDGgEkAyEB
LwMRARYEAgQBAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwMBBAMTARkD
JQE2AjsBPAFkAkEBQgFyAzMBUQMhAS8DCQEMPAADXwHQA2IB7iAAA2EB1ANjAenAAAEpAU4BAgH/AVIB
jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB
/wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFaAZUBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoB
nAEQAf8BWgGUARgB/wFaAZQBGAH/AWMBnAEYAf8BYwGcARgB/wFjAaUBFwH/AWMBpQEXAf8BYwGlARcB
/wFlAaABIQH/BAADHgErAjEBMAFMAzsBYwMiATEDBwEJAwQBBQMSARgDNgFXA1ABmgNiAeEB/wF4AQAC
/wF4AQAB/wJRAVABnwMSARgcAAMHAQkDHAEnAyMBMgIZARoBIwMPARQDBAEFPAADXwHQA2IB7iAAA2EB
1ANjAem8AANGAYABTgGIAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFSAYwBEAH/AVoBlAEQAf8BWgGXARAB/wFaAZwB
EAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8BWgGUARgB/wFjAZwBGAH/AWMBoQEYAf8B
YwGlARcB/wFjAaUBFwH/AXgBugEsAf8BCAEKAQAB/wQAAyQBNANFAXwCWgFYAbcDTgGUAz8BbQMoATsD
HwEsAzkBXgNQAZoDYgHhAf8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYHAADEgEXAzwBZgNKAYkDQAFvAy4B
RwMOARI8AANfAdADYgHuIAADYQHUA2MB6bwAATUBXgEHAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQB
EAH/AVoBlAEQAf8BWgGbARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZYBFgH/AVoBlAEYAf8B
XQGXARgB/wFjAZwBGAH/AWMBpQEXAf8BYwGlARcB/wFwAbMBIAH/ASsBUAEEAf8IAAMnAToCUwFRAaIB
gwFfASEB+wJlAVwB5wJcAVoBxAJDAUIBdQMxAU0CQAE/AW4CUQFQAZ8CZQFgAeMB/wF4AQAC/wF4AQAB
/wJRAVABnwMSARgcAAMZASIDTgGVAlsBXgHNAlYBVwGyA0QBegMdASkDBgEIBAIEATAAA18B0ANiAe4g
AANhAdQDYwHpvAABSgGEAQYB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
ZwGpARcB/wFSAYwBCAH/AUoBhAEGAf8BSgGEAQYB/wFSAYwBEAH/AVoBlAEQAf8BWgGXARAB/wFaAZwB
EAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8BWgGUARgB/wFfAZgBGAH/AWMBngEYAf8B
YwGlARcB/wFrAakBHQH/ASkBUAEAAf8MAAMiATEDTgGYAm8BYAHzAmgBXgHwAmoBYQHmAloBVwHCAlUB
UwGqA1MBpwJZAVcBvAJsAWEB6wH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBwAAxQBGwJDAUQBdwJWAVkB
tgJbAV4BzQJWAVgBswM/AWwDJgE4AwwBDwMCAQMIAAMCAQMDDQERAw0BEQMNAREDDQERAw0BEQMNARED
DQERAw0BEQMNAREDXwHTA2cB7wMNAREDDQERAw0BEQMNAREDDQERAw0BEQMNAREDDQERA2IB1wNnAeoD
DQERAw0BEQMNAREDDQERAw0BEQMNAREDDQERAw0BEQMNAREDBAEFlAABSgGEAQYB/wFSAYwBCAH/AVIB
jAEIAf8BUgGMAQgB/wFSAYwBCAH/AWkBpAEeAf8BLAFQAQMB/wEIAQoBAAH/ATcBYQEHAf8BUgGMAQ4B
/wFSAYwBEAH/AX8BwQEyAf8BWwFeAVsB0wNaAb8BLwFZAQAB/wFEAXoBBQH/AVABigEIAf8BWgGcAQ4B
/wFaAZwBEAH/AVoBnAEQAf8BTwGGARAB/wFEAXQBDAH/A1oBvxAAAhoBGQEjA0QBewJdAVsBygJjAV8B
2gJpAWAB6AJnAVkB7wJlAV0B7AJhAVwB1gJfAVsB2AJoAVMB9AH/AXgBAAL/AXgBAAH/AlEBUAGfAxIB
GBwAAw8BEwM0AVMDTQGSAl0BYQHPAl0BYQHUAlIBVAGoAkEBQgFyAyYBOAMUARsDBgEIAwIBAwNaAcID
fwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8D
fwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8DfwH/A38B/wN/Af8D
YgHclAABSgGMAQAB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AWcBnQEnAf8BCAEKAQAB
/wEKAQ8BAAH/AUIBdwEDAf8BUgGMARAB/wFaAZwBEAH/ASUBQQEFAf8EAAM6AWABCAEKAQAB/wEIAQoB
AAH/BAADRgGAA0YBgANGAYAcAAMKAQ0DIgExAzQBVANEAXgCUQFQAZ8CYQFdAc8CagFeAe0CZQFdAewC
aAFeAfABgwFfASEB+wH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBwAAwYBCAMYASEDMQFNAkwBTQGRAlgB
WgG9Al0BYQHRAlkBXAHDAlEBUwGiAz0BZwIZARoBIwMMAQ8DQAFvA1UBrQNVAa0DVQGtA1UBrQNVAa0D
VQGtA1UBrQNVAa0DVQGtA2gB8AN9AfoDVQGtA1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNwAfED
fAH4A1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA0cBgZQAATsBbQECAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFTAY4BEAH/AQ8BGwEAAf8BIwFDAQAB/wFSAYwBEAH/AVIB
jAEQAf8BewG9ATAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB
/wEIAQoBAAH/KAADAwEEAwkBCwMiATEDOAFbAk8BTgGXAl8BXAHIAmYBXwHlAm8BUQH3Ab4BRQFAAf0B
/wF4AQAC/wF4AQAB/wJRAVABnwMSARgcAAQBAwIBAwMVARwDNQFWAksBTAGPAlwBXwHIAl0BYwHfAmAB
ZQHjAlIBUwGlAzQBUwMcAScoAANfAdADYgHuIAADYQHUA2MB6bwAAQgBCgEAAf8BUgGMAQgB/wFSAYwB
CAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AU4BggEPAf8BSgGEAQYB/wFSAYwBEAH/AWMBpQEXAf8B
CAEKAQAB/wEIAQoBAAH/ARMBIwEAAf8BQgFzAQYB/wEgAT8BAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoB
AAH/AQgBCgEAAf8BCAEKAQAB/yAABAEDAwEEAwwBEAMYASADKQE9Az4BagJXAVYBtQJmAV8B5QJvAVEB
9wH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGCAABAEDBwEJAxQBGwMqAUADQwF2A1UBrwJaAWMB6QJbAV8B
0AJQAVEBnwMzAVEoAANfAdADYgHuIAADYQHUA2MB6cAAAUIBcwEGAf8BUgGMAQgB/wFSAYwBCAH/AVIB
jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFKAYQBBgH/ARABHgEAAf8BMQFaAQAB
/wFNAYQBDQH/AVoBnAEQAf8BWgGcARAB/wFSAYwBCAH/ARABHgEAAf8BCAEKAQAB/wEIAQoBAAH/A0YB
gCwAAwIBAwMLAQ4DIAEuA0MBdgJXAVUBsQJmAWAB4AJjAUgB9gFtAWoBQQH5AlEBUAGcAxIBFwwABAED
DAEQAxcBHwMdASgDHQEoAwwBDwgAAw8BFAImAScBOQJHAUgBgwNiAeECXAFlAecDXgHSAj8BQAFuKAAD
XwHQA2IB7iAAA2EB1ANjAenEAAFJAYkBAAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
UgGMARAB/wFSAYwBEAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwB
EAH/AVoBnAEQAf8BWgGUARgB/wFCAXMBBgH/AQoBDgEAAf8wAAQBAwMBBAMMAQ8DHgEqAzoBYQJZAVcB
uQJjAVsB5ANiAe4CTwFOAZcDEgEXDAADAgEDAyEBLwM2AVkCQgFDAXUCQQFCAXMDHgErCAADBAEFAwwB
DwM4AVwCXQFgAc4CXQFlAewCWQFnAe8DRgF/KAADXwHQA2IB7iAAA2EB1ANjAenIAAE6AWsBAAH/AVIB
jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoBlAEQAf8BWgGcARAB
/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZQBGAH/AVoBlAEYAf8BYwGlARcB/wEZASwBAwH/OAAD
AgEDAwgBCgMgAS0CQQFAAXECTQFMAZEDUAGaAjsBOgFiAwwBDwwAAwMBBAMrAUEDRAF6A1MBpwNVAa0D
NwFaAxgBIAMJAQsDBgEHAw0BEQM5AV8CXQFhAdECWQFnAe8CYAFvAfMCRgFHAYEoAANfAdADYgHuIAAD
YQHUA2MB6cwAASwBVQEAAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoB
lAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZQBGAH/AVoBlAEYAf8BIQFBAQAB
/0AABAIDBwEJAxAEFQEcAhYBFQEdAw8BEwMCAQMMAAMDAQQDMANLAUwBjwJbAV0BygJbAWEB3gNOAZQD
NAFTAxUBHAMNAREDHgErAkMBRAF3Al0BYwHfAmUBcAHxAloBYwHpA0QBeygAA18B0ANiAe4gAANhAdQD
YwHpyAABEgEiAQAB/wFMAYYBBgH/AVIBjAEIAf8BWgGcARAB/wFQAYoBCAH/AVIBjAEQAf8BWgGUARAB
/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAERAf8BaQGrAR8B/wEIAQoBAAH/bAAD
AgEDAyEBLwM6AWEDVQGtAl8BYwHaA1oBvwJQAVEBnwNEAXoCQQFCAXIDSQGFAlYBVwGyAl4BagHtAmAB
ZAHbAlUBVwGxAzgBWygAA18B0ANiAe4gAANhAdQDYwHpyAABEAEeAQAB/wFIAX0BBwH/AVkBkwETAf8B
VQGIARgB/wgAASEBQQEAAf8BQgF7AQAB/wFSAYwBCAH/AVUBkQEIAf8BOQFmAQQB/wNZAe94AAMGAQgD
FwEfA0QBewJZAVwBxgJbAWMB5AJTAWgB9ANiAe4DYgHuAmUBcAHxAkgBYwH2AisBfgH8AlkBXAG+Az4B
awMkATQoAANfAdADYgHuIAADYQHUA2MB6dAAAR4BOgEAAf+cAAQCAwkBDAMqAz8BQAFuA04BlgJWAVgB
swJZAVwBwQJaAV0BxwJaAV0BxwJXAVkBuQJRAVIBpAJAAUEBcQMlATYDEwEZKAADXwHQA2IB7iAAA2EB
1ANjAen/AHkABAEDCwEOAyYBOAM5AV4DRgF9AkoBSwGLA0oBiQM+AWsDKgE/AxcBHwMCAQMsAANTAaID
WgG/IAADVAGmA1kBu6gAAUIBTQE+BwABPgMAASgDAAGAAwABIAMAAQEBAAEBBgABAhYAA/8BAAP/AccC
AAEHAv8BzwHzAf8EAAL/Ac8BhwIAAQcC/wHPAfMB/wQAAf8B/gEAAQcCAAEHAv8BzwHzAf8EAAH/AfwB
AAEHAgABBwL/Ac8B8wH/BAAB/wH4AQABDwIAAQcC/wHPAfMB/wQAAf8B8AEAAQcCAAEHAv8BzwHzAf8E
AAH/AeABAAEDAgABBwL/Ac8B8wH/BAAB/wHAAQABAQH8AQMD/wHPAfMB/wQAAf8BwAEAAQEB/AEDA/8B
zwHzAf8EAAH/AeACAAH8AQMB4AEfCAAB/wH8ASABAAH8AQMB4AEfCAAB/wH+AeABAAH8AQMB4AEfCAAB
/wHAAgAB/AEDAeABHwH/Ac8B8wH/BAAB/wGAAgAB/AEDAeABHwH/Ac8B8wH/BAAB/wMAAfwBAwHgAR8B
/wHPAfMB/wQAAf4CAAEBAQABAwHgAR8B/wHPAfMB/wQAAfwCAAEBAQABAwH4AR8B/wHPAfMB/wQAAfgC
AAEBAQABAwH4AR8B/wHPAfMB/wQAAfgCAAEDAQABAwH4AQMB/wHPAfMB/wQAAfgCAAEHAQABAwH4AQMI
AAH4AgABDwEAAQMB+AkAAfgBAAFEAX8BAAEDAfgJAAH4AQABAQH/AYABAwH4AQAB/wHPAfMB/wQAAfgC
AAF/AYABAwH8AQAB/wHPAfMB/wQAAfwCAAF/AfABAwGBAYAB/wHPAfMB/wQAAf4CAAH/AfABAwGBAYAB
/wHPAfMB/wQAAf8CAAH/AfwBAwGAAQAB/wHPAfMB/wQAAf8BgAEBAf8B/gEDAYABAAH/Ac8B8wH/BAAB
/wEAAQMD/wGAAQAB/wHPAfMB/wQAAf8BDAEPA/8BwAEAAf8BzwHzAf8EAAH/Ad8E/wHAAQAB/wHPAfMB
/wQABv8B8AEBAf8BzwHzAf8EAAs=
</value>
</data>
<metadata name="ilStoreThumbnails.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>12, 128</value>
</metadata>
<metadata name="ilTabIcons.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>20, 44</value>
</metadata>
@ -461,8 +462,8 @@
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAuBIAAAJNU0Z0AUkBTAIBAQQB
AAF4AQIBeAECARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAASADAAEBAQABIAYAASD/
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAshIAAAJNU0Z0AUkBTAIBAQQB
AAEQAQMBEAEDARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAASADAAEBAQABIAYAASD/
AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/ACIAA2cB7wJnAVkB7wFnAV0BWQHvAWcBWwFZAe8B
ZwFbAVkB7wFnAlkB7wFnAWQBWQHvA2cB7wNnAe8DZwHvA2cB7wNnAe8DZwHvA2cB7wNnAe8DZwHvAwcB
CQMqAT8DRQF8A1kBuwNjAd8DaAH0A4AB/gOBAf8DgQH/A4EB/wOAAf4DaAH0A2MB3wNaAboDRAF6AycB
@ -488,7 +489,7 @@
YAH/AYMBggF/Af8BhQGCAX0B/wGzAZMBRAH/Ac8BlAEAAf8B3gHMAZ8B/wOwAf8DsAH/A7AB/wOwAf8D
sAH/A7AB/wOwAf8DsAH/A6gB/wMCAQMDGgEjAzgBXANUAagDYgHXA3AB8QOAAf4DgQH/A4EB/wOBAf8D
gQH9A2gB8ANhAdQDUwGlAzYBWQMYASAIAAMaASQDUgGgAmMBSAH2AaIBcwEAAf8BrgF8AQAB/wGwAX0B
AAH/AagBeAEAAf8BlQFqAQAB/wKAAU8B/gFcAlkBxgNXAbUDFgEeCAAB/wGyAW8B/wH9AYABEQH/AfwB
AAH/AagBeAEAAf8BlQFqAQAB/wKAAWIB/gFcAlkBxgNXAbUDFgEeCAAB/wGyAW8B/wH9AYABEQH/AfwB
dwEBAf8B/QF3AQAC/wF4AQAC/wF/AQ8C/wGSATsC/wGzAYMC/wHqAeAC/wGQAT0C/wF7AQoB/wH8AXcB
AAH/AeUBbAEAAf8BsQFUAQAB/wGEAUsBGgH/A2MB3wT/AdkBqgE3Af8BvgGYATgB/wHAAZgBNgH/AdwB
ogEUAf8BzwGUAQAB/wHeAcwBnwH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DkwH/RAAD
@ -524,24 +525,24 @@
AwH/AeIBhAEyAf8DYwHfBP8BpwGUAWcB/wGDAYIBfwH/AYUBgwF+Af8BsAGUAU4B/wHTAZcBAgH/AeAB
zgGfAf8D5wH/A+cB/wPnAf8D5wH/A+cB/wPnAf8D5wH/A+cB/wPAAf8DZAHbA2gB9AOBAf8DgQH/A4EB
/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DVQGvAwcBCQNiAe4B8QG8ATsB
/wH6AeoBwgH/A9wB/wN3Af8DaAH/A2gB/wNoAf8DMgn/AfIB3QGpAf8B6gGpAQgB/wM+AWoEAANeAdIB
dQJqAfkB/wG7AX0C/wGjAVIC/wGQASwC/wGCARMC/wF9AQgC/wF6AQQC/wF5AQIC/wF6AQQC/wF9AQkC
/wGAAQ8C/wF/AQ0B/wH4AYYBIAH/A2gB9ANYAbgE/wHXAbEBUgH/AZoBjwF0Af8BoAGSAW0B/wHgAa8B
NwH/AecBqQEQAf8B6wHVAaAB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A5MB/wM8AWQD
XwHJA30B+gOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A30B+gNVAaoE
AAMzAVEDaAHwAfMBxgFYAf8B+gHnAbgW/wH+AfsB/wH5AeIBqgH/Ae8BuAEtAf8DTgGWBAIEAAM8AWYD
YwHVA3wB+AH/Ab4BhQL/AaoBXwL/AZkBPgL/AY0BJwL/AYYBGgL/AYMBFQL/AYUBGQL/AYoBIgL/AY0B
KAL/AZMBMwH/Am4BaAH1A10BxQM2AVkE/wH0Ac0BbAH/AfQBywFmAf8B9AHLAWUB/wH0AcsBZQH/AfEB
wQFJAf8B+QHjAawB/wOJAf8DiQH/A4kB/wOJAf8DiQH/A4kB/wOJAf8DiQH/A5oB/wMGAQgDMQFMA1AB
mwNlAewDfQH6A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wN9AfoDagHtA1ABmwMvAUkIAAMiATED
XwHJAn0BZwH6AfMBygFlAf8B+QHhAaYB/wH7Ae0BzAH/AfsB7AHIAf8B+AHdAZsB/wOAAf4CZQFeAeID
PQFoBAEMAANCAXIDYwHVAXUBawFqAfkB/wHJAZkC/wG8AX8C/wGuAWYC/wGkAVMC/wGfAUoC/wGfAUsC
/wGjAVEC/wGnAVgB/wN8AfgDXQHHAzsBYgQAQP8IAAMFAQYDEgEXAzoBYANRAZ8DXwHTA2cB7wNjAfYD
YgHuA14B0gNRAZ4DOQFfAxEBFgMFAQYUAAMPARMDRwGCA2QB2wJ+AW8B/ANnAeoDVAGoAygBOxwAAzwB
ZQNeAdIB/wHvAeAC/wHcAbwC/wHNAZ8C/wHBAYoC/wG7AX8C/wG/AYYC/wHNAaEC/wHpAdYB/wNcAcQD
NQFVCAABQgFNAT4HAAE+AwABKAMAAUADAAEgAwABAQEAAQEGAAEBFgAD/4UAAf8B/AHAAQMEAAH/AfwB
gAEBBAAB/wH5BgAB/wH5BgAB8AETBgABwAEDBAAC/wGAAQEEAAHAAQEBgAEBBAABgAIAAQEHAAEBBwAB
AQcAAQEHAAEBBgABgAEBBgABwAEDAYABAQIAAcABAQHwAR8BwAEDCw==
/wH6AeoBwgH/A9wB/wN3Af8DaAH/A2gB/wNoAf8DMgn/AfIB3QGpAf8B6gGpAQgB/wM+AWoEAANeAdID
agH5Af8BuwF9Av8BowFSAv8BkAEsAv8BggETAv8BfQEIAv8BegEEAv8BeQECAv8BegEEAv8BfQEJAv8B
gAEPAv8BfwENAf8B+AGGASAB/wNoAfQDWAG4BP8B1wGxAVIB/wGaAY8BdAH/AaABkgFtAf8B4AGvATcB
/wHnAakBEAH/AesB1QGgAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOTAf8DPAFkA18B
yQN9AfoDgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wN9AfoDVQGqBAAD
MwFRA2gB8AHzAcYBWAH/AfoB5wG4Fv8B/gH7Af8B+QHiAaoB/wHvAbgBLQH/A04BlgQCBAADPAFmA2MB
1QN8AfgB/wG+AYUC/wGqAV8C/wGZAT4C/wGNAScC/wGGARoC/wGDARUC/wGFARkC/wGKASIC/wGNASgC
/wGTATMB/wJuAWgB9QNdAcUDNgFZBP8B9AHNAWwB/wH0AcsBZgH/AfQBywFlAf8B9AHLAWUB/wHxAcEB
SQH/AfkB4wGsAf8DiQH/A4kB/wOJAf8DiQH/A4kB/wOJAf8DiQH/A4kB/wOaAf8DBgEIAzEBTANQAZsD
ZQHsA30B+gOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DfQH6A2oB7QNQAZsDLwFJCAADIgExA18B
yQJ9AWcB+gHzAcoBZQH/AfkB4QGmAf8B+wHtAcwB/wH7AewByAH/AfgB3QGbAf8DgAH+AmUBXgHiAz0B
aAQBDAADQgFyA2MB1QNqAfkB/wHJAZkC/wG8AX8C/wGuAWYC/wGkAVMC/wGfAUoC/wGfAUsC/wGjAVEC
/wGnAVgB/wN8AfgDXQHHAzsBYgQAQP8IAAMFAQYDEgEXAzoBYANRAZ8DXwHTA2cB7wNjAfYDYgHuA14B
0gNRAZ4DOQFfAxEBFgMFAQYUAAMPARMDRwGCA2QB2wJ+AW8B/ANnAeoDVAGoAygBOxwAAzwBZQNeAdIB
/wHvAeAC/wHcAbwC/wHNAZ8C/wHBAYoC/wG7AX8C/wG/AYYC/wHNAaEC/wHpAdYB/wNcAcQDNQFVCAAB
QgFNAT4HAAE+AwABKAMAAUADAAEgAwABAQEAAQEGAAEBFgAD/4UAAf8B/AHAAQMEAAH/AfwBgAEBBAAB
/wH5BgAB/wH5BgAB8AETBgABwAEDBAAC/wGAAQEEAAHAAQEBgAEBBAABgAIAAQEHAAEBBwABAQcAAQEH
AAEBBgABgAEBBgABwAEDAYABAQIAAcABAQHwAR8BwAEDCw==
</value>
</data>
<metadata name="ctxmChangeStatus.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@ -627,8 +628,14 @@
AB/+AAA//wAAf/+AAP//4AP///4///////8=
</value>
</data>
<metadata name="ctxmAdminUserList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>461, 24</value>
</metadata>
<metadata name="ctxmAdminRoomList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>461, 49</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>70</value>
<value>90</value>
</metadata>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>

View File

@ -37,25 +37,22 @@
btnDecline = new Button();
btnCancelRequest = new Button();
btnMessage = new Button();
pbUserStatus = new PictureBox();
pbCurrencyIcon = new PictureBox();
lblCurrencyAmount = new Label();
flpUsernameCurrency = new FlowLayoutPanel();
panel1 = new Panel();
pCurrency = new Panel();
((System.ComponentModel.ISupportInitialize)pbUserPfp).BeginInit();
((System.ComponentModel.ISupportInitialize)pbUserStatus).BeginInit();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).BeginInit();
flpUsernameCurrency.SuspendLayout();
panel1.SuspendLayout();
pCurrency.SuspendLayout();
SuspendLayout();
//
// pbUserPfp
//
pbUserPfp.BorderStyle = BorderStyle.FixedSingle;
pbUserPfp.Image = Properties.Resources.DefaultPfp;
pbUserPfp.Location = new Point(12, 12);
pbUserPfp.Location = new Point(9, 5);
pbUserPfp.Name = "pbUserPfp";
pbUserPfp.Size = new Size(128, 126);
pbUserPfp.Size = new Size(139, 138);
pbUserPfp.SizeMode = PictureBoxSizeMode.StretchImage;
pbUserPfp.TabIndex = 2;
pbUserPfp.TabStop = false;
@ -64,7 +61,7 @@
//
lblUsername.AutoSize = true;
lblUsername.Font = new Font("Segoe UI Light", 15F, FontStyle.Bold);
lblUsername.ForeColor = SystemColors.ControlLight;
lblUsername.ForeColor = Color.White;
lblUsername.Location = new Point(3, 0);
lblUsername.Name = "lblUsername";
lblUsername.Size = new Size(105, 28);
@ -149,17 +146,6 @@
btnMessage.Visible = false;
btnMessage.Click += btnMessage_Click;
//
// pbUserStatus
//
pbUserStatus.BackColor = Color.Transparent;
pbUserStatus.Image = Properties.Resources.OfflineIcon;
pbUserStatus.Location = new Point(115, 1);
pbUserStatus.Name = "pbUserStatus";
pbUserStatus.Size = new Size(32, 32);
pbUserStatus.SizeMode = PictureBoxSizeMode.StretchImage;
pbUserStatus.TabIndex = 10;
pbUserStatus.TabStop = false;
//
// pbCurrencyIcon
//
pbCurrencyIcon.Image = Properties.Resources.CurrencyIcon;
@ -188,21 +174,21 @@
//
flpUsernameCurrency.BackColor = Color.Transparent;
flpUsernameCurrency.Controls.Add(lblUsername);
flpUsernameCurrency.Controls.Add(panel1);
flpUsernameCurrency.Controls.Add(pCurrency);
flpUsernameCurrency.Location = new Point(152, 8);
flpUsernameCurrency.Name = "flpUsernameCurrency";
flpUsernameCurrency.Size = new Size(246, 33);
flpUsernameCurrency.TabIndex = 13;
flpUsernameCurrency.WrapContents = false;
//
// panel1
// pCurrency
//
panel1.Controls.Add(pbCurrencyIcon);
panel1.Controls.Add(lblCurrencyAmount);
panel1.Location = new Point(114, 3);
panel1.Name = "panel1";
panel1.Size = new Size(73, 24);
panel1.TabIndex = 14;
pCurrency.Controls.Add(pbCurrencyIcon);
pCurrency.Controls.Add(lblCurrencyAmount);
pCurrency.Location = new Point(114, 3);
pCurrency.Name = "pCurrency";
pCurrency.Size = new Size(73, 24);
pCurrency.TabIndex = 14;
//
// Profile
//
@ -214,12 +200,12 @@
Controls.Add(btnAccept);
Controls.Add(btnDecline);
Controls.Add(btnCancelRequest);
Controls.Add(pbUserStatus);
Controls.Add(btnAddContact);
Controls.Add(rtxtBio);
Controls.Add(pbUserPfp);
Controls.Add(btnMessage);
Font = new Font("Segoe UI Light", 9F);
ForeColor = Color.White;
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
@ -230,12 +216,11 @@
FormClosed += Profile_FormClosed;
Load += frmProfile_Load;
((System.ComponentModel.ISupportInitialize)pbUserPfp).EndInit();
((System.ComponentModel.ISupportInitialize)pbUserStatus).EndInit();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).EndInit();
flpUsernameCurrency.ResumeLayout(false);
flpUsernameCurrency.PerformLayout();
panel1.ResumeLayout(false);
panel1.PerformLayout();
pCurrency.ResumeLayout(false);
pCurrency.PerformLayout();
ResumeLayout(false);
}
@ -249,10 +234,9 @@
private Button btnDecline;
private Button btnCancelRequest;
private Button btnMessage;
private PictureBox pbUserStatus;
private PictureBox pbCurrencyIcon;
private Label lblCurrencyAmount;
private FlowLayoutPanel flpUsernameCurrency;
private Panel panel1;
private Panel pCurrency;
}
}

View File

@ -1,18 +1,25 @@
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Services.ApiService;
using qtc_net_client_2.ClientModel;
using qtc_net_client_2.Properties;
using qtc_net_client_2.Services;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Models;
using QtCNETAPI.Schema;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using QtCNETAPI.Services.GatewayService;
using qtc_net_client_2.ClientModel;
using qtc_net_client_2.Properties;
namespace qtc_net_client_2.Forms
{
@ -23,50 +30,66 @@ namespace qtc_net_client_2.Forms
private IGatewayService _gatewayService;
private ServiceResponse<byte[]>? pfpRes;
byte[]? cosmeticRes;
private List<Contact> contactsList;
public Profile(UserInformationDto userInfo, ServiceResponse<byte[]>? pfp, List<Contact> contacts, IApiService apiService, IGatewayService gatewayService)
public Profile(UserInformationDto userInfo, ServiceResponse<byte[]>? pfp, List<Contact> contacts, IApiService apiService, IGatewayService gatewayService, byte[]? cosmetic = null)
{
_userInformationDto = userInfo;
_apiService = apiService;
_gatewayService = gatewayService;
pfpRes = pfp;
cosmeticRes = cosmetic;
contactsList = contacts;
InitializeComponent();
}
private void frmProfile_Load(object sender, EventArgs e)
private async void frmProfile_Load(object sender, EventArgs e)
{
lblUsername.Text = _userInformationDto.Username;
lblCurrencyAmount.Text = _userInformationDto.CurrencyAmount.ToString("N0");
rtxtBio.Text = _userInformationDto.Bio;
Bitmap? pfp = null;
if (pfpRes != null && pfpRes.Success && pfpRes.Data != null)
{
using (var ms = new MemoryStream(pfpRes.Data))
{
pbUserPfp.Image = Image.FromStream(ms);
ms.Dispose();
pfp = new Bitmap(ms);
}
}
var userStatus = (UserStatus)_userInformationDto.Status;
Bitmap precenseImage = Resources.OnlineIcon;
switch (userStatus)
{
case UserStatus.Online:
pbUserStatus.Image = Resources.OnlineIcon;
precenseImage = Resources.OnlineIcon;
break;
case UserStatus.Away:
pbUserStatus.Image = Resources.AwayIcon;
precenseImage = Resources.AwayIcon;
break;
case UserStatus.DoNotDisturb:
pbUserStatus.Image = Resources.DNDIcon;
precenseImage = Resources.DNDIcon;
break;
case UserStatus.Offline:
pbUserStatus.Image = Resources.OfflineIcon;
precenseImage = Resources.OfflineIcon;
break;
}
Bitmap? cosmetic = null;
if(cosmeticRes != null)
{
using var ms = new MemoryStream(cosmeticRes);
cosmetic = new Bitmap(ms);
}
CreateProfileImage(precenseImage, pfp, cosmetic);
precenseImage.Dispose();
pfp?.Dispose();
cosmetic?.Dispose();
if (_userInformationDto.Id == _apiService.CurrentUser!.Id)
{
btnAddContact.Visible = false;
@ -215,5 +238,32 @@ namespace qtc_net_client_2.Forms
Close();
frmDirectMessage.Show();
}
private void CreateProfileImage(Bitmap precenseImage, Bitmap? pfp = null, Bitmap? cosmetic = null)
{
Bitmap combined = new Bitmap(139, 138);
using Graphics g = Graphics.FromImage(combined);
g.Clear(Color.Transparent);
g.CompositingMode = CompositingMode.SourceOver;
if (pfp != null)
{
pfp.MakeTransparent();
g.DrawImage(pfp, 4, 6, 128, 128);
}
else g.DrawImage(pbUserPfp.Image, 4, 6, 128, 128);
if (cosmetic != null)
{
cosmetic.MakeTransparent();
g.DrawImage(cosmetic, 0, 0, 139, 138);
}
precenseImage.MakeTransparent();
g.DrawImage(precenseImage, 104, 0, 35, 35);
pbUserPfp.Image = combined;
}
}
}

View File

@ -33,6 +33,8 @@
rtxtBio = new RichTextBox();
lblBio = new Label();
btnSave = new Button();
cbCosmetic = new ComboBox();
lblCosmetic = new Label();
SuspendLayout();
//
// tbUsername
@ -71,7 +73,7 @@
// btnSave
//
btnSave.ForeColor = Color.Black;
btnSave.Location = new Point(76, 148);
btnSave.Location = new Point(76, 177);
btnSave.Name = "btnSave";
btnSave.Size = new Size(43, 23);
btnSave.TabIndex = 4;
@ -79,12 +81,32 @@
btnSave.UseVisualStyleBackColor = true;
btnSave.Click += btnSave_Click;
//
// frmProfileEdit
// cbCosmetic
//
cbCosmetic.FormattingEnabled = true;
cbCosmetic.Items.AddRange(new object[] { "(None)" });
cbCosmetic.Location = new Point(76, 148);
cbCosmetic.Name = "cbCosmetic";
cbCosmetic.Size = new Size(121, 23);
cbCosmetic.TabIndex = 5;
//
// lblCosmetic
//
lblCosmetic.AutoSize = true;
lblCosmetic.Location = new Point(21, 151);
lblCosmetic.Name = "lblCosmetic";
lblCosmetic.Size = new Size(53, 15);
lblCosmetic.TabIndex = 6;
lblCosmetic.Text = "Cosmetic";
//
// ProfileEdit
//
AutoScaleDimensions = new SizeF(6F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(345, 187);
ClientSize = new Size(345, 208);
Controls.Add(lblCosmetic);
Controls.Add(cbCosmetic);
Controls.Add(btnSave);
Controls.Add(lblBio);
Controls.Add(rtxtBio);
@ -95,7 +117,7 @@
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
Name = "frmProfileEdit";
Name = "ProfileEdit";
StartPosition = FormStartPosition.CenterParent;
Text = "QtC.NET Client - Edit Profile";
Load += frmProfileEdit_Load;
@ -110,5 +132,7 @@
private RichTextBox rtxtBio;
private Label lblBio;
private Button btnSave;
private ComboBox cbCosmetic;
private Label lblCosmetic;
}
}

View File

@ -1,4 +1,6 @@
using QtCNETAPI.Dtos.User;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using qtc_net_client_2.ClientModel;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Services.ApiService;
using System;
using System.Collections.Generic;
@ -21,16 +23,54 @@ namespace qtc_net_client_2.Forms
InitializeComponent();
}
private void frmProfileEdit_Load(object sender, EventArgs e)
private async void frmProfileEdit_Load(object sender, EventArgs e)
{
tbUsername.Text = _apiService.CurrentUser!.Username;
rtxtBio.Text = _apiService.CurrentUser!.Bio;
tbUsername.Text = _apiService.CurrentUser.Username;
rtxtBio.Text = _apiService.CurrentUser.Bio;
// get all owned cosmetics
var boughtItems = await _apiService.GetOwnedStoreItems();
List<ComboBoxItem> items = new List<ComboBoxItem>();
if(boughtItems != null && boughtItems.Success && boughtItems.Data != null)
{
items.Add(new ComboBoxItem
{
Name = "(None)",
Value = 0
});
foreach (var item in boughtItems.Data)
{
// get item from the store
var storeItem = await _apiService.GetStoreItem(item.StoreItemId);
if(storeItem != null && storeItem.Success && storeItem.Data != null)
{
var cbi = new ComboBoxItem
{
Name = storeItem.Data.Name,
Value = storeItem.Data.Id
};
items.Add(cbi);
}
}
} else
{
items.Add(new ComboBoxItem
{
Name = "(None)",
Value = 0
});
}
cbCosmetic.DataSource = items;
cbCosmetic.SelectedIndex = cbCosmetic.Items.IndexOf(items.FirstOrDefault(e => (int?)e.Value == _apiService.CurrentUser.ActiveProfileCosmetic));
}
private async void btnSave_Click(object sender, EventArgs e)
{
if(!string.IsNullOrEmpty(tbUsername.Text) && (tbUsername.Text != _apiService.CurrentUser!.Username || rtxtBio.Text != _apiService.CurrentUser!.Bio))
{
ComboBoxItem? selectedItem = (ComboBoxItem?)cbCosmetic.SelectedItem;
// update user info
UserUpdateInformationDto userUpdateInformationDto = new UserUpdateInformationDto
{
@ -40,6 +80,12 @@ namespace qtc_net_client_2.Forms
DateOfBirth = _apiService.CurrentUser.DateOfBirth
};
if (selectedItem != null)
{
int selectedItemId = (int?)selectedItem.Value ?? 0;
userUpdateInformationDto.ProfileCosmeticId = selectedItemId;
}
var res = await _apiService.UpdateUserInformationAsync(userUpdateInformationDto);
if (res.Success)
@ -56,4 +102,3 @@ namespace qtc_net_client_2.Forms
}
}
}
}

View File

@ -43,6 +43,7 @@ namespace qtc_net_client_2.Forms
if(registerResult.Success)
{
MessageBox.Show("Registration Complete. If the server has email verification on, you may need to check your email for a verification link.\nIf you do not receive one, try logging in.");
DialogResult = DialogResult.OK;
Close();
} else

View File

@ -0,0 +1,104 @@
namespace qtc_net_client_2.Forms
{
partial class ResendVerificationEmail
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
pbLoginBanner = new PictureBox();
tbEmail = new TextBox();
lblHeader = new Label();
btnSend = new Button();
((System.ComponentModel.ISupportInitialize)pbLoginBanner).BeginInit();
SuspendLayout();
//
// pbLoginBanner
//
pbLoginBanner.Image = Properties.Resources.LoginBanner;
pbLoginBanner.Location = new Point(-4, 0);
pbLoginBanner.Name = "pbLoginBanner";
pbLoginBanner.Size = new Size(521, 99);
pbLoginBanner.SizeMode = PictureBoxSizeMode.StretchImage;
pbLoginBanner.TabIndex = 1;
pbLoginBanner.TabStop = false;
//
// tbEmail
//
tbEmail.Location = new Point(50, 124);
tbEmail.Name = "tbEmail";
tbEmail.Size = new Size(424, 23);
tbEmail.TabIndex = 3;
//
// lblHeader
//
lblHeader.AutoSize = true;
lblHeader.Font = new Font("Segoe UI Light", 9F);
lblHeader.ForeColor = SystemColors.ControlLight;
lblHeader.Location = new Point(54, 106);
lblHeader.Name = "lblHeader";
lblHeader.Size = new Size(412, 15);
lblHeader.TabIndex = 5;
lblHeader.Text = "Please Enter Your Email, If An Account Exists With This Email, We'll Send You A Link";
//
// btnSend
//
btnSend.Location = new Point(224, 153);
btnSend.Name = "btnSend";
btnSend.Size = new Size(75, 23);
btnSend.TabIndex = 6;
btnSend.Text = "Send";
btnSend.UseVisualStyleBackColor = true;
btnSend.Click += btnSend_Click;
//
// ResendVerificationEmail
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(515, 187);
Controls.Add(btnSend);
Controls.Add(lblHeader);
Controls.Add(tbEmail);
Controls.Add(pbLoginBanner);
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
Name = "ResendVerificationEmail";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client - Resend Verification Email";
((System.ComponentModel.ISupportInitialize)pbLoginBanner).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private PictureBox pbLoginBanner;
private TextBox tbEmail;
private Label lblHeader;
private Button btnSend;
}
}

View File

@ -0,0 +1,43 @@
using QtCNETAPI.Services.ApiService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace qtc_net_client_2.Forms
{
public partial class ResendVerificationEmail : Form
{
private IApiService _apiService;
public ResendVerificationEmail(IApiService apiService)
{
_apiService = apiService;
InitializeComponent();
}
private async void btnSend_Click(object sender, EventArgs e)
{
if(!string.IsNullOrEmpty(tbEmail.Text))
{
tbEmail.Enabled = false;
btnSend.Enabled = false;
var result = await _apiService.ResendVerificationEmail(tbEmail.Text);
if(result != null && result.Success && result.Data)
{
MessageBox.Show("Got It! You should receive an email shortly.\nIf you do not receive an email, check your spam.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
Close();
} else
{
MessageBox.Show("Sorry, This Server Doesn't Have Email Features Enabled Or Something Went Wrong.\nIf you cannot login, you may need to contact the server admin.", "", MessageBoxButtons.OK, MessageBoxIcon.Error);
Close();
}
}
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,221 @@
namespace qtc_net_client_2.Forms
{
partial class ResetPassword
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
pEmailBox = new Panel();
btnSend = new Button();
lblHeader = new Label();
tbEmail = new TextBox();
pResetPasswordBox = new Panel();
btnResetPassword = new Button();
tbConfirmPassword = new TextBox();
lblConfirmPassword = new Label();
tbNewPassword = new TextBox();
lblNewPassword = new Label();
tbToken = new TextBox();
lblToken = new Label();
pbLoginBanner = new PictureBox();
pEmailBox.SuspendLayout();
pResetPasswordBox.SuspendLayout();
((System.ComponentModel.ISupportInitialize)pbLoginBanner).BeginInit();
SuspendLayout();
//
// pEmailBox
//
pEmailBox.Anchor = AnchorStyles.Bottom;
pEmailBox.BorderStyle = BorderStyle.FixedSingle;
pEmailBox.Controls.Add(btnSend);
pEmailBox.Controls.Add(lblHeader);
pEmailBox.Controls.Add(tbEmail);
pEmailBox.Location = new Point(101, 125);
pEmailBox.Name = "pEmailBox";
pEmailBox.Size = new Size(458, 94);
pEmailBox.TabIndex = 0;
//
// btnSend
//
btnSend.Location = new Point(193, 59);
btnSend.Name = "btnSend";
btnSend.Size = new Size(75, 23);
btnSend.TabIndex = 9;
btnSend.Text = "Send";
btnSend.UseVisualStyleBackColor = true;
btnSend.Click += btnSend_Click;
//
// lblHeader
//
lblHeader.AutoSize = true;
lblHeader.Font = new Font("Segoe UI Light", 9F);
lblHeader.ForeColor = SystemColors.ControlLight;
lblHeader.Location = new Point(23, 12);
lblHeader.Name = "lblHeader";
lblHeader.Size = new Size(412, 15);
lblHeader.TabIndex = 8;
lblHeader.Text = "Please Enter Your Email, If An Account Exists With This Email, We'll Send You A Link";
//
// tbEmail
//
tbEmail.Location = new Point(19, 30);
tbEmail.Name = "tbEmail";
tbEmail.Size = new Size(424, 23);
tbEmail.TabIndex = 7;
//
// pResetPasswordBox
//
pResetPasswordBox.Anchor = AnchorStyles.Bottom;
pResetPasswordBox.BorderStyle = BorderStyle.FixedSingle;
pResetPasswordBox.Controls.Add(btnResetPassword);
pResetPasswordBox.Controls.Add(tbConfirmPassword);
pResetPasswordBox.Controls.Add(lblConfirmPassword);
pResetPasswordBox.Controls.Add(tbNewPassword);
pResetPasswordBox.Controls.Add(lblNewPassword);
pResetPasswordBox.Controls.Add(tbToken);
pResetPasswordBox.Controls.Add(lblToken);
pResetPasswordBox.Location = new Point(17, 106);
pResetPasswordBox.Name = "pResetPasswordBox";
pResetPasswordBox.Size = new Size(596, 138);
pResetPasswordBox.TabIndex = 1;
pResetPasswordBox.Visible = false;
//
// btnResetPassword
//
btnResetPassword.Location = new Point(270, 102);
btnResetPassword.Name = "btnResetPassword";
btnResetPassword.Size = new Size(100, 23);
btnResetPassword.TabIndex = 15;
btnResetPassword.Text = "Reset Password";
btnResetPassword.UseVisualStyleBackColor = true;
btnResetPassword.Click += btnResetPassword_Click;
//
// tbConfirmPassword
//
tbConfirmPassword.Location = new Point(123, 73);
tbConfirmPassword.Name = "tbConfirmPassword";
tbConfirmPassword.PasswordChar = '*';
tbConfirmPassword.Size = new Size(424, 23);
tbConfirmPassword.TabIndex = 14;
//
// lblConfirmPassword
//
lblConfirmPassword.AutoSize = true;
lblConfirmPassword.Font = new Font("Segoe UI Light", 9F);
lblConfirmPassword.ForeColor = SystemColors.ControlLight;
lblConfirmPassword.Location = new Point(20, 76);
lblConfirmPassword.Name = "lblConfirmPassword";
lblConfirmPassword.Size = new Size(97, 15);
lblConfirmPassword.TabIndex = 13;
lblConfirmPassword.Text = "Confirm Password";
//
// tbNewPassword
//
tbNewPassword.Location = new Point(123, 44);
tbNewPassword.Name = "tbNewPassword";
tbNewPassword.PasswordChar = '*';
tbNewPassword.Size = new Size(424, 23);
tbNewPassword.TabIndex = 12;
//
// lblNewPassword
//
lblNewPassword.AutoSize = true;
lblNewPassword.Font = new Font("Segoe UI Light", 9F);
lblNewPassword.ForeColor = SystemColors.ControlLight;
lblNewPassword.Location = new Point(36, 47);
lblNewPassword.Name = "lblNewPassword";
lblNewPassword.Size = new Size(81, 15);
lblNewPassword.TabIndex = 11;
lblNewPassword.Text = "New Password";
//
// tbToken
//
tbToken.Location = new Point(123, 15);
tbToken.Name = "tbToken";
tbToken.Size = new Size(424, 23);
tbToken.TabIndex = 10;
//
// lblToken
//
lblToken.AutoSize = true;
lblToken.Font = new Font("Segoe UI Light", 9F);
lblToken.ForeColor = SystemColors.ControlLight;
lblToken.Location = new Point(82, 18);
lblToken.Name = "lblToken";
lblToken.Size = new Size(35, 15);
lblToken.TabIndex = 9;
lblToken.Text = "Token";
//
// pbLoginBanner
//
pbLoginBanner.Image = Properties.Resources.LoginBanner;
pbLoginBanner.Location = new Point(-3, -1);
pbLoginBanner.Name = "pbLoginBanner";
pbLoginBanner.Size = new Size(521, 99);
pbLoginBanner.SizeMode = PictureBoxSizeMode.StretchImage;
pbLoginBanner.TabIndex = 2;
pbLoginBanner.TabStop = false;
//
// ResetPassword
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(622, 256);
Controls.Add(pEmailBox);
Controls.Add(pResetPasswordBox);
Controls.Add(pbLoginBanner);
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
Name = "ResetPassword";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client - Reset Password";
pEmailBox.ResumeLayout(false);
pEmailBox.PerformLayout();
pResetPasswordBox.ResumeLayout(false);
pResetPasswordBox.PerformLayout();
((System.ComponentModel.ISupportInitialize)pbLoginBanner).EndInit();
ResumeLayout(false);
}
#endregion
private Panel pEmailBox;
private Button btnSend;
private Label lblHeader;
private TextBox tbEmail;
private Panel pResetPasswordBox;
private TextBox tbConfirmPassword;
private Label lblConfirmPassword;
private TextBox tbNewPassword;
private Label lblNewPassword;
private TextBox tbToken;
private Label lblToken;
private Button btnResetPassword;
private PictureBox pbLoginBanner;
}
}

View File

@ -0,0 +1,68 @@
using QtCNETAPI.Services.ApiService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace qtc_net_client_2.Forms
{
public partial class ResetPassword : Form
{
private IApiService _apiService;
public ResetPassword(IApiService apiService)
{
_apiService = apiService;
InitializeComponent();
}
private async void btnSend_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(tbEmail.Text))
{
tbEmail.Enabled = false;
btnSend.Enabled = false;
var result = await _apiService.SendPasswordResetEmail(tbEmail.Text);
if (result != null && result.Success && result.Data)
{
pEmailBox.Visible = false;
pResetPasswordBox.Visible = true;
MessageBox.Show("Got It! You should receive an email shortly.\nIf you do not receive an email, check your spam.");
}
else
{
MessageBox.Show("Sorry, This Server Doesn't Have Email Features Enabled Or Something Went Wrong.\nIf you cannot login, you may need to contact the server admin.", "", MessageBoxButtons.OK, MessageBoxIcon.Error);
Close();
}
}
}
private async void btnResetPassword_Click(object sender, EventArgs e)
{
if(!string.IsNullOrEmpty(tbToken.Text) && !string.IsNullOrEmpty(tbNewPassword.Text) && !string.IsNullOrEmpty(tbConfirmPassword.Text))
{
tbToken.Enabled = false;
tbNewPassword.Enabled = false;
tbConfirmPassword.Enabled = false;
btnResetPassword.Enabled = false;
var result = await _apiService.ResetPassword(new QtCNETAPI.Dtos.User.UserPasswordResetDto { Token = tbToken.Text, Password = tbNewPassword.Text });
if(result != null && result.Success && result.Data)
{
MessageBox.Show("Your Password Has Been Reset. You may now login with the new password.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
Close();
} else
{
MessageBox.Show(result?.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
Close();
}
}
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -52,7 +52,7 @@
pStockManagement.Controls.Add(btnSell);
pStockManagement.Controls.Add(btnBuy);
pStockManagement.Controls.Add(lblStockCount);
pStockManagement.Location = new Point(242, 134);
pStockManagement.Location = new Point(230, 138);
pStockManagement.Name = "pStockManagement";
pStockManagement.Size = new Size(135, 81);
pStockManagement.TabIndex = 3;
@ -106,7 +106,7 @@
pMarketStatus.Controls.Add(lblMarketStatus);
pMarketStatus.Controls.Add(pbDollarSignRight);
pMarketStatus.Controls.Add(pbDollarSignLeft);
pMarketStatus.Location = new Point(9, 7);
pMarketStatus.Location = new Point(3, 7);
pMarketStatus.Name = "pMarketStatus";
pMarketStatus.Size = new Size(586, 100);
pMarketStatus.TabIndex = 4;
@ -135,7 +135,7 @@
// pbDollarSignRight
//
pbDollarSignRight.Image = Properties.Resources.dollar_money;
pbDollarSignRight.Location = new Point(498, 3);
pbDollarSignRight.Location = new Point(500, 3);
pbDollarSignRight.Name = "pbDollarSignRight";
pbDollarSignRight.Size = new Size(83, 94);
pbDollarSignRight.SizeMode = PictureBoxSizeMode.Zoom;
@ -145,7 +145,7 @@
// pbDollarSignLeft
//
pbDollarSignLeft.Image = Properties.Resources.dollar_money;
pbDollarSignLeft.Location = new Point(6, 3);
pbDollarSignLeft.Location = new Point(3, 3);
pbDollarSignLeft.Name = "pbDollarSignLeft";
pbDollarSignLeft.Size = new Size(83, 94);
pbDollarSignLeft.SizeMode = PictureBoxSizeMode.Zoom;
@ -156,7 +156,7 @@
//
btnRefresh.Enabled = false;
btnRefresh.ForeColor = Color.Black;
btnRefresh.Location = new Point(282, 109);
btnRefresh.Location = new Point(270, 113);
btnRefresh.Name = "btnRefresh";
btnRefresh.Size = new Size(56, 23);
btnRefresh.TabIndex = 10;
@ -169,7 +169,7 @@
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(606, 227);
ClientSize = new Size(595, 227);
Controls.Add(btnRefresh);
Controls.Add(pMarketStatus);
Controls.Add(pStockManagement);

View File

@ -44,19 +44,6 @@ namespace qtc_net_client_2.Forms
_apiService.CurrentUser.StockAmount = result.Data.StockAmount;
_apiService.CurrentUser.CurrencyAmount = result.Data.CurrencyAmount;
Main? mainWindow = (Main?)Application.OpenForms[0];
if (mainWindow != null)
{
if (mainWindow.InvokeRequired)
{
mainWindow.Invoke(mainWindow.RefreshCurrencyCounter);
}
else
{
mainWindow.RefreshCurrencyCounter();
}
}
nudStockBuySellAmount.Enabled = true;
nudStockBuySellAmount.Value = 0;
btnBuy.Enabled = true;
@ -95,19 +82,6 @@ namespace qtc_net_client_2.Forms
_apiService.CurrentUser.StockAmount = result.Data.StockAmount;
_apiService.CurrentUser.CurrencyAmount = result.Data.CurrencyAmount;
Main? mainWindow = (Main?)Application.OpenForms[0];
if (mainWindow != null)
{
if (mainWindow.InvokeRequired)
{
mainWindow.Invoke(mainWindow.RefreshCurrencyCounter);
}
else
{
mainWindow.RefreshCurrencyCounter();
}
}
nudStockBuySellAmount.Enabled = true;
nudStockBuySellAmount.Value = 0;
btnBuy.Enabled = true;

View File

@ -0,0 +1,135 @@
namespace qtc_net_client_2.Forms
{
partial class StoreItemDisplay
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(StoreItemDisplay));
pbItemThumbnail = new PictureBox();
lblName = new Label();
lblDescription = new Label();
btnBuy = new Button();
pbCurrencyIcon = new PictureBox();
lblPrice = new Label();
((System.ComponentModel.ISupportInitialize)pbItemThumbnail).BeginInit();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).BeginInit();
SuspendLayout();
//
// pbItemThumbnail
//
pbItemThumbnail.Location = new Point(12, 12);
pbItemThumbnail.Name = "pbItemThumbnail";
pbItemThumbnail.Size = new Size(148, 141);
pbItemThumbnail.SizeMode = PictureBoxSizeMode.StretchImage;
pbItemThumbnail.TabIndex = 0;
pbItemThumbnail.TabStop = false;
//
// lblName
//
lblName.AutoSize = true;
lblName.Font = new Font("Segoe UI", 21.75F, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Point, 0);
lblName.Location = new Point(165, 7);
lblName.Name = "lblName";
lblName.Size = new Size(239, 40);
lblName.TabIndex = 1;
lblName.Text = "Item Name Here";
//
// lblDescription
//
lblDescription.Location = new Point(169, 44);
lblDescription.Name = "lblDescription";
lblDescription.Size = new Size(448, 109);
lblDescription.TabIndex = 2;
lblDescription.Text = resources.GetString("lblDescription.Text");
//
// btnBuy
//
btnBuy.ForeColor = Color.Black;
btnBuy.Location = new Point(542, 157);
btnBuy.Name = "btnBuy";
btnBuy.Size = new Size(75, 23);
btnBuy.TabIndex = 3;
btnBuy.Text = "Buy";
btnBuy.UseVisualStyleBackColor = true;
btnBuy.Click += btnBuy_Click;
//
// pbCurrencyIcon
//
pbCurrencyIcon.Image = Properties.Resources.CurrencyIcon;
pbCurrencyIcon.Location = new Point(48, 161);
pbCurrencyIcon.Name = "pbCurrencyIcon";
pbCurrencyIcon.Size = new Size(15, 14);
pbCurrencyIcon.SizeMode = PictureBoxSizeMode.StretchImage;
pbCurrencyIcon.TabIndex = 19;
pbCurrencyIcon.TabStop = false;
//
// lblPrice
//
lblPrice.AutoEllipsis = true;
lblPrice.Font = new Font("Segoe UI", 8F, FontStyle.Bold, GraphicsUnit.Point, 0);
lblPrice.Location = new Point(64, 162);
lblPrice.Name = "lblPrice";
lblPrice.Size = new Size(61, 13);
lblPrice.TabIndex = 20;
lblPrice.Text = "99,999,999";
//
// StoreItemDisplay
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(629, 188);
Controls.Add(lblPrice);
Controls.Add(pbCurrencyIcon);
Controls.Add(btnBuy);
Controls.Add(lblDescription);
Controls.Add(lblName);
Controls.Add(pbItemThumbnail);
ForeColor = Color.White;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
Name = "StoreItemDisplay";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client - Store Item";
Load += StoreItemDisplay_Load;
((System.ComponentModel.ISupportInitialize)pbItemThumbnail).EndInit();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private PictureBox pbItemThumbnail;
private Label lblName;
private Label lblDescription;
private Button btnBuy;
private PictureBox pbCurrencyIcon;
private Label lblPrice;
}
}

View File

@ -0,0 +1,97 @@
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Schema;
using QtCNETAPI.Services;
using QtCNETAPI.Services.ApiService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace qtc_net_client_2.Forms
{
public partial class StoreItemDisplay : Form
{
private StoreItem StoreItem { get; set; }
private LoggingService _loggingService;
private IApiService _apiService;
public StoreItemDisplay(StoreItem item, LoggingService loggingService, IApiService apiService)
{
StoreItem = item;
_loggingService = loggingService;
_apiService = apiService;
InitializeComponent();
}
private async void StoreItemDisplay_Load(object sender, EventArgs e)
{
lblName.Text = StoreItem.Name;
lblDescription.Text = StoreItem.Description;
lblPrice.Text = StoreItem.Price.ToString("N0");
// does the user already own this item?
var ownedItem = await _apiService.GetOwnedStoreItem(StoreItem.Id);
if (ownedItem != null && ownedItem.Success && ownedItem.Data != null)
{
btnBuy.Enabled = false;
btnBuy.Text = "Bought";
}
try
{
using HttpClient client = new();
var response = await client.GetAsync(StoreItem.ThumbnailUrl);
if (response != null && response.IsSuccessStatusCode)
{
using var stream = await response.Content.ReadAsStreamAsync();
Image image = Image.FromStream(stream);
stream.Dispose();
pbItemThumbnail.Image = 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);
}
}
private async void btnBuy_Click(object sender, EventArgs e)
{
Enabled = false;
// attempt to buy item
var ownedStoreItem = await _apiService.BuyStoreItem(StoreItem.Id);
if (ownedStoreItem != null && ownedStoreItem.Success)
{
Enabled = true;
var result = MessageBox.Show($"Successfully Bought '{StoreItem.Name}'! Would You Like To Wear It Now?", "Success!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
// send an update dto that only updates the current users cosmetic
UserUpdateInformationDto updateDto = new UserUpdateInformationDto
{
Id = _apiService.CurrentUser.Id,
Bio = _apiService.CurrentUser.Bio,
DateOfBirth = _apiService.CurrentUser.DateOfBirth,
Username = _apiService.CurrentUser.Username,
ProfileCosmeticId = StoreItem.Id
};
await _apiService.UpdateUserInformationAsync(updateDto);
}
}
else MessageBox.Show("We Weren't Able To Complete Your Purchase.\nYou May Not Have Enough Funds For This Item.", "Oops.", MessageBoxButtons.OK, MessageBoxIcon.Error);
Close();
}
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="lblDescription.Text" xml:space="preserve">
<value>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum</value>
</data>
</root>

View File

@ -261,11 +261,6 @@ namespace qtc_net_client_2
{
await _apiService.AddCurrencyToCurrentUser(currencyJackpotSpinner.TokensWon, false);
_apiService.CurrentUser.CurrencyAmount += currencyJackpotSpinner.TokensWon;
// find the main form to refresh currency count
Main? mainForm = (Main?)Application.OpenForms[0];
if (mainForm != null)
mainForm.RefreshCurrencyCounter();
}
}

View File

@ -1,5 +1,6 @@
using qtc_net_client_2.ClientModel;
using qtc_net_client_2.Services;
using QtCNETAPI.Services;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using System.Text.Json;
@ -52,8 +53,8 @@ namespace qtc_net_client_2
// instantiate new ApiService and GatewayService for this session
// this will keep the gateway thread in the main thread (i think)
IApiService api = new ApiService($"{clientConfig.ServerUrl}/api");
IGatewayService gateway = new GatewayService($"{clientConfig.ServerUrl}/chat", api);
IApiService api = new ApiService($"{clientConfig.ServerUrl}/api", loggingService);
IGatewayService gateway = new GatewayService($"{clientConfig.ServerUrl}/chat", api, loggingService);
// ping api
var pingResult = await api.PingServerAsync();

View File

@ -81,7 +81,7 @@ namespace qtc_net_client_2.Properties {
}
/// <summary>
/// Looks up a localized string similar to 5.2.
/// Looks up a localized string similar to 6.3.4.
/// </summary>
internal static string AssemblyVersion {
get {

View File

@ -118,9 +118,6 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="AddContactIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\AddContactIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="right-horn-animated" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Anims\right-horn-animated.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
@ -157,8 +154,8 @@
<data name="CurrencyIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\CurrencyIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="DefaultPfp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\DefaultPfp.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="AddContactIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\AddContactIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="RequestSentIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\RequestSentIcon.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
@ -176,7 +173,10 @@
<value>..\Icons\MessageIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="AssemblyVersion" xml:space="preserve">
<value>5.2</value>
<value>6.3.4</value>
</data>
<data name="cobalt_sittingatputer" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\cobalt_sittingatputer.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="LoginBanner" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\LoginBanner.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
@ -190,7 +190,7 @@
<data name="AcceptContactIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\AcceptContactIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="cobalt_sittingatputer" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\cobalt_sittingatputer.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="DefaultPfp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\DefaultPfp.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 13 KiB