Compare commits

...

26 Commits
6.1 ... master

Author SHA1 Message Date
a95d012d69 Update Packages That Could Be Updated
Rework Missed Refresh Token Code
Minor Version Bump
2025-09-21 14:16:41 -07:00
d1d3af2ec9 Rework Refresh Token Storage Solution To Use Windows Credentials API
Minor Version Bump
2025-09-21 13:49:11 -07:00
4e61a1d7b9 Version Bump 2025-08-03 15:00:37 -07:00
446745d4df Implement CreateProfileImage To Combine Precense Icon, Profile Image, and Cosmetic To Make Final Profile Image 2025-08-03 15:00:09 -07:00
00df7505a7 Move Cosmetic Download To Main Form Thread 2025-08-03 14:27:00 -07:00
b17d391406 Minor Version Bump 2025-07-31 15:21:50 -07:00
2092d2c6d3 Keep Default KA Values 2025-07-31 15:05:03 -07:00
fd743dcb42 Do Not Set Server Timeout (it was the default value anyways) 2025-07-31 15:00:37 -07:00
9214460927 Server Timeout = 30s
Keep-Alive Interval  = 1m
2025-07-31 14:59:13 -07:00
1b30636937 Configure TimeSpans For ServerTimeout And KeepAliveInterval 2025-07-31 10:18:57 -07:00
72d9b99c4c Refresh Session Everytime Gateway Connection Is Used To Prevent The Server From Aborting Requests 2025-07-31 10:06:12 -07:00
c7137e4c7e Version Bump 2025-07-27 13:56:55 -07:00
4ee524778d Merge pull request 'Email Features' (#5) from email-features into master
Reviewed-on: #5
2025-07-27 13:55:57 -07:00
cfcc5ad4c1 Message Box On Register To Inform User Of Email Verification 2025-07-27 13:54:17 -07:00
b81b059177 Initial Implementation Of Email Features 2025-07-27 13:51:44 -07:00
5d553cdf58 Change Default Pfp 2025-07-19 13:54:14 -07:00
8cb59e1b21 Work Around Weird Offline Behaviour 2025-07-19 13:31:54 -07:00
6acb72975e Optimizations To Main Form 2025-07-17 15:33:17 -07:00
098a59f555 Avoid Updating Store Items ListView If Count Is Still Equal (No New Items)
Fix Stock Market Game UI Positioning
2025-07-17 15:27:16 -07:00
e6c842cb19 Remove Uneeded Log Entry 2025-07-17 15:20:53 -07:00
758a5a96dc Add Additional Logging/Diagnostics For Weird Status Behaviours 2025-07-17 14:36:56 -07:00
6bf77aa12e Add enableDebugLogs Config Property 2025-07-15 14:15:31 -07:00
f98e5a490c Implement Extra Logging To Diagnose Status Issues
Better Connection Error Handling
2025-07-15 14:04:46 -07:00
75b61c0a8c Move LoggingService to API library
Add Additional Logging For SignalR
2025-07-14 19:07:54 -07:00
0432fec3cd Finish Initial Implementation Of Admin Menus
Implement Logout Event
Fix Own Direct Messages Not New Lining The Chat
Version Bump
2025-07-13 17:27:16 -07:00
de2a81d485 Initial WIP Implementation Of Admin Menus 2025-07-12 14:23:15 -07:00
33 changed files with 1706 additions and 535 deletions

View File

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

View File

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

View File

@ -13,6 +13,9 @@ 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;
@ -30,9 +33,10 @@ namespace QtCNETAPI.Services.ApiService
get { return user; } get { return user; }
} }
public ApiService(string apiUrl) public ApiService(string apiUrl, LoggingService loggingService)
{ {
apiUri = apiUrl; apiUri = apiUrl;
_loggingService = loggingService;
_client = new RestClient(apiUri); _client = new RestClient(apiUri);
} }
@ -233,9 +237,9 @@ namespace QtCNETAPI.Services.ApiService
} }
} }
public async Task<ServiceResponse<User>> LoginAsync(UserLoginDto userLoginDto) public async Task<ServiceResponse<string>> LoginAsync(UserLoginDto userLoginDto)
{ {
var serviceResponse = new ServiceResponse<User>(); var serviceResponse = new ServiceResponse<string>();
try try
{ {
@ -255,13 +259,11 @@ 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 = user; serviceResponse.Data = response.Message;
} }
else else
{ {
@ -278,6 +280,72 @@ namespace QtCNETAPI.Services.ApiService
return serviceResponse; return serviceResponse;
} }
public async Task<ServiceResponse<bool>> ResendVerificationEmail(string email)
{
var serviceResponse = new ServiceResponse<bool>();
var restRequest = new RestRequest($"auth/resend-verification-email")
.AddQueryParameter("email", email);
var response = await _client.PostAsync<ServiceResponse<bool>>(restRequest);
if (response != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded.";
}
return serviceResponse;
}
public async Task<ServiceResponse<bool>> SendPasswordResetEmail(string email)
{
var serviceResponse = new ServiceResponse<bool>();
var restRequest = new RestRequest($"auth/request-password-reset")
.AddQueryParameter("email", email);
var response = await _client.PostAsync<ServiceResponse<bool>>(restRequest);
if (response != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded.";
}
return serviceResponse;
}
public async Task<ServiceResponse<bool>> ResetPassword(UserPasswordResetDto request)
{
var serviceResponse = new ServiceResponse<bool>();
var restRequest = new RestRequest($"auth/reset-password")
.AddJsonBody(request);
var response = await _client.PostAsync<ServiceResponse<bool>>(restRequest);
if (response != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded.";
}
return serviceResponse;
}
public async Task<User> SetCurrentUser() public async Task<User> SetCurrentUser()
{ {
var userRequest = new RestRequest("users/user-authorized") var userRequest = new RestRequest("users/user-authorized")
@ -287,6 +355,9 @@ namespace QtCNETAPI.Services.ApiService
if (userResponse != null && userResponse.Success && userResponse.Data != null) if (userResponse != null && userResponse.Success && userResponse.Data != null)
{ {
user = userResponse.Data; user = userResponse.Data;
_loggingService.LogString($"Current User's Status Is {userResponse.Data.Status}");
return userResponse.Data; return userResponse.Data;
} else } else
{ {
@ -342,21 +413,23 @@ 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 = await File.ReadAllTextAsync("./session.token"); var refToken = _credService.GetAccessToken(); // fuck CA1416, if this is being ran on linux it should just crash (theoretically)
if (refToken == null)
{
// treat as session expired
return new ServiceResponse<string> { Success = false, Message = "Refresh Token Not Found. Session Expired." };
}
JwtSecurityToken token = tokenHandler.ReadJwtToken(SessionToken); 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)
{ {
File.Delete("./session.token"); return new ServiceResponse<string> { Success = false, Message = "Session Expired." }; // logging in again should overwrite old 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 };
} }
@ -852,5 +925,29 @@ namespace QtCNETAPI.Services.ApiService
return serviceResponse; 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

@ -22,7 +22,11 @@ namespace QtCNETAPI.Services.ApiService
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>> LoginAsync(UserLoginDto userLoginDto); public Task<ServiceResponse<User>> DeleteUserById(string id);
public Task<ServiceResponse<string>> LoginAsync(UserLoginDto userLoginDto);
public Task<ServiceResponse<bool>> ResendVerificationEmail(string email);
public Task<ServiceResponse<bool>> SendPasswordResetEmail(string email);
public Task<ServiceResponse<bool>> ResetPassword(UserPasswordResetDto request);
public Task<ServiceResponse<User>> RefreshLogin(string refreshToken); public Task<ServiceResponse<User>> RefreshLogin(string refreshToken);
public Task<ServiceResponse<string>> RefreshSessionIfInvalid(); public Task<ServiceResponse<string>> RefreshSessionIfInvalid();
public Task<User> SetCurrentUser(); public Task<User> SetCurrentUser();

View File

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

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.SignalR.Client; using Microsoft.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;
@ -6,48 +7,55 @@ using QtCNETAPI.Services.ApiService;
namespace QtCNETAPI.Services.GatewayService namespace QtCNETAPI.Services.GatewayService
{ {
public class GatewayService : IGatewayService, IAsyncDisposable public class GatewayService(string GWUrl, IApiService apiService, LoggingService loggingService) : IGatewayService, IAsyncDisposable
{ {
internal string gwBaseUri = "127.0.0.1"; internal string gwBaseUri = GWUrl;
public Room? CurrentRoom { get; private set; } public 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 OnRefreshContactsListReceived; public event EventHandler? OnRoomDeleted;
public event EventHandler OnClientFunctionReceived; public event EventHandler? OnRefreshContactsListReceived;
public event EventHandler OnDirectMessageReceived; public event EventHandler? OnClientFunctionReceived;
public event EventHandler OnServerConfigReceived; public event EventHandler? OnDirectMessageReceived;
public event EventHandler OnServerDisconnect; public event EventHandler? OnServerConfigReceived;
public event EventHandler OnServerReconnecting; public event EventHandler? OnServerDisconnect;
public event EventHandler OnServerReconnected; public event EventHandler? OnServerReconnecting;
public event EventHandler? OnServerReconnected;
public event EventHandler? OnUserForceLogout;
private IApiService _apiService; private IApiService _apiService = 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 = () => Task.FromResult(_apiService.SessionToken); options.AccessTokenProvider = async () =>
}); {
// this should hopefully refresh the session every time the gateway connection is used to prevent connection aborts
await _apiService.RefreshSessionIfInvalid();
return _apiService.SessionToken;
};
})
.WithStatefulReconnect();
HubConnection = gwConBuilder.Build(); HubConnection = gwConBuilder.Build();
// register events // register events
@ -60,13 +68,26 @@ 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
await HubConnection.StartAsync(); try
{
await HubConnection.StartAsync();
}
catch (HttpRequestException ex)
{
_loggingService.LogString($"Unable To Connect To SignalR.\n{ex.Message}\n{ex.StackTrace}");
return;
}
// ensure current user is up to date (particularly status)
await _apiService.SetCurrentUser();
} }
public async Task StopAsync() public async Task StopAsync()
@ -101,8 +122,6 @@ 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);
@ -112,8 +131,6 @@ 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)
@ -132,8 +149,6 @@ 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)
@ -150,8 +165,6 @@ 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);
@ -168,8 +181,6 @@ 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);

View File

@ -104,6 +104,11 @@ 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>
@ -153,5 +158,10 @@ 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

@ -5,10 +5,12 @@ 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 qtc_net_client_2.Services namespace QtCNETAPI.Services
{ {
public class LoggingService : IDisposable public class LoggingService : IDisposable, ILogger
{ {
private DateTime LogDate { get; set; } private DateTime LogDate { get; set; }
private string LogFilePath { get; set; } private string LogFilePath { get; set; }
@ -16,7 +18,7 @@ namespace qtc_net_client_2.Services
public LoggingService() public LoggingService()
{ {
LogDate = DateTime.Now; LogDate = DateTime.Now;
LogFilePath = $"./Logs/QtCClientLog_{LogDate.ToString("ddMMyyy-hhmm")}.log"; LogFilePath = $"./Logs/QtCClientLog_{LogDate:ddMMyyy-hhmm}.log";
// create log file // create log file
@ -30,8 +32,8 @@ namespace qtc_net_client_2.Services
{ {
try try
{ {
Debug.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {message}"); Debug.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {message}");
LogFile.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {message}"); LogFile.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {message}");
} catch (ObjectDisposedException) } catch (ObjectDisposedException)
{ {
} }
@ -45,8 +47,8 @@ namespace qtc_net_client_2.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().ToString("hh:mm")}) {modelSerialized}"); Debug.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {modelSerialized}");
LogFile.WriteLine($"({DateTime.Now.ToLocalTime().ToString("hh:mm")}) {modelSerialized}"); LogFile.WriteLine($"({DateTime.Now.ToLocalTime():hh:mm}) {modelSerialized}");
} catch (ObjectDisposedException) } catch (ObjectDisposedException)
{ {
} }
@ -58,5 +60,34 @@ namespace qtc_net_client_2.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() { }
} }
} }

View File

@ -15,6 +15,9 @@ 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,17 +1,7 @@
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
{ {
@ -33,6 +23,7 @@ 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; }
@ -94,13 +85,16 @@ 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);
var res = await _apiService.GetUserInformationAsync(user!.Id); if (user != null)
var pfpRes = await _apiService.GetUserProfilePic(user!.Id);
if (res.Data != null && res.Success)
{ {
Profile frmProfile = new Profile(res.Data, pfpRes, mainForm.Contacts, _apiService, _gatewayService); var res = await _apiService.GetUserInformationAsync(user.Id);
frmProfile.Show(); var pfpRes = await _apiService.GetUserProfilePic(user.Id);
if (res.Data != null && res.Success)
{
Profile frmProfile = new Profile(res.Data, pfpRes, mainForm.Contacts, _apiService, _gatewayService);
frmProfile.Show();
}
} }
} }
} }
@ -119,12 +113,16 @@ namespace qtc_net_client_2.Forms
{ {
var args = (RoomListEventArgs)e; var args = (RoomListEventArgs)e;
if (!IsHandleCreated) return; if (IsHandleCreated && !IsDisposed)
lvUserList.BeginInvoke(lvUserList.Clear);
foreach (var user in args.UserList)
{ {
lvUserList.BeginInvoke(delegate () { lvUserList.Items.Add(user.Username, user.Status); }); Invoke(delegate ()
{
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)
@ -133,12 +131,32 @@ 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 (InvokeRequired) if(IsHandleCreated && !IsDisposed)
Invoke(delegate { rtxtChat.AppendText(message + Environment.NewLine); }); {
else rtxtChat.AppendText(message + Environment.NewLine); Invoke(delegate ()
{
rtxtChat.AppendText(message + Environment.NewLine);
});
}
} }
} }
} }

View File

