Compare commits

..

No commits in common. "master" and "5.2" have entirely different histories.
master ... 5.2

46 changed files with 766 additions and 2702 deletions

View File

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

View File

@ -1,14 +0,0 @@
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,6 +6,5 @@
public string Username { get; set; } = string.Empty; public string Username { get; set; } = string.Empty;
public string Bio { get; set; } = string.Empty; public string Bio { get; set; } = string.Empty;
public DateTime DateOfBirth { get; set; } = new DateTime(); public DateTime DateOfBirth { get; set; } = new DateTime();
public int ProfileCosmeticId { get; set; } = 0;
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,23 +0,0 @@
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.Dtos.User;
using QtCNETAPI.Enums; using QtCNETAPI.Enums;
using QtCNETAPI.Models; using QtCNETAPI.Models;
using QtCNETAPI.Schema;
using RestSharp; using RestSharp;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Resources;
using System.Text.Json; using System.Text.Json;
namespace QtCNETAPI.Services.ApiService namespace QtCNETAPI.Services.ApiService
@ -13,15 +13,10 @@ namespace QtCNETAPI.Services.ApiService
{ {
private User? user; private User? user;
private RestClient _client; private RestClient _client;
private LoggingService _loggingService;
private CredentialService _credService = new();
internal string? sessionToken; internal string? sessionToken;
internal string apiUri; internal string apiUri;
public event EventHandler? OnCurrentUserUpdate;
public string? SessionToken public string? SessionToken
{ {
get { return sessionToken; } get { return sessionToken; }
@ -33,10 +28,9 @@ namespace QtCNETAPI.Services.ApiService
get { return user; } get { return user; }
} }
public ApiService(string apiUrl, LoggingService loggingService) public ApiService(string apiUrl)
{ {
apiUri = apiUrl; apiUri = apiUrl;
_loggingService = loggingService;
_client = new RestClient(apiUri); _client = new RestClient(apiUri);
} }
@ -157,9 +151,10 @@ namespace QtCNETAPI.Services.ApiService
serviceResponse.Success = true; serviceResponse.Success = true;
serviceResponse.Data = response.Data; serviceResponse.Data = response.Data;
// anything that changes the user should tell the api service to set it again // update currentuser model
await SetCurrentUser(); CurrentUser!.Username = response.Data!.Username;
OnCurrentUserUpdate?.Invoke(this, EventArgs.Empty); CurrentUser.Bio = response.Data.Bio;
CurrentUser.DateOfBirth = response.Data.DateOfBirth;
} else } else
{ {
serviceResponse.Success = false; serviceResponse.Success = false;
@ -185,10 +180,6 @@ namespace QtCNETAPI.Services.ApiService
{ {
serviceResponse.Success = true; serviceResponse.Success = true;
serviceResponse.Data = response.Data; 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 else
{ {
@ -237,9 +228,9 @@ namespace QtCNETAPI.Services.ApiService
} }
} }
public async Task<ServiceResponse<string>> LoginAsync(UserLoginDto userLoginDto) public async Task<ServiceResponse<User>> LoginAsync(UserLoginDto userLoginDto)
{ {
var serviceResponse = new ServiceResponse<string>(); var serviceResponse = new ServiceResponse<User>();
try try
{ {
@ -259,11 +250,13 @@ namespace QtCNETAPI.Services.ApiService
{ {
SessionToken = response.Data!; SessionToken = response.Data!;
await File.WriteAllTextAsync("./session.token", response.Message);
var user = await SetCurrentUser(); var user = await SetCurrentUser();
serviceResponse.Success = true; serviceResponse.Success = true;
if (response.Message != null) serviceResponse.Message = response.Message; if (response.Message != null) serviceResponse.Message = response.Message;
serviceResponse.Data = response.Message; serviceResponse.Data = user;
} }
else else
{ {
@ -280,85 +273,16 @@ namespace QtCNETAPI.Services.ApiService
return serviceResponse; return serviceResponse;
} }
public async Task<ServiceResponse<bool>> ResendVerificationEmail(string email) private async Task<User> SetCurrentUser()
{
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") var userRequest = new RestRequest("users/user-authorized")
.AddHeader("Authorization", $"Bearer {SessionToken}"); .AddHeader("Authorization", $"Bearer {SessionToken}");
var userResponse = await _client.GetAsync<ServiceResponse<User>>(userRequest); var userResponse = await _client.GetAsync<ServiceResponse<User>>(userRequest);
if (userResponse != null && userResponse.Success && userResponse.Data != null) if (userResponse != null || userResponse!.Data != null)
{ {
user = userResponse.Data; user = userResponse.Data;
return userResponse.Data!;
_loggingService.LogString($"Current User's Status Is {userResponse.Data.Status}");
return userResponse.Data;
} else } else
{ {
throw new NullReferenceException("Current User could not be set."); throw new NullReferenceException("Current User could not be set.");
@ -413,23 +337,21 @@ namespace QtCNETAPI.Services.ApiService
public async Task<ServiceResponse<string>> RefreshSessionIfInvalid() public async Task<ServiceResponse<string>> RefreshSessionIfInvalid()
{ {
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
var refToken = _credService.GetAccessToken(); // fuck CA1416, if this is being ran on linux it should just crash (theoretically) var refToken = await File.ReadAllTextAsync("./session.token");
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); JwtSecurityToken token = tokenHandler.ReadJwtToken(SessionToken);
if(DateTime.Compare(DateTime.UtcNow, token.ValidTo) > 0) 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); var result = await RefreshLogin(refToken);
if (result == null || result.Success == false) if (result == null || result.Success == false)
{ {
return new ServiceResponse<string> { Success = false, Message = "Session Expired." }; // logging in again should overwrite old token File.Delete("./session.token");
return new ServiceResponse<string> { Success = false, Message = "Session Expired." };
} else return new ServiceResponse<string> { Success = true, Data = refToken }; } else return new ServiceResponse<string> { Success = true, Data = refToken };
} else return new ServiceResponse<string> { Success = true, Data = refToken }; } else return new ServiceResponse<string> { Success = true, Data = refToken };
} }
@ -668,10 +590,6 @@ namespace QtCNETAPI.Services.ApiService
{ {
serviceResponse.Success = true; serviceResponse.Success = true;
serviceResponse.Data = response.Data; 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; return serviceResponse;
@ -719,10 +637,6 @@ namespace QtCNETAPI.Services.ApiService
{ {
serviceResponse.Success = true; serviceResponse.Success = true;
serviceResponse.Data = response.Data; 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; return serviceResponse;
@ -747,10 +661,6 @@ namespace QtCNETAPI.Services.ApiService
{ {
serviceResponse.Success = true; serviceResponse.Success = true;
serviceResponse.Data = response.Data; 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; return serviceResponse;
@ -803,151 +713,5 @@ namespace QtCNETAPI.Services.ApiService
return serviceResponse; 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,7 +8,6 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using QtCNETAPI.Enums; using QtCNETAPI.Enums;
using QtCNETAPI.Schema;
namespace QtCNETAPI.Services.ApiService namespace QtCNETAPI.Services.ApiService
{ {
@ -17,19 +16,12 @@ namespace QtCNETAPI.Services.ApiService
public string? SessionToken { get; set; } public string? SessionToken { get; set; }
public User CurrentUser { get; } public User CurrentUser { get; }
public event EventHandler? OnCurrentUserUpdate;
public Task<ServiceResponse<string>> PingServerAsync(); public Task<ServiceResponse<string>> PingServerAsync();
public Task<ServiceResponse<List<UserInformationDto>>> GetOnlineUsersAsync(); public Task<ServiceResponse<List<UserInformationDto>>> GetOnlineUsersAsync();
public Task<ServiceResponse<List<UserInformationDto>>> GetAllUsersAsync(); public Task<ServiceResponse<List<UserInformationDto>>> GetAllUsersAsync();
public Task<ServiceResponse<User>> DeleteUserById(string id); public Task<ServiceResponse<User>> LoginAsync(UserLoginDto userLoginDto);
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<User>> RefreshLogin(string refreshToken);
public Task<ServiceResponse<string>> RefreshSessionIfInvalid(); public Task<ServiceResponse<string>> RefreshSessionIfInvalid();
public Task<User> SetCurrentUser();
public Task<ServiceResponse<User>> RegisterAsync(UserDto userDto); public Task<ServiceResponse<User>> RegisterAsync(UserDto userDto);
public Task<ServiceResponse<UserInformationDto>> GetUserInformationAsync(string id); public Task<ServiceResponse<UserInformationDto>> GetUserInformationAsync(string id);
public Task<ServiceResponse<UserInformationDto>> UpdateUserInformationAsync(UserUpdateInformationDto request); public Task<ServiceResponse<UserInformationDto>> UpdateUserInformationAsync(UserUpdateInformationDto request);
@ -48,10 +40,5 @@ namespace QtCNETAPI.Services.ApiService
public Task<ServiceResponse<UserStockActionResultDto>> SellStock(int amount); public Task<ServiceResponse<UserStockActionResultDto>> SellStock(int amount);
public Task<ServiceResponse<int>> GetRandomNumber(); public Task<ServiceResponse<int>> GetRandomNumber();
public Task<ServiceResponse<NumberGuessResult>> GuessRandomNumber(int original, int guess); 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

@ -1,26 +0,0 @@
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,5 +1,4 @@
using Microsoft.AspNetCore.SignalR.Client; using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
using QtCNETAPI.Dtos.User; using QtCNETAPI.Dtos.User;
using QtCNETAPI.Events; using QtCNETAPI.Events;
using QtCNETAPI.Models; using QtCNETAPI.Models;
@ -7,55 +6,48 @@ using QtCNETAPI.Services.ApiService;
namespace QtCNETAPI.Services.GatewayService namespace QtCNETAPI.Services.GatewayService
{ {
public class GatewayService(string GWUrl, IApiService apiService, LoggingService loggingService) : IGatewayService, IAsyncDisposable public class GatewayService : IGatewayService, IAsyncDisposable
{ {
internal string gwBaseUri = GWUrl; internal string gwBaseUri = "127.0.0.1";
public Room? CurrentRoom { get; private set; } public Room? CurrentRoom { get; private set; }
public bool InLobby { get; private set; } public bool InLobby { get; private set; }
public HubConnection? HubConnection { get; private set; } public HubConnection? HubConnection { get; private set; }
public event EventHandler? OnRoomMessageReceived; public event EventHandler OnRoomMessageReceived;
public event EventHandler? OnRoomUserListReceived; public event EventHandler OnRoomUserListReceived;
public event EventHandler? OnGuestUserJoin; public event EventHandler OnGuestUserJoin;
public event EventHandler? OnRefreshUserListsReceived; public event EventHandler OnRefreshUserListsReceived;
public event EventHandler? OnRefreshRoomListReceived; public event EventHandler OnRefreshRoomListReceived;
public event EventHandler? OnRoomDeleted; public event EventHandler OnRefreshContactsListReceived;
public event EventHandler? OnRefreshContactsListReceived; public event EventHandler OnClientFunctionReceived;
public event EventHandler? OnClientFunctionReceived; public event EventHandler OnDirectMessageReceived;
public event EventHandler? OnDirectMessageReceived; public event EventHandler OnServerConfigReceived;
public event EventHandler? OnServerConfigReceived; public event EventHandler OnServerDisconnect;
public event EventHandler? OnServerDisconnect; public event EventHandler OnServerReconnecting;
public event EventHandler? OnServerReconnecting; public event EventHandler OnServerReconnected;
public event EventHandler? OnServerReconnected;
public event EventHandler? OnUserForceLogout;
private IApiService _apiService = apiService; private IApiService _apiService;
private LoggingService _loggingService = loggingService;
public GatewayService(string GWUrl, IApiService apiService)
{
gwBaseUri = GWUrl;
_apiService = apiService;
}
public async Task StartAsync() 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 // build connection
var gwConBuilder = new HubConnectionBuilder() var gwConBuilder = new HubConnectionBuilder()
.WithAutomaticReconnect() .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 => .WithUrl(gwBaseUri, options =>
{ {
options.AccessTokenProvider = async () => options.AccessTokenProvider = () => Task.FromResult(_apiService.SessionToken);
{ });
// 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(); HubConnection = gwConBuilder.Build();
// register events // register events
@ -68,26 +60,13 @@ namespace QtCNETAPI.Services.GatewayService
HubConnection.On<ServerConfig>("ReceiveServerConfig", (serverConfig) => OnServerConfigReceived?.Invoke(this, new ServerConfigEventArgs { ServerConfig = serverConfig })); 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<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<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.Closed += HubConnection_Closed;
HubConnection.Reconnecting += HubConnection_Reconnecting; HubConnection.Reconnecting += HubConnection_Reconnecting;
HubConnection.Reconnected += HubConnection_Reconnected; HubConnection.Reconnected += HubConnection_Reconnected;
// start connection // start connection
try await HubConnection.StartAsync();
{
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() public async Task StopAsync()
@ -122,6 +101,8 @@ namespace QtCNETAPI.Services.GatewayService
public async Task JoinLobbyAsync() public async Task JoinLobbyAsync()
{ {
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made."); if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("JoinLobby", _apiService.CurrentUser); await HubConnection.SendAsync("JoinLobby", _apiService.CurrentUser);
@ -131,6 +112,8 @@ namespace QtCNETAPI.Services.GatewayService
public async Task JoinRoomAsync(Room room) 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 (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
if (InLobby == true) if (InLobby == true)
@ -149,6 +132,8 @@ namespace QtCNETAPI.Services.GatewayService
public async Task LeaveRoomAsync() 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 (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
if (InLobby) if (InLobby)
@ -165,6 +150,8 @@ namespace QtCNETAPI.Services.GatewayService
public async Task PostMessageAsync(Message message) 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."); 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); await HubConnection.SendAsync("SendMessage", _apiService.CurrentUser, message, InLobby, CurrentRoom);
@ -181,12 +168,11 @@ namespace QtCNETAPI.Services.GatewayService
public async Task UpdateStatus(int status) 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."); if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("UpdateStatus", _apiService.CurrentUser, status); 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,11 +104,6 @@ namespace QtCNETAPI.Services.GatewayService
/// </summary> /// </summary>
public event EventHandler OnRoomUserListReceived; public event EventHandler OnRoomUserListReceived;
/// <summary>
/// Fires When The Room The User Is In Gets Deleted By An Admin
/// </summary>
public event EventHandler OnRoomDeleted;
/// <summary> /// <summary>
/// Fires When A Guest User Joins Your Room /// Fires When A Guest User Joins Your Room
/// </summary> /// </summary>
@ -158,10 +153,5 @@ namespace QtCNETAPI.Services.GatewayService
/// When the Connection Reconnects, This Event Fires /// When the Connection Reconnects, This Event Fires
/// </summary> /// </summary>
public event EventHandler OnServerReconnected; public event EventHandler OnServerReconnected;
/// <summary>
/// Fires When The Current User Is Signed Out By The Server
/// </summary>
public event EventHandler OnUserForceLogout;
} }
} }

View File

@ -1,19 +0,0 @@
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,9 +15,6 @@ namespace qtc_net_client_2.ClientModel
[JsonPropertyName("minimizeToTray")] [JsonPropertyName("minimizeToTray")]
[JsonRequired] [JsonRequired]
public bool MinimizeToTray { get; set; } = true; public bool MinimizeToTray { get; set; } = true;
[JsonPropertyName("enableDebugLogs")]
[JsonRequired]
public bool EnableDebugLogs { get; set; } = false;
[JsonPropertyName("serverUrl")] [JsonPropertyName("serverUrl")]
[JsonRequired] [JsonRequired]

View File

@ -1,7 +1,17 @@
using qtc_net_client_2.Services; using qtc_net_client_2.Services;
using QtCNETAPI.Events; using QtCNETAPI.Events;
using QtCNETAPI.Models;
using QtCNETAPI.Services.ApiService; using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService; 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 namespace qtc_net_client_2.Forms
{ {
@ -23,7 +33,6 @@ namespace qtc_net_client_2.Forms
// subscribe to server message event // subscribe to server message event
_gatewayService.OnRoomMessageReceived += _gatewayService_OnServerMessageReceived; _gatewayService.OnRoomMessageReceived += _gatewayService_OnServerMessageReceived;
_gatewayService.OnRoomUserListReceived += _gatewayService_OnRoomUserListReceived; _gatewayService.OnRoomUserListReceived += _gatewayService_OnRoomUserListReceived;
_gatewayService.OnRoomDeleted += _gatewayService_OnRoomDeleted;
_gatewayService.OnGuestUserJoin += _gatewayService_OnGuestUserJoin; _gatewayService.OnGuestUserJoin += _gatewayService_OnGuestUserJoin;
if (_gatewayService.CurrentRoom != null) { Text = $"QtC.NET Client - Chat Room - {_gatewayService.CurrentRoom.Name}"; lblRoomName.Text = _gatewayService.CurrentRoom.Name; } if (_gatewayService.CurrentRoom != null) { Text = $"QtC.NET Client - Chat Room - {_gatewayService.CurrentRoom.Name}"; lblRoomName.Text = _gatewayService.CurrentRoom.Name; }
@ -85,16 +94,13 @@ namespace qtc_net_client_2.Forms
{ {
// get user info and open profile dialog // get user info and open profile dialog
var user = mainForm.UserDirectory.FirstOrDefault(e => e.Username == selectedUser); var user = mainForm.UserDirectory.FirstOrDefault(e => e.Username == selectedUser);
if (user != null) var res = await _apiService.GetUserInformationAsync(user!.Id);
{ var pfpRes = await _apiService.GetUserProfilePic(user!.Id);
var res = await _apiService.GetUserInformationAsync(user.Id);
var pfpRes = await _apiService.GetUserProfilePic(user.Id);
if (res.Data != null && res.Success) if (res.Data != null && res.Success)
{ {
Profile frmProfile = new Profile(res.Data, pfpRes, mainForm.Contacts, _apiService, _gatewayService); Profile frmProfile = new Profile(res.Data, pfpRes, mainForm.Contacts, _apiService, _gatewayService);
frmProfile.Show(); frmProfile.Show();
}
} }
} }
} }
@ -113,16 +119,12 @@ namespace qtc_net_client_2.Forms
{ {
var args = (RoomListEventArgs)e; var args = (RoomListEventArgs)e;
if (IsHandleCreated && !IsDisposed) if (!IsHandleCreated) return;
lvUserList.BeginInvoke(lvUserList.Clear);
foreach (var user in args.UserList)
{ {
Invoke(delegate () lvUserList.BeginInvoke(delegate () { lvUserList.Items.Add(user.Username, user.Status); });
{
lvUserList.Clear();
foreach (var user in args.UserList)
{
lvUserList.Items.Add(user.Username, user.Status);
}
});
} }
} }
private void _gatewayService_OnGuestUserJoin(object? sender, EventArgs e) private void _gatewayService_OnGuestUserJoin(object? sender, EventArgs e)
@ -131,32 +133,12 @@ namespace qtc_net_client_2.Forms
AddMessage($"[SERVER] Guest User {args.Username} Has Joined {_gatewayService.CurrentRoom?.Name}"); 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) private void AddMessage(string message)
{ {
if(IsHandleCreated && !IsDisposed) if (InvokeRequired)
{ Invoke(delegate { rtxtChat.AppendText(message + Environment.NewLine); });
Invoke(delegate () else rtxtChat.AppendText(message + Environment.NewLine);
{
rtxtChat.AppendText(message + Environment.NewLine);
});
}
} }
} }
} }

View File

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

View File

@ -12,12 +12,19 @@ namespace qtc_net_client_2.Forms
{ {
public partial class ConnectionClosed : Form public partial class ConnectionClosed : Form
{ {
public Label StatusLabel { get { return lblHead; } } public string? Reason { get; set; } = string.Empty;
public ConnectionClosed() public ConnectionClosed(string? reason = "")
{ {
Reason = reason;
InitializeComponent(); 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) private void btnReconnect_Click(object sender, EventArgs e)
{ {
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;

View File

@ -42,11 +42,6 @@ namespace qtc_net_client_2.Forms
Text = $"QtC.NET Client - Direct Message With {User.Username}"; Text = $"QtC.NET Client - Direct Message With {User.Username}";
Messages.CollectionChanged += Messages_CollectionChanged; 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) if (InitMessage != null)
{ {
Messages.Add($"[{User.Username}] {InitMessage.Content}\n"); Messages.Add($"[{User.Username}] {InitMessage.Content}\n");
@ -63,7 +58,7 @@ namespace qtc_net_client_2.Forms
if (!string.IsNullOrEmpty(rtxtChatbox.Text)) if (!string.IsNullOrEmpty(rtxtChatbox.Text))
{ {
await _gatewayService.SendDirectMessageAsync(User, new QtCNETAPI.Models.Message { Content = rtxtChatbox.Text, AuthorId = _apiService.CurrentUser.Id }); await _gatewayService.SendDirectMessageAsync(User, new QtCNETAPI.Models.Message { Content = rtxtChatbox.Text, AuthorId = _apiService.CurrentUser.Id });
Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}\n"); Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}");
rtxtChatbox.Clear(); rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick"); AudioService.PlaySoundEffect("sndSendClick");
} }
@ -74,7 +69,7 @@ namespace qtc_net_client_2.Forms
if (!string.IsNullOrEmpty(rtxtChatbox.Text)) if (!string.IsNullOrEmpty(rtxtChatbox.Text))
{ {
await _gatewayService.SendDirectMessageAsync(User, new QtCNETAPI.Models.Message { Content = rtxtChatbox.Text, AuthorId = _apiService.CurrentUser.Id }); await _gatewayService.SendDirectMessageAsync(User, new QtCNETAPI.Models.Message { Content = rtxtChatbox.Text, AuthorId = _apiService.CurrentUser.Id });
Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}\n"); Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}");
rtxtChatbox.Clear(); rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick"); AudioService.PlaySoundEffect("sndSendClick");
} }

View File

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

View File

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

View File

@ -40,6 +40,7 @@
refreshToolStripMenuItem = new ToolStripMenuItem(); refreshToolStripMenuItem = new ToolStripMenuItem();
ilProfilePics = new ImageList(components); ilProfilePics = new ImageList(components);
tbpRooms = new TabPage(); tbpRooms = new TabPage();
btnAddRoom = new Button();
lbRooms = new ListBox(); lbRooms = new ListBox();
tbpUsers = new TabPage(); tbpUsers = new TabPage();
lvUserDirectory = new ListView(); lvUserDirectory = new ListView();
@ -47,9 +48,6 @@
tbpGames = new TabPage(); tbpGames = new TabPage();
lvGames = new ListView(); lvGames = new ListView();
ilGames = new ImageList(components); ilGames = new ImageList(components);
tbpStore = new TabPage();
lvStoreItems = new ListView();
ilStoreThumbnails = new ImageList(components);
ilTabIcons = new ImageList(components); ilTabIcons = new ImageList(components);
ctxmChangeStatus = new ContextMenuStrip(components); ctxmChangeStatus = new ContextMenuStrip(components);
onlineToolStripMenuItem = new ToolStripMenuItem(); onlineToolStripMenuItem = new ToolStripMenuItem();
@ -69,33 +67,18 @@
lblWelcome = new Label(); lblWelcome = new Label();
pbUserPfp = new PictureBox(); pbUserPfp = new PictureBox();
pbDonate = 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(); tbcMain.SuspendLayout();
tbpContacts.SuspendLayout(); tbpContacts.SuspendLayout();
ctxmRefresh.SuspendLayout(); ctxmRefresh.SuspendLayout();
tbpRooms.SuspendLayout(); tbpRooms.SuspendLayout();
tbpUsers.SuspendLayout(); tbpUsers.SuspendLayout();
tbpGames.SuspendLayout(); tbpGames.SuspendLayout();
tbpStore.SuspendLayout();
ctxmChangeStatus.SuspendLayout(); ctxmChangeStatus.SuspendLayout();
pCurrencyArea.SuspendLayout(); pCurrencyArea.SuspendLayout();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).BeginInit(); ((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).BeginInit();
pCurrentUser.SuspendLayout(); pCurrentUser.SuspendLayout();
((System.ComponentModel.ISupportInitialize)pbUserPfp).BeginInit(); ((System.ComponentModel.ISupportInitialize)pbUserPfp).BeginInit();
((System.ComponentModel.ISupportInitialize)pbDonate).BeginInit(); ((System.ComponentModel.ISupportInitialize)pbDonate).BeginInit();
ctxmAdminUserList.SuspendLayout();
ctxmAdminRoomList.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// tbcMain // tbcMain
@ -105,15 +88,13 @@
tbcMain.Controls.Add(tbpRooms); tbcMain.Controls.Add(tbpRooms);
tbcMain.Controls.Add(tbpUsers); tbcMain.Controls.Add(tbpUsers);
tbcMain.Controls.Add(tbpGames); tbcMain.Controls.Add(tbpGames);
tbcMain.Controls.Add(tbpStore);
tbcMain.Enabled = false; tbcMain.Enabled = false;
tbcMain.ImageList = ilTabIcons; tbcMain.ImageList = ilTabIcons;
tbcMain.Location = new Point(12, 83); tbcMain.Location = new Point(12, 83);
tbcMain.Name = "tbcMain"; tbcMain.Name = "tbcMain";
tbcMain.SelectedIndex = 0; tbcMain.SelectedIndex = 0;
tbcMain.Size = new Size(352, 499); tbcMain.Size = new Size(305, 499);
tbcMain.TabIndex = 0; tbcMain.TabIndex = 0;
tbcMain.SelectedIndexChanged += tbcMain_SelectedIndexChanged;
// //
// tbpContacts // tbpContacts
// //
@ -122,7 +103,7 @@
tbpContacts.Location = new Point(4, 24); tbpContacts.Location = new Point(4, 24);
tbpContacts.Name = "tbpContacts"; tbpContacts.Name = "tbpContacts";
tbpContacts.Padding = new Padding(3); tbpContacts.Padding = new Padding(3);
tbpContacts.Size = new Size(344, 471); tbpContacts.Size = new Size(297, 471);
tbpContacts.TabIndex = 0; tbpContacts.TabIndex = 0;
tbpContacts.Text = "Contacts"; tbpContacts.Text = "Contacts";
tbpContacts.UseVisualStyleBackColor = true; tbpContacts.UseVisualStyleBackColor = true;
@ -136,7 +117,7 @@
lvContacts.Location = new Point(0, 0); lvContacts.Location = new Point(0, 0);
lvContacts.MultiSelect = false; lvContacts.MultiSelect = false;
lvContacts.Name = "lvContacts"; lvContacts.Name = "lvContacts";
lvContacts.Size = new Size(344, 478); lvContacts.Size = new Size(297, 478);
lvContacts.SmallImageList = ilProfilePics; lvContacts.SmallImageList = ilProfilePics;
lvContacts.TabIndex = 1; lvContacts.TabIndex = 1;
lvContacts.UseCompatibleStateImageBehavior = false; lvContacts.UseCompatibleStateImageBehavior = false;
@ -165,16 +146,28 @@
// //
// tbpRooms // tbpRooms
// //
tbpRooms.Controls.Add(btnAddRoom);
tbpRooms.Controls.Add(lbRooms); tbpRooms.Controls.Add(lbRooms);
tbpRooms.ImageKey = "RoomsChatIcon.png"; tbpRooms.ImageKey = "RoomsChatIcon.png";
tbpRooms.Location = new Point(4, 24); tbpRooms.Location = new Point(4, 24);
tbpRooms.Name = "tbpRooms"; tbpRooms.Name = "tbpRooms";
tbpRooms.Padding = new Padding(3); tbpRooms.Padding = new Padding(3);
tbpRooms.Size = new Size(344, 471); tbpRooms.Size = new Size(297, 471);
tbpRooms.TabIndex = 1; tbpRooms.TabIndex = 1;
tbpRooms.Text = "Rooms"; tbpRooms.Text = "Rooms";
tbpRooms.UseVisualStyleBackColor = true; 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
// //
lbRooms.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; lbRooms.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
@ -183,7 +176,7 @@
lbRooms.ItemHeight = 15; lbRooms.ItemHeight = 15;
lbRooms.Location = new Point(0, 0); lbRooms.Location = new Point(0, 0);
lbRooms.Name = "lbRooms"; lbRooms.Name = "lbRooms";
lbRooms.Size = new Size(344, 484); lbRooms.Size = new Size(297, 514);
lbRooms.TabIndex = 0; lbRooms.TabIndex = 0;
lbRooms.DoubleClick += lbRooms_DoubleClick; lbRooms.DoubleClick += lbRooms_DoubleClick;
// //
@ -193,7 +186,7 @@
tbpUsers.ImageIndex = 1; tbpUsers.ImageIndex = 1;
tbpUsers.Location = new Point(4, 24); tbpUsers.Location = new Point(4, 24);
tbpUsers.Name = "tbpUsers"; tbpUsers.Name = "tbpUsers";
tbpUsers.Size = new Size(344, 471); tbpUsers.Size = new Size(297, 471);
tbpUsers.TabIndex = 3; tbpUsers.TabIndex = 3;
tbpUsers.Text = "Users"; tbpUsers.Text = "Users";
tbpUsers.UseVisualStyleBackColor = true; tbpUsers.UseVisualStyleBackColor = true;
@ -207,7 +200,7 @@
lvUserDirectory.MultiSelect = false; lvUserDirectory.MultiSelect = false;
lvUserDirectory.Name = "lvUserDirectory"; lvUserDirectory.Name = "lvUserDirectory";
lvUserDirectory.RightToLeft = RightToLeft.Yes; lvUserDirectory.RightToLeft = RightToLeft.Yes;
lvUserDirectory.Size = new Size(344, 484); lvUserDirectory.Size = new Size(297, 514);
lvUserDirectory.SmallImageList = ilStatusIcons; lvUserDirectory.SmallImageList = ilStatusIcons;
lvUserDirectory.TabIndex = 0; lvUserDirectory.TabIndex = 0;
lvUserDirectory.UseCompatibleStateImageBehavior = false; lvUserDirectory.UseCompatibleStateImageBehavior = false;
@ -230,7 +223,7 @@
tbpGames.ImageIndex = 3; tbpGames.ImageIndex = 3;
tbpGames.Location = new Point(4, 24); tbpGames.Location = new Point(4, 24);
tbpGames.Name = "tbpGames"; tbpGames.Name = "tbpGames";
tbpGames.Size = new Size(344, 471); tbpGames.Size = new Size(297, 471);
tbpGames.TabIndex = 4; tbpGames.TabIndex = 4;
tbpGames.Text = "Games"; tbpGames.Text = "Games";
tbpGames.UseVisualStyleBackColor = true; tbpGames.UseVisualStyleBackColor = true;
@ -246,7 +239,7 @@
lvGames.Location = new Point(0, 0); lvGames.Location = new Point(0, 0);
lvGames.MultiSelect = false; lvGames.MultiSelect = false;
lvGames.Name = "lvGames"; lvGames.Name = "lvGames";
lvGames.Size = new Size(344, 484); lvGames.Size = new Size(297, 533);
lvGames.SmallImageList = ilGames; lvGames.SmallImageList = ilGames;
lvGames.TabIndex = 2; lvGames.TabIndex = 2;
lvGames.UseCompatibleStateImageBehavior = false; lvGames.UseCompatibleStateImageBehavior = false;
@ -261,35 +254,6 @@
ilGames.Images.SetKeyName(1, "NumberGuessGameIcon.png"); ilGames.Images.SetKeyName(1, "NumberGuessGameIcon.png");
ilGames.Images.SetKeyName(2, "Tic-tac-toe.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
// //
ilTabIcons.ColorDepth = ColorDepth.Depth32Bit; ilTabIcons.ColorDepth = ColorDepth.Depth32Bit;
@ -337,7 +301,7 @@
// //
// lblRequestNotif // lblRequestNotif
// //
lblRequestNotif.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; lblRequestNotif.Anchor = AnchorStyles.Top | AnchorStyles.Right;
lblRequestNotif.AutoSize = true; lblRequestNotif.AutoSize = true;
lblRequestNotif.Font = new Font("Segoe UI", 6F, FontStyle.Bold); lblRequestNotif.Font = new Font("Segoe UI", 6F, FontStyle.Bold);
lblRequestNotif.Location = new Point(12, 67); lblRequestNotif.Location = new Point(12, 67);
@ -373,7 +337,7 @@
pCurrencyArea.Controls.Add(llblClaimSpin); pCurrencyArea.Controls.Add(llblClaimSpin);
pCurrencyArea.Controls.Add(pbCurrencyIcon); pCurrencyArea.Controls.Add(pbCurrencyIcon);
pCurrencyArea.Controls.Add(lblCurrencyAmount); pCurrencyArea.Controls.Add(lblCurrencyAmount);
pCurrencyArea.Location = new Point(270, 5); pCurrencyArea.Location = new Point(223, 5);
pCurrencyArea.Name = "pCurrencyArea"; pCurrencyArea.Name = "pCurrencyArea";
pCurrencyArea.Size = new Size(95, 46); pCurrencyArea.Size = new Size(95, 46);
pCurrencyArea.TabIndex = 12; pCurrencyArea.TabIndex = 12;
@ -483,10 +447,9 @@
// //
// pbDonate // pbDonate
// //
pbDonate.Anchor = AnchorStyles.Top | AnchorStyles.Right;
pbDonate.Cursor = Cursors.Hand; pbDonate.Cursor = Cursors.Hand;
pbDonate.Image = Properties.Resources.donatebtn; pbDonate.Image = Properties.Resources.donatebtn;
pbDonate.Location = new Point(288, 56); pbDonate.Location = new Point(241, 56);
pbDonate.Name = "pbDonate"; pbDonate.Name = "pbDonate";
pbDonate.Size = new Size(77, 22); pbDonate.Size = new Size(77, 22);
pbDonate.SizeMode = PictureBoxSizeMode.Zoom; pbDonate.SizeMode = PictureBoxSizeMode.Zoom;
@ -494,103 +457,17 @@
pbDonate.TabStop = false; pbDonate.TabStop = false;
pbDonate.Click += pbDonate_Click; 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 // Main
// //
AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue; BackColor = Color.DodgerBlue;
ClientSize = new Size(376, 594); ClientSize = new Size(329, 594);
Controls.Add(pbDonate); Controls.Add(pbDonate);
Controls.Add(pCurrentUser); Controls.Add(pCurrentUser);
Controls.Add(pCurrencyArea); Controls.Add(pCurrencyArea);
Controls.Add(lblRequestNotif); Controls.Add(lblRequestNotif);
Controls.Add(tbcMain); Controls.Add(tbcMain);
Controls.Add(lblConnectionLost);
FormBorderStyle = FormBorderStyle.FixedDialog; FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon"); Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false; MaximizeBox = false;
@ -606,7 +483,6 @@
tbpRooms.ResumeLayout(false); tbpRooms.ResumeLayout(false);
tbpUsers.ResumeLayout(false); tbpUsers.ResumeLayout(false);
tbpGames.ResumeLayout(false); tbpGames.ResumeLayout(false);
tbpStore.ResumeLayout(false);
ctxmChangeStatus.ResumeLayout(false); ctxmChangeStatus.ResumeLayout(false);
pCurrencyArea.ResumeLayout(false); pCurrencyArea.ResumeLayout(false);
pCurrencyArea.PerformLayout(); pCurrencyArea.PerformLayout();
@ -615,8 +491,6 @@
pCurrentUser.PerformLayout(); pCurrentUser.PerformLayout();
((System.ComponentModel.ISupportInitialize)pbUserPfp).EndInit(); ((System.ComponentModel.ISupportInitialize)pbUserPfp).EndInit();
((System.ComponentModel.ISupportInitialize)pbDonate).EndInit(); ((System.ComponentModel.ISupportInitialize)pbDonate).EndInit();
ctxmAdminUserList.ResumeLayout(false);
ctxmAdminRoomList.ResumeLayout(false);
ResumeLayout(false); ResumeLayout(false);
PerformLayout(); PerformLayout();
} }
@ -637,6 +511,7 @@
private ToolStripMenuItem awayToolStripMenuItem; private ToolStripMenuItem awayToolStripMenuItem;
private ToolStripMenuItem doNotDisturbToolStripMenuItem; private ToolStripMenuItem doNotDisturbToolStripMenuItem;
private ToolStripMenuItem invisibleToolStripMenuItem; private ToolStripMenuItem invisibleToolStripMenuItem;
private Button btnAddRoom;
private LinkLabel llblClaimSpin; private LinkLabel llblClaimSpin;
private Panel pCurrencyArea; private Panel pCurrencyArea;
private Panel pCurrentUser; private Panel pCurrentUser;
@ -656,20 +531,5 @@
private TabPage tbpUsers; private TabPage tbpUsers;
private ListView lvUserDirectory; private ListView lvUserDirectory;
private PictureBox pbDonate; 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,9 +6,6 @@ using QtCNETAPI.Models;
using qtc_net_client_2.Forms; using qtc_net_client_2.Forms;
using qtc_net_client_2.Services; using qtc_net_client_2.Services;
using qtc_net_client_2.ClientModel; using qtc_net_client_2.ClientModel;
using System.Threading.Tasks;
using QtCNETAPI.Schema;
using QtCNETAPI.Services;
namespace qtc_net_client_2 namespace qtc_net_client_2
{ {
@ -16,9 +13,7 @@ namespace qtc_net_client_2
{ {
private IApiService _apiService; private IApiService _apiService;
private IGatewayService _gatewayService; private IGatewayService _gatewayService;
private Config _config; private Config _config;
private ServerConfig _serverConfig;
private AudioService AudioService = new(); private AudioService AudioService = new();
private LoggingService LoggingService; private LoggingService LoggingService;
@ -153,7 +148,11 @@ namespace qtc_net_client_2
private void llblEditProfile_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) private void llblEditProfile_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{ {
ProfileEdit frmProfileEdit = new ProfileEdit(_apiService); ProfileEdit frmProfileEdit = new ProfileEdit(_apiService);
frmProfileEdit.ShowDialog(); 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.");
}
} }
private async void lvContacts_DoubleClick(object sender, EventArgs e) private async void lvContacts_DoubleClick(object sender, EventArgs e)
@ -172,29 +171,12 @@ namespace qtc_net_client_2
var res = await _apiService.GetUserInformationAsync(user!.Id); var res = await _apiService.GetUserInformationAsync(user!.Id);
var pfpRes = await _apiService.GetUserProfilePic(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 (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) if (res.Data != null && res.Success)
{ {
LoggingService.LogString($"Opening Profile For User '{res.Data.Username}'"); LoggingService.LogString($"Opening Profile For User '{res.Data.Username}'");
Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService, cosmeticData); Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService);
frmProfile.Show(); frmProfile.Show();
} }
} }
@ -293,7 +275,7 @@ namespace qtc_net_client_2
var result = await _apiService.AddCurrencyToCurrentUser(tokenJackpotSpinner.TokensWon, true); var result = await _apiService.AddCurrencyToCurrentUser(tokenJackpotSpinner.TokensWon, true);
if (result.Success) if (result.Success)
{ {
lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0"); lblCurrencyAmount.Text = (_apiService.CurrentUser.CurrencyAmount + tokenJackpotSpinner.TokensWon).ToString("N0");
llblClaimSpin.Visible = false; 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); else MessageBox.Show("We Were Unable To Claim Your Prize At This Time. Please Try Again Later.", "Uh Oh.", MessageBoxButtons.OK, MessageBoxIcon.Error);
@ -313,29 +295,12 @@ namespace qtc_net_client_2
var res = await _apiService.GetUserInformationAsync(user!.Id); var res = await _apiService.GetUserInformationAsync(user!.Id);
var pfpRes = await _apiService.GetUserProfilePic(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 (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) if (res.Data != null && res.Success)
{ {
LoggingService.LogString($"Opening Profile For User '{res.Data.Username}'"); LoggingService.LogString($"Opening Profile For User '{res.Data.Username}'");
Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService, cosmeticData); Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService);
frmProfile.Show(); frmProfile.Show();
} }
} }
@ -368,9 +333,10 @@ namespace qtc_net_client_2
private async void refreshToolStripMenuItem_Click(object sender, EventArgs e) private async void refreshToolStripMenuItem_Click(object sender, EventArgs e)
{ {
if (tbcMain.SelectedIndex == 0) await RefreshContactsList(); // refresh all controls (TODO - probably restrict this to currently visible control)
if (tbcMain.SelectedIndex == 1) await RefreshRoomsList(); await RefreshContactsList();
if (tbcMain.SelectedIndex == 2) await RefreshUsers(); await RefreshRoomsList();
await RefreshUsers();
} }
private void pbDonate_Click(object sender, EventArgs e) private void pbDonate_Click(object sender, EventArgs e)
@ -379,192 +345,6 @@ namespace qtc_net_client_2
donationWindow.Show(); 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() private async Task OnSuccessfulLogin()
{ {
// double check // double check
@ -585,14 +365,10 @@ namespace qtc_net_client_2
_gatewayService.OnRefreshRoomListReceived += _gatewayService_OnRefreshRoomListReceived; _gatewayService.OnRefreshRoomListReceived += _gatewayService_OnRefreshRoomListReceived;
_gatewayService.OnRefreshContactsListReceived += _gatewayService_OnRefreshContactsListReceived; _gatewayService.OnRefreshContactsListReceived += _gatewayService_OnRefreshContactsListReceived;
_gatewayService.OnServerConfigReceived += _gatewayService_OnServerConfigReceived; _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) if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
{ {
LoggingService.LogString("Connected To SignalR Succesfully."); LoggingService.LogString("Connected To SignalR Succesfully.");
LoggingService.LogString("Building UI...");
// we are fully logged in, get current user profile pic and set up ui // we are fully logged in, get current user profile pic and set up ui
llblSignIn.Visible = false; llblSignIn.Visible = false;
@ -614,14 +390,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 RefreshContactsList();
await RefreshRoomsList(); 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 // set status context menu checked
// TODO - figure out more efficient way to do this
UserStatus cuStatus = (UserStatus)_apiService.CurrentUser.Status; UserStatus cuStatus = (UserStatus)_apiService.CurrentUser.Status;
switch (cuStatus) switch (cuStatus)
{ {
@ -651,19 +427,15 @@ namespace qtc_net_client_2
break; break;
} }
if (_apiService.CurrentUser.Role == "Admin") btnAddRoom.Enabled = true;
else btnAddRoom.Enabled = false;
var current = DateTime.UtcNow; var current = DateTime.UtcNow;
if (current > _apiService.CurrentUser.LastCurrencySpin.ToUniversalTime() || _apiService.CurrentUser.LastCurrencySpin == new DateTime()) llblClaimSpin.Visible = true; if (current > _apiService.CurrentUser.LastCurrencySpin.ToUniversalTime() || _apiService.CurrentUser.LastCurrencySpin == new DateTime()) llblClaimSpin.Visible = true;
else llblClaimSpin.Visible = false; else llblClaimSpin.Visible = false;
if (_config.StartMinimized) WindowState = FormWindowState.Minimized; 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"); LoggingService.LogString("Client Ready");
} }
} }
@ -673,30 +445,29 @@ namespace qtc_net_client_2
{ {
LoggingService.LogString("Refreshing All Users List..."); LoggingService.LogString("Refreshing All Users List...");
if (IsHandleCreated && !IsDisposed) // get all users on server
var userList = await _apiService.GetAllUsersAsync();
if (userList != null && userList.Success && userList.Data != null)
{ {
await Invoke(async delegate () // clear the list
if (lvUserDirectory.InvokeRequired) lvUserDirectory.BeginInvoke(lvUserDirectory.Items.Clear);
else lvUserDirectory.Items.Clear();
UserDirectory.Clear();
// populate list
foreach (var user in userList.Data)
{ {
// get all users on server if (lvUserDirectory.InvokeRequired)
var userList = await _apiService.GetAllUsersAsync();
if (userList != null && userList.Success && userList.Data != null)
{ {
// clear the list lvUserDirectory.BeginInvoke(delegate () { lvUserDirectory.Items.Add(user.Username, user.Status); });
lvUserDirectory.Items.Clear();
UserDirectory.Clear();
// populate list
foreach (var user in userList.Data)
{
lvUserDirectory.Items.Add(user.Username, user.Status);
UserDirectory.Add(user);
}
if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs)
LoggingService.LogModel(userList.Data);
} }
}); else
{
lvUserDirectory.Items.Add(user.Username, user.Status);
}
UserDirectory.Add(user);
}
} }
} }
@ -704,7 +475,7 @@ namespace qtc_net_client_2
{ {
LoggingService.LogString("Refreshing Rooms List..."); LoggingService.LogString("Refreshing Rooms List...");
if (IsHandleCreated && !IsDisposed) if (InvokeRequired)
{ {
await Invoke(async delegate () await Invoke(async delegate ()
{ {
@ -717,21 +488,35 @@ namespace qtc_net_client_2
lbRooms.Items.Add(room.Name); lbRooms.Items.Add(room.Name);
} }
RoomList = roomsRes.Data; RoomList = roomsRes.Data;
if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs)
LoggingService.LogModel(roomsRes.Data);
} }
// always add lobby room to rooms list // always add lobby room to rooms list
lbRooms.Items.Add("Lobby"); 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() private async Task RefreshContactsList()
{ {
LoggingService.LogString("Refreshing Contacts List..."); LoggingService.LogString("Refreshing Contacts List...");
if (IsHandleCreated && !IsDisposed) if (InvokeRequired)
{ {
await Invoke(async delegate () await Invoke(async delegate ()
{ {
@ -800,11 +585,76 @@ 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;
}
}
}
}
} }
} }
@ -812,9 +662,11 @@ namespace qtc_net_client_2
{ {
while (true) while (true)
{ {
if(IsHandleCreated && !IsDisposed) if (!IsHandleCreated) { LoggingService.LogString("Form Handle Wasn't Created"); return; }
if (InvokeRequired && !IsDisposed)
{ {
await Invoke(async delegate () BeginInvoke(async delegate ()
{ {
label.ForeColor = Color.Red; label.ForeColor = Color.Red;
await Task.Delay(500); await Task.Delay(500);
@ -822,6 +674,11 @@ namespace qtc_net_client_2
await Task.Delay(500); await Task.Delay(500);
}); });
} }
label.ForeColor = Color.Red;
await Task.Delay(500);
label.ForeColor = Color.Blue;
await Task.Delay(500);
} }
} }
@ -839,34 +696,18 @@ 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}"); 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() public void RefreshCurrencyCounter()
{ {
if (IsHandleCreated && !IsDisposed) if (lblCurrencyAmount.InvokeRequired)
{ {
Invoke(delegate () { lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0"); }); lblCurrencyAmount.BeginInvoke(delegate ()
{
lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0");
});
}
else
{
lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0");
} }
} }
@ -885,9 +726,9 @@ namespace qtc_net_client_2
await _gatewayService.StartAsync(); await _gatewayService.StartAsync();
if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected) if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
{ BeginInvoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; }); return; } { BeginInvoke(delegate () { Enabled = true; }); return; }
ConnectionClosed frmConnectionClosed = new ConnectionClosed(); ConnectionClosed frmConnectionClosed = new ConnectionClosed(error);
var result = frmConnectionClosed.ShowDialog(); var result = frmConnectionClosed.ShowDialog();
if (result == DialogResult.OK) if (result == DialogResult.OK)
@ -896,19 +737,18 @@ namespace qtc_net_client_2
Reconnect: Reconnect:
if (_gatewayService.HubConnection != null) if (_gatewayService.HubConnection != null)
{ {
await _gatewayService.StopAsync(); try
await _gatewayService.StartAsync();
if (_gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
{ {
Invoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; }); await _gatewayService.StopAsync();
await _gatewayService.StartAsync();
if (_gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
BeginInvoke(delegate () { Enabled = true; });
} }
else catch (HttpRequestException ex)
{ {
// reshow the dialog // reshow the dialog
frmConnectionClosed.StatusLabel.Text = "Couldn't Reconnect. The Server Could Be Down."; frmConnectionClosed.Reason = ex.Message;
var result1 = frmConnectionClosed.ShowDialog(); var result1 = frmConnectionClosed.ShowDialog();
if (result1 == DialogResult.OK) goto Reconnect; if (result1 == DialogResult.OK) goto Reconnect;
else Environment.Exit(0); else Environment.Exit(0);
} }
@ -917,47 +757,8 @@ namespace qtc_net_client_2
else Environment.Exit(0); else Environment.Exit(0);
} }
private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e) private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e) => BeginInvoke(delegate () { Enabled = false; });
{ private void _gatewayService_OnServerReconnected(object? sender, EventArgs e) => BeginInvoke(delegate () { Enabled = true; });
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) private async void _gatewayService_OnServerConfigReceived(object? sender, EventArgs e)
{ {
@ -966,11 +767,8 @@ namespace qtc_net_client_2
LoggingService.LogString($"Server Config Received"); LoggingService.LogString($"Server Config Received");
LoggingService.LogModel(args.ServerConfig); 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) if (args.ServerConfig != null)
{ {
_serverConfig = args.ServerConfig;
if (args.ServerConfig.IsDown) if (args.ServerConfig.IsDown)
{ {
LoggingService.LogString("Server Is Marked As Down"); LoggingService.LogString("Server Is Marked As Down");
@ -1006,23 +804,6 @@ 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_OnRefreshContactsListReceived(object? sender, EventArgs e) => await RefreshContactsList();
private async void _gatewayService_OnRefreshRoomListReceived(object? sender, EventArgs e) => await RefreshRoomsList(); private async void _gatewayService_OnRefreshRoomListReceived(object? sender, EventArgs e) => await RefreshRoomsList();
private async void _gatewayService_OnRefreshUserListReceived(object? sender, EventArgs e) => await RefreshUsers(); private async void _gatewayService_OnRefreshUserListReceived(object? sender, EventArgs e) => await RefreshUsers();

View File

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

View File

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

View File

@ -1,25 +1,18 @@
using qtc_net_client_2.ClientModel; using QtCNETAPI.Dtos.User;
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.ApiService;
using QtCNETAPI.Services.GatewayService; using QtCNETAPI.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Data; using System.Data;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; 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 namespace qtc_net_client_2.Forms
{ {
@ -30,66 +23,50 @@ namespace qtc_net_client_2.Forms
private IGatewayService _gatewayService; private IGatewayService _gatewayService;
private ServiceResponse<byte[]>? pfpRes; private ServiceResponse<byte[]>? pfpRes;
byte[]? cosmeticRes;
private List<Contact> contactsList; private List<Contact> contactsList;
public Profile(UserInformationDto userInfo, ServiceResponse<byte[]>? pfp, List<Contact> contacts, IApiService apiService, IGatewayService gatewayService, byte[]? cosmetic = null) public Profile(UserInformationDto userInfo, ServiceResponse<byte[]>? pfp, List<Contact> contacts, IApiService apiService, IGatewayService gatewayService)
{ {
_userInformationDto = userInfo; _userInformationDto = userInfo;
_apiService = apiService; _apiService = apiService;
_gatewayService = gatewayService; _gatewayService = gatewayService;
pfpRes = pfp; pfpRes = pfp;
cosmeticRes = cosmetic;
contactsList = contacts; contactsList = contacts;
InitializeComponent(); InitializeComponent();
} }
private async void frmProfile_Load(object sender, EventArgs e) private void frmProfile_Load(object sender, EventArgs e)
{ {
lblUsername.Text = _userInformationDto.Username; lblUsername.Text = _userInformationDto.Username;
lblCurrencyAmount.Text = _userInformationDto.CurrencyAmount.ToString("N0"); lblCurrencyAmount.Text = _userInformationDto.CurrencyAmount.ToString("N0");
rtxtBio.Text = _userInformationDto.Bio; rtxtBio.Text = _userInformationDto.Bio;
Bitmap? pfp = null;
if (pfpRes != null && pfpRes.Success && pfpRes.Data != null) if (pfpRes != null && pfpRes.Success && pfpRes.Data != null)
{ {
using (var ms = new MemoryStream(pfpRes.Data)) using (var ms = new MemoryStream(pfpRes.Data))
{ {
pfp = new Bitmap(ms); pbUserPfp.Image = Image.FromStream(ms);
ms.Dispose();
} }
} }
var userStatus = (UserStatus)_userInformationDto.Status; var userStatus = (UserStatus)_userInformationDto.Status;
Bitmap precenseImage = Resources.OnlineIcon;
switch (userStatus) switch (userStatus)
{ {
case UserStatus.Online: case UserStatus.Online:
precenseImage = Resources.OnlineIcon; pbUserStatus.Image = Resources.OnlineIcon;
break; break;
case UserStatus.Away: case UserStatus.Away:
precenseImage = Resources.AwayIcon; pbUserStatus.Image = Resources.AwayIcon;
break; break;
case UserStatus.DoNotDisturb: case UserStatus.DoNotDisturb:
precenseImage = Resources.DNDIcon; pbUserStatus.Image = Resources.DNDIcon;
break; break;
case UserStatus.Offline: case UserStatus.Offline:
precenseImage = Resources.OfflineIcon; pbUserStatus.Image = Resources.OfflineIcon;
break; 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) if (_userInformationDto.Id == _apiService.CurrentUser!.Id)
{ {
btnAddContact.Visible = false; btnAddContact.Visible = false;
@ -238,32 +215,5 @@ namespace qtc_net_client_2.Forms
Close(); Close();
frmDirectMessage.Show(); 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,8 +33,6 @@
rtxtBio = new RichTextBox(); rtxtBio = new RichTextBox();
lblBio = new Label(); lblBio = new Label();
btnSave = new Button(); btnSave = new Button();
cbCosmetic = new ComboBox();
lblCosmetic = new Label();
SuspendLayout(); SuspendLayout();
// //
// tbUsername // tbUsername
@ -73,7 +71,7 @@
// btnSave // btnSave
// //
btnSave.ForeColor = Color.Black; btnSave.ForeColor = Color.Black;
btnSave.Location = new Point(76, 177); btnSave.Location = new Point(76, 148);
btnSave.Name = "btnSave"; btnSave.Name = "btnSave";
btnSave.Size = new Size(43, 23); btnSave.Size = new Size(43, 23);
btnSave.TabIndex = 4; btnSave.TabIndex = 4;
@ -81,32 +79,12 @@
btnSave.UseVisualStyleBackColor = true; btnSave.UseVisualStyleBackColor = true;
btnSave.Click += btnSave_Click; btnSave.Click += btnSave_Click;
// //
// cbCosmetic // frmProfileEdit
//
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); AutoScaleDimensions = new SizeF(6F, 15F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue; BackColor = Color.DodgerBlue;
ClientSize = new Size(345, 208); ClientSize = new Size(345, 187);
Controls.Add(lblCosmetic);
Controls.Add(cbCosmetic);
Controls.Add(btnSave); Controls.Add(btnSave);
Controls.Add(lblBio); Controls.Add(lblBio);
Controls.Add(rtxtBio); Controls.Add(rtxtBio);
@ -117,7 +95,7 @@
FormBorderStyle = FormBorderStyle.FixedDialog; FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false; MaximizeBox = false;
MinimizeBox = false; MinimizeBox = false;
Name = "ProfileEdit"; Name = "frmProfileEdit";
StartPosition = FormStartPosition.CenterParent; StartPosition = FormStartPosition.CenterParent;
Text = "QtC.NET Client - Edit Profile"; Text = "QtC.NET Client - Edit Profile";
Load += frmProfileEdit_Load; Load += frmProfileEdit_Load;
@ -132,7 +110,5 @@
private RichTextBox rtxtBio; private RichTextBox rtxtBio;
private Label lblBio; private Label lblBio;
private Button btnSave; private Button btnSave;
private ComboBox cbCosmetic;
private Label lblCosmetic;
} }
} }

View File

@ -1,6 +1,4 @@
using Microsoft.AspNetCore.Mvc.TagHelpers; using QtCNETAPI.Dtos.User;
using qtc_net_client_2.ClientModel;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Services.ApiService; using QtCNETAPI.Services.ApiService;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -23,81 +21,38 @@ namespace qtc_net_client_2.Forms
InitializeComponent(); InitializeComponent();
} }
private async void frmProfileEdit_Load(object sender, EventArgs e) private void frmProfileEdit_Load(object sender, EventArgs e)
{ {
tbUsername.Text = _apiService.CurrentUser.Username; tbUsername.Text = _apiService.CurrentUser!.Username;
rtxtBio.Text = _apiService.CurrentUser.Bio; 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) private async void btnSave_Click(object sender, EventArgs e)
{ {
ComboBoxItem? selectedItem = (ComboBoxItem?)cbCosmetic.SelectedItem; if(!string.IsNullOrEmpty(tbUsername.Text) && (tbUsername.Text != _apiService.CurrentUser!.Username || rtxtBio.Text != _apiService.CurrentUser!.Bio))
// update user info
UserUpdateInformationDto userUpdateInformationDto = new UserUpdateInformationDto
{ {
Id = _apiService.CurrentUser.Id, // update user info
Username = tbUsername.Text, UserUpdateInformationDto userUpdateInformationDto = new UserUpdateInformationDto
Bio = rtxtBio.Text, {
DateOfBirth = _apiService.CurrentUser.DateOfBirth Id = _apiService.CurrentUser.Id,
}; Username = tbUsername.Text,
Bio = rtxtBio.Text,
DateOfBirth = _apiService.CurrentUser.DateOfBirth
};
if (selectedItem != null) var res = await _apiService.UpdateUserInformationAsync(userUpdateInformationDto);
{
int selectedItemId = (int?)selectedItem.Value ?? 0;
userUpdateInformationDto.ProfileCosmeticId = selectedItemId;
}
var res = await _apiService.UpdateUserInformationAsync(userUpdateInformationDto); if (res.Success)
{
if (res.Success) DialogResult = DialogResult.OK;
{ Close();
DialogResult = DialogResult.OK; }
Close(); else
} {
else MessageBox.Show(res.Message, "Info Update Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
{ DialogResult = DialogResult.Cancel;
MessageBox.Show(res.Message, "Info Update Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Close();
DialogResult = DialogResult.Cancel; }
Close();
} }
} }
} }

View File

@ -43,7 +43,6 @@ namespace qtc_net_client_2.Forms
if(registerResult.Success) 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; DialogResult = DialogResult.OK;
Close(); Close();
} else } else

View File

@ -1,104 +0,0 @@
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

@ -1,43 +0,0 @@
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

@ -1,120 +0,0 @@
<?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

@ -1,221 +0,0 @@
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

@ -1,68 +0,0 @@
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

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

View File

@ -44,6 +44,19 @@ namespace qtc_net_client_2.Forms
_apiService.CurrentUser.StockAmount = result.Data.StockAmount; _apiService.CurrentUser.StockAmount = result.Data.StockAmount;
_apiService.CurrentUser.CurrencyAmount = result.Data.CurrencyAmount; _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.Enabled = true;
nudStockBuySellAmount.Value = 0; nudStockBuySellAmount.Value = 0;
btnBuy.Enabled = true; btnBuy.Enabled = true;
@ -82,6 +95,19 @@ namespace qtc_net_client_2.Forms
_apiService.CurrentUser.StockAmount = result.Data.StockAmount; _apiService.CurrentUser.StockAmount = result.Data.StockAmount;
_apiService.CurrentUser.CurrencyAmount = result.Data.CurrencyAmount; _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.Enabled = true;
nudStockBuySellAmount.Value = 0; nudStockBuySellAmount.Value = 0;
btnBuy.Enabled = true; btnBuy.Enabled = true;

View File

@ -1,135 +0,0 @@
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

@ -1,97 +0,0 @@
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

@ -1,123 +0,0 @@
<?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,6 +261,11 @@ namespace qtc_net_client_2
{ {
await _apiService.AddCurrencyToCurrentUser(currencyJackpotSpinner.TokensWon, false); await _apiService.AddCurrencyToCurrentUser(currencyJackpotSpinner.TokensWon, false);
_apiService.CurrentUser.CurrencyAmount += currencyJackpotSpinner.TokensWon; _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,6 +1,5 @@
using qtc_net_client_2.ClientModel; using qtc_net_client_2.ClientModel;
using qtc_net_client_2.Services; using qtc_net_client_2.Services;
using QtCNETAPI.Services;
using QtCNETAPI.Services.ApiService; using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService; using QtCNETAPI.Services.GatewayService;
using System.Text.Json; using System.Text.Json;
@ -53,8 +52,8 @@ namespace qtc_net_client_2
// instantiate new ApiService and GatewayService for this session // instantiate new ApiService and GatewayService for this session
// this will keep the gateway thread in the main thread (i think) // this will keep the gateway thread in the main thread (i think)
IApiService api = new ApiService($"{clientConfig.ServerUrl}/api", loggingService); IApiService api = new ApiService($"{clientConfig.ServerUrl}/api");
IGatewayService gateway = new GatewayService($"{clientConfig.ServerUrl}/chat", api, loggingService); IGatewayService gateway = new GatewayService($"{clientConfig.ServerUrl}/chat", api);
// ping api // ping api
var pingResult = await api.PingServerAsync(); var pingResult = await api.PingServerAsync();

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -5,12 +5,10 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Diagnostics; using System.Diagnostics;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Abstractions;
namespace QtCNETAPI.Services namespace qtc_net_client_2.Services
{ {
public class LoggingService : IDisposable, ILogger public class LoggingService : IDisposable
{ {
private DateTime LogDate { get; set; } private DateTime LogDate { get; set; }
private string LogFilePath { get; set; } private string LogFilePath { get; set; }
@ -18,7 +16,7 @@ namespace QtCNETAPI.Services
public LoggingService() public LoggingService()
{ {
LogDate = DateTime.Now; LogDate = DateTime.Now;
LogFilePath = $"./Logs/QtCClientLog_{LogDate:ddMMyyy-hhmm}.log"; LogFilePath = $"./Logs/QtCClientLog_{LogDate.ToString("ddMMyyy-hhmm")}.log";
// create log file // create log file
@ -32,8 +30,8 @@ namespace QtCNETAPI.Services
{ {
try try
{ {
Debug.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {message}"); Debug.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {message}");
LogFile.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {message}"); LogFile.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {message}");
} catch (ObjectDisposedException) } catch (ObjectDisposedException)
{ {
} }
@ -47,8 +45,8 @@ namespace QtCNETAPI.Services
string modelSerialized = JsonSerializer.Serialize(model, options: new JsonSerializerOptions { WriteIndented = true }); string modelSerialized = JsonSerializer.Serialize(model, options: new JsonSerializerOptions { WriteIndented = true });
// log it // log it
Debug.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {modelSerialized}"); Debug.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {modelSerialized}");
LogFile.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {modelSerialized}"); LogFile.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {modelSerialized}");
} catch (ObjectDisposedException) } catch (ObjectDisposedException)
{ {
} }
@ -60,34 +58,5 @@ namespace QtCNETAPI.Services
LogFile.Close(); LogFile.Close();
LogFile.Dispose(); 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() { }
} }
} }