@ -31,18 +31,17 @@
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
// //
@ -66,23 +65,12 @@
btnQuit.UseVisualStyleBackColor = true; btnQuit.UseVisualStyleBackColor = true;
btnQuit.Click += btnQuit_Click; btnQuit.Click += btnQuit_Click;
// //
// lblReason // ConnectionClosed
//
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);
@ -92,12 +80,10 @@
Margin = new Padding(4, 3, 4, 3); Margin = new Padding(4, 3, 4, 3);
MaximizeBox = false; MaximizeBox = false;
MinimizeBox = false; MinimizeBox = false;
Name = "frmConnectionClosed"; Name = "ConnectionClosed";
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
@ -105,6 +91,5 @@
private Label lblHead; private Label lblHead;
private Button btnReconnect; private Button btnReconnect;
private Button btnQuit; private Button btnQuit;
private Label lblReason;
} }
} }

View File

@ -12,19 +12,12 @@ namespace qtc_net_client_2.Forms
{ {
public partial class ConnectionClosed : Form public partial class ConnectionClosed : Form
{ {
public string? Reason { get; set; } = string.Empty; public Label StatusLabel { get { return lblHead; } }
public ConnectionClosed(string? reason = "") public ConnectionClosed()
{ {
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,6 +42,11 @@ 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");
@ -58,7 +63,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}"); Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}\n");
rtxtChatbox.Clear(); rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick"); AudioService.PlaySoundEffect("sndSendClick");
} }
@ -69,7 +74,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}"); Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}\n");
rtxtChatbox.Clear(); rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick"); AudioService.PlaySoundEffect("sndSendClick");
} }

View File

@ -32,11 +32,13 @@
pbLoginBanner = new PictureBox(); pbLoginBanner = new PictureBox();
tbEmail = new TextBox(); tbEmail = new TextBox();
lblEmail = new Label(); lblEmail = new Label();
label1 = new Label(); lblPassword = 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();
// //
@ -68,16 +70,16 @@
lblEmail.TabIndex = 2; lblEmail.TabIndex = 2;
lblEmail.Text = "Email"; lblEmail.Text = "Email";
// //
// label1 // lblPassword
// //
label1.AutoSize = true; lblPassword.AutoSize = true;
label1.Font = new Font("Segoe UI Light", 9F); lblPassword.Font = new Font("Segoe UI Light", 9F);
label1.ForeColor = SystemColors.ControlLight; lblPassword.ForeColor = SystemColors.ControlLight;
label1.Location = new Point(11, 138); lblPassword.Location = new Point(11, 138);
label1.Name = "label1"; lblPassword.Name = "lblPassword";
label1.Size = new Size(55, 15); lblPassword.Size = new Size(55, 15);
label1.TabIndex = 4; lblPassword.TabIndex = 4;
label1.Text = "Password"; lblPassword.Text = "Password";
// //
// tbPassword // tbPassword
// //
@ -122,16 +124,44 @@
cbRememberMe.Text = "Remember Me For 7 Days"; cbRememberMe.Text = "Remember Me For 7 Days";
cbRememberMe.UseVisualStyleBackColor = true; cbRememberMe.UseVisualStyleBackColor = true;
// //
// frmLogin // llblResendEmail
//
llblResendEmail.AutoSize = true;
llblResendEmail.Font = new Font("Segoe UI Light", 9F);
llblResendEmail.LinkColor = SystemColors.ControlLight;
llblResendEmail.Location = new Point(369, 164);
llblResendEmail.Name = "llblResendEmail";
llblResendEmail.Size = new Size(129, 15);
llblResendEmail.TabIndex = 8;
llblResendEmail.TabStop = true;
llblResendEmail.Text = "Resend Verification Email";
llblResendEmail.LinkClicked += llblResendEmail_LinkClicked;
//
// llblForgotPasswor
//
llblForgotPasswor.AutoSize = true;
llblForgotPasswor.Font = new Font("Segoe UI Light", 9F);
llblForgotPasswor.LinkColor = SystemColors.ControlLight;
llblForgotPasswor.Location = new Point(401, 181);
llblForgotPasswor.Name = "llblForgotPasswor";
llblForgotPasswor.Size = new Size(98, 15);
llblForgotPasswor.TabIndex = 9;
llblForgotPasswor.TabStop = true;
llblForgotPasswor.Text = "Forgot Password?";
llblForgotPasswor.LinkClicked += llblForgotPasswor_LinkClicked;
//
// llblForgotPassword
// //
AutoScaleDimensions = new SizeF(7F, 15F); 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(label1); Controls.Add(lblPassword);
Controls.Add(tbPassword); Controls.Add(tbPassword);
Controls.Add(lblEmail); Controls.Add(lblEmail);
Controls.Add(tbEmail); Controls.Add(tbEmail);
@ -139,7 +169,7 @@
FormBorderStyle = FormBorderStyle.FixedDialog; FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon"); Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false; MaximizeBox = false;
Name = "frmLogin"; Name = "llblForgotPassword";
StartPosition = FormStartPosition.CenterParent; StartPosition = FormStartPosition.CenterParent;
Text = "QtC.NET Client - Login"; Text = "QtC.NET Client - Login";
Load += frmLogin_Load; Load += frmLogin_Load;
@ -153,10 +183,12 @@
private PictureBox pbLoginBanner; private PictureBox pbLoginBanner;
private TextBox tbEmail; private TextBox tbEmail;
private Label lblEmail; private Label lblEmail;
private Label label1; private Label lblPassword;
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,4 +1,5 @@
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;
@ -9,12 +10,14 @@ 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;
@ -24,14 +27,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)
{ {
if (File.Exists("./session.token")) string? accessToken = _credService.GetAccessToken();
if (accessToken != null)
{ {
ToggleControls(false, false); ToggleControls(false, false);
// try logging in with the token in the file // try logging in with the token in cred storage
string token = File.ReadAllText("./session.token"); var result = await _apiService.RefreshLogin(accessToken);
var result = await _apiService.RefreshLogin(token);
if (result.Success) if (result.Success)
{ {
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
@ -55,8 +58,9 @@ namespace qtc_net_client_2.Forms
RememberMe = cbRememberMe.Checked RememberMe = cbRememberMe.Checked
}); });
if (result.Success) if (result.Success && result.Data != null)
{ {
_credService.SaveAccessToken(_apiService.CurrentUser.Username, result.Data);
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
Close(); Close();
} }
@ -68,6 +72,24 @@ namespace qtc_net_client_2.Forms
} }
} }
private void llblRegister_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Register frmRegister = new Register(_apiService);
frmRegister.ShowDialog();
}
private void llblResendEmail_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ResendVerificationEmail resendVerificationEmail = new ResendVerificationEmail(_apiService);
resendVerificationEmail.ShowDialog();
}
private void llblForgotPasswor_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ResetPassword resetPassword = new ResetPassword(_apiService);
resetPassword.ShowDialog();
}
private void ToggleControls(bool enable, bool clearText) private void ToggleControls(bool enable, bool clearText)
{ {
tbEmail.Enabled = enable; tbEmail.Enabled = enable;
@ -81,11 +103,5 @@ 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,7 +40,6 @@
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();
@ -70,6 +69,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();
@ -83,6 +94,8 @@
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
@ -152,7 +165,6 @@
// //
// 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);
@ -163,17 +175,6 @@
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;
@ -493,6 +494,91 @@
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);
@ -504,6 +590,7 @@
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;
@ -528,6 +615,8 @@
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();
} }
@ -548,7 +637,6 @@
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;
@ -571,5 +659,17 @@
private TabPage tbpStore; private TabPage tbpStore;
private ListView lvStoreItems; private ListView lvStoreItems;
private ImageList ilStoreThumbnails; 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

@ -8,6 +8,7 @@ using qtc_net_client_2.Services;
using qtc_net_client_2.ClientModel; using qtc_net_client_2.ClientModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using QtCNETAPI.Schema; using QtCNETAPI.Schema;
using QtCNETAPI.Services;
namespace qtc_net_client_2 namespace qtc_net_client_2
{ {
@ -15,7 +16,9 @@ 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;
@ -169,12 +172,29 @@ 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); Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService, cosmeticData);
frmProfile.Show(); frmProfile.Show();
} }
} }
@ -293,12 +313,29 @@ 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); Profile frmProfile = new Profile(res.Data, pfpRes, Contacts, _apiService, _gatewayService, cosmeticData);
frmProfile.Show(); frmProfile.Show();
} }
} }
@ -350,6 +387,8 @@ namespace qtc_net_client_2
var storeItems = await _apiService.GetStoreItems(); var storeItems = await _apiService.GetStoreItems();
if (storeItems != null && storeItems.Success && storeItems.Data != null) if (storeItems != null && storeItems.Success && storeItems.Data != null)
{ {
if (lvStoreItems.Items.Count == storeItems.Data.Count) return;
ilStoreThumbnails.Images.Clear(); ilStoreThumbnails.Images.Clear();
foreach (var item in storeItems.Data) foreach (var item in storeItems.Data)
{ {
@ -359,7 +398,6 @@ namespace qtc_net_client_2
} }
} }
} }
else lvStoreItems.Clear();
} }
private async void lvStoreItems_DoubleClick(object sender, EventArgs e) private async void lvStoreItems_DoubleClick(object sender, EventArgs e)
@ -367,11 +405,11 @@ namespace qtc_net_client_2
if (lvStoreItems.SelectedItems.Count > 0) if (lvStoreItems.SelectedItems.Count > 0)
{ {
string? itemSelected = (string?)lvStoreItems.SelectedItems[lvStoreItems.SelectedItems.Count - 1].Name; string? itemSelected = (string?)lvStoreItems.SelectedItems[lvStoreItems.SelectedItems.Count - 1].Name;
if(itemSelected != null) if (itemSelected != null)
{ {
// get item // get item
var item = await _apiService.GetStoreItem(int.Parse(itemSelected)); var item = await _apiService.GetStoreItem(int.Parse(itemSelected));
if(item != null && item.Success && item.Data != null) if (item != null && item.Success && item.Data != null)
{ {
StoreItemDisplay storeItemDisplay = new StoreItemDisplay(item.Data, LoggingService, _apiService); StoreItemDisplay storeItemDisplay = new StoreItemDisplay(item.Data, LoggingService, _apiService);
storeItemDisplay.ShowDialog(); storeItemDisplay.ShowDialog();
@ -380,6 +418,153 @@ namespace qtc_net_client_2
} }
} }
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
@ -400,12 +585,14 @@ 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; _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;
@ -427,13 +614,14 @@ namespace qtc_net_client_2
} }
} }
if (lvUserDirectory.Items.Count <= 0)
await RefreshUsers(); // prevent edge case where the refresh event never gets sent to connecting client
await RefreshContactsList(); await RefreshContactsList();
await RefreshRoomsList(); await RefreshRoomsList();
// 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)
{ {
@ -463,15 +651,19 @@ 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");
} }
} }
@ -481,29 +673,30 @@ namespace qtc_net_client_2
{ {
LoggingService.LogString("Refreshing All Users List..."); LoggingService.LogString("Refreshing All Users List...");
// get all users on server if (IsHandleCreated && !IsDisposed)
var userList = await _apiService.GetAllUsersAsync();
if (userList != null && userList.Success && userList.Data != null)
{ {
// clear the list await Invoke(async delegate ()
if (lvUserDirectory.InvokeRequired) lvUserDirectory.BeginInvoke(lvUserDirectory.Items.Clear);
else lvUserDirectory.Items.Clear();
UserDirectory.Clear();
// populate list
foreach (var user in userList.Data)
{ {
if (lvUserDirectory.InvokeRequired) // get all users on server
var userList = await _apiService.GetAllUsersAsync();
if (userList != null && userList.Success && userList.Data != null)
{ {
lvUserDirectory.BeginInvoke(delegate () { lvUserDirectory.Items.Add(user.Username, user.Status); }); // clear the list
lvUserDirectory.Items.Clear();
UserDirectory.Clear();
// populate list
foreach (var user in userList.Data)
{
lvUserDirectory.Items.Add(user.Username, user.Status);
UserDirectory.Add(user);
}
if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs)
LoggingService.LogModel(userList.Data);
} }
else });
{
lvUserDirectory.Items.Add(user.Username, user.Status);
}
UserDirectory.Add(user);
}
} }
} }
@ -511,7 +704,7 @@ namespace qtc_net_client_2
{ {
LoggingService.LogString("Refreshing Rooms List..."); LoggingService.LogString("Refreshing Rooms List...");
if (InvokeRequired) if (IsHandleCreated && !IsDisposed)
{ {
await Invoke(async delegate () await Invoke(async delegate ()
{ {
@ -524,35 +717,21 @@ 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 (InvokeRequired) if (IsHandleCreated && !IsDisposed)
{ {
await Invoke(async delegate () await Invoke(async delegate ()
{ {
@ -621,76 +800,11 @@ namespace qtc_net_client_2
} }
} }
} }
if (System.Diagnostics.Debugger.IsAttached || _config.EnableDebugLogs)
LoggingService.LogModel(contactsRes.Data);
} }
}); });
return;
}
lvContacts.Items.Clear();
Contacts.Clear();
lblRequestNotif.Visible = false;
var contactsRes = await _apiService.GetCurrentUserContacts();
if (contactsRes.Success && contactsRes.Data != null)
{
if (contactsRes.Data.Where(e => e.UserId == _apiService.CurrentUser!.Id && e.UserStatus == Contact.ContactStatus.AwaitingApprovalFromSelf).Count() >= 1)
lblRequestNotif.Visible = true;
else
lblRequestNotif.Visible = false;
foreach (var contact in contactsRes.Data)
{
ServiceResponse<UserInformationDto> user = null!;
if (contact.OwnerId == _apiService.CurrentUser!.Id)
user = await _apiService.GetUserInformationAsync(contact.UserId);
else if (contact.UserId == _apiService.CurrentUser!.Id)
user = await _apiService.GetUserInformationAsync(contact.OwnerId);
if (user.Data != null)
{
Contacts.Add(contact);
if (contact.OwnerId == _apiService.CurrentUser!.Id)
{
switch (contact.OwnerStatus)
{
case Contact.ContactStatus.AwaitingApprovalFromOther:
var lvi = lvContacts.Items.Add($"{user.Data.Username} [Request Sent]");
await AddProfilePicToList(user.Data.Id);
if (ilProfilePics.Images.ContainsKey(user.Data.Id))
lvi.ImageKey = user.Data.Id;
else lvi.ImageKey = "DEFAULT";
break;
case Contact.ContactStatus.Accepted:
var lvi2 = lvContacts.Items.Add($"{user.Data.Username}");
await AddProfilePicToList(user.Data.Id);
if (ilProfilePics.Images.ContainsKey(user.Data.Id))
lvi2.ImageKey = user.Data.Id;
else lvi2.ImageKey = "DEFAULT";
break;
}
}
else if (contact.UserId == _apiService.CurrentUser!.Id)
{
switch (contact.UserStatus)
{
case Contact.ContactStatus.AwaitingApprovalFromSelf:
var lvi = lvContacts.Items.Add($"{user.Data.Username} [Contact Request]");
await AddProfilePicToList(user.Data.Id);
if (ilProfilePics.Images.ContainsKey(user.Data.Id))
lvi.ImageKey = user.Data.Id;
else lvi.ImageKey = "DEFAULT";
AudioService.PlaySoundEffect("sndContactRequest");
break;
case Contact.ContactStatus.Accepted:
var lvi2 = lvContacts.Items.Add($"{user.Data.Username}");
await AddProfilePicToList(user.Data.Id);
if (ilProfilePics.Images.ContainsKey(user.Data.Id))
lvi2.ImageKey = user.Data.Id;
else lvi2.ImageKey = "DEFAULT";
break;
}
}
}
}
} }
} }
@ -698,11 +812,9 @@ namespace qtc_net_client_2
{ {
while (true) while (true)
{ {
if (!IsHandleCreated) { LoggingService.LogString("Form Handle Wasn't Created"); return; } if(IsHandleCreated && !IsDisposed)
if (InvokeRequired && !IsDisposed)
{ {
BeginInvoke(async delegate () await Invoke(async delegate ()
{ {
label.ForeColor = Color.Red; label.ForeColor = Color.Red;
await Task.Delay(500); await Task.Delay(500);
@ -710,11 +822,6 @@ 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);
} }
} }
@ -757,16 +864,9 @@ namespace qtc_net_client_2
public void RefreshCurrencyCounter() public void RefreshCurrencyCounter()
{ {
if (lblCurrencyAmount.InvokeRequired) if (IsHandleCreated && !IsDisposed)
{ {
lblCurrencyAmount.BeginInvoke(delegate () Invoke(delegate () { lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0"); });
{
lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0");
});
}
else
{
lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString("N0");
} }
} }
@ -785,9 +885,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 () { Enabled = true; }); return; } { BeginInvoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; }); return; }
ConnectionClosed frmConnectionClosed = new ConnectionClosed(error); ConnectionClosed frmConnectionClosed = new ConnectionClosed();
var result = frmConnectionClosed.ShowDialog(); var result = frmConnectionClosed.ShowDialog();
if (result == DialogResult.OK) if (result == DialogResult.OK)
@ -796,18 +896,19 @@ namespace qtc_net_client_2
Reconnect: Reconnect:
if (_gatewayService.HubConnection != null) if (_gatewayService.HubConnection != null)
{ {
try await _gatewayService.StopAsync();
await _gatewayService.StartAsync();
if (_gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
{ {
await _gatewayService.StopAsync(); Invoke(delegate () { tbcMain.Enabled = true; lblConnectionLost.Visible = false; });
await _gatewayService.StartAsync();
if (_gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
BeginInvoke(delegate () { Enabled = true; });
} }
catch (HttpRequestException ex) else
{ {
// reshow the dialog // reshow the dialog
frmConnectionClosed.Reason = ex.Message; frmConnectionClosed.StatusLabel.Text = "Couldn't Reconnect. The Server Could Be Down.";
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);
} }
@ -816,8 +917,47 @@ namespace qtc_net_client_2
else Environment.Exit(0); else Environment.Exit(0);
} }
private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e) => BeginInvoke(delegate () { Enabled = false; }); private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e)
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)
{ {
@ -826,8 +966,11 @@ 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");
@ -863,6 +1006,17 @@ 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) private void _apiService_OnCurrentUserUpdate(object? sender, EventArgs e)
{ {
lblWelcome.Text = $"Welcome, {_apiService.CurrentUser.Username}"; lblWelcome.Text = $"Welcome, {_apiService.CurrentUser.Username}";

View File

@ -128,7 +128,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA0A0AAAJNU0Z0AUkBTAMBAQAB SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA0A0AAAJNU0Z0AUkBTAMBAQAB
MAEBATABAQEgAQABIAEABP8BIQEACP8BQgFNATYHAAE2AwABKAMAAYADAAEgAwABAQEAASAGAAFAEgAD mAEBAZgBAQEgAQABIAEABP8BIQEACP8BQgFNATYHAAE2AwABKAMAAYADAAEgAwABAQEAASAGAAFAEgAD
rQH/A7oB/wO6Af8DuQH/A7oB/wO6Af8D2gX/A/wB/wP+Df8D/QH/A/wR/wP8Af8D/g3/A/0B/wPZAf8D rQH/A7oB/wO6Af8DuQH/A7oB/wO6Af8D2gX/A/wB/wP+Df8D/QH/A/wR/wP8Af8D/g3/A/0B/wPZAf8D
ugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA58B/wO6Af8DugH/A7oB/wO6Af8DugH/A9oJ/wP9Af8D ugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA58B/wO6Af8DugH/A7oB/wO6Af8DugH/A9oJ/wP9Af8D
/g3/A/0B/wP9Ef8D/AH/A/4N/wPaAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA6IB/wO6Af8D /g3/A/0B/wP9Ef8D/AH/A/4N/wPaAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af//AIEAA6IB/wO6Af8D
@ -196,92 +196,91 @@
<value> <value>
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA4hMAAAJNU0Z0AUkBTAIBAQQB SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAohMAAAJNU0Z0AUkBTAIBAQQB
AAHoAQAB6AEAARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAASADAAEBAQABIAYAASD/ AAFQAQEBUAEBARABAAEQAQAE/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
YAHoA10B0QNQAZ8DMQFNAwUBBhQAAyABLQNUAasDWwHkA1oB9QMkAfsDQQH+A0EB/gMkAfsDUwH0A2IB YAHoA10B0QNQAZ8DMQFNAwUBBhQAAyABLQNUAasDWwHkA1oB9QMkAfsDTgH+A04B/gMkAfsDUwH0A2IB
4QNRAaEDHgEqEAADIAEtA1QBqwNbAeQBUwFaAVMB9QEhAV0BIQH7AR0BXQEdAf4BHQFdAR0B/gEhAV0B 4QNRAaEDHgEqEAADIAEtA1QBqwNbAeQDWgH1ASEBXgEhAfsBKgFqASoB/gEqAWoBKgH+ASEBXgEhAfsD
IQH7A1MB9ANiAeEDUQGhAx4BKhAAAyABLQNUAasDWwHkAVMCWgH1ASECXQH7AR0CXQH+AR0CXQH+ASEC UwH0A2IB4QNRAaEDHgEqEAADIAEtA1QBqwNbAeQDWgH1ASECXgH7ASoCagH+ASoCagH+ASECXgH7A1MB
XQH7A1MB9ANiAeEDUQGhAx4BKhAAAyABLQNUAasDWwHkAlMBWgH1AiEBXQH7Ah0BXQH+Ah0BXQH+AiEB 9ANiAeEDUQGhAx4BKhAAAyABLQNUAasDWwHkA1oB9QIhAV4B+wIqAWoB/gIqAWoB/gIhAV4B+wNTAfQD
XQH7A1MB9ANiAeEDUQGhAx4BKgwAAxsBJQNYAb0DWgHyA0UB/gMwAf8DOQH/AzwB/wM2Af8DKgH/AyQB YgHhA1EBoQMeASoMAAMbASUDWAG9A1oB8gNSAf4DMAH/AzkB/wM8Af8DNgH/AyoB/wMkAf8DQAH9A14B
/wNAAf0DXgHwA1YBsgMaASMIAAMbASUDWAG9A1oB8gEdAWUBHQH+AQABVwEAAf8BAAFnAQAB/wEAAWwB 8ANWAbIDGgEjCAADGwElA1gBvQNaAfIBKgFyASoB/gEAAVcBAAH/AQABZwEAAf8BAAFsAQAB/wEAAWEB
AAH/AQABYQEAAf8BAAFMAQAB/wEAAUABAAH/AToBQAE6Af0DXgHwA1YBsgEZARoBGQEjCAADGwElA1gB AAH/AQABTAEAAf8BAAFAAQAB/wNAAf0DXgHwA1YBsgEZARoBGQEjCAADGwElA1gBvQNaAfIBKgJyAf4B
vQNaAfIBHQJlAf4BAAJXAf8BAAJnAf8BAAJsAf8BAAJhAf8BAAJMAf8BAAJAAf8BOgJAAf0DXgHwA1YB AAJXAf8BAAJnAf8BAAJsAf8BAAJhAf8BAAJMAf8BAAJAAf8DQAH9A14B8ANWAbIBGQIaASMIAAMbASUD
sgEZAhoBIwgAAxsBJQNYAb0DWgHyAh0BZQH+AgABVwH/AgABZwH/AgABbAH/AgABYQH/AgABTAH/AgAB WAG9A1oB8gIqAXIB/gIAAVcB/wIAAWcB/wIAAWwB/wIAAWEB/wIAAUwB/wIAAUAB/wNAAf0DXgHwA1YB
QAH/AjoBQAH9A14B8ANWAbICGQEaASMEAAMDAQQDUgGlA2AB8wNJAf8DVQH/A2UB/wNxAf8DdQH/A3EB sgIZARoBIwQAAwMBBANSAaUDYAHzA0kB/wNVAf8DZQH/A3EB/wN1Af8DcQH/A2QB/wNMAf8DMQH/A04B
/wNkAf8DTAH/AzEB/wNBAf4DYgHuA1ABmgMDAQQDAwEEAVIBUwFSAaUBXQFvAV0B8wEAAYIBAAH/AQAB /gNiAe4DUAGaAwMBBAMDAQQBUgFTAVIBpQFgAW8BYAHzAQABggEAAf8BAAGZAQAB/wEAAbYBAAH/AQAB
mQEAAf8BAAG2AQAB/wEAAcwBAAH/AQAB0wEAAf8BAAHLAQAB/wEAAbMBAAH/AQABiAEAAf8BAAFXAQAB zAEAAf8BAAHTAQAB/wEAAcsBAAH/AQABswEAAf8BAAGIAQAB/wEAAVcBAAH/ASoBagEqAf4DYgHuA1AB
/wEdAV0BHQH+A2IB7gNQAZoDAwEEAwMBBAFSAlMBpQFdAm8B8wEAAoIB/wEAApkB/wEAArYB/wEAAswB mgMDAQQDAwEEAVICUwGlAWACbwHzAQACggH/AQACmQH/AQACtgH/AQACzAH/AQAC0wH/AQACywH/AQAC
/wEAAtMB/wEAAssB/wEAArMB/wEAAogB/wEAAlcB/wEdAl0B/gNiAe4DUAGaAwMBBAMDAQQCUgFTAaUC swH/AQACiAH/AQACVwH/ASoCagH+A2IB7gNQAZoDAwEEAwMBBAJSAVMBpQJgAW8B8wIAAYIB/wIAAZkB
XQFvAfMCAAGCAf8CAAGZAf8CAAG2Af8CAAHMAf8CAAHTAf8CAAHLAf8CAAGzAf8CAAGIAf8CAAFXAf8C /wIAAbYB/wIAAcwB/wIAAdMB/wIAAcsB/wIAAbMB/wIAAYgB/wIAAVcB/wIqAWoB/gNiAe4DUAGaAwMB
HQFdAf4DYgHuA1ABmgMDAQQDLQFEA2AB6AOAAf4DbgH/A3sB/wOFAf8DigH/A4wB/wOKAf8DhQH/A3YB BAMtAUQDYAHoA4AB/gNuAf8DewH/A4UB/wOKAf8DjAH/A4oB/wOFAf8DdgH/A1cB/wMyAf8DQAH9A14B
/wNXAf8DMgH/A0AB/QNeAd0DKgE/Ay0BRAFgAWkBYAHoAR0BgAEdAf4BAAHGAQAB/wEAAdwBAAH/AQAB 3QMqAT8DLQFEAWABaQFgAegBKgGAASoB/gEAAcYBAAH/AQAB3AEAAf8BAAHuAQAB/wEAAfgBAAH/AQAB
7gEAAf8BAAH4AQAB/wEAAfsBAAH/AQAB+QEAAf8BAAHvAQAB/wEAAdQBAAH/AQABnAEAAf8BAAFaAQAB +wEAAf8BAAH5AQAB/wEAAe8BAAH/AQAB1AEAAf8BAAGcAQAB/wEAAVoBAAH/A0AB/QNeAd0DKgE/Ay0B
/wE6AUABOgH9A14B3QMqAT8DLQFEAWACaQHoAR0CgAH+AQACxgH/AQAC3AH/AQAC7gH/AQAC+AH/AQAC RAFgAmkB6AEqAoAB/gEAAsYB/wEAAtwB/wEAAu4B/wEAAvgB/wEAAvsB/wEAAvkB/wEAAu8B/wEAAtQB
+wH/AQAC+QH/AQAC7wH/AQAC1AH/AQACnAH/AQACWgH/AToCQAH9A14B3QMqAT8DLQFEAmABaQHoAh0B /wEAApwB/wEAAloB/wNAAf0DXgHdAyoBPwMtAUQCYAFpAegCKgGAAf4CAAHGAf8CAAHcAf8CAAHuAf8C
gAH+AgABxgH/AgAB3AH/AgAB7gH/AgAB+AH/AgAB+wH/AgAB+QH/AgAB7wH/AgAB1AH/AgABnAH/AgAB AAH4Af8CAAH7Af8CAAH5Af8CAAHvAf8CAAHUAf8CAAGcAf8CAAFaAf8DQAH9A14B3QMqAT8DTgGVA3cB
WgH/AjoBQAH9A14B3QMqAT8DTgGVA3cB+AN/Af8DhQH/A4oB/wONAf8DjgH/A44B/wOOAf8DjQH/A4kB +AN/Af8DhQH/A4oB/wONAf8DjgH/A44B/wOOAf8DjQH/A4kB/wN3Af8DTQH/AyUB/wNaAfIDSgGLA04B
/wN3Af8DTQH/AyUB/wNaAfIDSgGLA04BlQFHAXwBRwH4AQAB5QEAAf8BAAHvAQAB/wEAAfgBAAH/AQAB lQFcAXwBXAH4AQAB5QEAAf8BAAHvAQAB/wEAAfgBAAH/AQAB/QEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/QEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH+AQAB/wEAAfYBAAH/AQAB1QEAAf8BAAGLAQAB /wEAAf8BAAH+AQAB/wEAAfYBAAH/AQAB1QEAAf8BAAGLAQAB/wEAAUEBAAH/A1oB8gNKAYsDTgGVAVwC
/wEAAUEBAAH/A1oB8gNKAYsDTgGVAUcCfAH4AQAC5QH/AQAC7wH/AQAC+AH/AQAC/QH/AQAD/wEAA/8B fAH4AQAC5QH/AQAC7wH/AQAC+AH/AQAC/QH/AQAD/wEAA/8BAAP/AQAC/gH/AQAC9gH/AQAC1QH/AQAC
AAP/AQAC/gH/AQAC9gH/AQAC1QH/AQACiwH/AQACQQH/A1oB8gNKAYsDTgGVAkcBfAH4AgAB5QH/AgAB iwH/AQACQQH/A1oB8gNKAYsDTgGVAlwBfAH4AgAB5QH/AgAB7wH/AgAB+AH/AgAB/QH/AgAC/wIAAv8C
7wH/AgAB+AH/AgAB/QH/AgAC/wIAAv8CAAL/AgAB/gH/AgAB9gH/AgAB1QH/AgABiwH/AgABQQH/A1oB AAL/AgAB/gH/AgAB9gH/AgAB1QH/AgABiwH/AgABQQH/A1oB8gNKAYsDXwHTA34B/AOTAf8DjgH/A40B
8gNKAYsDXwHTA34B/AOTAf8DjgH/A40B/wOOAf8DjgH/A44B/wOOAf8DjgH/A40B/wOFAf8DZwH/AzQB /wOOAf8DjgH/A44B/wOOAf8DjgH/A40B/wOFAf8DZwH/AzQB/wNBAfkDWgHEAVsBXwFbAdMBKwF+ASsB
/wNBAfkDWgHEAVsBXwFbAdMBKwGkASsB/AEOAfsBDgH/AQMB/QEDAf8BAAH+AQAB/wEAAf8BAAH/AQAB /AEOAfsBDgH/AQMB/QEDAf8BAAH+AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH9AQAB/wEAAe8BAAH/AQABuQEAAf8BAAFdAQAB /wEAAf8BAAH9AQAB/wEAAe8BAAH/AQABuQEAAf8BAAFdAQAB/wNBAfkDWgHEAVsCXwHTASsCfgH8AQ4C
/wNBAfkDWgHEAVsCXwHTASsCpAH8AQ4C+wH/AQMC/QH/AQAC/gH/AQAD/wEAA/8BAAP/AQAD/wEAA/8B +wH/AQMC/QH/AQAC/gH/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL9Af8BAALvAf8BAAK5Af8BAAJdAf8D
AAL9Af8BAALvAf8BAAK5Af8BAAJdAf8DQQH5A1oBxAJbAV8B0wIrAaQB/AIOAfsB/wIDAf0B/wIAAf4B QQH5A1oBxAJbAV8B0wIrAX4B/AIOAfsB/wIDAf0B/wIAAf4B/wIAAv8CAAL/AgAC/wIAAv8CAAL/AgAB
/wIAAv8CAAL/AgAC/wIAAv8CAAL/AgAB/QH/AgAB7wH/AgABuQH/AgABXQH/A0EB+QNaAcQDbgH1A4AB /QH/AgAB7wH/AgABuQH/AgABXQH/A0EB+QNaAcQDbgH1A4AB/gOfAf8DkwH/A48B/wOOAf8DjgH/A44B
/gOfAf8DkwH/A48B/wOOAf8DjgH/A44B/wOOAf8DjgH/A44B/wOLAf8DdwH/A0gB/wNAAf0DYgHhAVkB /wOOAf8DjgH/A44B/wOLAf8DdwH/A0gB/wNAAf0DYgHhAVoBbgFaAfUBTgGrAU4B/gEnAf8BJwH/AQsB
bgFZAfUBQQHFAUEB/gEnAf8BJwH/AQsB/wELAf8BAQH/AQEB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB /wELAf8BAQH/AQEB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAfkBAAH/AQAB1gEAAf8BAAGBAQAB/wE6AUABOgH9A2IB /wEAAfkBAAH/AQAB1gEAAf8BAAGBAQAB/wNAAf0DYgHhAVoCbgH1AU4CqwH+AScD/wELA/8BAQP/AQAD
4QFZAm4B9QFBAsUB/gEnA/8BCwP/AQED/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAAvkB/wEAAtYB /wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAC+QH/AQAC1gH/AQACgQH/A0AB/QNiAeECWgFuAfUCTgGrAf4C
/wEAAoEB/wE6AkAB/QNiAeECWQFuAfUCQQHFAf4CJwL/AgsC/wIBAv8CAAL/AgAC/wIAAv8CAAL/AgAC JwL/AgsC/wIBAv8CAAL/AgAC/wIAAv8CAAL/AgAC/wIAAv8CAAH5Af8CAAHWAf8CAAGBAf8DQAH9A2IB
/wIAAv8CAAH5Af8CAAHWAf8CAAGBAf8COgFAAf0DYgHhA2MB9gOAAf4DqwH/A5kB/wOQAf8DjgH/A44B 4QNjAfYDgAH+A6sB/wOZAf8DkAH/A44B/wOOAf8DjgH/A44B/wOOAf8DjgH/A40B/wN/Af8DVQH/A0AB
/wOOAf8DjgH/A44B/wOOAf8DjQH/A38B/wNVAf8DQAH9A14B4gFIAXUBSAH2AWQBxQFkAf4BQgH/AUIB /QNeAeIBSAFjAUgB9gFxAasBcQH+AUIB/wFCAf8BGQH/ARkB/wEEAf8BBAH/AQAB/wEAAf8BAAH/AQAB
/wEZAf8BGQH/AQQB/wEEAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB /wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/QEAAf8BAAHkAQAB/wEAAZgBAAH/A0AB
/wEAAf8BAAH9AQAB/wEAAeQBAAH/AQABmAEAAf8BOgFAAToB/QNeAeIBSAJ1AfYBZALFAf4BQgP/ARkD /QNeAeIBSAJjAfYBcQKrAf4BQgP/ARkD/wEEA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL9Af8B
/wEEA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL9Af8BAALkAf8BAAKYAf8BOgJAAf0DXgHiAkgB AALkAf8BAAKYAf8DQAH9A14B4gJIAWMB9gJxAasB/gJCAv8CGQL/AgQC/wIAAv8CAAL/AgAC/wIAAv8C
dQH2AmQBxQH+AkIC/wIZAv8CBAL/AgAC/wIAAv8CAAL/AgAC/wIAAv8CAAL/AgAB/QH/AgAB5AH/AgAB AAL/AgAC/wIAAf0B/wIAAeQB/wIAAZgB/wNAAf0DXgHiA2EB1gN+AfwDuAH/A6MB/wOTAf8DjgH/A44B
mAH/AjoBQAH9A14B4gNhAdYDfgH8A7gB/wOjAf8DkwH/A44B/wOOAf8DjgH/A44B/wOOAf8DjgH/A40B /wOOAf8DjgH/A44B/wOOAf8DjQH/A4IB/wNcAf8DTQH6A1oBxwFcAWEBXAHWAWQBgwFkAfwBXwH/AV8B
/wOCAf8DXAH/A00B+gNaAccBXAFhAVwB1gFkAaoBZAH8AV8B/wFfAf8BLwH/AS8B/wEMAf8BDAH/AQEB /wEvAf8BLwH/AQwB/wEMAf8BAQH/AQEB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB
/wEBAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQAB/gEAAf8BAAHqAQAB /wEAAf8BAAH+AQAB/wEAAeoBAAH/AQABpQEAAf8BSAFNAUgB+gNaAccBXAJhAdYBZAKDAfwBXwP/AS8D
/wEAAaUBAAH/ATEBTQExAfoDWgHHAVwCYQHWAWQCqgH8AV8D/wEvA/8BDAP/AQED/wEAA/8BAAP/AQAD /wEMA/8BAQP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL+Af8BAALqAf8BAAKlAf8BSAJNAfoDWgHHAlwB
/wEAA/8BAAP/AQAC/gH/AQAC6gH/AQACpQH/ATECTQH6A1oBxwJcAWEB1gJkAaoB/AJfAv8CLwL/AgwC YQHWAmQBgwH8Al8C/wIvAv8CDAL/AgEC/wIAAv8CAAL/AgAC/wIAAv8CAAL/AgAB/gH/AgAB6gH/AgAB
/wIBAv8CAAL/AgAC/wIAAv8CAAL/AgAC/wIAAf4B/wIAAeoB/wIAAaUB/wIxAU0B+gNaAccDUAGaA4MB pQH/AkgBTQH6A1oBxwNQAZoDbQH5A8UB/wOyAf8DnAH/A5EB/wOOAf8DjgH/A44B/wOOAf8DjwH/A44B
+QPFAf8DsgH/A5wB/wORAf8DjgH/A44B/wOOAf8DjgH/A48B/wOOAf8DgwH/A2AB/wNaAfIDTAGQA1AB /wODAf8DYAH/A1oB8gNMAZADUAGaAWoBfwFqAfkBfAH/AXwB/wFRAf8BUQH/AR8B/wEfAf8BBwH/AQcB
mgFqAYwBagH5AXwB/wF8Af8BUQH/AVEB/wEfAf8BHwH/AQcB/wEHAf8BAQH/AQEB/wEAAf8BAAH/AQAB /wEBAf8BAQH/AQAB/wEAAf8BAAH/AQAB/wEAAf8BAAH/AQIB/wECAf8BAgH+AQIB/wEAAesBAAH/AQAB
/wEAAf8BAAH/AQAB/wECAf8BAgH/AQIB/gECAf8BAAHrAQAB/wEAAa0BAAH/AVoBawFaAfIDTAGQA1AB rQEAAf8BWgFrAVoB8gNMAZADUAGaAWoCfwH5AXwD/wFRA/8BHwP/AQcD/wEBA/8BAAP/AQAD/wEAA/8B
mgFqAowB+QF8A/8BUQP/AR8D/wEHA/8BAQP/AQAD/wEAA/8BAAP/AQID/wECAv4B/wEAAusB/wEAAq0B AgP/AQIC/gH/AQAC6wH/AQACrQH/AVoCawHyA0wBkANQAZoCagF/AfkCfAL/AlEC/wIfAv8CBwL/AgEC
/wFaAmsB8gNMAZADUAGaAmoBjAH5AnwC/wJRAv8CHwL/AgcC/wIBAv8CAAL/AgAC/wIAAv8CAgL/AgIB /wIAAv8CAAL/AgAC/wICAv8CAgH+Af8CAAHrAf8CAAGtAf8CWgFrAfIDTAGQAy8BSQNsAesDgAH+A8YB
/gH/AgAB6wH/AgABrQH/AloBawHyA0wBkAMvAUkDbAHrA5YB/gPGAf8DrgH/A5wB/wOTAf8DkAH/A48B /wOuAf8DnAH/A5MB/wOQAf8DjwH/A5AB/wOTAf8DkwH/A4UB/wNAAf0DYAHgAy0BRQMvAUkDbAHrAYAB
/wOQAf8DkwH/A5MB/wOFAf8DSQH9A2AB4AMtAUUDLwFJA2wB6wGAAcUBgAH+AX8B/wF/Af8BSQH/AUkB qwGAAf4BfwH/AX8B/wFJAf8BSQH/AR8B/wEfAf8BDAH/AQwB/wEFAf8BBQH/AQMB/wEDAf8BBQH/AQUB
/wEfAf8BHwH/AQwB/wEMAf8BBQH/AQUB/wEDAf8BAwH/AQUB/wEFAf8BCgH/AQoB/wEKAf4BCgH/AQEB /wEKAf8BCgH/AQoB/gEKAf8BAQHtAQEB/wFAAbYBQAH9AWABZgFgAeADLQFFAy8BSQNsAesBgAKrAf4B
7QEBAf8BOgG2AToB/QFgAWYBYAHgAy0BRQMvAUkDbAHrAYACxQH+AX8D/wFJA/8BHwP/AQwD/wEFA/8B fwP/AUkD/wEfA/8BDAP/AQUD/wEDA/8BBQP/AQoD/wEKAv4B/wEBAu0B/wFAArYB/QFgAmYB4AMtAUUD
AwP/AQUD/wEKA/8BCgL+Af8BAQLtAf8BOgK2Af0BYAJmAeADLQFFAy8BSQNsAesCgAHFAf4CfwL/AkkC LwFJA2wB6wKAAasB/gJ/Av8CSQL/Ah8C/wIMAv8CBQL/AgMC/wIFAv8CCgL/AgoB/gH/AgEB7QH/AkAB
/wIfAv8CDAL/AgUC/wIDAv8CBQL/AgoC/wIKAf4B/wIBAe0B/wI6AbYB/QJgAWYB4AMtAUUDAwEEA1YB tgH9AmABZgHgAy0BRQMDAQQDVgGuA24B9QPZAf8DywH/A7cB/wOnAf8DnQH/A5oB/wOcAf8DnwH/A5sB
rgNuAfUD2QH/A8sB/wO3Af8DpwH/A50B/wOaAf8DnAH/A58B/wObAf8DiQH/A2gB8ANSAaMDAwEEAwMB /wOJAf8DaAHwA1IBowMDAQQDAwEEA1YBrgNuAfUBqAH/AagB/wGJAf8BiQH/AVwB/wFcAf8BNwH/ATcB
BANWAa4DbgH1AagB/wGoAf8BiQH/AYkB/wFcAf8BXAH/ATcB/wE3Af8BIgH/ASIB/wEbAf8BGwH/AR8B /wEiAf8BIgH/ARsB/wEbAf8BHwH/AR8B/wEmAf8BJgH/AR0B/wEdAf8BBQHzAQUB/wFeAWgBXgHwA1IB
/wEfAf8BJgH/ASYB/wEdAf8BHQH/AQUB8wEFAf8BXgFoAV4B8ANSAaMDAwEEAwMBBANWAa4DbgH1AagD owMDAQQDAwEEA1YBrgNuAfUBqAP/AYkD/wFcA/8BNwP/ASID/wEbA/8BHwP/ASYD/wEdA/8BBQLzAf8B
/wGJA/8BXAP/ATcD/wEiA/8BGwP/AR8D/wEmA/8BHQP/AQUC8wH/AV4CaAHwA1IBowMDAQQDAwEEA1YB XgJoAfADUgGjAwMBBAMDAQQDVgGuA24B9QKoAv8CiQL/AlwC/wI3Av8CIgL/AhsC/wIfAv8CJgL/Ah0C
rgNuAfUCqAL/AokC/wJcAv8CNwL/AiIC/wIbAv8CHwL/AiYC/wIdAv8CBQHzAf8CXgFoAfADUgGjAwMB /wIFAfMB/wJeAWgB8ANSAaMDAwEEBAADHAEnA10BxwNjAfYDiwH+A9cB/wPMAf8DwgH/A7sB/wO3Af8D
BAQAAxwBJwNdAccDcgH2A6UB/gPXAf8DzAH/A8IB/wO7Af8DtwH/A7EB/wOAAf4DaAH0A1kBvAMbASYI sQH/A4AB/gNoAfQDWQG8AxsBJggAAxwBJwNdAccDYwH2AYABqwGAAf4BpQH/AaUB/wGLAf8BiwH/AXQB
AAMcAScDXQHHAWkBdQFpAfYBgAHFAYAB/gGlAf8BpQH/AYsB/wGLAf8BdAH/AXQB/wFmAf8BZgH/AVwB /wF0Af8BZgH/AWYB/wFcAf8BXAH/AU4B/wFOAf8BWgGrAVoB/gFTAWgBUwH0AVcBWQFXAbwDGwEmCAAD
/wFcAf8BTgH/AU4B/wFNAcUBTQH+AVMBcQFTAfQBVwFZAVcBvAMbASYIAAMcAScDXQHHAWkCdQH2AYAC HAEnA10BxwNjAfYBgAKrAf4BpQP/AYsD/wF0A/8BZgP/AVwD/wFOA/8BWgKrAf4BUwJoAfQBVwJZAbwD
xQH+AaUD/wGLA/8BdAP/AWYD/wFcA/8BTgP/AU0CxQH+AVMCcQH0AVcCWQG8AxsBJggAAxwBJwNdAccC GwEmCAADHAEnA10BxwNjAfYCgAGrAf4CpQL/AosC/wJ0Av8CZgL/AlwC/wJOAv8CWgGrAf4CUwFoAfQC
aQF1AfYCgAHFAf4CpQL/AosC/wJ0Av8CZgL/AlwC/wJOAv8CTQHFAf4CUwFxAfQCVwFZAbwDGwEmDAAD VwFZAbwDGwEmDAADIQEwA1kBtgNiAe4DfQH6A74B/QPUAf8DzAH/A74B/QNqAfkDbAHrA1UBrAMfASwQ
IQEwA1kBtgNiAe4DhAH6A74B/QPUAf8DzAH/A74B/QN/AfkDbAHrA1UBrAMfASwQAAMhATADWQG2AWIB AAMhATADWQG2A2IB7gN9AfoBrgG+Aa4B/QGfAf8BnwH/AYwB/wGMAf8BTAG+AUwB/QFoAX8BaAH5AWEB
ZAFiAe4BfQGTAX0B+gGuAb4BrgH9AZ8B/wGfAf8BjAH/AYwB/wFZAb4BWQH9AWgBjAFoAfkBYQFsAWEB bAFhAesDVQGsAx8BLBAAAyEBMANZAbYDYgHuA30B+gGuAr4B/QGfA/8BjAP/AUwCvgH9AWgCfwH5AWEC
6wNVAawDHwEsEAADIQEwA1kBtgFiAmQB7gF9ApMB+gGuAr4B/QGfA/8BjAP/AVkCvgH9AWgCjAH5AWEC bAHrA1UBrAMfASwQAAMhATADWQG2A2IB7gN9AfoCrgG+Af0CnwL/AowC/wJMAb4B/QJoAX8B+QJhAWwB
bAHrA1UBrAMfASwQAAMhATADWQG2AmIBZAHuAn0BkwH6Aq4BvgH9Ap8C/wKMAv8CWQG+Af0CaAGMAfkC 6wNVAawDHwEsFAADBgEHAzYBWANVAawDZgHlA34B/AOBAfsDZQHiA1MBpwMzAVEDBgEHGAADBgEHAzYB
YQFsAesDVQGsAx8BLBQAAwYBBwM2AVgDVQGsA2YB5QORAfwDjgH7A2UB4gNTAacDMwFRAwYBBxgAAwYB WANVAawDZgHlAX4BgwF+AfwBXwGMAV8B+wNlAeIDUwGnAzMBUQMGAQcYAAMGAQcDNgFYA1UBrANmAeUB
BwM2AVgDVQGsA2YB5QF+AaoBfgH8AWMBmQFjAfsDZQHiA1MBpwMzAVEDBgEHGAADBgEHAzYBWANVAawD fgKDAfwBXwKMAfsDZQHiA1MBpwMzAVEDBgEHGAADBgEHAzYBWANVAawDZgHlAn4BgwH8Al8BjAH7A2UB
ZgHlAX4CqgH8AWMCmQH7A2UB4gNTAacDMwFRAwYBBxgAAwYBBwM2AVgDVQGsA2YB5QJ+AaoB/AJjAZkB 4gNTAacDMwFRAwYBBwwAAUIBTQE+BwABPgMAASgDAAFAAwABIAMAAQEBAAEBBgABARYAA/+BAAHgAQcB
+wNlAeIDUwGnAzMBUQMGAQcMAAFCAU0BPgcAAT4DAAEoAwABQAMAASADAAEBAQABAQYAAQEWAAP/gQAB 4AEHAeABBwHgAQcBwAEDAcABAwHAAQMBwAEDAYABAQGAAQEBgAEBAYABAVAAAYABAQGAAQEBgAEBAYAB
4AEHAeABBwHgAQcB4AEHAcABAwHAAQMBwAEDAcABAwGAAQEBgAEBAYABAQGAAQFQAAGAAQEBgAEBAYAB AQHAAQMBwAEDAcABAwHAAQMB4AEHAeABBwHgAQcB4AEHCw==
AQGAAQEBwAEDAcABAwHAAQMBwAEDAeABBwHgAQcB4AEHAeABBws=
</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">
@ -291,43 +290,43 @@
<value> <value>
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAALiUAAAJNU0Z0AUkBTAIBAQMB SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAKCUAAAJNU0Z0AUkBTAIBAQMB
AAGoAQEBqAEBASABAAEgAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABgAMAASADAAEBAQABIAYAAUB6 AAEQAQIBEAECASABAAEgAQAE/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
YQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYAFdAc4DWQG7AlABTwGbAyoBQFQAA18B0ANpAe4g YQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYQFdAc8CYAFdAc4DWQG7AlABTwGbAyoBQFQAA18B0ANiAe4g
AANhAdQDZgHp5AADWgG/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoB AANhAdQDYwHp5AADWgG/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoB
AAH/AQgBCgEAAf8DWgG/A1oBvwFDAXQBBwH/AUcBdwEMAf8BJQFHAQAB/wEOARkBAAH/DAACSwFKAYoC AAH/AQgBCgEAAf8DWgG/A1oBvwFDAXQBBwH/AUcBdwEMAf8BJQFHAQAB/wEOARkBAAH/DAACSwFKAYoC
WgFYAb0CZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoC WgFYAb0CZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoC
ZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCYwFaAekCXwFbAdMDVQGvAy4BSFQAA18B ZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCZwFdAeoCYwFaAekCXwFbAdMDVQGvAy4BSFQAA18B
0ANpAe4gAANhAdQDZgHp4AABCAEKAQAB/wEQAR4BAAH/ASEBQQEAAf8BJwFLAQAB/wEmAUoBAAH/ASEB 0ANiAe4gAANhAdQDYwHp4AABCAEKAQAB/wEQAR4BAAH/ASEBQQEAAf8BJwFLAQAB/wEmAUoBAAH/ASEB
QQEAAf8BEAEeAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEpAU4BAAH/AVoBlgEQAf8BWgGcARAB QQEAAf8BEAEeAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEpAU4BAAH/AVoBlgEQAf8BWgGcARAB
/wFzAbYBJgH/ARgBLgEAAf8MAANJAYYCWgFYAbcCZQFgAeMCZQFgAeMCZQFgAeMCZQFgAeMCagFhAeYC /wFzAbYBJgH/ARgBLgEAAf8MAANJAYYCWgFYAbcCZQFgAeMCZQFgAeMCZQFgAeMCZQFgAeMCagFhAeYC
agFeAe0CaAFTAfQBjwF3ASsB/AH/AXgBAAL/AXgBAAH/Am4BWgH1AmoBYQHmAmUBYAHjAmUBYAHjAmUB agFeAe0CaAFTAfQBfgF3ASsB/AH/AXgBAAL/AXgBAAH/Am4BWgH1AmoBYQHmAmUBYAHjAmUBYAHjAmUB
YAHjAmUBXgHiAl4BWwHNAlUBUwGqAi4BLQFGVAADXwHQA2kB7iAAA2EB1ANmAencAAEIAQoBAAH/AT0B YAHjAmUBXgHiAl4BWwHNAlUBUwGqAi4BLQFGVAADXwHQA2IB7iAAA2EB1ANjAencAAEIAQoBAAH/AT0B
cQECAf8BTAGGAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BTAF/AQ8B cQECAf8BTAGGAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BTAF/AQ8B
/wE7AWcBCAH/ASEBNAEIAf8BUwGOAQsB/wFaAZwBEAH/AXIBswEnAf8BUwGBARsB/xAAAysBQQM2AVkC /wE7AWcBCAH/ASEBNAEIAf8BUwGOAQsB/wFaAZwBEAH/AXIBswEnAf8BUwGBARsB/xAAAysBQQM2AVkC
QAE/AW4CQAE/AW4CQAE/AW4CQAE/AW4DRAF7AlEBUAGfAl0BWwHFA2IB7gH/AXgBAAL/AXgBAAH/Al8B QAE/AW4CQAE/AW4CQAE/AW4CQAE/AW4DRAF7AlEBUAGfAl0BWwHFA2IB7gH/AXgBAAL/AXgBAAH/Al8B
XQHJA0UBfAJAAT8BbgJAAT8BbgJAAT8BbgM/AW0DOwFjAzMBUgMZASJUAANfAdADaQHuIAADYQHUA2YB XQHJA0UBfAJAAT8BbgJAAT8BbgJAAT8BbgM/AW0DOwFjAzMBUgMZASJUAANfAdADYgHuIAADYQHUA2MB
6dgAARUBKQEAAf8BSgGEAQQB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B 6dgAARUBKQEAAf8BSgGEAQQB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
UgGMARAB/wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFWAZABDAH/AVoBmgEQAf8BWgGcARAB/wFyAakB UgGMARAB/wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFWAZABDAH/AVoBmgEQAf8BWgGcARAB/wFyAakB
MAH/AQgBCgEAAf8BCAEKAQAB/wwAAwwBEAMRARYDFQEcAxUBHAMVARwDFQEcAyEBMAI+AT0BaQJTAVIB MAH/AQgBCgEAAf8BCAEKAQAB/wwAAwwBEAMRARYDFQEcAxUBHAMVARwDFQEcAyEBMAI+AT0BaQJTAVIB
pQJmAV8B5QH/AXgBAAL/AXgBAAH/AlUBUwGqAyIBMQMVARwDFQEcAxUBHAMUARsDEwEZAxABFQMHAQlU pQJmAV8B5QH/AXgBAAL/AXgBAAH/AlUBUwGqAyIBMQMVARwDFQEcAxUBHAMUARsDEwEZAxABFQMHAQlU
AANfAdADaQHuIAADYQHUA2YB6dQAARYBJwECAf8BSQGAAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB AANfAdADYgHuIAADYQHUA2MB6dQAARYBJwECAf8BSQGAAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQsB/wFSAYwBEAH/AVIBjAEQAf8BWAGSARAB/wFaAZQBEAH/AVoB /wFSAYwBCAH/AVIBjAEIAf8BUgGMAQsB/wFSAYwBEAH/AVIBjAEQAf8BWAGSARAB/wFaAZQBEAH/AVoB
mwEQAf8BWgGcARAB/wFcAZ4BEgH/ATsBbQEDAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8IAAMFBAYE mwEQAf8BWgGcARAB/wFcAZ4BEgH/ATsBbQEDAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8IAAMFBAYE
CAEKAwgBCgMIAQoDCAEKAxcBHwI5ATgBXQNRAZ4CZQFgAeMB/wF4AQAC/wF4AQAB/wNSAaMDGAEhAwgB CAEKAwgBCgMIAQoDCAEKAxcBHwI5ATgBXQNRAZ4CZQFgAeMB/wF4AQAC/wF4AQAB/wNSAaMDGAEhAwgB
CgMIAQoDCAEKAwgBCgMHAQkDBgEHAwIBA1QAA18B0ANpAe4gAANhAdQDZgHp0AADXQHfAUcBgAEDAf8B CgMIAQoDCAEKAwgBCgMHAQkDBgEHAwIBA1QAA18B0ANiAe4gAANhAdQDYwHp0AADXQHfAUcBgAEDAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB/wFSAYwB UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB/wFSAYwB
EAH/AVIBjAEQAf8BWgGUARAB/wFaAZQBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8B EAH/AVIBjAEQAf8BWgGUARAB/wFaAZQBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8B
WgGYARAB/wEWASoBAAH/AQgBCgEAAf8BCAEKAQAB/xwAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgB WgGYARAB/wEWASoBAAH/AQgBCgEAAf8BCAEKAQAB/xwAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgB
AAH/AlEBUAGfAxIBGHAAA18B0ANpAe4gAANhAdQDZgHp0AABLAFQAQAB/wFKAYwBAAH/AVIBjAEIAf8B AAH/AlEBUAGfAxIBGHAAA18B0ANiAe4gAANhAdQDYwHp0AABLAFQAQAB/wFKAYwBAAH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBDgH/AVUBkgEQAf8BWgGcARAB/wFaAZwB UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBDgH/AVUBkgEQAf8BWgGcARAB/wFaAZwB
EAH/AVoBlAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8B EAH/AVoBlAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8B
WAGSARYB/wEQAR4BAAH/AQgBCgEAAf8cAAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVAB WAGSARYB/wEQAR4BAAH/AQgBCgEAAf8cAAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVAB
nwMSARhwAANfAdADaQHuIAADYQHUA2YB6dQAA0sBjwEzAV4BAAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwB nwMSARhwAANfAdADYgHuIAADYQHUA2MB6dQAA0sBjwEzAV4BAAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwB
CAH/AVIBjAEQAf8BfwHAATMB/wFfAZ4BFwH/ASABPwEAAf8BCAEKAQAB/wFSAYwBEAH/AVoBnAEQAf8B CAH/AVIBjAEQAf8BfwHAATMB/wFfAZ4BFwH/ASABPwEAAf8BCAEKAQAB/wFSAYwBEAH/AVoBnAEQAf8B
WgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGVARcB/wFaAZQBGAH/AVoBlAEYAf8BWgGUARgB/wEIAQoB WgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGVARcB/wFaAZQBGAH/AVoBlAEYAf8BWgGUARgB/wEIAQoB
AAH/AQgBCgEAAf8YAAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVABnwMSARgUAAQBAwMB AAH/AQgBCgEAAf8YAAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVABnwMSARgUAAQBAwMB
@ -343,44 +342,44 @@
AAH/AVoBlAESAf8BWgGUARgB/wFaAZQBGAH/AWMBnAEYAf8BYwGlARcB/wFrAaUBIQH/ARABHgEAAf8Y AAH/AVoBlAESAf8BWgGUARgB/wFaAZQBGAH/AWMBnAEYAf8BYwGlARcB/wFrAaUBIQH/ARABHgEAAf8Y
AAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVABnwMSARgUAAMFAQYDHQEpAzABSgI7ATwB AAMSARcDNQFWA1ABmgNiAeEB/wF4AQAC/wF4AQAB/wJRAVABnwMSARgUAAMFAQYDHQEpAzABSgI7ATwB
ZQI7ATwBZAMoATwCFQEWAR0DBgEIFAADPAFkA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8D ZQI7ATwBZAMoATwCFQEWAR0DBgEIFAADPAFkA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8D
TAGPA2cB6gN8AfgDTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPA0wBjwNrAewDbgH1A0wBjwNMAY8D TAGPA2cB6gN8AfgDTAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPA0wBjwNlAewDbgH1A0wBjwNMAY8D
TAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPAzUBVqgAA10B3wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB TAGPA0wBjwNMAY8DTAGPA0wBjwNMAY8DTAGPAzUBVqgAA10B3wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB
/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wMzAVADOgFgAUsBhQEHAf8BWgGUARAB/wFaAZwBEAH/ATkB /wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wMzAVADOgFgAUsBhQEHAf8BWgGUARAB/wFaAZwBEAH/ATkB
awEAAf8DCQEMARABHgEAAf8BVAGOARIB/wFaAZQBGAH/AWMBnAEYAf8BYwGcARgB/wFjAaUBFwH/AXMB awEAAf8DCQEMARABHgEAAf8BVAGOARIB/wFaAZQBGAH/AWMBnAEYAf8BYwGcARgB/wFjAaUBFwH/AXMB
tAEpAf8BEAEeAQAB/xgAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwkB tAEpAf8BEAEeAQAB/xgAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwkB
DAMzAVACTAFNAZECXQFfAckCWwFdAcoDRAF6AykBPQMMARA8AANfAdADaQHuIAADYQHUA2YB6cwAAQgB DAMzAVACTAFNAZECXQFfAckCWwFdAcoDRAF6AykBPQMMARA8AANfAdADYgHuIAADYQHUA2MB6cwAAQgB
CgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB CgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB
/wEIAQoBAAH/ATMBXwECAf8BWgGUARAB/wFaAZwBEAH/Aa0B5wFjAf8BEAEeAQAB/wEIAQoBAAH/ARQB /wEIAQoBAAH/ATMBXwECAf8BWgGUARAB/wFaAZwBEAH/Aa0B5wFjAf8BEAEeAQAB/wEIAQoBAAH/ARQB
JgEAAf8BVAGOARIB/wFiAZsBGAH/AWMBnAEYAf8BYwGlARcB/wFjAaUBFwH/AWMBnAEhAf8BGAEuAQAB JgEAAf8BVAGOARIB/wFiAZsBGAH/AWMBnAEYAf8BYwGlARcB/wFjAaUBFwH/AWMBnAEhAf8BGAEuAQAB
/xgAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwkBCwMuAUcDSAGEAlkB /xgAAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwkBCwMuAUcDSAGEAlkB
XAHDAlwBXwHLA0kBhQMuAUcDDwETPAADXwHQA2kB7iAAA2EB1ANmAenIAAEIAQoBAAH/ASsBUgEAAf8B XAHDAlwBXwHLA0kBhQMuAUcDDwETPAADXwHQA2IB7iAAA2EB1ANjAenIAAEIAQoBAAH/ASsBUgEAAf8B
QQFyAQUB/wFKAYQBBgH/AUoBhAEGAf8BSgGEAQYB/wFCAXMBBgH/ASkBUAEAAf8BEAEeAQAB/wEfAToB QQFyAQUB/wFKAYQBBgH/AUoBhAEGAf8BSgGEAQYB/wFCAXMBBgH/ASkBUAEAAf8BEAEeAQAB/wEfAToB
AAH/AVUBjwELAf8BWgGVARAB/wFaAZwBEAH/AdYB/wGMAf8BEAEeAQAB/wEYAS4BAAH/AUIBeAEDAf8B AAH/AVUBjwELAf8BWgGVARAB/wFaAZwBEAH/AdYB/wGMAf8BEAEeAQAB/wEYAS4BAAH/AUIBeAEDAf8B
WgGUARgB/wFiAZsBGAH/AWMBnAEYAf8BYwGlARcB/wFjAaUBFwH/AXMBtAEpAf8BEAEeAQAB/xgAAxIB WgGUARgB/wFiAZsBGAH/AWMBnAEYAf8BYwGlARcB/wFjAaUBFwH/AXMBtAEpAf8BEAEeAQAB/xgAAxIB
FwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwYBCAMkATQDPAFmA1MBpwNYAbgC FwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwYBCAMkATQDPAFmA1MBpwNYAbgC
RwFIAYMDMQFNAw8BFDwAA18B0ANpAe4gAANhAdQDZgHpxAABKAFNAQAB/wFOAYgBBwH/AVIBjAEIAf8B RwFIAYMDMQFNAw8BFDwAA18B0ANiAe4gAANhAdQDYwHpxAABKAFNAQAB/wFOAYgBBwH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFSAYwB UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFSAYwB
EAH/AVoBlAEQAf8BWgGcARAB/wFjAaUBFwH/AUIBewEAAf8BSgGEAQYB/wFSAYwBCAH/AVoBmAEUAf8B EAH/AVoBlAEQAf8BWgGcARAB/wFjAaUBFwH/AUIBewEAAf8BSgGEAQYB/wFSAYwBCAH/AVoBmAEUAf8B
WgGUARgB/wFjAZwBGAH/AWMBpQEXAf8BYwGlARcB/wFjAaUBFwH/AZQByQFNAf8EAAMQARUDGgEkAyEB WgGUARgB/wFjAZwBGAH/AWMBpQEXAf8BYwGlARcB/wFjAaUBFwH/AZQByQFNAf8EAAMQARUDGgEkAyEB
LwMRARYEAgQBAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwMBBAMTARkD LwMRARYEAgQBAxIBFwM1AVYDUAGaA2IB4QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBQAAwMBBAMTARkD
JQE2AjsBPAFkAkEBQgFyAzMBUQMhAS8DCQEMPAADXwHQA2kB7iAAA2EB1ANmAenAAAEpAU4BAgH/AVIB JQE2AjsBPAFkAkEBQgFyAzMBUQMhAS8DCQEMPAADXwHQA2IB7iAAA2EB1ANjAenAAAEpAU4BAgH/AVIB
jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMARAB
/wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFaAZUBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoB /wFSAYwBEAH/AVIBjAEQAf8BWgGUARAB/wFaAZUBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoB
nAEQAf8BWgGUARgB/wFaAZQBGAH/AWMBnAEYAf8BYwGcARgB/wFjAaUBFwH/AWMBpQEXAf8BYwGlARcB nAEQAf8BWgGUARgB/wFaAZQBGAH/AWMBnAEYAf8BYwGcARgB/wFjAaUBFwH/AWMBpQEXAf8BYwGlARcB
/wFlAaABIQH/BAADHgErAjEBMAFMAzsBYwMiATEDBwEJAwQBBQMSARgDNgFXA1ABmgNiAeEB/wF4AQAC /wFlAaABIQH/BAADHgErAjEBMAFMAzsBYwMiATEDBwEJAwQBBQMSARgDNgFXA1ABmgNiAeEB/wF4AQAC
/wF4AQAB/wJRAVABnwMSARgcAAMHAQkDHAEnAyMBMgIZARoBIwMPARQDBAEFPAADXwHQA2kB7iAAA2EB /wF4AQAB/wJRAVABnwMSARgcAAMHAQkDHAEnAyMBMgIZARoBIwMPARQDBAEFPAADXwHQA2IB7iAAA2EB
1ANmAem8AANGAYABTgGIAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B 1ANjAem8AANGAYABTgGIAQcB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFSAYwBEAH/AVoBlAEQAf8BWgGXARAB/wFaAZwB UgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFSAYwBEAH/AVoBlAEQAf8BWgGXARAB/wFaAZwB
EAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8BWgGUARgB/wFjAZwBGAH/AWMBoQEYAf8B EAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8BWgGUARgB/wFjAZwBGAH/AWMBoQEYAf8B
YwGlARcB/wFjAaUBFwH/AXgBugEsAf8BCAEKAQAB/wQAAyQBNANFAXwCWgFYAbcDTgGUAz8BbQMoATsD YwGlARcB/wFjAaUBFwH/AXgBugEsAf8BCAEKAQAB/wQAAyQBNANFAXwCWgFYAbcDTgGUAz8BbQMoATsD
HwEsAzkBXgNQAZoDYgHhAf8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYHAADEgEXAzwBZgNKAYkDQAFvAy4B HwEsAzkBXgNQAZoDYgHhAf8BeAEAAv8BeAEAAf8CUQFQAZ8DEgEYHAADEgEXAzwBZgNKAYkDQAFvAy4B
RwMOARI8AANfAdADaQHuIAADYQHUA2YB6bwAATUBXgEHAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B RwMOARI8AANfAdADYgHuIAADYQHUA2MB6bwAATUBXgEHAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQB UgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQB
EAH/AVoBlAEQAf8BWgGbARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZYBFgH/AVoBlAEYAf8B EAH/AVoBlAEQAf8BWgGbARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZYBFgH/AVoBlAEYAf8B
XQGXARgB/wFjAZwBGAH/AWMBpQEXAf8BYwGlARcB/wFwAbMBIAH/ASsBUAEEAf8IAAMnAToCUwFRAaIB XQGXARgB/wFjAZwBGAH/AWMBpQEXAf8BYwGlARcB/wFwAbMBIAH/ASsBUAEEAf8IAAMnAToCUwFRAaIB
kAFfASEB+wJlAVwB5wJcAVoBxAJDAUIBdQMxAU0CQAE/AW4CUQFQAZ8CZQFgAeMB/wF4AQAC/wF4AQAB gwFfASEB+wJlAVwB5wJcAVoBxAJDAUIBdQMxAU0CQAE/AW4CUQFQAZ8CZQFgAeMB/wF4AQAC/wF4AQAB
/wJRAVABnwMSARgcAAMZASIDTgGVAlsBXgHNAlYBVwGyA0QBegMdASkDBgEIBAIEATAAA18B0ANpAe4g /wJRAVABnwMSARgcAAMZASIDTgGVAlsBXgHNAlYBVwGyA0QBegMdASkDBgEIBAIEATAAA18B0ANiAe4g
AANhAdQDZgHpvAABSgGEAQYB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B AANhAdQDYwHpvAABSgGEAQYB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
ZwGpARcB/wFSAYwBCAH/AUoBhAEGAf8BSgGEAQYB/wFSAYwBEAH/AVoBlAEQAf8BWgGXARAB/wFaAZwB ZwGpARcB/wFSAYwBCAH/AUoBhAEGAf8BSgGEAQYB/wFSAYwBEAH/AVoBlAEQAf8BWgGXARAB/wFaAZwB
EAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8BWgGUARgB/wFfAZgBGAH/AWMBngEYAf8B EAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBlAEYAf8BWgGUARgB/wFfAZgBGAH/AWMBngEYAf8B
YwGlARcB/wFrAakBHQH/ASkBUAEAAf8MAAMiATEDTgGYAm8BYAHzAmgBXgHwAmoBYQHmAloBVwHCAlUB YwGlARcB/wFrAakBHQH/ASkBUAEAAf8MAAMiATEDTgGYAm8BYAHzAmgBXgHwAmoBYQHmAloBVwHCAlUB
@ -398,59 +397,59 @@
YgHclAABSgGMAQAB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AWcBnQEnAf8BCAEKAQAB YgHclAABSgGMAQAB/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AWcBnQEnAf8BCAEKAQAB
/wEKAQ8BAAH/AUIBdwEDAf8BUgGMARAB/wFaAZwBEAH/ASUBQQEFAf8EAAM6AWABCAEKAQAB/wEIAQoB /wEKAQ8BAAH/AUIBdwEDAf8BUgGMARAB/wFaAZwBEAH/ASUBQQEFAf8EAAM6AWABCAEKAQAB/wEIAQoB
AAH/BAADRgGAA0YBgANGAYAcAAMKAQ0DIgExAzQBVANEAXgCUQFQAZ8CYQFdAc8CagFeAe0CZQFdAewC AAH/BAADRgGAA0YBgANGAYAcAAMKAQ0DIgExAzQBVANEAXgCUQFQAZ8CYQFdAc8CagFeAe0CZQFdAewC
aAFeAfABkAFfASEB+wH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBwAAwYBCAMYASEDMQFNAkwBTQGRAlgB aAFeAfABgwFfASEB+wH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBwAAwYBCAMYASEDMQFNAkwBTQGRAlgB
WgG9Al0BYQHRAlkBXAHDAlEBUwGiAz0BZwIZARoBIwMMAQ8DQAFvA1UBrQNVAa0DVQGtA1UBrQNVAa0D WgG9Al0BYQHRAlkBXAHDAlEBUwGiAz0BZwIZARoBIwMMAQ8DQAFvA1UBrQNVAa0DVQGtA1UBrQNVAa0D
VQGtA1UBrQNVAa0DVQGtA2gB8AN9AfoDVQGtA1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNwAfED VQGtA1UBrQNVAa0DVQGtA2gB8AN9AfoDVQGtA1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNwAfED
fAH4A1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA0cBgZQAATsBbQECAf8BUgGMAQgB fAH4A1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA1UBrQNVAa0DVQGtA0cBgZQAATsBbQECAf8BUgGMAQgB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFTAY4BEAH/AQ8BGwEAAf8BIwFDAQAB/wFSAYwBEAH/AVIB /wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFTAY4BEAH/AQ8BGwEAAf8BIwFDAQAB/wFSAYwBEAH/AVIB
jAEQAf8BewG9ATAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB jAEQAf8BewG9ATAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoBAAH/AQgBCgEAAf8BCAEKAQAB
/wEIAQoBAAH/KAADAwEEAwkBCwMiATEDOAFbAk8BTgGXAl8BXAHIAmYBXwHlAXcBbwFRAfcBvgFSAUAB /wEIAQoBAAH/KAADAwEEAwkBCwMiATEDOAFbAk8BTgGXAl8BXAHIAmYBXwHlAm8BUQH3Ab4BRQFAAf0B
/QH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGBwABAEDAgEDAxUBHAM1AVYCSwFMAY8CXAFfAcgCXQFjAd8C /wF4AQAC/wF4AQAB/wJRAVABnwMSARgcAAQBAwIBAwMVARwDNQFWAksBTAGPAlwBXwHIAl0BYwHfAmAB
YAFlAeMCUgFTAaUDNAFTAxwBJygAA18B0ANpAe4gAANhAdQDZgHpvAABCAEKAQAB/wFSAYwBCAH/AVIB ZQHjAlIBUwGlAzQBUwMcAScoAANfAdADYgHuIAADYQHUA2MB6bwAAQgBCgEAAf8BUgGMAQgB/wFSAYwB
jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8BTgGCAQ8B/wFKAYQBBgH/AVIBjAEQAf8BYwGlARcB CAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AU4BggEPAf8BSgGEAQYB/wFSAYwBEAH/AWMBpQEXAf8B
/wEIAQoBAAH/AQgBCgEAAf8BEwEjAQAB/wFCAXMBBgH/ASABPwEAAf8BCAEKAQAB/wEIAQoBAAH/AQgB CAEKAQAB/wEIAQoBAAH/ARMBIwEAAf8BQgFzAQYB/wEgAT8BAAH/AQgBCgEAAf8BCAEKAQAB/wEIAQoB
CgEAAf8BCAEKAQAB/wEIAQoBAAH/IAAEAQMDAQQDDAEQAxgBIAMpAT0DPgFqAlcBVgG1AmYBXwHlAXcB AAH/AQgBCgEAAf8BCAEKAQAB/yAABAEDAwEEAwwBEAMYASADKQE9Az4BagJXAVYBtQJmAV8B5QJvAVEB
bwFRAfcB/wF4AQAC/wF4AQAB/wJRAVABnwMSARggAAQBAwcBCQMUARsDKgFAA0MBdgNVAa8CWgFjAekC 9wH/AXgBAAL/AXgBAAH/AlEBUAGfAxIBGCAABAEDBwEJAxQBGwMqAUADQwF2A1UBrwJaAWMB6QJbAV8B
WwFfAdACUAFRAZ8DMwFRKAADXwHQA2kB7iAAA2EB1ANmAenAAAFCAXMBBgH/AVIBjAEIAf8BUgGMAQgB 0AJQAVEBnwMzAVEoAANfAdADYgHuIAADYQHUA2MB6cAAAUIBcwEGAf8BUgGMAQgB/wFSAYwBCAH/AVIB
/wFSAYwBCAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBEAH/AVIBjAEQAf8BSgGEAQYB/wEQAR4BAAH/ATEB jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFKAYQBBgH/ARABHgEAAf8BMQFaAQAB
WgEAAf8BTQGEAQ0B/wFaAZwBEAH/AVoBnAEQAf8BUgGMAQgB/wEQAR4BAAH/AQgBCgEAAf8BCAEKAQAB /wFNAYQBDQH/AVoBnAEQAf8BWgGcARAB/wFSAYwBCAH/ARABHgEAAf8BCAEKAQAB/wEIAQoBAAH/A0YB
/wNGAYAsAAMCAQMDCwEOAyABLgNDAXYCVwFVAbECZgFgAeABZwFjAUgB9gGDAWoBQQH5AlEBUAGcAxIB gCwAAwIBAwMLAQ4DIAEuA0MBdgJXAVUBsQJmAWAB4AJjAUgB9gFtAWoBQQH5AlEBUAGcAxIBFwwABAED
FwwABAEDDAEQAxcBHwMdASgDHQEoAwwBDwgAAw8BFAImAScBOQJHAUgBgwNiAeECXAFlAecDXgHSAj8B DAEQAxcBHwMdASgDHQEoAwwBDwgAAw8BFAImAScBOQJHAUgBgwNiAeECXAFlAecDXgHSAj8BQAFuKAAD
QAFuKAADXwHQA2kB7iAAA2EB1ANmAenEAAFJAYkBAAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIB XwHQA2IB7iAAA2EB1ANjAenEAAFJAYkBAAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEIAf8B
jAEIAf8BUgGMARAB/wFSAYwBEAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoBnAEQAf8BWgGcARAB UgGMARAB/wFSAYwBEAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwB
/wFaAZwBEAH/AVoBnAEQAf8BWgGUARgB/wFCAXMBBgH/AQoBDgEAAf8wAAQBAwMBBAMMAQ8DHgEqAzoB EAH/AVoBnAEQAf8BWgGUARgB/wFCAXMBBgH/AQoBDgEAAf8wAAQBAwMBBAMMAQ8DHgEqAzoBYQJZAVcB
YQJZAVcBuQJjAVsB5ANiAe4CTwFOAZcDEgEXDAADAgEDAyEBLwM2AVkCQgFDAXUCQQFCAXMDHgErCAAD uQJjAVsB5ANiAe4CTwFOAZcDEgEXDAADAgEDAyEBLwM2AVkCQgFDAXUCQQFCAXMDHgErCAADBAEFAwwB
BAEFAwwBDwM4AVwCXQFgAc4CXQFlAewCWQFnAe8DRgF/KAADXwHQA2kB7iAAA2EB1ANmAenIAAE6AWsB DwM4AVwCXQFgAc4CXQFlAewCWQFnAe8DRgF/KAADXwHQA2IB7iAAA2EB1ANjAenIAAE6AWsBAAH/AVIB
AAH/AVIBjAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoBlAEQAf8B jAEIAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoBlAEQAf8BWgGcARAB
WgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZQBGAH/AVoBlAEYAf8BYwGlARcB/wEZASwB /wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZQBGAH/AVoBlAEYAf8BYwGlARcB/wEZASwBAwH/OAAD
AwH/OAADAgEDAwgBCgMgAS0CQQFAAXECTQFMAZEDUAGaAjsBOgFiAwwBDwwAAwMBBAMrAUEDRAF6A1MB AgEDAwgBCgMgAS0CQQFAAXECTQFMAZEDUAGaAjsBOgFiAwwBDwwAAwMBBAMrAUEDRAF6A1MBpwNVAa0D
pwNVAa0DNwFaAxgBIAMJAQsDBgEHAw0BEQM5AV8CXQFhAdECWQFnAe8CYAFvAfMCRgFHAYEoAANfAdAD NwFaAxgBIAMJAQsDBgEHAw0BEQM5AV8CXQFhAdECWQFnAe8CYAFvAfMCRgFHAYEoAANfAdADYgHuIAAD
aQHuIAADYQHUA2YB6cwAASwBVQEAAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQB YQHUA2MB6cwAASwBVQEAAf8BUgGMAQgB/wFSAYwBCAH/AVIBjAEQAf8BUgGMARAB/wFaAZQBEAH/AVoB
EAH/AVoBlAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZQBGAH/AVoBlAEYAf8B lAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZQBGAH/AVoBlAEYAf8BIQFBAQAB
IQFBAQAB/0AABAIDBwEJAxAEFQEcAhYBFQEdAw8BEwMCAQMMAAMDAQQDMANLAUwBjwJbAV0BygJbAWEB /0AABAIDBwEJAxAEFQEcAhYBFQEdAw8BEwMCAQMMAAMDAQQDMANLAUwBjwJbAV0BygJbAWEB3gNOAZQD
3gNOAZQDNAFTAxUBHAMNAREDHgErAkMBRAF3Al0BYwHfAmUBcAHxAloBYwHpA0QBeygAA18B0ANpAe4g NAFTAxUBHAMNAREDHgErAkMBRAF3Al0BYwHfAmUBcAHxAloBYwHpA0QBeygAA18B0ANiAe4gAANhAdQD
AANhAdQDZgHpyAABEgEiAQAB/wFMAYYBBgH/AVIBjAEIAf8BWgGcARAB/wFQAYoBCAH/AVIBjAEQAf8B YwHpyAABEgEiAQAB/wFMAYYBBgH/AVIBjAEIAf8BWgGcARAB/wFQAYoBCAH/AVIBjAEQAf8BWgGUARAB
WgGUARAB/wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAERAf8BaQGrAR8B/wEIAQoB /wFaAZwBEAH/AVoBnAEQAf8BWgGcARAB/wFaAZwBEAH/AVoBnAERAf8BaQGrAR8B/wEIAQoBAAH/bAAD
AAH/bAADAgEDAyEBLwM6AWEDVQGtAl8BYwHaA1oBvwJQAVEBnwNEAXoCQQFCAXIDSQGFAlYBVwGyAl4B AgEDAyEBLwM6AWEDVQGtAl8BYwHaA1oBvwJQAVEBnwNEAXoCQQFCAXIDSQGFAlYBVwGyAl4BagHtAmAB
agHtAmABZAHbAlUBVwGxAzgBWygAA18B0ANpAe4gAANhAdQDZgHpyAABEAEeAQAB/wFIAX0BBwH/AVkB ZAHbAlUBVwGxAzgBWygAA18B0ANiAe4gAANhAdQDYwHpyAABEAEeAQAB/wFIAX0BBwH/AVkBkwETAf8B
kwETAf8BVQGIARgB/wgAASEBQQEAAf8BQgF7AQAB/wFSAYwBCAH/AVUBkQEIAf8BOQFmAQQB/wNZAe94 VQGIARgB/wgAASEBQQEAAf8BQgF7AQAB/wFSAYwBCAH/AVUBkQEIAf8BOQFmAQQB/wNZAe94AAMGAQgD
AAMGAQgDFwEfA0QBewJZAVwBxgJbAWMB5AJTAWgB9ANiAe4DYgHuAmUBcAHxAkgBZwH2AisBjwH8AlkB FwEfA0QBewJZAVwBxgJbAWMB5AJTAWgB9ANiAe4DYgHuAmUBcAHxAkgBYwH2AisBfgH8AlkBXAG+Az4B
XAG+Az4BawMkATQoAANfAdADaQHuIAADYQHUA2YB6dAAAR4BOgEAAf+cAAQCAwkBDAMqAz8BQAFuA04B awMkATQoAANfAdADYgHuIAADYQHUA2MB6dAAAR4BOgEAAf+cAAQCAwkBDAMqAz8BQAFuA04BlgJWAVgB
lgJWAVgBswJZAVwBwQJaAV0BxwJaAV0BxwJXAVkBuQJRAVIBpAJAAUEBcQMlATYDEwEZKAADXwHQA2kB swJZAVwBwQJaAV0BxwJaAV0BxwJXAVkBuQJRAVIBpAJAAUEBcQMlATYDEwEZKAADXwHQA2IB7iAAA2EB
7iAAA2EB1ANmAen/AHkABAEDCwEOAyYBOAM5AV4DRgF9AkoBSwGLA0oBiQM+AWsDKgE/AxcBHwMCAQMs 1ANjAen/AHkABAEDCwEOAyYBOAM5AV4DRgF9AkoBSwGLA0oBiQM+AWsDKgE/AxcBHwMCAQMsAANTAaID
AANTAaIDWgG/IAADVAGmA1kBu6gAAUIBTQE+BwABPgMAASgDAAGAAwABIAMAAQEBAAEBBgABAhYAA/8B WgG/IAADVAGmA1kBu6gAAUIBTQE+BwABPgMAASgDAAGAAwABIAMAAQEBAAEBBgABAhYAA/8BAAP/AccC
AAP/AccCAAEHAv8BzwHzAf8EAAL/Ac8BhwIAAQcC/wHPAfMB/wQAAf8B/gEAAQcCAAEHAv8BzwHzAf8E AAEHAv8BzwHzAf8EAAL/Ac8BhwIAAQcC/wHPAfMB/wQAAf8B/gEAAQcCAAEHAv8BzwHzAf8EAAH/AfwB
AAH/AfwBAAEHAgABBwL/Ac8B8wH/BAAB/wH4AQABDwIAAQcC/wHPAfMB/wQAAf8B8AEAAQcCAAEHAv8B AAEHAgABBwL/Ac8B8wH/BAAB/wH4AQABDwIAAQcC/wHPAfMB/wQAAf8B8AEAAQcCAAEHAv8BzwHzAf8E
zwHzAf8EAAH/AeABAAEDAgABBwL/Ac8B8wH/BAAB/wHAAQABAQH8AQMD/wHPAfMB/wQAAf8BwAEAAQEB AAH/AeABAAEDAgABBwL/Ac8B8wH/BAAB/wHAAQABAQH8AQMD/wHPAfMB/wQAAf8BwAEAAQEB/AEDA/8B
/AEDA/8BzwHzAf8EAAH/AeACAAH8AQMB4AEfCAAB/wH8ASABAAH8AQMB4AEfCAAB/wH+AeABAAH8AQMB zwHzAf8EAAH/AeACAAH8AQMB4AEfCAAB/wH8ASABAAH8AQMB4AEfCAAB/wH+AeABAAH8AQMB4AEfCAAB
4AEfCAAB/wHAAgAB/AEDAeABHwH/Ac8B8wH/BAAB/wGAAgAB/AEDAeABHwH/Ac8B8wH/BAAB/wMAAfwB /wHAAgAB/AEDAeABHwH/Ac8B8wH/BAAB/wGAAgAB/AEDAeABHwH/Ac8B8wH/BAAB/wMAAfwBAwHgAR8B
AwHgAR8B/wHPAfMB/wQAAf4CAAEBAQABAwHgAR8B/wHPAfMB/wQAAfwCAAEBAQABAwH4AR8B/wHPAfMB /wHPAfMB/wQAAf4CAAEBAQABAwHgAR8B/wHPAfMB/wQAAfwCAAEBAQABAwH4AR8B/wHPAfMB/wQAAfgC
/wQAAfgCAAEBAQABAwH4AR8B/wHPAfMB/wQAAfgCAAEDAQABAwH4AQMB/wHPAfMB/wQAAfgCAAEHAQAB AAEBAQABAwH4AR8B/wHPAfMB/wQAAfgCAAEDAQABAwH4AQMB/wHPAfMB/wQAAfgCAAEHAQABAwH4AQMI
AwH4AQMIAAH4AgABDwEAAQMB+AkAAfgBAAFEAX8BAAEDAfgJAAH4AQABAQH/AYABAwH4AQAB/wHPAfMB AAH4AgABDwEAAQMB+AkAAfgBAAFEAX8BAAEDAfgJAAH4AQABAQH/AYABAwH4AQAB/wHPAfMB/wQAAfgC
/wQAAfgCAAF/AYABAwH8AQAB/wHPAfMB/wQAAfwCAAF/AfABAwGBAYAB/wHPAfMB/wQAAf4CAAH/AfAB AAF/AYABAwH8AQAB/wHPAfMB/wQAAfwCAAF/AfABAwGBAYAB/wHPAfMB/wQAAf4CAAH/AfABAwGBAYAB
AwGBAYAB/wHPAfMB/wQAAf8CAAH/AfwBAwGAAQAB/wHPAfMB/wQAAf8BgAEBAf8B/gEDAYABAAH/Ac8B /wHPAfMB/wQAAf8CAAH/AfwBAwGAAQAB/wHPAfMB/wQAAf8BgAEBAf8B/gEDAYABAAH/Ac8B8wH/BAAB
8wH/BAAB/wEAAQMD/wGAAQAB/wHPAfMB/wQAAf8BDAEPA/8BwAEAAf8BzwHzAf8EAAH/Ad8E/wHAAQAB /wEAAQMD/wGAAQAB/wHPAfMB/wQAAf8BDAEPA/8BwAEAAf8BzwHzAf8EAAH/Ad8E/wHAAQAB/wHPAfMB
/wHPAfMB/wQABv8B8AEBAf8BzwHzAf8EAAs= /wQABv8B8AEBAf8BzwHzAf8EAAs=
</value> </value>
</data> </data>
<metadata name="ilStoreThumbnails.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="ilStoreThumbnails.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@ -464,7 +463,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAshIAAAJNU0Z0AUkBTAIBAQQB SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAshIAAAJNU0Z0AUkBTAIBAQQB
AAGoAQIBqAECARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAASADAAEBAQABIAYAASD/ AAEQAQMBEAEDARABAAEQAQAE/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
@ -490,7 +489,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/wKAAVUB/gFcAlkBxgNXAbUDFgEeCAAB/wGyAW8B/wH9AYABEQH/AfwB AAH/AagBeAEAAf8BlQFqAQAB/wKAAWIB/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
@ -629,6 +628,12 @@
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>90</value>
</metadata> </metadata>

View File

@ -37,13 +37,11 @@
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(); pCurrency = 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(); pCurrency.SuspendLayout();
@ -52,9 +50,9 @@
// pbUserPfp // pbUserPfp
// //
pbUserPfp.Image = Properties.Resources.DefaultPfp; pbUserPfp.Image = Properties.Resources.DefaultPfp;
pbUserPfp.Location = new Point(13, 11); pbUserPfp.Location = new Point(9, 5);
pbUserPfp.Name = "pbUserPfp"; pbUserPfp.Name = "pbUserPfp";
pbUserPfp.Size = new Size(128, 128); pbUserPfp.Size = new Size(139, 138);
pbUserPfp.SizeMode = PictureBoxSizeMode.StretchImage; pbUserPfp.SizeMode = PictureBoxSizeMode.StretchImage;
pbUserPfp.TabIndex = 2; pbUserPfp.TabIndex = 2;
pbUserPfp.TabStop = false; pbUserPfp.TabStop = false;
@ -148,17 +146,6 @@
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;
@ -213,7 +200,6 @@
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);
@ -230,7 +216,6 @@
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();
@ -249,7 +234,6 @@
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;

View File

@ -1,24 +1,25 @@
using QtCNETAPI.Dtos.User; using qtc_net_client_2.ClientModel;
using QtCNETAPI.Services.ApiService; using qtc_net_client_2.Properties;
using qtc_net_client_2.Services;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Models; using QtCNETAPI.Models;
using QtCNETAPI.Schema;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
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;
using qtc_net_client_2.Services;
using QtCNETAPI.Schema;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
namespace qtc_net_client_2.Forms namespace qtc_net_client_2.Forms
{ {
@ -29,13 +30,16 @@ 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) public Profile(UserInformationDto userInfo, ServiceResponse<byte[]>? pfp, List<Contact> contacts, IApiService apiService, IGatewayService gatewayService, byte[]? cosmetic = null)
{ {
_userInformationDto = userInfo; _userInformationDto = userInfo;
_apiService = apiService; _apiService = apiService;
_gatewayService = gatewayService; _gatewayService = gatewayService;
pfpRes = pfp; pfpRes = pfp;
cosmeticRes = cosmetic;
contactsList = contacts; contactsList = contacts;
InitializeComponent(); InitializeComponent();
@ -47,53 +51,45 @@ namespace qtc_net_client_2.Forms
lblCurrencyAmount.Text = _userInformationDto.CurrencyAmount.ToString("N0"); lblCurrencyAmount.Text = _userInformationDto.CurrencyAmount.ToString("N0");
rtxtBio.Text = _userInformationDto.Bio; rtxtBio.Text = _userInformationDto.Bio;
pbUserPfp.Location = new(13, 11); Bitmap? pfp = null;
pbUserPfp.Size = new(128, 128);
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))
{ {
pbUserPfp.Image = new Bitmap(ms); pfp = new Bitmap(ms);
} }
} }
var userStatus = (UserStatus)_userInformationDto.Status; var userStatus = (UserStatus)_userInformationDto.Status;
Bitmap precenseImage = Resources.OnlineIcon;
switch (userStatus) switch (userStatus)
{ {
case UserStatus.Online: case UserStatus.Online:
pbUserStatus.Image = Resources.OnlineIcon; precenseImage = Resources.OnlineIcon;
break; break;
case UserStatus.Away: case UserStatus.Away:
pbUserStatus.Image = Resources.AwayIcon; precenseImage = Resources.AwayIcon;
break; break;
case UserStatus.DoNotDisturb: case UserStatus.DoNotDisturb:
pbUserStatus.Image = Resources.DNDIcon; precenseImage = Resources.DNDIcon;
break; break;
case UserStatus.Offline: case UserStatus.Offline:
pbUserStatus.Image = Resources.OfflineIcon; precenseImage = Resources.OfflineIcon;
break; break;
} }
if (_userInformationDto.ProfileCosmeticId != 0) Bitmap? cosmetic = null;
if(cosmeticRes != null)
{ {
var res = await _apiService.GetStoreItem(_userInformationDto.ProfileCosmeticId); using var ms = new MemoryStream(cosmeticRes);
if (res != null && res.Success && res.Data != null) cosmetic = new Bitmap(ms);
{
var client = new HttpClient();
var response = await client.GetAsync(res.Data.AssetUrl);
if (response.IsSuccessStatusCode)
{
using (var stream = await response.Content.ReadAsStreamAsync())
{
CombineProfileImageWithCosmetic(pbUserPfp.Image, new Bitmap(stream));
}
response.Dispose();
}
client.Dispose();
}
} }
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;
@ -243,22 +239,30 @@ namespace qtc_net_client_2.Forms
frmDirectMessage.Show(); frmDirectMessage.Show();
} }
private void CombineProfileImageWithCosmetic(Image pfp, Bitmap cosmetic) private void CreateProfileImage(Bitmap precenseImage, Bitmap? pfp = null, Bitmap? cosmetic = null)
{ {
cosmetic.MakeTransparent();
Bitmap combined = new Bitmap(139, 138); Bitmap combined = new Bitmap(139, 138);
using (Graphics g = Graphics.FromImage(combined)) using Graphics g = Graphics.FromImage(combined);
{ g.Clear(Color.Transparent);
g.Clear(Color.Transparent); g.CompositingMode = CompositingMode.SourceOver;
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
if (pfp != null)
{
pfp.MakeTransparent();
g.DrawImage(pfp, 4, 6, 128, 128); 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); g.DrawImage(cosmetic, 0, 0, 139, 138);
} }
pbUserPfp.Location = new(9, 5); precenseImage.MakeTransparent();
pbUserPfp.Size = new(139, 138); g.DrawImage(precenseImage, 104, 0, 35, 35);
pbUserPfp.Image = combined; pbUserPfp.Image = combined;
} }
} }

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -52,7 +52,7 @@
pStockManagement.Controls.Add(btnSell); pStockManagement.Controls.Add(btnSell);
pStockManagement.Controls.Add(btnBuy); pStockManagement.Controls.Add(btnBuy);
pStockManagement.Controls.Add(lblStockCount); pStockManagement.Controls.Add(lblStockCount);
pStockManagement.Location = new Point(242, 134); pStockManagement.Location = new Point(230, 138);
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(9, 7); pMarketStatus.Location = new Point(3, 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(498, 3); pbDollarSignRight.Location = new Point(500, 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(6, 3); pbDollarSignLeft.Location = new Point(3, 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(282, 109); btnRefresh.Location = new Point(270, 113);
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(606, 227); ClientSize = new Size(595, 227);
Controls.Add(btnRefresh); Controls.Add(btnRefresh);
Controls.Add(pMarketStatus); Controls.Add(pMarketStatus);
Controls.Add(pStockManagement); Controls.Add(pStockManagement);

View File

@ -1,6 +1,6 @@
using qtc_net_client_2.Services; using QtCNETAPI.Dtos.User;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Schema; using QtCNETAPI.Schema;
using QtCNETAPI.Services;
using QtCNETAPI.Services.ApiService; using QtCNETAPI.Services.ApiService;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -1,5 +1,6 @@
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;
@ -52,8 +53,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"); IApiService api = new ApiService($"{clientConfig.ServerUrl}/api", loggingService);
IGatewayService gateway = new GatewayService($"{clientConfig.ServerUrl}/chat", api); IGatewayService gateway = new GatewayService($"{clientConfig.ServerUrl}/chat", api, loggingService);
// 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.1. /// Looks up a localized string similar to 6.3.4.
/// </summary> /// </summary>
internal static string AssemblyVersion { internal static string AssemblyVersion {
get { get {

View File

@ -118,9 +118,6 @@
<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>
@ -157,8 +154,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="DefaultPfp" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="AddContactIcon" 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>..\Icons\AddContactIcon.png;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>
@ -176,7 +173,10 @@
<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.1</value> <value>6.3.4</value>
</data>
<data name="cobalt_sittingatputer" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\cobalt_sittingatputer.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
<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="cobalt_sittingatputer" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="DefaultPfp" 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> <value>..\Resources\DefaultPfp.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: 62 KiB

After

Width:  |  Height:  |  Size: 13 KiB