Add project files.

This commit is contained in:
Alan Moon 2025-06-19 23:24:39 -07:00
parent 157f2d722d
commit 85362e9823
90 changed files with 6669 additions and 0 deletions

View File

@ -0,0 +1,8 @@
namespace QtCNETAPI.Dtos.Room
{
public class RoomDto
{
public string Name { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = new DateTime();
}
}

View File

@ -0,0 +1,10 @@
namespace QtCNETAPI.Dtos.User
{
public class UserDto
{
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public DateTime DateOfBirth { get; set; } = new DateTime();
}
}

View File

@ -0,0 +1,15 @@
namespace QtCNETAPI.Dtos.User
{
public class UserInformationDto
{
public string Id { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public string ProfilePicture { get; set; } = string.Empty;
public string Bio { get; set; } = string.Empty;
public string Role { get; set; } = string.Empty;
public DateTime DateOfBirth { get; set; } = new DateTime();
public DateTime CreatedAt { get; set; } = new DateTime();
public int Status { get; set; } = 0;
public int CurrencyAmount { get; set; } = 0;
}
}

View File

@ -0,0 +1,9 @@
namespace QtCNETAPI.Dtos.User
{
public class UserLoginDto
{
public string Email { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public bool RememberMe { get; set; } = false;
}
}

View File

@ -0,0 +1,8 @@
namespace QtCNETAPI.Dtos.User
{
public class UserRefreshLoginDto
{
public string Email { get; set; } = string.Empty;
public string RefreshToken { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,8 @@
namespace QtCNETAPI.Dtos.User
{
public class UserStatusDto
{
public string Id { get; set; } = string.Empty;
public int Status { get; set; } = 0;
}
}

View File

@ -0,0 +1,10 @@
namespace QtCNETAPI.Dtos.User
{
public class UserUpdateInformationDto
{
public string Id { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public string Bio { get; set; } = string.Empty;
public DateTime DateOfBirth { get; set; } = new DateTime();
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Events
{
public class ClientFunctionEventArgs : EventArgs
{
public required string Function { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Events
{
public class DirectMessageEventArgs : EventArgs
{
public required Message Message { get; set; }
public required UserInformationDto User { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using QtCNETAPI.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Events
{
public class ServerConfigEventArgs : EventArgs
{
public required ServerConfig ServerConfig { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Events
{
public class ServerConnectionClosedEventArgs : EventArgs
{
public Exception? Error { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Events
{
public class ServerConnectionReconnectingEventArgs : EventArgs
{
public Exception? Error { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Events
{
public class ServerMessageEventArgs : EventArgs
{
public required string Message { get; set; }
}
}

View File

@ -0,0 +1,16 @@
namespace QtCNETAPI.Models
{
public class Contact
{
public string Id { get; set; } = string.Empty;
public string OwnerId { get; set; } = string.Empty;
public string UserId { get; set; } = string.Empty;
public ContactStatus OwnerStatus { get; set; } = ContactStatus.AwaitingApprovalFromOther;
public ContactStatus UserStatus { get; set; } = ContactStatus.AwaitingApprovalFromSelf;
public virtual User? Owner { get; }
public virtual User? User { get; }
public enum ContactStatus { AwaitingApprovalFromOther = 0, AwaitingApprovalFromSelf = 1, Accepted = 2 }
}
}

View File

@ -0,0 +1,10 @@
namespace QtCNETAPI.Models
{
public class Message
{
public string Id { get; set; } = string.Empty;
public string AuthorId { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = new DateTime();
}
}

View File

@ -0,0 +1,12 @@
namespace QtCNETAPI.Models
{
public class RefreshToken
{
public string ID { get; set; } = string.Empty;
public string UserID { get; set; } = string.Empty;
public string Token { get; set; } = string.Empty;
public DateTime Expires { get; set; }
public virtual User User { get; } = null!;
}
}

10
QtCNETAPI/Models/Room.cs Normal file
View File

@ -0,0 +1,10 @@
namespace QtCNETAPI.Models
{
public class Room
{
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string CreatorId { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = new DateTime();
}
}

View File

@ -0,0 +1,11 @@
namespace QtCNETAPI.Models
{
public class ServerConfig
{
public string? Name { get; set; }
public string? Description { get; set; }
public string? AdminUserId { get; set; }
public bool IsDown { get; set; }
public string? IsDownMessage { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace QtCNETAPI.Models
{
public class ServiceResponse<T>
{
public T? Data { get; set; }
public bool Success { get; set; } = false;
public string Message { get; set; } = string.Empty;
}
}

22
QtCNETAPI/Models/User.cs Normal file
View File

@ -0,0 +1,22 @@
namespace QtCNETAPI.Models
{
public class User
{
public string Id { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public string ProfilePicture { get; set; } = string.Empty;
public string Bio { get; set; } = string.Empty;
public string Role { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public DateTime DateOfBirth { get; set; }
public DateTime CreatedAt { get; set; }
public int Status { get; set; } = 0;
public int CurrencyAmount { get; set; } = 0;
public DateTime LastCurrencySpin { get; set; }
public virtual IEnumerable<RefreshToken>? RefreshTokens { get; }
public virtual IEnumerable<Contact>? ContactsMade { get; }
public virtual IEnumerable<Contact>? ContactsList { get; }
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.5" />
<PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.10.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,570 @@
using QtCNETAPI.Dtos.Room;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Models;
using RestSharp;
using System.IdentityModel.Tokens.Jwt;
using System.Text.Json;
namespace QtCNETAPI.Services.ApiService
{
public class ApiService : IApiService
{
private User? user;
private RestClient _client;
internal string? sessionToken;
internal string apiUri;
public string? SessionToken
{
get { return sessionToken; }
set { sessionToken = value; }
}
public User? CurrentUser
{
get { return user; }
}
public ApiService(string apiUrl)
{
apiUri = apiUrl;
_client = new RestClient(apiUri);
}
public async Task<ServiceResponse<string>> PingServerAsync()
{
var serviceResponse = new ServiceResponse<string>();
try
{
var request = new RestRequest("general/ping");
var response = await _client.GetAsync<string>(request);
if (response != null)
{
if (response != "Pong!") serviceResponse.Success = false;
else serviceResponse.Success = true;
}
else serviceResponse.Success = false;
} catch (HttpRequestException ex)
{
serviceResponse.Success = false;
serviceResponse.Message = ex.Message;
}
return serviceResponse;
}
public async Task<ServiceResponse<List<UserInformationDto>>> GetOnlineUsersAsync()
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<List<UserInformationDto>>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var request = new RestRequest("users/users-online")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.GetAsync<ServiceResponse<List<UserInformationDto>>>(request);
if (response != null || response!.Data != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
} else
{
serviceResponse.Success = false;
serviceResponse.Message = "API didn't respond with online users.";
}
return serviceResponse;
}
public async Task<ServiceResponse<UserInformationDto>> GetUserInformationAsync(string id)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<UserInformationDto>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var request = new RestRequest($"users/user-info")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("id", id);
var response = await _client.GetAsync<ServiceResponse<UserInformationDto>>(request);
if(response != null || response!.Data != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
} else
{
serviceResponse.Success = false;
serviceResponse.Message = "API did not return user information.";
}
return serviceResponse;
}
public async Task<ServiceResponse<UserInformationDto>> UpdateUserInformationAsync(UserUpdateInformationDto request)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<UserInformationDto>();
var restRequest = new RestRequest("users/update")
.AddJsonBody(request)
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.PutAsync<ServiceResponse<UserInformationDto>>(restRequest);
if(response != null || response!.Data != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
// update currentuser model
CurrentUser!.Username = response.Data!.Username;
CurrentUser.Bio = response.Data.Bio;
CurrentUser.DateOfBirth = response.Data.DateOfBirth;
} else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded.";
}
return serviceResponse;
}
public async Task<ServiceResponse<string>> UpdateUserProfilePic(string filePath)
{
var serviceResponse = new ServiceResponse<string>();
try
{
var restRequest = new RestRequest($"users/upload-profile-pic")
.AddQueryParameter("userId", CurrentUser!.Id)
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddFile("file", filePath);
var response = await _client.PostAsync<ServiceResponse<string>>(restRequest);
if (response != null && response.Success != false)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
else
{
serviceResponse.Success = false;
serviceResponse.Data = "Upload Failed.";
}
} catch (JsonException)
{
serviceResponse.Success = false;
serviceResponse.Message = "Profile Pictures Can Only Be Less Then 3 MB In Size.";
} catch (HttpRequestException ex)
{
serviceResponse.Success = false;
serviceResponse.Data = ex.Message;
}
return serviceResponse;
}
public async Task<ServiceResponse<byte[]>> GetUserProfilePic(string userId)
{
var serviceResponse = new ServiceResponse<byte[]>();
try
{
var restRequest = new RestRequest($"users/profile-pic/{userId}")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.GetAsync(restRequest);
if (response != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.RawBytes;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "No Profile Picture Received.";
}
return serviceResponse;
} catch (HttpRequestException ex)
{
serviceResponse.Success = false;
serviceResponse.Message = ex.Message;
return serviceResponse;
}
}
public async Task<ServiceResponse<User>> LoginAsync(UserLoginDto userLoginDto)
{
var serviceResponse = new ServiceResponse<User>();
try
{
if (string.IsNullOrWhiteSpace(userLoginDto.Email) || string.IsNullOrWhiteSpace(userLoginDto.Password))
{
serviceResponse.Success = false;
serviceResponse.Message = "Email or Password cannot be null.";
}
var request = new RestRequest("auth/login")
.AddJsonBody(userLoginDto);
var response = await _client.PostAsync<ServiceResponse<string>>(request);
if (response != null)
{
if (response.Data != null && response.Success)
{
SessionToken = response.Data!;
await File.WriteAllTextAsync("./session.token", response.Message);
var user = await SetCurrentUser();
serviceResponse.Success = true;
if (response.Message != null) serviceResponse.Message = response.Message;
serviceResponse.Data = user;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = response.Message;
}
}
} catch (Exception ex)
{
serviceResponse.Success = false;
serviceResponse.Message = ex.Message;
}
return serviceResponse;
}
private async Task<User> SetCurrentUser()
{
var userRequest = new RestRequest("users/user-authorized")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var userResponse = await _client.GetAsync<ServiceResponse<User>>(userRequest);
if (userResponse != null || userResponse!.Data != null)
{
user = userResponse.Data;
return userResponse.Data!;
} else
{
throw new NullReferenceException("Current User could not be set.");
}
}
public async Task<ServiceResponse<User>> RefreshLogin(string refreshToken)
{
var serviceResponse = new ServiceResponse<User>();
try
{
if (string.IsNullOrEmpty(refreshToken))
{
serviceResponse.Success = false;
serviceResponse.Message = "No Refresh Token Specified.";
}
var request = new RestRequest("auth/refresh")
.AddQueryParameter("token", refreshToken);
var response = await _client.PostAsync<ServiceResponse<string>>(request);
if (response != null && response.Success != false)
{
SessionToken = response.Data;
if (user == null)
{
var user = await SetCurrentUser();
serviceResponse.Success = true;
serviceResponse.Data = user;
}
serviceResponse.Success = true;
serviceResponse.Data = CurrentUser;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "API didn't respond with a session token.";
}
} catch (Exception ex)
{
serviceResponse.Success = false;
serviceResponse.Message = ex.Message;
}
return serviceResponse;
}
public async Task<ServiceResponse<string>> RefreshSessionIfInvalid()
{
var tokenHandler = new JwtSecurityTokenHandler();
var refToken = await File.ReadAllTextAsync("./session.token");
JwtSecurityToken token = tokenHandler.ReadJwtToken(SessionToken);
if(DateTime.Compare(DateTime.UtcNow, token.ValidTo) > 0)
{
if (!File.Exists("./session.token")) { return new ServiceResponse<string> { Success = false, Message = "Session File Not Found. Session Expired." }; }
var result = await RefreshLogin(refToken);
if (result == null || result.Success == false)
{
File.Delete("./session.token");
return new ServiceResponse<string> { Success = false, Message = "Session Expired." };
} else return new ServiceResponse<string> { Success = true, Data = refToken };
} else return new ServiceResponse<string> { Success = true, Data = refToken };
}
public async Task<ServiceResponse<User>> RegisterAsync(UserDto userDto)
{
var serviceResponse = new ServiceResponse<User>();
if (string.IsNullOrEmpty(userDto.Username) || string.IsNullOrEmpty(userDto.Password) || string.IsNullOrEmpty(userDto.Email))
{
serviceResponse.Success = false;
serviceResponse.Message = "Incomplete UserDto.";
}
var request = new RestRequest("auth/register")
.AddJsonBody(userDto);
var response = await _client.PostAsync<ServiceResponse<User>>(request);
if (response != null || response!.Success == true)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
} else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded with created user.";
}
return serviceResponse;
}
public async Task<ServiceResponse<Room>> CreateRoomAsync(RoomDto request)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<Room>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest($"rooms/create-room?userId={user!.Id}")
.AddJsonBody(request)
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.PostAsync<ServiceResponse<Room>>(restRequest);
if(response != null || response!.Data != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
} else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded with created room.";
}
return serviceResponse;
}
public async Task<ServiceResponse<List<Room>>> GetAllRoomsAsync()
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<List<Room>>();
if(SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var request = new RestRequest("rooms/get-all-rooms")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.GetAsync<ServiceResponse<List<Room>>>(request);
if (response != null || response!.Data != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded with rooms.";
}
return serviceResponse;
}
public async Task<ServiceResponse<Room>> DeleteRoomAsync(string roomId)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<Room>();
if(SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var request = new RestRequest($"rooms/delete-room?roomId={roomId}")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.DeleteAsync<ServiceResponse<Room>>(request);
if (response != null || response!.Data != null)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
} else
{
serviceResponse.Success = false;
serviceResponse.Message = "API never responded.";
}
return serviceResponse;
}
public async Task<ServiceResponse<List<Contact>>> GetCurrentUserContacts()
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<List<Contact>>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var request = new RestRequest("contacts/get-user-contacts")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddJsonBody(CurrentUser!);
var response = await _client.GetAsync<ServiceResponse<List<Contact>>>(request);
if (response == null)
{
serviceResponse.Success = false;
serviceResponse.Message = "API didn't respond.";
return serviceResponse;
}
if (response.Message == "User Has No Contacts.")
{
serviceResponse.Success = true;
serviceResponse.Message = "No Contacts Found.";
}
if (response.Success == true)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
public async Task<ServiceResponse<Contact>> AddContactToCurrentUser(string userId)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<Contact>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("contacts/add-contact")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("ownerId", CurrentUser!.Id)
.AddQueryParameter("userId", userId);
var response = await _client.PostAsync<ServiceResponse<Contact>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
public async Task<ServiceResponse<bool>> AcceptContactRequest(string userId)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<bool>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("contacts/approve-contact")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("ownerId", CurrentUser!.Id)
.AddQueryParameter("userId", userId);
var response = await _client.PostAsync<ServiceResponse<bool>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
public async Task<ServiceResponse<Contact>> RemoveContactFromCurrentUser(string userId)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<Contact>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("contacts/remove-contact")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("userId", userId);
var response = await _client.DeleteAsync<ServiceResponse<Contact>>(restRequest);
if (response == null) { serviceResponse.Success = false; serviceResponse.Message = "API did not respond."; return serviceResponse; }
if (response.Success)
{
serviceResponse.Success = true;
serviceResponse.Data = response.Data;
}
return serviceResponse;
}
public async Task<ServiceResponse<int>> AddCurrencyToCurrentUser(int amount, bool isSpinClaim)
{
await RefreshSessionIfInvalid();
var serviceResponse = new ServiceResponse<int>();
if (SessionToken == null) throw new NullReferenceException("Function Was Called Before A Session Was Made.");
var restRequest = new RestRequest("users/update-user-currency")
.AddHeader("Authorization", $"Bearer {SessionToken}")
.AddQueryParameter("amount", amount)
.AddQueryParameter("isSpinClaim", isSpinClaim);
var response = await _client.PostAsync<ServiceResponse<int>>(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

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Mvc;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Dtos.Room;
using QtCNETAPI.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Services.ApiService
{
public interface IApiService
{
public string? SessionToken { get; set; }
public User CurrentUser { get; }
public Task<ServiceResponse<string>> PingServerAsync();
public Task<ServiceResponse<List<UserInformationDto>>> GetOnlineUsersAsync();
public Task<ServiceResponse<User>> LoginAsync(UserLoginDto userLoginDto);
public Task<ServiceResponse<User>> RefreshLogin(string refreshToken);
public Task<ServiceResponse<string>> RefreshSessionIfInvalid();
public Task<ServiceResponse<User>> RegisterAsync(UserDto userDto);
public Task<ServiceResponse<UserInformationDto>> GetUserInformationAsync(string id);
public Task<ServiceResponse<UserInformationDto>> UpdateUserInformationAsync(UserUpdateInformationDto request);
public Task<ServiceResponse<string>> UpdateUserProfilePic(string filePath);
public Task<ServiceResponse<byte[]>> GetUserProfilePic(string userId);
public Task<ServiceResponse<Room>> CreateRoomAsync(RoomDto request);
public Task<ServiceResponse<Room>> DeleteRoomAsync(string roomId);
public Task<ServiceResponse<List<Room>>> GetAllRoomsAsync();
public Task<ServiceResponse<List<Contact>>> GetCurrentUserContacts();
public Task<ServiceResponse<Contact>> AddContactToCurrentUser(string userId);
public Task<ServiceResponse<bool>> AcceptContactRequest(string userId);
public Task<ServiceResponse<Contact>> RemoveContactFromCurrentUser(string userId);
public Task<ServiceResponse<int>> AddCurrencyToCurrentUser(int amount, bool isSpinClaim);
}
}

View File

@ -0,0 +1,188 @@
using Microsoft.AspNetCore.SignalR.Client;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Events;
using QtCNETAPI.Models;
using QtCNETAPI.Services.ApiService;
namespace QtCNETAPI.Services.GatewayService
{
public class GatewayService : IGatewayService
{
internal string gwBaseUri = "127.0.0.1";
public Room? CurrentRoom { get; private set; }
public bool InLobby { get; private set; }
public HubConnection? HubConnection { get; private set; }
public event EventHandler OnServerMessageReceived;
public event EventHandler OnClientFunctionReceived;
public event EventHandler OnDirectMessageReceived;
public event EventHandler OnServerConfigReceived;
public event EventHandler OnServerDisconnect;
public event EventHandler OnServerReconnecting;
public event EventHandler OnServerReconnected;
private IApiService _apiService;
public GatewayService(string GWUrl, IApiService apiService)
{
gwBaseUri = GWUrl;
_apiService = apiService;
}
public async Task StartAsync()
{
// just to be safe (it doesn't load the server since it shouldn't request a new one unless its actually expired)
await _apiService.RefreshSessionIfInvalid();
// build connection
var gwConBuilder = new HubConnectionBuilder()
.WithAutomaticReconnect()
.WithUrl(gwBaseUri, options =>
{
options.AccessTokenProvider = () => Task.FromResult(_apiService.SessionToken);
});
HubConnection = gwConBuilder.Build();
// start connection
await HubConnection.StartAsync();
// register events
HubConnection.On<string>("rm", (serverMessage) => OnServerMessageReceived?.Invoke(this, new ServerMessageEventArgs{Message = serverMessage}));
HubConnection.On<string>("cf", (function) => OnClientFunctionReceived?.Invoke(this, new ClientFunctionEventArgs{Function = function}));
HubConnection.On<Message, UserInformationDto>("rdm", (message, user) => OnDirectMessageReceived?.Invoke(this, new DirectMessageEventArgs { Message = message, User = user }));
HubConnection.On<ServerConfig>("rc", (serverConfig) => OnServerConfigReceived?.Invoke(this, new ServerConfigEventArgs{ServerConfig = serverConfig}));
HubConnection.Closed += HubConnection_Closed;
HubConnection.Reconnecting += HubConnection_Reconnecting;
HubConnection.Reconnected += HubConnection_Reconnected;
if (HubConnection != null && HubConnection.State == HubConnectionState.Connected)
await HubConnection.SendAsync("l", _apiService.CurrentUser);
}
public async Task StopAsync()
{
if (HubConnection != null && HubConnection.State == HubConnectionState.Connected)
{
await HubConnection.StopAsync();
}
}
public async Task DisposeAsync()
{
if (HubConnection != null && HubConnection.State == HubConnectionState.Disconnected)
{
await HubConnection.DisposeAsync();
HubConnection = null;
CurrentRoom = null;
}
}
public async Task JoinLobbyAsync()
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("jl", _apiService.CurrentUser);
InLobby = true;
CurrentRoom = null;
}
public async Task JoinRoomAsync(Room room)
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
if (InLobby == true)
{
await HubConnection.SendAsync("ll", _apiService.CurrentUser);
InLobby = false;
}
else if (CurrentRoom != null)
{
await HubConnection.SendAsync("lr", _apiService.CurrentUser, CurrentRoom);
}
await HubConnection.SendAsync("jr", _apiService.CurrentUser, room);
CurrentRoom = room;
}
public async Task LeaveRoomAsync()
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
if (InLobby)
{
await HubConnection.SendAsync("ll", _apiService.CurrentUser);
InLobby = false;
}
else
{
await HubConnection.SendAsync("lr", _apiService.CurrentUser, CurrentRoom);
CurrentRoom = null;
}
}
public async Task RefreshContactsForUser(UserInformationDto user)
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("rcl", user, _apiService.CurrentUser);
}
public async Task PostMessageAsync(Message message)
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("s", _apiService.CurrentUser, message, InLobby, CurrentRoom);
}
public async Task SendDirectMessageAsync(UserInformationDto user, Message message)
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("sdm", _apiService.CurrentUser, user, message);
}
public async Task UpdateStatus(int status)
{
await _apiService.RefreshSessionIfInvalid();
if (HubConnection == null || HubConnection.State != HubConnectionState.Connected) throw new InvalidOperationException("Function was called before connection was made.");
await HubConnection.SendAsync("us", _apiService.CurrentUser, status);
}
private Task HubConnection_Closed(Exception? arg)
{
OnServerDisconnect?.Invoke(this, new ServerConnectionClosedEventArgs { Error = arg });
return Task.CompletedTask;
}
private Task HubConnection_Reconnecting(Exception? arg)
{
OnServerReconnecting?.Invoke(this, new ServerConnectionReconnectingEventArgs { Error = arg });
return Task.CompletedTask;
}
private Task HubConnection_Reconnected(string? arg)
{
OnServerReconnected?.Invoke(this, EventArgs.Empty);
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,140 @@
using Microsoft.AspNetCore.SignalR.Client;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace QtCNETAPI.Services.GatewayService
{
public interface IGatewayService
{
// VARIABLES
/// <summary>
/// The Current Room The Current User Is In
/// </summary>
public Room? CurrentRoom { get; }
/// <summary>
/// Is The User Currently In The Lobby?
/// </summary>
public bool InLobby { get; }
/// <summary>
/// The Current Connection To The Gateway
/// </summary>
public HubConnection? HubConnection { get; }
// FUNCTIONS
/// <summary>
/// The Function Used To Connect To The Gateway Server Asynchronously
/// </summary>
/// <returns></returns>
public Task StartAsync();
/// <summary>
/// Stops The Connection To The Gateway Server
/// </summary>
/// <returns></returns>
public Task StopAsync();
/// <summary>
/// Disposes Of The Gateway Connection And Clears Other Variables
/// </summary>
/// <returns></returns>
public Task DisposeAsync();
/// <summary>
/// Joins The Lobby Of The Server
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException">Thrown if the function is called before the connection is established.</exception>
public Task JoinLobbyAsync();
/// <summary>
/// Joins The Current User To A Room On The Server
/// </summary>
/// <param name="room">Room To Join</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">Thrown if the function is called before the connection is established.</exception>
public Task JoinRoomAsync(Room room);
/// <summary>
/// Leaves The Current Room The Current User Is In
/// </summary>
/// <returns></returns>
public Task LeaveRoomAsync();
/// <summary>
/// Posts A Message To Whatever Room The User Is In
/// </summary>
/// <param name="message">Message To Post</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">Thrown if the function is called before the connection is established.</exception>
public Task PostMessageAsync(Message message);
/// <summary>
/// Sends A Direct Message To The Specified User
/// </summary>
/// <param name="user">The User You Wish To DM</param>
/// <param name="currentUser">Yourself</param>
/// <param name="message"></param>
/// <returns></returns>
public Task SendDirectMessageAsync(UserInformationDto user, Message message);
/// <summary>
/// Refreshes Contacts List For A Specified User
/// </summary>
/// <param name="user">The User You Wish To Refresh</param>
/// <param name="currentUser">Yourself</param>
/// <returns></returns>
public Task RefreshContactsForUser(UserInformationDto user);
/// <summary>
/// Updates The Status For The Current User
/// </summary>
/// <param name="status">The Status You Want To Set On The Current User</param>
/// <returns></returns>
public Task UpdateStatus(int status);
// EVENTS
/// <summary>
/// When A Server Message Is Received, This Event Fires
/// </summary>
public event EventHandler OnServerMessageReceived;
/// <summary>
/// When A Client Function/Event Is Received, This Event Fires
/// </summary>
public event EventHandler OnClientFunctionReceived;
/// <summary>
/// When The Client Received A DM, This Event Fires
/// </summary>
public event EventHandler OnDirectMessageReceived;
/// <summary>
/// When The Server Config Is Received, This Event Fires
/// </summary>
public event EventHandler OnServerConfigReceived;
/// <summary>
/// When The Connection To The Gateway Is Lost, This Event Fires
/// </summary>
public event EventHandler OnServerDisconnect;
/// <summary>
/// When The Connection Attempts To Reconnect, This Event Fires
/// </summary>
public event EventHandler OnServerReconnecting;
/// <summary>
/// When the Connection Reconnects, This Event Fires
/// </summary>
public event EventHandler OnServerReconnected;
}
}

31
qtc-net-client-2.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.36105.23 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qtc-net-client-2", "qtc-net-client-2\qtc-net-client-2.csproj", "{D12EE167-2DE3-4AD8-9D74-2E3956EAECC9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtCNETAPI", "QtCNETAPI\QtCNETAPI.csproj", "{92DB3A4B-4B30-4719-8608-5772608428CD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D12EE167-2DE3-4AD8-9D74-2E3956EAECC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D12EE167-2DE3-4AD8-9D74-2E3956EAECC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D12EE167-2DE3-4AD8-9D74-2E3956EAECC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D12EE167-2DE3-4AD8-9D74-2E3956EAECC9}.Release|Any CPU.Build.0 = Release|Any CPU
{92DB3A4B-4B30-4719-8608-5772608428CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92DB3A4B-4B30-4719-8608-5772608428CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92DB3A4B-4B30-4719-8608-5772608428CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92DB3A4B-4B30-4719-8608-5772608428CD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E5B052F2-C964-4F70-ADF5-82E32374D35B}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace qtc_net_client_2.ClientModel
{
public class Config
{
[JsonPropertyName("apiEndpoint")]
public string ApiEndpoint { get; set; } = "https://qtc.alanmoon.net/api";
[JsonPropertyName("gatewayEndpoint")]
public string GatewayEndpoint { get; set; } = "https://qtc.alanmoon.net/chat";
}
}

View File

@ -0,0 +1,11 @@
namespace qtc_net_client_2.ClientModel
{
public enum UserStatus
{
Offline = 0,
Online = 1,
Away = 2,
DoNotDisturb = 3,
Unknown
}
}

91
qtc-net-client-2/Forms/Chat.Designer.cs generated Normal file
View File

@ -0,0 +1,91 @@
namespace qtc_net_client_2.Forms
{
partial class Chat
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Chat));
rtxtChatbox = new RichTextBox();
btnSend = new Button();
rtxtChat = new RichTextBox();
SuspendLayout();
//
// rtxtChatbox
//
rtxtChatbox.Location = new Point(12, 268);
rtxtChatbox.Name = "rtxtChatbox";
rtxtChatbox.Size = new Size(512, 54);
rtxtChatbox.TabIndex = 1;
rtxtChatbox.Text = "";
//
// btnSend
//
btnSend.FlatAppearance.BorderSize = 0;
btnSend.FlatStyle = FlatStyle.Flat;
btnSend.Image = Properties.Resources.SendIcon;
btnSend.Location = new Point(530, 268);
btnSend.Name = "btnSend";
btnSend.Size = new Size(75, 54);
btnSend.TabIndex = 2;
btnSend.UseVisualStyleBackColor = true;
btnSend.Click += btnSend_Click;
//
// rtxtChat
//
rtxtChat.Location = new Point(12, 12);
rtxtChat.Name = "rtxtChat";
rtxtChat.ReadOnly = true;
rtxtChat.Size = new Size(593, 250);
rtxtChat.TabIndex = 3;
rtxtChat.Text = "";
//
// frmChat
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(617, 334);
Controls.Add(rtxtChat);
Controls.Add(btnSend);
Controls.Add(rtxtChatbox);
FormBorderStyle = FormBorderStyle.FixedSingle;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
Name = "frmChat";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client - Chat Room";
FormClosing += frmChat_FormClosing;
Load += frmChat_Load;
ResumeLayout(false);
}
#endregion
private RichTextBox rtxtChatbox;
private Button btnSend;
private RichTextBox rtxtChat;
}
}

View File

@ -0,0 +1,80 @@
using qtc_net_client_2.Services;
using QtCNETAPI.Events;
using QtCNETAPI.Models;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace qtc_net_client_2.Forms
{
public partial class Chat : Form
{
IGatewayService _gatewayService;
IApiService _apiService;
AudioService AudioService = new();
public Chat(IGatewayService gatewayService, IApiService apiService)
{
_gatewayService = gatewayService;
_apiService = apiService;
InitializeComponent();
}
private void frmChat_Load(object sender, EventArgs e)
{
// subscribe to server message event
_gatewayService.OnServerMessageReceived += _gatewayService_OnServerMessageReceived;
if (_gatewayService.CurrentRoom != null) Text = $"QtC.NET Client - Chat Room - {_gatewayService.CurrentRoom.Name}";
else if (_gatewayService.InLobby) Text = $"QtC.NET Client - Chat Room - Lobby";
}
private async void frmChat_FormClosing(object sender, FormClosingEventArgs e)
{
// unsubscribe from server message event
_gatewayService.OnServerMessageReceived -= _gatewayService_OnServerMessageReceived;
if (_gatewayService.CurrentRoom != null || _gatewayService.InLobby)
{
// leave any room user is in
await _gatewayService.LeaveRoomAsync();
}
}
private async void btnSend_Click(object sender, EventArgs e)
{
if(!string.IsNullOrWhiteSpace(rtxtChatbox.Text))
{
// construct message
QtCNETAPI.Models.Message message = new() { Content = rtxtChatbox.Text };
// send it and clear text box
await _gatewayService.PostMessageAsync(message);
rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick");
}
}
private void _gatewayService_OnServerMessageReceived(object? sender, EventArgs e)
{
var msgEventArgs = (ServerMessageEventArgs)e;
AddMessage(msgEventArgs.Message);
if (!msgEventArgs.Message.Contains(_apiService.CurrentUser.Username)) AudioService.PlaySoundEffect("sndMessage");
}
private void AddMessage(string message)
{
if (InvokeRequired)
Invoke(delegate { rtxtChat.Text += message + "\n"; });
else rtxtChat.Text += message + "\n";
}
}
}

View File

@ -0,0 +1,148 @@
<?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>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAEA8AAAEACABDBQAAFgAAACgAAAAQAAAAHgAAAAEACAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA9dN/APXTgAD00XoA9M5wAPPLZwDzyF8A8sZZAPLEVADywk4A8L9FAPC8PAD104AA9dF6APTL
aQD0z3UA9dSDAPC9PwDvtisA7rIeAPTOcgD0zW8A8sRVAPC7OgDsqgkA4J8AAPPJYwDxwUkA4KIMAM2S
AADxwUoAtYEAAO+3LQCbbgAA7K0PAOuoBACneQYAfloAANubAADYmQAAgVsAAGRHAADMkQAAuIMAALqF
AAB2VAAAWT8AAF5CAACpeAAAmm4AAJhsAACfcQAAfFgAAG9VFgCjlncAxr6sAI9mAACCXQAAfVkAAHdU
AABxUAAAa0wAAGRHAABcQgAAVTwAAN3a0QDt7e0A3t7eAOnp6QDi4uIA+fn5APPz8wD09PQA4uLiAM7O
zgD///8A/Pz8APPz8wDc3NwA+OOtAPzy2QD9+e4A/fblAPrpwAD22I8A6+fcAN3d3QDZ2dkA19fXAL6+
vgD+/foA+uvEAPTMawD++/QAoqKiAF9fXwBdXV0AW1tbAFRUVAD///8A+vr6AOPPnADusyIA+eSyAP7+
/gDk5OQA4uLiAN7e3gDo6OgA4+PjALOvpQDLmR4A7awPAPnmuAD9/f0A9/f3AEFBQQA9PT0ANTU1AIiI
iAC9vb0Afnx1ALWHFgDrxGUA39/dAKurqwCjo6MAlpaWAHl5eQDExMQAdHR0AGhbOgDgogkAw6dkAGlp
aABGRkYATExMAEVFRQA6OjoAXFxcAFRMOACgdg0AzJIDALuQJQCJdkgAaGFSAFZVUwBTUEgAXFI2AH9k
IgChdAUAq3oDAKx8BQCoegYAoXQGAJJpAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAABNTgAAAAAAAAAAAAAAAAAAS0wAAAAAAAAAAAAAAABISUoAAAAAAAAA
AAAAAABERUZHAAAAODk6Ozw9Pj9AQUJDAAAAMDEyM5eYmZqbNDU2NwAAKissjo+QkZKTlJWWLS4vACYn
hIWGh4iIiYqLjI0oKQAiI3t8WX1+Xn+AgYKDJCUAIHBxcnN0dXV1dnd4eXohAB5mZ2NoaWpqamtsbW5v
HwAaG1xdXl9gYGFiY2RlHB0AFBUWVFVWV1dYWVpbFxgZAAAMDQ4PT1BRUlMQERITAAAAAAECAwQFBgcI
CQoLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
</value>
</data>
</root>

View File

@ -0,0 +1,110 @@
namespace qtc_net_client_2.Forms
{
partial class ConnectionClosed
{
/// <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()
{
lblHead = new Label();
btnReconnect = new Button();
btnQuit = new Button();
lblReason = new Label();
SuspendLayout();
//
// lblHead
//
lblHead.AutoSize = true;
lblHead.Font = new Font("Segoe UI", 11F, FontStyle.Bold);
lblHead.Location = new Point(12, 9);
lblHead.Name = "lblHead";
lblHead.Size = new Size(444, 20);
lblHead.TabIndex = 0;
lblHead.Text = "Your Connection To The QtC.NET Server Was Lost. What Now?";
//
// btnReconnect
//
btnReconnect.ForeColor = Color.Black;
btnReconnect.Location = new Point(12, 33);
btnReconnect.Name = "btnReconnect";
btnReconnect.Size = new Size(75, 23);
btnReconnect.TabIndex = 1;
btnReconnect.Text = "Reconnect";
btnReconnect.UseVisualStyleBackColor = true;
btnReconnect.Click += btnReconnect_Click;
//
// btnQuit
//
btnQuit.ForeColor = Color.Black;
btnQuit.Location = new Point(381, 32);
btnQuit.Name = "btnQuit";
btnQuit.Size = new Size(75, 23);
btnQuit.TabIndex = 2;
btnQuit.Text = "Quit";
btnQuit.UseVisualStyleBackColor = true;
btnQuit.Click += btnQuit_Click;
//
// lblReason
//
lblReason.AutoSize = true;
lblReason.Font = new Font("Segoe UI", 5F, FontStyle.Bold);
lblReason.Location = new Point(88, 40);
lblReason.Name = "lblReason";
lblReason.Size = new Size(72, 10);
lblReason.TabIndex = 3;
lblReason.Text = "Reason: ${REASON}";
//
// frmConnectionClosed
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(463, 65);
Controls.Add(lblReason);
Controls.Add(btnQuit);
Controls.Add(btnReconnect);
Controls.Add(lblHead);
Font = new Font("Segoe UI", 9F);
ForeColor = Color.White;
FormBorderStyle = FormBorderStyle.FixedDialog;
Margin = new Padding(4, 3, 4, 3);
MaximizeBox = false;
MinimizeBox = false;
Name = "frmConnectionClosed";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client - Connection Lost";
Load += frmConnectionClosed_Load;
ResumeLayout(false);
PerformLayout();
}
#endregion
private Label lblHead;
private Button btnReconnect;
private Button btnQuit;
private Label lblReason;
}
}

View File

@ -0,0 +1,40 @@
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 ConnectionClosed : Form
{
public string? Reason { get; set; } = string.Empty;
public ConnectionClosed(string? reason = "")
{
Reason = reason;
InitializeComponent();
}
private void frmConnectionClosed_Load(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(Reason)) lblReason.Visible = false;
else lblReason.Text = $"Reason: {Reason}";
}
private void btnReconnect_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
Close();
}
private void btnQuit_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
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,104 @@
namespace qtc_net_client_2.Forms
{
partial class CreateRoom
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CreateRoom));
lblHeader = new Label();
lblHeader2 = new Label();
txtRoomName = new TextBox();
btnCreate = new Button();
SuspendLayout();
//
// lblHeader
//
lblHeader.AutoSize = true;
lblHeader.Location = new Point(12, 9);
lblHeader.Name = "lblHeader";
lblHeader.Size = new Size(273, 15);
lblHeader.TabIndex = 0;
lblHeader.Text = "Enter The Name Of The Room You Want To Create.";
//
// lblHeader2
//
lblHeader2.AutoSize = true;
lblHeader2.Font = new Font("Segoe UI", 6F);
lblHeader2.Location = new Point(47, 24);
lblHeader2.Name = "lblHeader2";
lblHeader2.Size = new Size(195, 11);
lblHeader2.TabIndex = 1;
lblHeader2.Text = "Tip: Make Rooms With Names That Describe Its Topic!";
//
// txtRoomName
//
txtRoomName.Location = new Point(12, 41);
txtRoomName.Name = "txtRoomName";
txtRoomName.Size = new Size(273, 23);
txtRoomName.TabIndex = 2;
//
// btnCreate
//
btnCreate.ForeColor = Color.Black;
btnCreate.Location = new Point(106, 70);
btnCreate.Name = "btnCreate";
btnCreate.Size = new Size(75, 23);
btnCreate.TabIndex = 3;
btnCreate.Text = "Create";
btnCreate.UseVisualStyleBackColor = true;
btnCreate.Click += btnCreate_Click;
//
// frmCreateRoom
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(298, 100);
Controls.Add(btnCreate);
Controls.Add(txtRoomName);
Controls.Add(lblHeader2);
Controls.Add(lblHeader);
ForeColor = Color.White;
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
MinimizeBox = false;
Name = "frmCreateRoom";
StartPosition = FormStartPosition.CenterParent;
Text = "Create Room";
ResumeLayout(false);
PerformLayout();
}
#endregion
private Label lblHeader;
private Label lblHeader2;
private TextBox txtRoomName;
private Button btnCreate;
}
}

View File

@ -0,0 +1,37 @@
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 CreateRoom : Form
{
private IApiService _apiService;
public CreateRoom(IApiService apiService)
{
_apiService = apiService;
InitializeComponent();
}
private async void btnCreate_Click(object sender, EventArgs e)
{
if(!string.IsNullOrEmpty(txtRoomName.Text))
{
await _apiService.CreateRoomAsync(new QtCNETAPI.Dtos.Room.RoomDto
{
Name = txtRoomName.Text,
CreatedAt = DateTime.UtcNow,
});
DialogResult = DialogResult.OK;
Close();
}
}
}
}

View File

@ -0,0 +1,197 @@
<?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>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABMLAAATCwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAO24wAMtuMAFjbjABy24wAetuMAHHbjABW24wAL9uMAAzbjAAA24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAf24wAdNuMAMTbjADu24wA/duMAP/bjAD/24wA/9uMAPzbjADs24wAv9uM
AG3bjAAa24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAJ24wAYduMANXbjAD+24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/duMAM7bjABY24wABtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAA24wAEtuMAJPbjAD324wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPTbjACH24wADtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMABDbjACf24wA/duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPzbjACT24wAC9uMAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAE24wAh9uMAP3bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPrbjAB524wAAtuM
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAE3bjADw24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AOrbjABA24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAQ24wAvduMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAK/bjAAK24wAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAFHbjAD324wA/9uM
AP/bjAD/24wA/9uLAP/biwD/24wA/9uMAP/bjAD/24wA/9uMAP/biwD/24wA/9uMAP/bjAD/24sA/9uL
AP/bjAD/24wA/9uMAP/bjAD/24wA8tuMAEPbjAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAC24wAntuM
AP/bjAD/24wA/9uLAP/ckQz/4Jwj/9+YG//bjQP/3JEM/9yPB//biwD/3ZIN/96YGv/bjQP/24wA/9uM
Af/elRX/4Jwk/92TD//biwD/24wA/9uMAP/bjAD/24wAj9uMAADbjAAAAAAAAAAAAAAAAAAA24wAANuM
ABbbjADU24wA/9uMAP/biwD/4qM1//Tet//57dj/+OnR/+/Ok//y16f/5atG/92TD//y2Kr/9N63/96V
FP/bjAL/6Lhi//fnzf/47Nf/9uPB/+WrR//biwD/24wA/9uMAP/bjADH24wAD9uMAAAAAAAAAAAAAAAA
AADbjAAA24wAMtuMAO7bjAD/24wA/92TD//14b3/89mt/+SoP//w0Jj///////Parv/dkg3/4qIy//rw
3v/msFH/24wA/+OlOf/68N//7MJ5/+KkNf/uy47/+OrS/+CcJP/biwD/24wA/9uMAObbjAAm24wAAAAA
AAAAAAAAAAAAANuMAADbjABI24wA+duMAP/biwD/4qIy//nv3f/iozX/3pUX//LWpv/25cj/8tis/9uM
AP/ipDb/+ezY/+CcJ//aiQD/68F3//bjwv/ckAz/24oA/92RD//puWX/4Jwl/9uLAP/bjAD/24wA89uM
ADrbjAAAAAAAAAAAAAAAAAAA24wAANuMAFHbjAD824wA/9uKAP/jpzz/+e7b/+CdJ//bjAH/3ZMQ/+zD
fP/137v/240D/+KkNv/57Nj/4J0n/9qJAP/tx4X/89yz/9uNBP/bjAD/24wA/9qKAP/biwD/24wA/9uM
AP/bjAD324wAQtuMAAAAAAAAAAAAAAAAAADbjAAA24wATNuMAPrbjAD/24oA/+OnPP/57tv/4J0n/9uL
AP/aigD/7MJ6//Xfu//cjgX/5axI//rv3v/kqED/24sA/+3Hhf/z3LL/244E/9uMAP/bjAD/24sA/9uM
AP/bjAD/24wA/9uMAPXbjAA924wAAAAAAAAAAAAAAAAAANuMAADbjAA524wA8tuMAP/bigD/46Y6//nu
2//gnin/24sA/9qKAP/sw37/9N+4/9yRC//x0p7//v37//Tdtv/dkxD/7cWB//Tdtv/cjgX/24wA/9uM
Av/elRb/3JAK/9uMAP/bjAD/24wA69uMACzbjAAAAAAAAAAAAAAAAAAA24wAANuMAB7bjADd24wA/9uL
AP/gmyL/+e3Z/+i4Yv/bjAD/3ZMP//TdtP/w0Jj/24sA/+WuS//68eD/5KpD/9uLAP/otl3/+e3Y/+Gf
Kv/biwD/46c8//XiwP/iozT/24sA/9uMAP/bjADS24wAFduMAAAAAAAAAAAAAAAAAADbjAAA24wABtuM
AK/bjAD/24wA/9uNAv/sxH3/+vDg//LZq//14sH/+OvU/+KkNv/aigD/4Z4p//HUov/fmB3/24oA/96V
Fv/03bX/+OnQ//LWpv/57dn/8NGa/9yQCv/bjAD/24wA/9uMAKDbjAAC24wAAAAAAAAAAAAAAAAAANuM
AADbjAAA24wAZtuMAP3bjAD/24wA/9yPCf/mr0//7cmI/+zCef/hoCz/24wA/9uMAP/bjQL/3I8I/9uN
Af/bjAD/24sA/96XGf/pu2r/7sqK/+i1XP/dkg//24sA/9uMAP/bjAD624wAVtuMAADbjAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAc24wA09uMAP/bjAD/24wA/9qKAP/biwD/2ooA/9uLAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24sA/9qKAP/biwD/2ooA/9uMAP/bjAD/24wA/9uMAMfbjAAU24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjABr24wA+9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD324wAXNuM
AADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAA7bjACr24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AJ7bjAAJ24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMACTbjADG24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAC724wAHduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuM
ACvbjADA24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD+24wAtduMACPbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADbjAAA24wAANuMABzbjACV24wA8duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA7tuMAIvbjAAW24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAAbbjABG24wAqtuMAOnbjAD+24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD924wA59uMAKTbjAA/24wABNuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjAAH24wALtuMAGXbjACS24wAq9uM
ALPbjACq24wAj9uMAGHbjAAq24wABduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuM
AADbjAAD24wABduMAALbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA//////////////////AH///AAf//AAB//gAAP/wAAB/4AAAP+AAAD/AA
AAfwAAAH4AAAB+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD8AAAB/AAAAf4AAAP+AAAD/wA
AB/+AAA//wAAf/+AAP//4AP///4///////8=
</value>
</data>
</root>

View File

@ -0,0 +1,113 @@
namespace qtc_net_client_2.Forms
{
partial class DirectMessage
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DirectMessage));
rtxtChat = new RichTextBox();
btnSend = new Button();
rtxtChatbox = new RichTextBox();
lblUsername = new Label();
SuspendLayout();
//
// rtxtChat
//
rtxtChat.Location = new Point(12, 56);
rtxtChat.Margin = new Padding(4, 3, 4, 3);
rtxtChat.Name = "rtxtChat";
rtxtChat.ReadOnly = true;
rtxtChat.Size = new Size(593, 317);
rtxtChat.TabIndex = 6;
rtxtChat.Text = "";
//
// btnSend
//
btnSend.FlatAppearance.BorderSize = 0;
btnSend.FlatStyle = FlatStyle.Flat;
btnSend.ForeColor = Color.Black;
btnSend.Image = Properties.Resources.SendIcon;
btnSend.Location = new Point(530, 379);
btnSend.Margin = new Padding(4, 3, 4, 3);
btnSend.Name = "btnSend";
btnSend.Size = new Size(75, 54);
btnSend.TabIndex = 5;
btnSend.UseVisualStyleBackColor = true;
btnSend.Click += btnSend_Click;
//
// rtxtChatbox
//
rtxtChatbox.Location = new Point(12, 379);
rtxtChatbox.Margin = new Padding(4, 3, 4, 3);
rtxtChatbox.Name = "rtxtChatbox";
rtxtChatbox.Size = new Size(511, 54);
rtxtChatbox.TabIndex = 4;
rtxtChatbox.Text = "";
//
// lblUsername
//
lblUsername.AutoSize = true;
lblUsername.Font = new Font("Segoe UI", 25F, FontStyle.Bold | FontStyle.Italic);
lblUsername.Location = new Point(6, 7);
lblUsername.Margin = new Padding(4, 0, 4, 0);
lblUsername.Name = "lblUsername";
lblUsername.Size = new Size(181, 46);
lblUsername.TabIndex = 7;
lblUsername.Text = "Username";
//
// frmDirectMessage
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(618, 450);
Controls.Add(lblUsername);
Controls.Add(rtxtChat);
Controls.Add(btnSend);
Controls.Add(rtxtChatbox);
Font = new Font("Segoe UI", 9F);
ForeColor = Color.White;
FormBorderStyle = FormBorderStyle.FixedSingle;
Icon = (Icon)resources.GetObject("$this.Icon");
Margin = new Padding(4, 3, 4, 3);
MaximizeBox = false;
Name = "frmDirectMessage";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client - Direct Message With ${USER}";
Load += frmDirectMessage_Load;
ResumeLayout(false);
PerformLayout();
}
#endregion
private RichTextBox rtxtChat;
private Button btnSend;
private RichTextBox rtxtChatbox;
private Label lblUsername;
}
}

View File

@ -0,0 +1,99 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using qtc_net_client_2.Services;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Events;
using QtCNETAPI.Models;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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 DirectMessage : Form
{
private IGatewayService _gatewayService;
private IApiService _apiService;
private QtCNETAPI.Models.Message? InitMessage;
private AudioService AudioService = new();
public UserInformationDto User { get; set; }
public ObservableCollection<string> Messages { get; set; }
public DirectMessage(IGatewayService gatewayService, IApiService apiService, UserInformationDto user, QtCNETAPI.Models.Message? initMsg = null)
{
_gatewayService = gatewayService;
_apiService = apiService;
User = user;
Messages = new ObservableCollection<string>();
InitMessage = initMsg;
InitializeComponent();
}
private void frmDirectMessage_Load(object sender, EventArgs e)
{
lblUsername.Text = User.Username;
Text = $"QtC.NET Client - Direct Message With {User.Username}";
Messages.CollectionChanged += Messages_CollectionChanged;
if (InitMessage != null)
{
Messages.Add($"[{User.Username}] {InitMessage.Content}\n");
AudioService.PlaySoundEffect("sndDirectMsg");
}
}
private async void btnSend_Click(object sender, EventArgs e)
{
if(InvokeRequired)
{
await Invoke(async delegate ()
{
if (!string.IsNullOrEmpty(rtxtChatbox.Text))
{
await _gatewayService.SendDirectMessageAsync(User, new QtCNETAPI.Models.Message { Content = rtxtChatbox.Text, AuthorId = _apiService.CurrentUser.Id });
Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}\n");
rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick");
}
});
}
else
{
if (!string.IsNullOrEmpty(rtxtChatbox.Text))
{
await _gatewayService.SendDirectMessageAsync(User, new QtCNETAPI.Models.Message { Content = rtxtChatbox.Text, AuthorId = _apiService.CurrentUser.Id });
Messages.Add($"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}\n");
rtxtChatbox.Clear();
AudioService.PlaySoundEffect("sndSendClick");
}
}
}
private void Messages_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if(e.NewItems != null)
{
if (InvokeRequired)
{
Invoke(delegate ()
{
rtxtChat.Text += e.NewItems.Cast<string>().FirstOrDefault();
AudioService.PlaySoundEffect("sndMessage");
});
}
else
{
rtxtChat.Text += e.NewItems.Cast<string>().FirstOrDefault();
AudioService.PlaySoundEffect("sndMessage");
}
}
}
}
}

View File

@ -0,0 +1,148 @@
<?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>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAEA8AAAEACABDBQAAFgAAACgAAAAQAAAAHgAAAAEACAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA9dN/APXTgAD00XoA9M5wAPPLZwDzyF8A8sZZAPLEVADywk4A8L9FAPC8PAD104AA9dF6APTL
aQD0z3UA9dSDAPC9PwDvtisA7rIeAPTOcgD0zW8A8sRVAPC7OgDsqgkA4J8AAPPJYwDxwUkA4KIMAM2S
AADxwUoAtYEAAO+3LQCbbgAA7K0PAOuoBACneQYAfloAANubAADYmQAAgVsAAGRHAADMkQAAuIMAALqF
AAB2VAAAWT8AAF5CAACpeAAAmm4AAJhsAACfcQAAfFgAAG9VFgCjlncAxr6sAI9mAACCXQAAfVkAAHdU
AABxUAAAa0wAAGRHAABcQgAAVTwAAN3a0QDt7e0A3t7eAOnp6QDi4uIA+fn5APPz8wD09PQA4uLiAM7O
zgD///8A/Pz8APPz8wDc3NwA+OOtAPzy2QD9+e4A/fblAPrpwAD22I8A6+fcAN3d3QDZ2dkA19fXAL6+
vgD+/foA+uvEAPTMawD++/QAoqKiAF9fXwBdXV0AW1tbAFRUVAD///8A+vr6AOPPnADusyIA+eSyAP7+
/gDk5OQA4uLiAN7e3gDo6OgA4+PjALOvpQDLmR4A7awPAPnmuAD9/f0A9/f3AEFBQQA9PT0ANTU1AIiI
iAC9vb0Afnx1ALWHFgDrxGUA39/dAKurqwCjo6MAlpaWAHl5eQDExMQAdHR0AGhbOgDgogkAw6dkAGlp
aABGRkYATExMAEVFRQA6OjoAXFxcAFRMOACgdg0AzJIDALuQJQCJdkgAaGFSAFZVUwBTUEgAXFI2AH9k
IgChdAUAq3oDAKx8BQCoegYAoXQGAJJpAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAABNTgAAAAAAAAAAAAAAAAAAS0wAAAAAAAAAAAAAAABISUoAAAAAAAAA
AAAAAABERUZHAAAAODk6Ozw9Pj9AQUJDAAAAMDEyM5eYmZqbNDU2NwAAKissjo+QkZKTlJWWLS4vACYn
hIWGh4iIiYqLjI0oKQAiI3t8WX1+Xn+AgYKDJCUAIHBxcnN0dXV1dnd4eXohAB5mZ2NoaWpqamtsbW5v
HwAaG1xdXl9gYGFiY2RlHB0AFBUWVFVWV1dYWVpbFxgZAAAMDQ4PT1BRUlMQERITAAAAAAECAwQFBgcI
CQoLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
</value>
</data>
</root>

162
qtc-net-client-2/Forms/Login.Designer.cs generated Normal file
View File

@ -0,0 +1,162 @@
namespace qtc_net_client_2.Forms
{
partial class Login
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Login));
pbLoginBanner = new PictureBox();
tbEmail = new TextBox();
lblEmail = new Label();
label1 = new Label();
tbPassword = new TextBox();
btnLogin = new Button();
llblRegister = new LinkLabel();
cbRememberMe = new CheckBox();
((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 = 0;
pbLoginBanner.TabStop = false;
//
// tbEmail
//
tbEmail.Location = new Point(71, 106);
tbEmail.Name = "tbEmail";
tbEmail.Size = new Size(424, 23);
tbEmail.TabIndex = 1;
//
// lblEmail
//
lblEmail.AutoSize = true;
lblEmail.Font = new Font("Segoe UI Light", 9F);
lblEmail.ForeColor = SystemColors.ControlLight;
lblEmail.Location = new Point(32, 109);
lblEmail.Name = "lblEmail";
lblEmail.Size = new Size(33, 15);
lblEmail.TabIndex = 2;
lblEmail.Text = "Email";
//
// label1
//
label1.AutoSize = true;
label1.Font = new Font("Segoe UI Light", 9F);
label1.ForeColor = SystemColors.ControlLight;
label1.Location = new Point(11, 138);
label1.Name = "label1";
label1.Size = new Size(55, 15);
label1.TabIndex = 4;
label1.Text = "Password";
//
// tbPassword
//
tbPassword.Location = new Point(71, 135);
tbPassword.Name = "tbPassword";
tbPassword.PasswordChar = '*';
tbPassword.Size = new Size(424, 23);
tbPassword.TabIndex = 3;
//
// btnLogin
//
btnLogin.Location = new Point(71, 164);
btnLogin.Name = "btnLogin";
btnLogin.Size = new Size(75, 23);
btnLogin.TabIndex = 5;
btnLogin.Text = "Login";
btnLogin.UseVisualStyleBackColor = true;
btnLogin.Click += btnLogin_Click;
//
// llblRegister
//
llblRegister.AutoSize = true;
llblRegister.Font = new Font("Segoe UI Light", 9F);
llblRegister.LinkColor = SystemColors.ControlLight;
llblRegister.Location = new Point(148, 168);
llblRegister.Name = "llblRegister";
llblRegister.Size = new Size(48, 15);
llblRegister.TabIndex = 6;
llblRegister.TabStop = true;
llblRegister.Text = "Register";
llblRegister.LinkClicked += llblRegister_LinkClicked;
//
// cbRememberMe
//
cbRememberMe.AutoSize = true;
cbRememberMe.Font = new Font("Segoe UI Light", 9F);
cbRememberMe.ForeColor = SystemColors.ControlLight;
cbRememberMe.Location = new Point(198, 168);
cbRememberMe.Name = "cbRememberMe";
cbRememberMe.Size = new Size(157, 19);
cbRememberMe.TabIndex = 7;
cbRememberMe.Text = "Remember Me For 7 Days";
cbRememberMe.UseVisualStyleBackColor = true;
//
// frmLogin
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(515, 203);
Controls.Add(cbRememberMe);
Controls.Add(llblRegister);
Controls.Add(btnLogin);
Controls.Add(label1);
Controls.Add(tbPassword);
Controls.Add(lblEmail);
Controls.Add(tbEmail);
Controls.Add(pbLoginBanner);
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
Name = "frmLogin";
StartPosition = FormStartPosition.CenterParent;
Text = "QtC.NET Client - Login";
Load += frmLogin_Load;
((System.ComponentModel.ISupportInitialize)pbLoginBanner).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private PictureBox pbLoginBanner;
private TextBox tbEmail;
private Label lblEmail;
private Label label1;
private TextBox tbPassword;
private Button btnLogin;
private LinkLabel llblRegister;
private CheckBox cbRememberMe;
}
}

View File

@ -0,0 +1,91 @@
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Dtos.User;
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 Login : Form
{
private IApiService _apiService;
public Login(IApiService apiService)
{
_apiService = apiService;
InitializeComponent();
}
private async void frmLogin_Load(object sender, EventArgs e)
{
if (File.Exists("./session.token"))
{
ToggleControls(false, false);
// try logging in with the token in the file
string token = File.ReadAllText("./session.token");
var result = await _apiService.RefreshLogin(token);
if (result.Success)
{
DialogResult = DialogResult.OK;
Close();
}
else
ToggleControls(true, false);
}
}
private async void btnLogin_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(tbEmail.Text) && !string.IsNullOrEmpty(tbPassword.Text))
{
ToggleControls(false, false);
var result = await _apiService.LoginAsync(new UserLoginDto
{
Email = tbEmail.Text,
Password = tbPassword.Text,
RememberMe = cbRememberMe.Checked
});
if (result.Success)
{
DialogResult = DialogResult.OK;
Close();
}
else
{
ToggleControls(true, true);
MessageBox.Show(result.Message, "Login Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void ToggleControls(bool enable, bool clearText)
{
tbEmail.Enabled = enable;
tbPassword.Enabled = enable;
btnLogin.Enabled = enable;
llblRegister.Enabled = enable;
if (clearText)
{
tbEmail.Text = string.Empty;
tbPassword.Text = string.Empty;
}
}
private void llblRegister_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Register frmRegister = new Register(_apiService);
frmRegister.ShowDialog();
}
}
}

View File

@ -0,0 +1,197 @@
<?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>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABMLAAATCwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAO24wAMtuMAFjbjABy24wAetuMAHHbjABW24wAL9uMAAzbjAAA24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAf24wAdNuMAMTbjADu24wA/duMAP/bjAD/24wA/9uMAPzbjADs24wAv9uM
AG3bjAAa24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAJ24wAYduMANXbjAD+24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/duMAM7bjABY24wABtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAA24wAEtuMAJPbjAD324wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPTbjACH24wADtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMABDbjACf24wA/duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPzbjACT24wAC9uMAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAE24wAh9uMAP3bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPrbjAB524wAAtuM
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAE3bjADw24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AOrbjABA24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAQ24wAvduMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAK/bjAAK24wAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAFHbjAD324wA/9uM
AP/bjAD/24wA/9uLAP/biwD/24wA/9uMAP/bjAD/24wA/9uMAP/biwD/24wA/9uMAP/bjAD/24sA/9uL
AP/bjAD/24wA/9uMAP/bjAD/24wA8tuMAEPbjAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAC24wAntuM
AP/bjAD/24wA/9uLAP/ckQz/4Jwj/9+YG//bjQP/3JEM/9yPB//biwD/3ZIN/96YGv/bjQP/24wA/9uM
Af/elRX/4Jwk/92TD//biwD/24wA/9uMAP/bjAD/24wAj9uMAADbjAAAAAAAAAAAAAAAAAAA24wAANuM
ABbbjADU24wA/9uMAP/biwD/4qM1//Tet//57dj/+OnR/+/Ok//y16f/5atG/92TD//y2Kr/9N63/96V
FP/bjAL/6Lhi//fnzf/47Nf/9uPB/+WrR//biwD/24wA/9uMAP/bjADH24wAD9uMAAAAAAAAAAAAAAAA
AADbjAAA24wAMtuMAO7bjAD/24wA/92TD//14b3/89mt/+SoP//w0Jj///////Parv/dkg3/4qIy//rw
3v/msFH/24wA/+OlOf/68N//7MJ5/+KkNf/uy47/+OrS/+CcJP/biwD/24wA/9uMAObbjAAm24wAAAAA
AAAAAAAAAAAAANuMAADbjABI24wA+duMAP/biwD/4qIy//nv3f/iozX/3pUX//LWpv/25cj/8tis/9uM
AP/ipDb/+ezY/+CcJ//aiQD/68F3//bjwv/ckAz/24oA/92RD//puWX/4Jwl/9uLAP/bjAD/24wA89uM
ADrbjAAAAAAAAAAAAAAAAAAA24wAANuMAFHbjAD824wA/9uKAP/jpzz/+e7b/+CdJ//bjAH/3ZMQ/+zD
fP/137v/240D/+KkNv/57Nj/4J0n/9qJAP/tx4X/89yz/9uNBP/bjAD/24wA/9qKAP/biwD/24wA/9uM
AP/bjAD324wAQtuMAAAAAAAAAAAAAAAAAADbjAAA24wATNuMAPrbjAD/24oA/+OnPP/57tv/4J0n/9uL
AP/aigD/7MJ6//Xfu//cjgX/5axI//rv3v/kqED/24sA/+3Hhf/z3LL/244E/9uMAP/bjAD/24sA/9uM
AP/bjAD/24wA/9uMAPXbjAA924wAAAAAAAAAAAAAAAAAANuMAADbjAA524wA8tuMAP/bigD/46Y6//nu
2//gnin/24sA/9qKAP/sw37/9N+4/9yRC//x0p7//v37//Tdtv/dkxD/7cWB//Tdtv/cjgX/24wA/9uM
Av/elRb/3JAK/9uMAP/bjAD/24wA69uMACzbjAAAAAAAAAAAAAAAAAAA24wAANuMAB7bjADd24wA/9uL
AP/gmyL/+e3Z/+i4Yv/bjAD/3ZMP//TdtP/w0Jj/24sA/+WuS//68eD/5KpD/9uLAP/otl3/+e3Y/+Gf
Kv/biwD/46c8//XiwP/iozT/24sA/9uMAP/bjADS24wAFduMAAAAAAAAAAAAAAAAAADbjAAA24wABtuM
AK/bjAD/24wA/9uNAv/sxH3/+vDg//LZq//14sH/+OvU/+KkNv/aigD/4Z4p//HUov/fmB3/24oA/96V
Fv/03bX/+OnQ//LWpv/57dn/8NGa/9yQCv/bjAD/24wA/9uMAKDbjAAC24wAAAAAAAAAAAAAAAAAANuM
AADbjAAA24wAZtuMAP3bjAD/24wA/9yPCf/mr0//7cmI/+zCef/hoCz/24wA/9uMAP/bjQL/3I8I/9uN
Af/bjAD/24sA/96XGf/pu2r/7sqK/+i1XP/dkg//24sA/9uMAP/bjAD624wAVtuMAADbjAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAc24wA09uMAP/bjAD/24wA/9qKAP/biwD/2ooA/9uLAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24sA/9qKAP/biwD/2ooA/9uMAP/bjAD/24wA/9uMAMfbjAAU24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjABr24wA+9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD324wAXNuM
AADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAA7bjACr24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AJ7bjAAJ24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMACTbjADG24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAC724wAHduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuM
ACvbjADA24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD+24wAtduMACPbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADbjAAA24wAANuMABzbjACV24wA8duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA7tuMAIvbjAAW24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAAbbjABG24wAqtuMAOnbjAD+24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD924wA59uMAKTbjAA/24wABNuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjAAH24wALtuMAGXbjACS24wAq9uM
ALPbjACq24wAj9uMAGHbjAAq24wABduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuM
AADbjAAD24wABduMAALbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA//////////////////AH///AAf//AAB//gAAP/wAAB/4AAAP+AAAD/AA
AAfwAAAH4AAAB+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD8AAAB/AAAAf4AAAP+AAAD/wA
AB/+AAA//wAAf/+AAP//4AP///4///////8=
</value>
</data>
</root>

393
qtc-net-client-2/Forms/Main.Designer.cs generated Normal file
View File

@ -0,0 +1,393 @@
namespace qtc_net_client_2
{
partial class Main
{
/// <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()
{
components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main));
tbcMain = new TabControl();
tbpContacts = new TabPage();
lvContacts = new ListView();
ilProfilePics = new ImageList(components);
tbpRooms = new TabPage();
lbRooms = new ListBox();
tbpOnlineUsers = new TabPage();
lbOnlineUsers = new ListBox();
ilTabIcons = new ImageList(components);
pbUserPfp = new PictureBox();
ctxmChangeStatus = new ContextMenuStrip(components);
onlineToolStripMenuItem = new ToolStripMenuItem();
awayToolStripMenuItem = new ToolStripMenuItem();
doNotDisturbToolStripMenuItem = new ToolStripMenuItem();
invisibleToolStripMenuItem = new ToolStripMenuItem();
lblWelcome = new Label();
llblSignIn = new LinkLabel();
llblSignOut = new LinkLabel();
llblEditProfile = new LinkLabel();
lblRequestNotif = new Label();
niMain = new NotifyIcon(components);
btnAddRoom = new Button();
pbCurrencyIcon = new PictureBox();
lblCurrencyAmount = new Label();
llblClaimSpin = new LinkLabel();
tbcMain.SuspendLayout();
tbpContacts.SuspendLayout();
tbpRooms.SuspendLayout();
tbpOnlineUsers.SuspendLayout();
((System.ComponentModel.ISupportInitialize)pbUserPfp).BeginInit();
ctxmChangeStatus.SuspendLayout();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).BeginInit();
SuspendLayout();
//
// tbcMain
//
tbcMain.Controls.Add(tbpContacts);
tbcMain.Controls.Add(tbpRooms);
tbcMain.Controls.Add(tbpOnlineUsers);
tbcMain.Enabled = false;
tbcMain.ImageList = ilTabIcons;
tbcMain.Location = new Point(12, 66);
tbcMain.Name = "tbcMain";
tbcMain.SelectedIndex = 0;
tbcMain.Size = new Size(258, 535);
tbcMain.TabIndex = 0;
//
// tbpContacts
//
tbpContacts.Controls.Add(lvContacts);
tbpContacts.ImageKey = "ContactsIcon.png";
tbpContacts.Location = new Point(4, 24);
tbpContacts.Name = "tbpContacts";
tbpContacts.Padding = new Padding(3);
tbpContacts.Size = new Size(250, 507);
tbpContacts.TabIndex = 0;
tbpContacts.Text = "Contacts";
tbpContacts.UseVisualStyleBackColor = true;
//
// lvContacts
//
lvContacts.LargeImageList = ilProfilePics;
lvContacts.Location = new Point(0, 0);
lvContacts.Name = "lvContacts";
lvContacts.Size = new Size(250, 507);
lvContacts.SmallImageList = ilProfilePics;
lvContacts.TabIndex = 1;
lvContacts.UseCompatibleStateImageBehavior = false;
lvContacts.View = View.List;
lvContacts.DoubleClick += lvContacts_DoubleClick;
//
// ilProfilePics
//
ilProfilePics.ColorDepth = ColorDepth.Depth32Bit;
ilProfilePics.ImageSize = new Size(16, 16);
ilProfilePics.TransparentColor = Color.Transparent;
//
// tbpRooms
//
tbpRooms.Controls.Add(lbRooms);
tbpRooms.ImageKey = "RoomsChatIcon.png";
tbpRooms.Location = new Point(4, 24);
tbpRooms.Name = "tbpRooms";
tbpRooms.Padding = new Padding(3);
tbpRooms.Size = new Size(250, 507);
tbpRooms.TabIndex = 1;
tbpRooms.Text = "Rooms";
tbpRooms.UseVisualStyleBackColor = true;
//
// lbRooms
//
lbRooms.FormattingEnabled = true;
lbRooms.ItemHeight = 15;
lbRooms.Location = new Point(0, 0);
lbRooms.Name = "lbRooms";
lbRooms.Size = new Size(250, 514);
lbRooms.TabIndex = 0;
lbRooms.DoubleClick += lbRooms_DoubleClick;
//
// tbpOnlineUsers
//
tbpOnlineUsers.Controls.Add(lbOnlineUsers);
tbpOnlineUsers.ImageIndex = 0;
tbpOnlineUsers.Location = new Point(4, 24);
tbpOnlineUsers.Name = "tbpOnlineUsers";
tbpOnlineUsers.Padding = new Padding(3);
tbpOnlineUsers.Size = new Size(250, 507);
tbpOnlineUsers.TabIndex = 2;
tbpOnlineUsers.Text = "Online Users";
tbpOnlineUsers.UseVisualStyleBackColor = true;
//
// lbOnlineUsers
//
lbOnlineUsers.FormattingEnabled = true;
lbOnlineUsers.ItemHeight = 15;
lbOnlineUsers.Location = new Point(0, 0);
lbOnlineUsers.Name = "lbOnlineUsers";
lbOnlineUsers.Size = new Size(250, 514);
lbOnlineUsers.TabIndex = 0;
lbOnlineUsers.DoubleClick += lbOnlineUsers_DoubleClick;
//
// ilTabIcons
//
ilTabIcons.ColorDepth = ColorDepth.Depth32Bit;
ilTabIcons.ImageStream = (ImageListStreamer)resources.GetObject("ilTabIcons.ImageStream");
ilTabIcons.TransparentColor = Color.Transparent;
ilTabIcons.Images.SetKeyName(0, "ContactsIcon.png");
ilTabIcons.Images.SetKeyName(1, "RoomsChatIcon.png");
//
// pbUserPfp
//
pbUserPfp.BorderStyle = BorderStyle.FixedSingle;
pbUserPfp.ContextMenuStrip = ctxmChangeStatus;
pbUserPfp.Image = Properties.Resources.DefaultPfp;
pbUserPfp.Location = new Point(12, 10);
pbUserPfp.Name = "pbUserPfp";
pbUserPfp.Size = new Size(51, 50);
pbUserPfp.SizeMode = PictureBoxSizeMode.StretchImage;
pbUserPfp.TabIndex = 1;
pbUserPfp.TabStop = false;
pbUserPfp.Click += pbUserPfp_Click;
//
// ctxmChangeStatus
//
ctxmChangeStatus.Items.AddRange(new ToolStripItem[] { onlineToolStripMenuItem, awayToolStripMenuItem, doNotDisturbToolStripMenuItem, invisibleToolStripMenuItem });
ctxmChangeStatus.Name = "ctxmChangeStatus";
ctxmChangeStatus.Size = new Size(154, 92);
ctxmChangeStatus.ItemClicked += ctxmChangeStatus_ItemClicked;
//
// onlineToolStripMenuItem
//
onlineToolStripMenuItem.Image = Properties.Resources.OnlineIcon;
onlineToolStripMenuItem.Name = "onlineToolStripMenuItem";
onlineToolStripMenuItem.Size = new Size(153, 22);
onlineToolStripMenuItem.Text = "Online";
//
// awayToolStripMenuItem
//
awayToolStripMenuItem.Image = Properties.Resources.AwayIcon;
awayToolStripMenuItem.Name = "awayToolStripMenuItem";
awayToolStripMenuItem.Size = new Size(153, 22);
awayToolStripMenuItem.Text = "Away";
//
// doNotDisturbToolStripMenuItem
//
doNotDisturbToolStripMenuItem.Image = Properties.Resources.DNDIcon;
doNotDisturbToolStripMenuItem.Name = "doNotDisturbToolStripMenuItem";
doNotDisturbToolStripMenuItem.Size = new Size(153, 22);
doNotDisturbToolStripMenuItem.Text = "Do Not Disturb";
//
// invisibleToolStripMenuItem
//
invisibleToolStripMenuItem.Image = Properties.Resources.OfflineIcon;
invisibleToolStripMenuItem.Name = "invisibleToolStripMenuItem";
invisibleToolStripMenuItem.Size = new Size(153, 22);
invisibleToolStripMenuItem.Text = "Invisible";
//
// lblWelcome
//
lblWelcome.AutoSize = true;
lblWelcome.Font = new Font("Segoe UI Light", 9F);
lblWelcome.ForeColor = SystemColors.ControlLightLight;
lblWelcome.Location = new Point(65, 17);
lblWelcome.Name = "lblWelcome";
lblWelcome.Size = new Size(60, 15);
lblWelcome.TabIndex = 2;
lblWelcome.Text = "Welcome, ";
//
// llblSignIn
//
llblSignIn.AutoSize = true;
llblSignIn.Font = new Font("Segoe UI Light", 9F);
llblSignIn.LinkColor = Color.White;
llblSignIn.Location = new Point(118, 17);
llblSignIn.Name = "llblSignIn";
llblSignIn.Size = new Size(40, 15);
llblSignIn.TabIndex = 3;
llblSignIn.TabStop = true;
llblSignIn.Text = "Sign In";
llblSignIn.LinkClicked += llblSignIn_LinkClicked;
//
// llblSignOut
//
llblSignOut.AutoSize = true;
llblSignOut.Font = new Font("Segoe UI Light", 9F);
llblSignOut.LinkColor = Color.White;
llblSignOut.Location = new Point(65, 35);
llblSignOut.Name = "llblSignOut";
llblSignOut.Size = new Size(50, 15);
llblSignOut.TabIndex = 4;
llblSignOut.TabStop = true;
llblSignOut.Text = "Sign Out";
llblSignOut.Visible = false;
llblSignOut.LinkClicked += llblSignOut_LinkClicked;
//
// llblEditProfile
//
llblEditProfile.AutoSize = true;
llblEditProfile.Font = new Font("Segoe UI Light", 9F);
llblEditProfile.LinkColor = Color.White;
llblEditProfile.Location = new Point(113, 35);
llblEditProfile.Name = "llblEditProfile";
llblEditProfile.Size = new Size(60, 15);
llblEditProfile.TabIndex = 5;
llblEditProfile.TabStop = true;
llblEditProfile.Text = "Edit Profile";
llblEditProfile.Visible = false;
llblEditProfile.LinkClicked += llblEditProfile_LinkClicked;
//
// lblRequestNotif
//
lblRequestNotif.AutoSize = true;
lblRequestNotif.Font = new Font("Segoe UI", 7F, FontStyle.Bold);
lblRequestNotif.Location = new Point(128, 54);
lblRequestNotif.Name = "lblRequestNotif";
lblRequestNotif.Size = new Size(138, 12);
lblRequestNotif.TabIndex = 6;
lblRequestNotif.Text = "You Have Contacts Requests!";
lblRequestNotif.Visible = false;
//
// niMain
//
niMain.Icon = (Icon)resources.GetObject("niMain.Icon");
niMain.Text = "QtC.NET Client";
niMain.Visible = true;
niMain.DoubleClick += niMain_DoubleClick;
//
// btnAddRoom
//
btnAddRoom.FlatAppearance.BorderSize = 0;
btnAddRoom.FlatStyle = FlatStyle.Flat;
btnAddRoom.Location = new Point(260, 1);
btnAddRoom.Name = "btnAddRoom";
btnAddRoom.Size = new Size(20, 22);
btnAddRoom.TabIndex = 7;
btnAddRoom.UseVisualStyleBackColor = true;
btnAddRoom.Click += btnAddRoom_Click;
//
// pbCurrencyIcon
//
pbCurrencyIcon.Image = Properties.Resources.CurrencyIcon;
pbCurrencyIcon.Location = new Point(223, 5);
pbCurrencyIcon.Name = "pbCurrencyIcon";
pbCurrencyIcon.Size = new Size(15, 14);
pbCurrencyIcon.SizeMode = PictureBoxSizeMode.StretchImage;
pbCurrencyIcon.TabIndex = 9;
pbCurrencyIcon.TabStop = false;
//
// lblCurrencyAmount
//
lblCurrencyAmount.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
lblCurrencyAmount.AutoSize = true;
lblCurrencyAmount.BackColor = Color.Transparent;
lblCurrencyAmount.Font = new Font("Segoe UI", 9F, FontStyle.Bold);
lblCurrencyAmount.ForeColor = Color.White;
lblCurrencyAmount.Location = new Point(239, 5);
lblCurrencyAmount.Name = "lblCurrencyAmount";
lblCurrencyAmount.Size = new Size(14, 15);
lblCurrencyAmount.TabIndex = 10;
lblCurrencyAmount.Text = "0";
lblCurrencyAmount.TextAlign = ContentAlignment.MiddleCenter;
//
// llblClaimSpin
//
llblClaimSpin.AutoSize = true;
llblClaimSpin.Font = new Font("Segoe UI", 9F, FontStyle.Bold);
llblClaimSpin.Location = new Point(185, 35);
llblClaimSpin.Name = "llblClaimSpin";
llblClaimSpin.Size = new Size(94, 15);
llblClaimSpin.TabIndex = 11;
llblClaimSpin.TabStop = true;
llblClaimSpin.Text = "Claim Daily Spin";
llblClaimSpin.Visible = false;
llblClaimSpin.LinkClicked += llblClaimSpin_LinkClicked;
//
// Main
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(282, 613);
Controls.Add(llblClaimSpin);
Controls.Add(pbCurrencyIcon);
Controls.Add(lblCurrencyAmount);
Controls.Add(btnAddRoom);
Controls.Add(lblRequestNotif);
Controls.Add(llblEditProfile);
Controls.Add(llblSignOut);
Controls.Add(llblSignIn);
Controls.Add(lblWelcome);
Controls.Add(pbUserPfp);
Controls.Add(tbcMain);
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
Name = "Main";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client";
FormClosed += frmMain_FormClosed;
Load += frmMain_Load;
Resize += frmMain_Resize;
tbcMain.ResumeLayout(false);
tbpContacts.ResumeLayout(false);
tbpRooms.ResumeLayout(false);
tbpOnlineUsers.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)pbUserPfp).EndInit();
ctxmChangeStatus.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private TabControl tbcMain;
private TabPage tbpContacts;
private TabPage tbpRooms;
private ListBox lbRooms;
private PictureBox pbUserPfp;
private Label lblWelcome;
private LinkLabel llblSignIn;
private LinkLabel llblSignOut;
private TabPage tbpOnlineUsers;
private ListBox lbOnlineUsers;
private LinkLabel llblEditProfile;
private Label lblRequestNotif;
private ListView lvContacts;
private ImageList ilProfilePics;
private NotifyIcon niMain;
private ImageList ilTabIcons;
private ContextMenuStrip ctxmChangeStatus;
private ToolStripMenuItem onlineToolStripMenuItem;
private ToolStripMenuItem awayToolStripMenuItem;
private ToolStripMenuItem doNotDisturbToolStripMenuItem;
private ToolStripMenuItem invisibleToolStripMenuItem;
private Button btnAddRoom;
private PictureBox pbCurrencyIcon;
private Label lblCurrencyAmount;
private LinkLabel llblClaimSpin;
}
}

View File

@ -0,0 +1,706 @@
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using QtCNETAPI.Events;
using QtCNETAPI.Models;
using qtc_net_client_2.Forms;
using qtc_net_client_2.Services;
using qtc_net_client_2.ClientModel;
namespace qtc_net_client_2
{
public partial class Main : Form
{
private IApiService _apiService;
private IGatewayService _gatewayService;
private AudioService AudioService = new();
private List<Room> RoomList = [];
private List<UserInformationDto> OnlineUsers = [];
private List<UserInformationDto> Contacts = [];
public Main(IApiService apiService, IGatewayService gatewayService)
{
_apiService = apiService;
_gatewayService = gatewayService;
InitializeComponent();
}
private async void frmMain_Load(object sender, EventArgs e)
{
// start request notif blink task
Thread blinkThread = new Thread(async () => await StartRequestNotifBlankLoop(lblRequestNotif));
blinkThread.Start();
// check if client is already logged in
if (_apiService.CurrentUser == null)
{
// not logged in, load the login form
Login frmLogin = new Login(_apiService);
var result = frmLogin.ShowDialog(this);
if (result == DialogResult.OK)
await OnSuccessfulLogin();
}
}
private async void llblSignIn_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
// just reshow the login dialog lol
Login frmLogin = new Login(_apiService);
var result = frmLogin.ShowDialog(this);
if (result == DialogResult.OK)
await OnSuccessfulLogin();
}
private async void lbRooms_DoubleClick(object sender, EventArgs e)
{
if (lbRooms.SelectedItems.Count > 0)
{
string? selectedRoom = (string?)lbRooms.SelectedItems[lbRooms.SelectedItems.Count - 1];
if (selectedRoom != null)
{
if (selectedRoom == "Lobby")
{
// join lobby
if (!_gatewayService.InLobby) await _gatewayService.JoinLobbyAsync();
Chat frmChat = new Chat(_gatewayService, _apiService);
frmChat.ShowDialog(this);
return;
}
// join the room
Room? room = RoomList.FirstOrDefault(e => e.Name == selectedRoom);
if (room != null)
{
if (_gatewayService.CurrentRoom != room) await _gatewayService.JoinRoomAsync(room);
Chat frmChat = new Chat(_gatewayService, _apiService);
frmChat.ShowDialog(this);
}
}
}
}
private void pbUserPfp_Click(object sender, EventArgs e)
{
var args = (MouseEventArgs)e;
if (args.Button == MouseButtons.Right) return;
Thread t = new Thread(new ThreadStart(async () =>
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = Environment.CurrentDirectory;
openFileDialog.Title = "Select New Profile Picture";
openFileDialog.Filter = "Image Files (*.png, *.jpg)|*.png;*.jpg";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
// upload to server with api endpoint
var res = await _apiService.UpdateUserProfilePic(openFileDialog.FileName);
if (res.Success)
{
// update profile pic in ui
var pfpRes = await _apiService.GetUserProfilePic(_apiService.CurrentUser.Id);
if (pfpRes.Success && pfpRes.Data != null)
{
using (var ms = new MemoryStream(pfpRes.Data))
{
pbUserPfp.Image = Image.FromStream(ms);
ms.Dispose();
}
}
}
}
}
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
private void llblSignOut_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (MessageBox.Show("Are You Sure You Want To Sign Out?\nThis Deletes Your session.token, Requiring You To Sign In Again", "are you sure...?", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
{
File.Delete("./session.token");
Environment.Exit(0);
}
}
private async void lbOnlineUsers_DoubleClick(object sender, EventArgs e)
{
if (lbOnlineUsers.SelectedItems.Count > 0)
{
string? selectedUser = (string?)lbOnlineUsers.SelectedItems[lbOnlineUsers.SelectedItems.Count - 1];
if (selectedUser != null)
{
// get user info and open profile dialog
var res = await _apiService.GetUserInformationAsync(OnlineUsers.FirstOrDefault(e => e.Username == selectedUser)!.Id);
if (res.Data != null && res.Success)
{
Profile frmProfile = new Profile(res.Data, _apiService, _gatewayService);
frmProfile.ShowDialog(this);
}
}
}
}
private void llblEditProfile_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProfileEdit frmProfileEdit = new ProfileEdit(_apiService);
var dialogResult = frmProfileEdit.ShowDialog(this);
if (dialogResult == DialogResult.OK)
{
MessageBox.Show("If you updated your username, hit the refresh button to see it update on your lists.\nThe top username will not update until you restart your client.");
}
}
private async void lvContacts_DoubleClick(object sender, EventArgs e)
{
if (lvContacts.SelectedItems.Count > 0)
{
string? selectedUser = (string?)lvContacts.SelectedItems[lvContacts.SelectedItems.Count - 1].Text;
if (selectedUser != null)
{
// split from [ if it exists
if (selectedUser.Contains('[')) selectedUser = selectedUser.Split('[', options: StringSplitOptions.TrimEntries)[0];
// get user info and open profile dialog
var res = await _apiService.GetUserInformationAsync(Contacts.FirstOrDefault(e => e.Username == selectedUser)!.Id);
if (res.Data != null && res.Success)
{
Profile frmProfile = new Profile(res.Data, _apiService, _gatewayService);
frmProfile.ShowDialog(this);
}
}
}
}
private async void btnRefresh_Click(object sender, EventArgs e)
{
// refresh all
await RefreshContactsList();
await RefreshRoomsList();
await RefreshOnlineUsersList();
}
private void frmMain_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized) Hide();
}
private void niMain_DoubleClick(object sender, EventArgs e)
{
Show();
WindowState = FormWindowState.Normal;
}
private async void frmMain_FormClosed(object sender, FormClosedEventArgs e)
{
DialogResult = DialogResult.OK;
// ensure the gateway stops the connection and disposes properly
await _gatewayService.StopAsync();
await _gatewayService.DisposeAsync();
}
private async void ctxmChangeStatus_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem != null)
{
UserStatus userStatus = UserStatus.Unknown;
switch (e.ClickedItem.Name)
{
case "onlineToolStripMenuItem":
userStatus = UserStatus.Online;
onlineToolStripMenuItem.Checked = true;
awayToolStripMenuItem.Checked = false;
doNotDisturbToolStripMenuItem.Checked = false;
invisibleToolStripMenuItem.Checked = false;
break;
case "awayToolStripMenuItem":
userStatus = UserStatus.Away;
onlineToolStripMenuItem.Checked = false;
awayToolStripMenuItem.Checked = true;
doNotDisturbToolStripMenuItem.Checked = false;
invisibleToolStripMenuItem.Checked = false;
break;
case "doNotDisturbToolStripMenuItem":
userStatus = UserStatus.DoNotDisturb;
onlineToolStripMenuItem.Checked = false;
awayToolStripMenuItem.Checked = false;
doNotDisturbToolStripMenuItem.Checked = true;
invisibleToolStripMenuItem.Checked = false;
break;
case "invisibleToolStripMenuItem":
userStatus = UserStatus.Offline;
onlineToolStripMenuItem.Checked = false;
awayToolStripMenuItem.Checked = false;
doNotDisturbToolStripMenuItem.Checked = false;
invisibleToolStripMenuItem.Checked = true;
break;
}
// set it
await _gatewayService.UpdateStatus((int)userStatus);
}
}
private void btnAddRoom_Click(object sender, EventArgs e)
{
CreateRoom createRoom = new CreateRoom(_apiService);
createRoom.ShowDialog();
}
private async void llblClaimSpin_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
TokenJackpotSpinner tokenJackpotSpinner = new TokenJackpotSpinner();
var frmResult = tokenJackpotSpinner.ShowDialog();
if (frmResult == DialogResult.OK && tokenJackpotSpinner.TokensWon > 0)
{
// claim
var result = await _apiService.AddCurrencyToCurrentUser(tokenJackpotSpinner.TokensWon, true);
if (result.Success)
{
lblCurrencyAmount.Text = (_apiService.CurrentUser.CurrencyAmount + tokenJackpotSpinner.TokensWon).ToString();
llblClaimSpin.Visible = false;
}
else MessageBox.Show("We Were Unable To Claim Your Prize At This Time. Please Try Again Later.", "Uh Oh.", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private async Task OnSuccessfulLogin()
{
// double check
if (_apiService.CurrentUser != null && _apiService.SessionToken != null)
{
// start gateway connection
await _gatewayService.StartAsync();
// subscribe to gateway events
_gatewayService.OnServerReconnecting += _gatewayService_OnServerReconnecting;
_gatewayService.OnServerReconnected += _gatewayService_OnServerReconnected;
_gatewayService.OnServerDisconnect += _gatewayService_OnServerDisconnect;
_gatewayService.OnClientFunctionReceived += _gatewayService_OnClientFunctionReceived;
_gatewayService.OnDirectMessageReceived += _gatewayService_OnDirectMessageReceived;
_gatewayService.OnServerConfigReceived += _gatewayService_OnServerConfigReceived;
if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
{
// we are fully logged in, get current user profile pic and set up ui
llblSignIn.Visible = false;
lblWelcome.Text = $"Welcome, {_apiService.CurrentUser.Username}";
lblCurrencyAmount.Text = _apiService.CurrentUser.CurrencyAmount.ToString();
llblSignOut.Visible = true;
llblEditProfile.Visible = true;
tbcMain.Enabled = true;
var pfpRes = await _apiService.GetUserProfilePic(_apiService.CurrentUser.Id);
if (pfpRes.Success && pfpRes.Data != null)
{
using (var ms = new MemoryStream(pfpRes.Data))
{
pbUserPfp.Image = Image.FromStream(ms);
ms.Dispose();
}
}
await RefreshContactsList();
await RefreshRoomsList();
await RefreshOnlineUsersList();
// TODO - figure out server side why online status is invisible on login
_apiService.CurrentUser.Status = 1;
// set status context menu checked
UserStatus cuStatus = (UserStatus)_apiService.CurrentUser.Status;
switch (cuStatus)
{
case UserStatus.Online:
onlineToolStripMenuItem.Checked = true;
awayToolStripMenuItem.Checked = false;
doNotDisturbToolStripMenuItem.Checked = false;
invisibleToolStripMenuItem.Checked = false;
break;
case UserStatus.Away:
onlineToolStripMenuItem.Checked = false;
awayToolStripMenuItem.Checked = true;
doNotDisturbToolStripMenuItem.Checked = false;
invisibleToolStripMenuItem.Checked = false;
break;
case UserStatus.DoNotDisturb:
onlineToolStripMenuItem.Checked = false;
awayToolStripMenuItem.Checked = false;
doNotDisturbToolStripMenuItem.Checked = true;
invisibleToolStripMenuItem.Checked = false;
break;
case UserStatus.Offline:
onlineToolStripMenuItem.Checked = false;
awayToolStripMenuItem.Checked = false;
doNotDisturbToolStripMenuItem.Checked = false;
invisibleToolStripMenuItem.Checked = true;
break;
}
if (_apiService.CurrentUser.Role == "Admin") btnAddRoom.Enabled = true;
else btnAddRoom.Enabled = false;
if (DateTime.Compare(DateTime.UtcNow, _apiService.CurrentUser.LastCurrencySpin) < 0 || _apiService.CurrentUser.LastCurrencySpin == new DateTime()) llblClaimSpin.Visible = true;
else llblClaimSpin.Visible = false;
}
}
}
private async Task RefreshOnlineUsersList()
{
if (InvokeRequired)
{
await Invoke(async delegate ()
{
lbOnlineUsers.Items.Clear();
var usersOnlineRes = await _apiService.GetOnlineUsersAsync();
if (usersOnlineRes.Success && usersOnlineRes.Data != null)
{
foreach (var user in usersOnlineRes.Data)
{
lbOnlineUsers.Items.Add(user.Username);
}
OnlineUsers = usersOnlineRes.Data;
}
});
return;
}
lbOnlineUsers.Items.Clear();
var usersOnlineRes = await _apiService.GetOnlineUsersAsync();
if (usersOnlineRes.Success && usersOnlineRes.Data != null)
{
lbOnlineUsers.Items.Clear();
foreach (var user in usersOnlineRes.Data)
{
lbOnlineUsers.Items.Add(user.Username);
}
OnlineUsers = usersOnlineRes.Data;
}
}
private async Task RefreshRoomsList()
{
if (InvokeRequired)
{
await Invoke(async delegate ()
{
lbRooms.Items.Clear();
var roomsRes = await _apiService.GetAllRoomsAsync();
if (roomsRes.Success && roomsRes.Data != null)
{
foreach (var room in roomsRes.Data)
{
lbRooms.Items.Add(room.Name);
}
RoomList = roomsRes.Data;
}
// always add lobby room to rooms list
lbRooms.Items.Add("Lobby");
});
return;
}
lbRooms.Items.Clear();
var roomsRes = await _apiService.GetAllRoomsAsync();
if (roomsRes.Success && roomsRes.Data != null)
{
lbRooms.Items.Clear();
foreach (var room in roomsRes.Data)
{
lbRooms.Items.Add(room.Name);
}
RoomList = roomsRes.Data;
}
// always add lobby room to rooms list
lbRooms.Items.Add("Lobby");
}
private async Task RefreshContactsList()
{
if (InvokeRequired)
{
await Invoke(async delegate ()
{
lvContacts.Items.Clear();
Contacts.Clear();
lblRequestNotif.Visible = false;
var contactsRes = await _apiService.GetCurrentUserContacts();
if (contactsRes.Success && contactsRes.Data != null)
{
if (contactsRes.Data.Where(e => e.UserId == _apiService.CurrentUser!.Id && e.UserStatus == Contact.ContactStatus.AwaitingApprovalFromSelf).Count() >= 1)
lblRequestNotif.Visible = true;
else
lblRequestNotif.Visible = false;
foreach (var contact in contactsRes.Data)
{
ServiceResponse<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(user.Data);
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);
lvi.ImageKey = user.Data.Id;
break;
case Contact.ContactStatus.Accepted:
var lvi2 = lvContacts.Items.Add($"{user.Data.Username}");
await AddProfilePicToList(user.Data.Id);
lvi2.ImageKey = user.Data.Id;
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);
lvi.ImageKey = user.Data.Id;
AudioService.PlaySoundEffect("sndContactRequest");
break;
case Contact.ContactStatus.Accepted:
var lvi2 = lvContacts.Items.Add($"{user.Data.Username}");
await AddProfilePicToList(user.Data.Id);
lvi2.ImageKey = user.Data.Id;
break;
}
}
}
}
}
});
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(user.Data);
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);
lvi.ImageKey = user.Data.Id;
break;
case Contact.ContactStatus.Accepted:
var lvi2 = lvContacts.Items.Add($"{user.Data.Username}");
await AddProfilePicToList(user.Data.Id);
lvi2.ImageKey = user.Data.Id;
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);
lvi.ImageKey = user.Data.Id;
AudioService.PlaySoundEffect("sndContactRequest");
break;
case Contact.ContactStatus.Accepted:
var lvi2 = lvContacts.Items.Add($"{user.Data.Username}");
await AddProfilePicToList(user.Data.Id);
lvi2.ImageKey = user.Data.Id;
break;
}
}
}
}
}
}
private async Task StartRequestNotifBlankLoop(Label label)
{
while (true)
{
if (InvokeRequired && label.IsHandleCreated)
{
await Invoke(async delegate ()
{
label.ForeColor = Color.Red;
await Task.Delay(500);
label.ForeColor = Color.Blue;
await Task.Delay(500);
});
}
else if (label.IsHandleCreated)
{
label.ForeColor = Color.Red;
await Task.Delay(500);
label.ForeColor = Color.Blue;
await Task.Delay(500);
}
}
}
private async Task AddProfilePicToList(string userId)
{
var result = await _apiService.GetUserProfilePic(userId);
if (result != null && result.Success && result.Data != null)
{
using (var ms = new MemoryStream(result.Data))
{
ilProfilePics.Images.Add(userId, Image.FromStream(ms));
ms.Dispose();
}
}
}
private async void _gatewayService_OnServerDisconnect(object? sender, EventArgs e)
{
if (DialogResult == DialogResult.OK) return;
var args = (ServerConnectionClosedEventArgs)e;
string? error = string.Empty;
if (args.Error != null) error = args.Error.Message;
// disconnect can sometimes be caused by an expired JWT token, attempt connection restart
await _gatewayService.StopAsync();
await _gatewayService.StartAsync();
if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
{ BeginInvoke(delegate () { Enabled = true; }); return; }
ConnectionClosed frmConnectionClosed = new ConnectionClosed(error);
var result = frmConnectionClosed.ShowDialog();
if (result == DialogResult.OK)
{
// tell the gateway service to attempt reconnection
Reconnect:
if (_gatewayService.HubConnection != null)
{
try
{
await _gatewayService.StopAsync();
await _gatewayService.StartAsync();
if (_gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
BeginInvoke(delegate () { Enabled = true; });
}
catch (HttpRequestException ex)
{
// reshow the dialog
frmConnectionClosed.Reason = ex.Message;
var result1 = frmConnectionClosed.ShowDialog();
if (result1 == DialogResult.OK) goto Reconnect;
else Environment.Exit(0);
}
}
}
else Environment.Exit(0);
}
private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e) => BeginInvoke(delegate () { Enabled = false; });
private void _gatewayService_OnServerReconnected(object? sender, EventArgs e) => BeginInvoke(delegate () { Enabled = true; });
private async void _gatewayService_OnClientFunctionReceived(object? sender, EventArgs e)
{
var args = (ClientFunctionEventArgs)e;
switch (args.Function)
{
case "rul":
await RefreshOnlineUsersList();
break;
case "rr":
await RefreshRoomsList();
break;
case "rcl":
await RefreshContactsList();
break;
}
}
private async void _gatewayService_OnServerConfigReceived(object? sender, EventArgs e)
{
var args = (ServerConfigEventArgs)e;
if (args.ServerConfig != null)
{
if (args.ServerConfig.IsDown)
{
MessageBox.Show($"Sorry, This Server Is Currently Down.\nMessage: {args.ServerConfig.IsDownMessage}\n\nPlease Try Again Later");
if (_gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
{
await _gatewayService.StopAsync();
await _gatewayService.DisposeAsync();
}
Environment.Exit(0);
}
BeginInvoke(delegate () { Text = $"QtC.NET Client = Connected To {args.ServerConfig.Name}"; });
}
}
private void _gatewayService_OnDirectMessageReceived(object? sender, EventArgs e)
{
var args = (DirectMessageEventArgs)e;
DirectMessage? existingForm = (DirectMessage?)Application.OpenForms.Cast<Form>().FirstOrDefault(e => e.Name == "frmDirectMessage");
if (existingForm != null && existingForm.User.Id == args.User.Id)
{
// we want to just add to its text box
existingForm.Messages.Add($"[{args.User.Username}] {args.Message.Content}\n");
}
else
{
// start a new form
DirectMessage frmDirectMessage = new DirectMessage(_gatewayService, _apiService, args.User, args.Message);
Task.Run(frmDirectMessage.ShowDialog);
}
}
}
}

View File

@ -0,0 +1,333 @@
<?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>
<metadata name="ilProfilePics.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="ilTabIcons.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>122, 19</value>
</metadata>
<data name="ilTabIcons.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA8AgAAAJNU0Z0AUkBTAIBAQIB
AAGAAQABgAEAARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAARADAAEBAQABIAYAARAS
AANvAe8BbQFnAVQB7wFnAV0BVAHvAWcBWwFUAe8BZwFbAVQB7wFnAVkBVAHvAWcBZAFZAe8DZwHvA2cB
7wNnAe8DZwHvA2cB7wNnAe8DZwHvA2cB7wNnAe84AAMzAVEDhwH1gAAD+AH/AbkBlQE8Af8BgwF9AW4B
/wGEAX0BbAH/AaoBhAEnAf8BrAF7AQAB/wHMAbwBlAH/A34B/wN+Af8DfgH/A34B/wN+Af8DfgH/A34B
/wN+Af8DjgH/OAADEgEYAz8BbYAABP8BlwGLAW0B/wKBAYAB/wGCAYEBgAH/AZgBiAFgAf8BygGQAQAB
/wHdAcwBnwH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DkwH/NAADXwHTAz0BZ4QABP8B
hgGEAX0B/wOBAf8DgQH/AYcBgwF6Af8BzwGUAQAB/wHeAcwBnwH/A+AB/wPgAf8D4AH/A+AB/wPgAf8D
4AH/A+AB/wPgAf8DvAH/NAADWgHCAzQBU4QABP8BiwGGAXoB/wOBAf8DgQH/AY4BhgFxAf8BzwGUAQAB
/wHeAcwBnyH/A8oB/xAAAw0BEQM/AWwDUwGnAVwCWQG+AVgCVgGzAUgCRwGDAyEBMAQAA4MB8wM6AWCI
AAT/AawBlgFgAf8BgwGCAX8B/wGFAYIBfQH/AbMBkwFEAf8BzwGUAQAB/wHeAcwBnwH/A7AB/wOwAf8D
sAH/A7AB/wOwAf8DsAH/A7AB/wOwAf8DqAH/CAADGgEkA1IBoAFyAWMBPAH2AaIBcwEAAf8BrgF8AQAB
/wGwAX0BAAH/AagBeAEAAf8BlQFqAQAB/wGAAWQBEAH+AVwCWQHGA1cBtQMWAR6IAAT/AdkBqgE3Af8B
vgGYATgB/wHAAZgBNgH/AdwBogEUAf8BzwGUAQAB/wHeAcwBnwH/A4EB/wOBAf8DgQH/A4EB/wOBAf8D
gQH/A4EB/wOBAf8DkwH/BAADIAEtAWgBYwFaAekBvwGIAQAB/wHNAZUBCgH/AbABiAEnAf8BcwFkAT8B
/wFNAUsBRwH/AU4BSwFCAf8BZgFXATEB/wGaAXQBFwH/AaQBdgEDAf8BcAFPAQAB/wNDAXYEAYQABP8B
4QGuATEB/wG9AZcBOwH/AcABmAE1Af8B4wGlAQoB/wHPAZQBAAH/Ad4BzAGfAf8DwAH/A8AB/wPAAf8D
wAH/A8AB/wPAAf8DwAH/A8AB/wOvAf8EAAFpAWQBWgHpAdkBmgEAAf8B2gGjARwB/wKOAYwB/wOKAf8D
lwH/A5sB/wORAf8DdAH/A0gB/wFDAUIBPwH/AbUBgwEHAf8BegFXAQAB/wM2AViEAAT/AbsBnQFTAf8B
iAGEAXkB/wGMAYUBdAH/AcQBmQEwAf8BzwGUAQAB/wHeAcwBnyH/A8oB/wNDAXYB6QGnAQIB/wHpAasB
EgH/AdABygG7Af8DrAH/A10B/wNMAf8DSwH/A0QB/wMPAf8DswH/A2YB/wFUAUwBOgH/Aa4BfQEEAf8D
XQHMhAAE/wGUAYoBcwH/A4EB/wOBAf8BmgGKAWMB/wHPAZQBAAH/Ad4BzAGfAf8D0AH/A9AB/wPQAf8D
0AH/A9AB/wPQAf8D0AH/A9AB/wO1Af8CagFhAeYB7QGtARAB/wH0AdABdgH/A/oB/wP6Af8DfQH/A38B
/wOAAf8DgAH/A34B/wOEAf8DuwH/A2oB/wGoAYABHAH/AXMBYwE8AfaEAAT/AYYBgwF+Af8DgQH/A4EB
/wGGAYMBewH/Ac8BlAEAAf8B3gHMAZ8B/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A5MB
/wFxAW0BXwHuAe8BtAEhAf8B9wHcAZcJ/wORAf8DiAH/A4cB/wOHAf8DgQH/A1cB/wPmAf8DowH/AbYB
kQE2Af8BiwFqAS8B+YQABP8BiwGGAXkB/wOBAf8DgQH/AY4BhgFxAf8BzwGUAQAB/wHeAcwBnwH/A6EB
/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A0sBjQHwAb4BPwH/AfQBzQFsIf8D+wH/A9AB
/wHXAacBMQH/AmEBXQHRhAAE/wGnAZQBZwH/AYMBggF/Af8BhQGDAX4B/wGwAZQBTgH/AdMBlwECAf8B
4AHOAZ8B/wPnAf8D5wH/A+cB/wPnAf8D5wH/A+cB/wPnAf8D5wH/A8AB/wMHAQkBcQFuAWIB7gHxAbwB
OwH/AfoB6gHCAf8D3AH/A3cB/wNoAf8DaAH/A2gB/wMyCf8B8gHdAakB/wHqAakBCAH/Az4BaoQABP8B
1wGxAVIB/wGaAY8BdAH/AaABkgFtAf8B4AGvATcB/wHnAakBEAH/AesB1QGgAf8DgQH/A4EB/wOBAf8D
gQH/A4EB/wOBAf8DgQH/A4EB/wOTAf8EAAMzAVEBdgFvAWgB8AHzAcYBWAH/AfoB5wG4Fv8B/gH7Af8B
+QHiAaoB/wHvAbgBLQH/A04BlgQChAAE/wH0Ac0BbAH/AfQBywFmAf8B9AHLAWUB/wH0AcsBZQH/AfEB
wQFJAf8B+QHjAawB/wOJAf8DiQH/A4kB/wOJAf8DiQH/A4kB/wOJAf8DiQH/A5oB/wgAAyIBMQNfAckB
rAGbAWcB+gHzAcoBZQH/AfkB4QGmAf8B+wHtAcwB/wH7AewByAH/AfgB3QGbAf8B0gGkAWEB/gJlAV4B
4gM9AWgEAYgAQP8QAAMPARMDRwGCA2QB2wHAAa4BbwH8AWoCZwHqA1QBqAMoATuUAAFCAU0BPgcAAT4D
AAEoAwABQAMAARADAAEBAQABAQUAAYAXAAP/AwAB/wH8BgAB/wH8BgAB/wH5BgAB/wH5BgAB8AETBgAB
wAEDBgABgAEBBgABgAEBBwABAQcAAQEHAAEBBwABAQcAAQEGAAGAAQEGAAHAAQMGAAHwAR8EAAs=
</value>
</data>
<metadata name="ctxmChangeStatus.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>297, 21</value>
</metadata>
<metadata name="niMain.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>216, 21</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="niMain.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABMLAAATCwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAO24wAMtuMAFjbjABy24wAetuMAHHbjABW24wAL9uMAAzbjAAA24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAf24wAdNuMAMTbjADu24wA/duMAP/bjAD/24wA/9uMAPzbjADs24wAv9uM
AG3bjAAa24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAJ24wAYduMANXbjAD+24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/duMAM7bjABY24wABtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAA24wAEtuMAJPbjAD324wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPTbjACH24wADtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMABDbjACf24wA/duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPzbjACT24wAC9uMAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAE24wAh9uMAP3bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPrbjAB524wAAtuM
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAE3bjADw24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AOrbjABA24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAQ24wAvduMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAK/bjAAK24wAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAFHbjAD324wA/9uM
AP/bjAD/24wA/9uLAP/biwD/24wA/9uMAP/bjAD/24wA/9uMAP/biwD/24wA/9uMAP/bjAD/24sA/9uL
AP/bjAD/24wA/9uMAP/bjAD/24wA8tuMAEPbjAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAC24wAntuM
AP/bjAD/24wA/9uLAP/ckQz/4Jwj/9+YG//bjQP/3JEM/9yPB//biwD/3ZIN/96YGv/bjQP/24wA/9uM
Af/elRX/4Jwk/92TD//biwD/24wA/9uMAP/bjAD/24wAj9uMAADbjAAAAAAAAAAAAAAAAAAA24wAANuM
ABbbjADU24wA/9uMAP/biwD/4qM1//Tet//57dj/+OnR/+/Ok//y16f/5atG/92TD//y2Kr/9N63/96V
FP/bjAL/6Lhi//fnzf/47Nf/9uPB/+WrR//biwD/24wA/9uMAP/bjADH24wAD9uMAAAAAAAAAAAAAAAA
AADbjAAA24wAMtuMAO7bjAD/24wA/92TD//14b3/89mt/+SoP//w0Jj///////Parv/dkg3/4qIy//rw
3v/msFH/24wA/+OlOf/68N//7MJ5/+KkNf/uy47/+OrS/+CcJP/biwD/24wA/9uMAObbjAAm24wAAAAA
AAAAAAAAAAAAANuMAADbjABI24wA+duMAP/biwD/4qIy//nv3f/iozX/3pUX//LWpv/25cj/8tis/9uM
AP/ipDb/+ezY/+CcJ//aiQD/68F3//bjwv/ckAz/24oA/92RD//puWX/4Jwl/9uLAP/bjAD/24wA89uM
ADrbjAAAAAAAAAAAAAAAAAAA24wAANuMAFHbjAD824wA/9uKAP/jpzz/+e7b/+CdJ//bjAH/3ZMQ/+zD
fP/137v/240D/+KkNv/57Nj/4J0n/9qJAP/tx4X/89yz/9uNBP/bjAD/24wA/9qKAP/biwD/24wA/9uM
AP/bjAD324wAQtuMAAAAAAAAAAAAAAAAAADbjAAA24wATNuMAPrbjAD/24oA/+OnPP/57tv/4J0n/9uL
AP/aigD/7MJ6//Xfu//cjgX/5axI//rv3v/kqED/24sA/+3Hhf/z3LL/244E/9uMAP/bjAD/24sA/9uM
AP/bjAD/24wA/9uMAPXbjAA924wAAAAAAAAAAAAAAAAAANuMAADbjAA524wA8tuMAP/bigD/46Y6//nu
2//gnin/24sA/9qKAP/sw37/9N+4/9yRC//x0p7//v37//Tdtv/dkxD/7cWB//Tdtv/cjgX/24wA/9uM
Av/elRb/3JAK/9uMAP/bjAD/24wA69uMACzbjAAAAAAAAAAAAAAAAAAA24wAANuMAB7bjADd24wA/9uL
AP/gmyL/+e3Z/+i4Yv/bjAD/3ZMP//TdtP/w0Jj/24sA/+WuS//68eD/5KpD/9uLAP/otl3/+e3Y/+Gf
Kv/biwD/46c8//XiwP/iozT/24sA/9uMAP/bjADS24wAFduMAAAAAAAAAAAAAAAAAADbjAAA24wABtuM
AK/bjAD/24wA/9uNAv/sxH3/+vDg//LZq//14sH/+OvU/+KkNv/aigD/4Z4p//HUov/fmB3/24oA/96V
Fv/03bX/+OnQ//LWpv/57dn/8NGa/9yQCv/bjAD/24wA/9uMAKDbjAAC24wAAAAAAAAAAAAAAAAAANuM
AADbjAAA24wAZtuMAP3bjAD/24wA/9yPCf/mr0//7cmI/+zCef/hoCz/24wA/9uMAP/bjQL/3I8I/9uN
Af/bjAD/24sA/96XGf/pu2r/7sqK/+i1XP/dkg//24sA/9uMAP/bjAD624wAVtuMAADbjAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAc24wA09uMAP/bjAD/24wA/9qKAP/biwD/2ooA/9uLAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24sA/9qKAP/biwD/2ooA/9uMAP/bjAD/24wA/9uMAMfbjAAU24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjABr24wA+9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD324wAXNuM
AADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAA7bjACr24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AJ7bjAAJ24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMACTbjADG24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAC724wAHduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuM
ACvbjADA24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD+24wAtduMACPbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADbjAAA24wAANuMABzbjACV24wA8duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA7tuMAIvbjAAW24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAAbbjABG24wAqtuMAOnbjAD+24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD924wA59uMAKTbjAA/24wABNuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjAAH24wALtuMAGXbjACS24wAq9uM
ALPbjACq24wAj9uMAGHbjAAq24wABduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuM
AADbjAAD24wABduMAALbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA//////////////////AH///AAf//AAB//gAAP/wAAB/4AAAP+AAAD/AA
AAfwAAAH4AAAB+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD8AAAB/AAAAf4AAAP+AAAD/wA
AB/+AAA//wAAf/+AAP//4AP///4///////8=
</value>
</data>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>55</value>
</metadata>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABMLAAATCwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAO24wAMtuMAFjbjABy24wAetuMAHHbjABW24wAL9uMAAzbjAAA24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAf24wAdNuMAMTbjADu24wA/duMAP/bjAD/24wA/9uMAPzbjADs24wAv9uM
AG3bjAAa24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAJ24wAYduMANXbjAD+24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/duMAM7bjABY24wABtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAA24wAEtuMAJPbjAD324wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPTbjACH24wADtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMABDbjACf24wA/duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPzbjACT24wAC9uMAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAE24wAh9uMAP3bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPrbjAB524wAAtuM
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAE3bjADw24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AOrbjABA24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAQ24wAvduMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAK/bjAAK24wAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAFHbjAD324wA/9uM
AP/bjAD/24wA/9uLAP/biwD/24wA/9uMAP/bjAD/24wA/9uMAP/biwD/24wA/9uMAP/bjAD/24sA/9uL
AP/bjAD/24wA/9uMAP/bjAD/24wA8tuMAEPbjAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAC24wAntuM
AP/bjAD/24wA/9uLAP/ckQz/4Jwj/9+YG//bjQP/3JEM/9yPB//biwD/3ZIN/96YGv/bjQP/24wA/9uM
Af/elRX/4Jwk/92TD//biwD/24wA/9uMAP/bjAD/24wAj9uMAADbjAAAAAAAAAAAAAAAAAAA24wAANuM
ABbbjADU24wA/9uMAP/biwD/4qM1//Tet//57dj/+OnR/+/Ok//y16f/5atG/92TD//y2Kr/9N63/96V
FP/bjAL/6Lhi//fnzf/47Nf/9uPB/+WrR//biwD/24wA/9uMAP/bjADH24wAD9uMAAAAAAAAAAAAAAAA
AADbjAAA24wAMtuMAO7bjAD/24wA/92TD//14b3/89mt/+SoP//w0Jj///////Parv/dkg3/4qIy//rw
3v/msFH/24wA/+OlOf/68N//7MJ5/+KkNf/uy47/+OrS/+CcJP/biwD/24wA/9uMAObbjAAm24wAAAAA
AAAAAAAAAAAAANuMAADbjABI24wA+duMAP/biwD/4qIy//nv3f/iozX/3pUX//LWpv/25cj/8tis/9uM
AP/ipDb/+ezY/+CcJ//aiQD/68F3//bjwv/ckAz/24oA/92RD//puWX/4Jwl/9uLAP/bjAD/24wA89uM
ADrbjAAAAAAAAAAAAAAAAAAA24wAANuMAFHbjAD824wA/9uKAP/jpzz/+e7b/+CdJ//bjAH/3ZMQ/+zD
fP/137v/240D/+KkNv/57Nj/4J0n/9qJAP/tx4X/89yz/9uNBP/bjAD/24wA/9qKAP/biwD/24wA/9uM
AP/bjAD324wAQtuMAAAAAAAAAAAAAAAAAADbjAAA24wATNuMAPrbjAD/24oA/+OnPP/57tv/4J0n/9uL
AP/aigD/7MJ6//Xfu//cjgX/5axI//rv3v/kqED/24sA/+3Hhf/z3LL/244E/9uMAP/bjAD/24sA/9uM
AP/bjAD/24wA/9uMAPXbjAA924wAAAAAAAAAAAAAAAAAANuMAADbjAA524wA8tuMAP/bigD/46Y6//nu
2//gnin/24sA/9qKAP/sw37/9N+4/9yRC//x0p7//v37//Tdtv/dkxD/7cWB//Tdtv/cjgX/24wA/9uM
Av/elRb/3JAK/9uMAP/bjAD/24wA69uMACzbjAAAAAAAAAAAAAAAAAAA24wAANuMAB7bjADd24wA/9uL
AP/gmyL/+e3Z/+i4Yv/bjAD/3ZMP//TdtP/w0Jj/24sA/+WuS//68eD/5KpD/9uLAP/otl3/+e3Y/+Gf
Kv/biwD/46c8//XiwP/iozT/24sA/9uMAP/bjADS24wAFduMAAAAAAAAAAAAAAAAAADbjAAA24wABtuM
AK/bjAD/24wA/9uNAv/sxH3/+vDg//LZq//14sH/+OvU/+KkNv/aigD/4Z4p//HUov/fmB3/24oA/96V
Fv/03bX/+OnQ//LWpv/57dn/8NGa/9yQCv/bjAD/24wA/9uMAKDbjAAC24wAAAAAAAAAAAAAAAAAANuM
AADbjAAA24wAZtuMAP3bjAD/24wA/9yPCf/mr0//7cmI/+zCef/hoCz/24wA/9uMAP/bjQL/3I8I/9uN
Af/bjAD/24sA/96XGf/pu2r/7sqK/+i1XP/dkg//24sA/9uMAP/bjAD624wAVtuMAADbjAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAc24wA09uMAP/bjAD/24wA/9qKAP/biwD/2ooA/9uLAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24sA/9qKAP/biwD/2ooA/9uMAP/bjAD/24wA/9uMAMfbjAAU24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjABr24wA+9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD324wAXNuM
AADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAA7bjACr24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AJ7bjAAJ24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMACTbjADG24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAC724wAHduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuM
ACvbjADA24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD+24wAtduMACPbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADbjAAA24wAANuMABzbjACV24wA8duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA7tuMAIvbjAAW24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAAbbjABG24wAqtuMAOnbjAD+24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD924wA59uMAKTbjAA/24wABNuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjAAH24wALtuMAGXbjACS24wAq9uM
ALPbjACq24wAj9uMAGHbjAAq24wABduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuM
AADbjAAD24wABduMAALbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA//////////////////AH///AAf//AAB//gAAP/wAAB/4AAAP+AAAD/AA
AAfwAAAH4AAAB+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD8AAAB/AAAAf4AAAP+AAAD/wA
AB/+AAA//wAAf/+AAP//4AP///4///////8=
</value>
</data>
</root>

257
qtc-net-client-2/Forms/Profile.Designer.cs generated Normal file
View File

@ -0,0 +1,257 @@
namespace qtc_net_client_2.Forms
{
partial class Profile
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Profile));
pbUserPfp = new PictureBox();
lblUsername = new Label();
rtxtBio = new RichTextBox();
btnAddContact = new Button();
btnAccept = new Button();
btnDecline = new Button();
btnCancelRequest = new Button();
btnMessage = new Button();
pbUserStatus = new PictureBox();
pbCurrencyIcon = new PictureBox();
lblCurrencyAmount = new Label();
flpUsernameCurrency = new FlowLayoutPanel();
flpCurrency = new FlowLayoutPanel();
((System.ComponentModel.ISupportInitialize)pbUserPfp).BeginInit();
((System.ComponentModel.ISupportInitialize)pbUserStatus).BeginInit();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).BeginInit();
flpUsernameCurrency.SuspendLayout();
flpCurrency.SuspendLayout();
SuspendLayout();
//
// pbUserPfp
//
pbUserPfp.BorderStyle = BorderStyle.FixedSingle;
pbUserPfp.Image = Properties.Resources.DefaultPfp;
pbUserPfp.Location = new Point(12, 12);
pbUserPfp.Name = "pbUserPfp";
pbUserPfp.Size = new Size(128, 126);
pbUserPfp.SizeMode = PictureBoxSizeMode.StretchImage;
pbUserPfp.TabIndex = 2;
pbUserPfp.TabStop = false;
//
// lblUsername
//
lblUsername.AutoSize = true;
lblUsername.Font = new Font("Segoe UI Light", 15F, FontStyle.Bold);
lblUsername.ForeColor = SystemColors.ControlLight;
lblUsername.Location = new Point(3, 0);
lblUsername.Name = "lblUsername";
lblUsername.Size = new Size(105, 28);
lblUsername.TabIndex = 3;
lblUsername.Text = "Username";
//
// rtxtBio
//
rtxtBio.Location = new Point(151, 42);
rtxtBio.Name = "rtxtBio";
rtxtBio.ReadOnly = true;
rtxtBio.Size = new Size(356, 96);
rtxtBio.TabIndex = 4;
rtxtBio.Text = "";
//
// btnAddContact
//
btnAddContact.BackColor = Color.DodgerBlue;
btnAddContact.FlatAppearance.BorderSize = 0;
btnAddContact.FlatStyle = FlatStyle.Flat;
btnAddContact.Image = Properties.Resources.AddContactIcon;
btnAddContact.Location = new Point(455, 4);
btnAddContact.Name = "btnAddContact";
btnAddContact.Size = new Size(59, 36);
btnAddContact.TabIndex = 5;
btnAddContact.UseVisualStyleBackColor = false;
btnAddContact.Visible = false;
//
// btnAccept
//
btnAccept.BackColor = Color.DodgerBlue;
btnAccept.FlatAppearance.BorderSize = 0;
btnAccept.FlatStyle = FlatStyle.Flat;
btnAccept.Image = Properties.Resources.AcceptContactIcon;
btnAccept.Location = new Point(404, 4);
btnAccept.Name = "btnAccept";
btnAccept.Size = new Size(54, 38);
btnAccept.TabIndex = 6;
btnAccept.UseVisualStyleBackColor = false;
btnAccept.Visible = false;
btnAccept.Click += btnAccept_Click;
//
// btnDecline
//
btnDecline.BackColor = Color.DodgerBlue;
btnDecline.FlatAppearance.BorderSize = 0;
btnDecline.FlatStyle = FlatStyle.Flat;
btnDecline.Image = Properties.Resources.DeclineContactIcon;
btnDecline.Location = new Point(455, 3);
btnDecline.Name = "btnDecline";
btnDecline.Size = new Size(52, 38);
btnDecline.TabIndex = 7;
btnDecline.UseVisualStyleBackColor = false;
btnDecline.Visible = false;
btnDecline.Click += btnDecline_Click;
//
// btnCancelRequest
//
btnCancelRequest.BackColor = Color.DodgerBlue;
btnCancelRequest.FlatAppearance.BorderSize = 0;
btnCancelRequest.FlatStyle = FlatStyle.Flat;
btnCancelRequest.Image = Properties.Resources.CancelRequestIcon;
btnCancelRequest.Location = new Point(400, 1);
btnCancelRequest.Name = "btnCancelRequest";
btnCancelRequest.Size = new Size(57, 42);
btnCancelRequest.TabIndex = 8;
btnCancelRequest.UseVisualStyleBackColor = false;
btnCancelRequest.Visible = false;
btnCancelRequest.Click += btnCancelRequest_Click;
//
// btnMessage
//
btnMessage.BackColor = Color.DodgerBlue;
btnMessage.FlatAppearance.BorderSize = 0;
btnMessage.FlatStyle = FlatStyle.Flat;
btnMessage.Image = Properties.Resources.MessageIcon;
btnMessage.Location = new Point(405, 1);
btnMessage.Name = "btnMessage";
btnMessage.Size = new Size(46, 42);
btnMessage.TabIndex = 9;
btnMessage.UseVisualStyleBackColor = false;
btnMessage.Visible = false;
btnMessage.Click += btnMessage_Click;
//
// pbUserStatus
//
pbUserStatus.BackColor = Color.Transparent;
pbUserStatus.Image = Properties.Resources.OfflineIcon;
pbUserStatus.Location = new Point(115, 1);
pbUserStatus.Name = "pbUserStatus";
pbUserStatus.Size = new Size(32, 32);
pbUserStatus.SizeMode = PictureBoxSizeMode.StretchImage;
pbUserStatus.TabIndex = 10;
pbUserStatus.TabStop = false;
//
// pbCurrencyIcon
//
pbCurrencyIcon.Image = Properties.Resources.CurrencyIcon;
pbCurrencyIcon.Location = new Point(3, 3);
pbCurrencyIcon.Name = "pbCurrencyIcon";
pbCurrencyIcon.Size = new Size(15, 14);
pbCurrencyIcon.SizeMode = PictureBoxSizeMode.StretchImage;
pbCurrencyIcon.TabIndex = 11;
pbCurrencyIcon.TabStop = false;
//
// lblCurrencyAmount
//
lblCurrencyAmount.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
lblCurrencyAmount.AutoSize = true;
lblCurrencyAmount.BackColor = Color.Transparent;
lblCurrencyAmount.Font = new Font("Segoe UI", 9F, FontStyle.Bold);
lblCurrencyAmount.ForeColor = Color.White;
lblCurrencyAmount.Location = new Point(24, 5);
lblCurrencyAmount.Name = "lblCurrencyAmount";
lblCurrencyAmount.Size = new Size(14, 15);
lblCurrencyAmount.TabIndex = 12;
lblCurrencyAmount.Text = "0";
lblCurrencyAmount.TextAlign = ContentAlignment.MiddleCenter;
//
// flpUsernameCurrency
//
flpUsernameCurrency.BackColor = Color.Transparent;
flpUsernameCurrency.Controls.Add(lblUsername);
flpUsernameCurrency.Controls.Add(flpCurrency);
flpUsernameCurrency.Location = new Point(152, 8);
flpUsernameCurrency.Name = "flpUsernameCurrency";
flpUsernameCurrency.Size = new Size(246, 33);
flpUsernameCurrency.TabIndex = 13;
flpUsernameCurrency.WrapContents = false;
//
// flpCurrency
//
flpCurrency.Controls.Add(pbCurrencyIcon);
flpCurrency.Controls.Add(lblCurrencyAmount);
flpCurrency.Location = new Point(114, 3);
flpCurrency.Name = "flpCurrency";
flpCurrency.Size = new Size(76, 23);
flpCurrency.TabIndex = 4;
//
// Profile
//
AutoScaleDimensions = new SizeF(6F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(519, 155);
Controls.Add(flpUsernameCurrency);
Controls.Add(btnAccept);
Controls.Add(btnDecline);
Controls.Add(btnCancelRequest);
Controls.Add(pbUserStatus);
Controls.Add(btnAddContact);
Controls.Add(rtxtBio);
Controls.Add(pbUserPfp);
Controls.Add(btnMessage);
Font = new Font("Segoe UI Light", 9F);
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
MinimizeBox = false;
Name = "Profile";
StartPosition = FormStartPosition.CenterParent;
Text = "QtC.NET Client - User Profile";
Load += frmProfile_Load;
((System.ComponentModel.ISupportInitialize)pbUserPfp).EndInit();
((System.ComponentModel.ISupportInitialize)pbUserStatus).EndInit();
((System.ComponentModel.ISupportInitialize)pbCurrencyIcon).EndInit();
flpUsernameCurrency.ResumeLayout(false);
flpUsernameCurrency.PerformLayout();
flpCurrency.ResumeLayout(false);
flpCurrency.PerformLayout();
ResumeLayout(false);
}
#endregion
private PictureBox pbUserPfp;
private Label lblUsername;
private RichTextBox rtxtBio;
private Button btnAddContact;
private Button btnAccept;
private Button btnDecline;
private Button btnCancelRequest;
private Button btnMessage;
private PictureBox pbUserStatus;
private PictureBox pbCurrencyIcon;
private Label lblCurrencyAmount;
private FlowLayoutPanel flpUsernameCurrency;
private FlowLayoutPanel flpCurrency;
}
}

View File

@ -0,0 +1,211 @@
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Models;
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;
using QtCNETAPI.Services.GatewayService;
using qtc_net_client_2.ClientModel;
using qtc_net_client_2.Properties;
namespace qtc_net_client_2.Forms
{
public partial class Profile : Form
{
UserInformationDto _userInformationDto;
IApiService _apiService;
IGatewayService _gatewayService;
public Profile(UserInformationDto userInfo, IApiService apiService, IGatewayService gatewayService)
{
_userInformationDto = userInfo;
_apiService = apiService;
_gatewayService = gatewayService;
InitializeComponent();
}
private async void frmProfile_Load(object sender, EventArgs e)
{
lblUsername.Text = _userInformationDto.Username;
lblCurrencyAmount.Text = _userInformationDto.CurrencyAmount.ToString();
rtxtBio.Text = _userInformationDto.Bio;
var pfpRes = await _apiService.GetUserProfilePic(_userInformationDto.Id);
if (pfpRes.Success && pfpRes.Data != null)
{
using (var ms = new MemoryStream(pfpRes.Data))
{
pbUserPfp.Image = Image.FromStream(ms);
ms.Dispose();
}
}
var userStatus = (UserStatus)_userInformationDto.Status;
switch(userStatus)
{
case UserStatus.Online:
pbUserStatus.Image = Resources.OnlineIcon;
break;
case UserStatus.Away:
pbUserStatus.Image = Resources.AwayIcon;
break;
case UserStatus.DoNotDisturb:
pbUserStatus.Image = Resources.DNDIcon;
break;
case UserStatus.Offline:
pbUserStatus.Image = Resources.OfflineIcon;
break;
}
if (_userInformationDto.Id == _apiService.CurrentUser!.Id)
{
btnAddContact.Visible = false;
btnMessage.Visible = false;
return; // do not run contact getting code
}
var contactsResult = await _apiService.GetCurrentUserContacts();
if (contactsResult.Success && contactsResult.Data != null)
{
var contact = contactsResult.Data.FirstOrDefault(e => e.UserId == _userInformationDto.Id || e.OwnerId == _userInformationDto.Id);
if (contact != null)
{
if (contact.OwnerId == _apiService.CurrentUser.Id)
{
// the user requesting this profile initiated a contact relationship with this user
switch (contact.OwnerStatus)
{
case Contact.ContactStatus.AwaitingApprovalFromOther:
btnAddContact.Enabled = false;
using (var ms = new MemoryStream(Resources.RequestSentIcon)) { btnAddContact.Image = Image.FromStream(ms); ms.Dispose(); }
btnCancelRequest.Visible = true;
break;
case Contact.ContactStatus.Accepted:
btnAddContact.Enabled = true;
btnAddContact.Image = Resources.RemoveContactIcon;
btnAddContact.Click += btnAddContact_Click_Remove;
if (_userInformationDto.Status >= 1) btnMessage.Visible = true;
break;
}
}
else if (contact.UserId == _apiService.CurrentUser.Id)
{
// the user requesting this profile did not initiate a contact relationship with the user
switch (contact.UserStatus)
{
case Contact.ContactStatus.AwaitingApprovalFromSelf:
btnAddContact.Visible = false;
btnAccept.Visible = true;
btnDecline.Visible = true;
btnCancelRequest.Visible = false;
break;
case Contact.ContactStatus.Accepted:
btnAccept.Visible = false;
btnDecline.Visible = false;
btnCancelRequest.Visible = false;
btnAddContact.Visible = true;
btnAddContact.Image = Resources.RemoveContactIcon;
btnAddContact.Click += btnAddContact_Click_Remove;
if (_userInformationDto.Status >= 1) btnMessage.Visible = true;
break;
}
}
}
}
else
{
btnAddContact.Visible = true;
btnAddContact.Click += btnAddContact_Click_Add;
}
}
private async void btnAddContact_Click_Add(object sender, EventArgs e)
{
var result = await _apiService.AddContactToCurrentUser(_userInformationDto.Id);
if (result.Success)
{
btnAddContact.Enabled = false;
using (var ms = new MemoryStream(Resources.RequestSentIcon)) { btnAddContact.Image = Image.FromStream(ms); ms.Dispose(); }
btnCancelRequest.Visible = true;
await _gatewayService.RefreshContactsForUser(_userInformationDto);
}
}
private async void btnAddContact_Click_Remove(object sender, EventArgs e)
{
var result = await _apiService.RemoveContactFromCurrentUser(_userInformationDto.Id);
if (result.Success)
{
btnAddContact.Image = Resources.AddContactIcon;
btnAddContact.Click += btnAddContact_Click_Add;
btnMessage.Visible = false;
await _gatewayService.RefreshContactsForUser(_userInformationDto);
}
}
private async void btnAccept_Click(object sender, EventArgs e)
{
var result = await _apiService.AcceptContactRequest(_userInformationDto.Id);
if (result.Success)
{
btnAccept.Visible = false;
btnDecline.Visible = false;
btnAddContact.Visible = true;
btnAddContact.Image = Resources.RemoveContactIcon;
btnAddContact.Click += btnAddContact_Click_Remove;
if (_userInformationDto.Status >= 1) btnMessage.Visible = true;
await _gatewayService.RefreshContactsForUser(_userInformationDto);
}
}
private async void btnDecline_Click(object sender, EventArgs e)
{
var result = await _apiService.RemoveContactFromCurrentUser(_userInformationDto.Id);
if (result.Success)
{
btnAccept.Visible = false;
btnDecline.Visible = false;
btnAddContact.Visible = true;
btnAddContact.Image = Resources.AddContactIcon;
btnAddContact.Click += btnAddContact_Click_Add;
await _gatewayService.RefreshContactsForUser(_userInformationDto);
}
}
private async void btnCancelRequest_Click(object sender, EventArgs e)
{
var result = await _apiService.RemoveContactFromCurrentUser(_userInformationDto.Id);
if (result.Success)
{
btnAddContact.Enabled = true;
btnAddContact.Image = Resources.AddContactIcon;
btnAddContact.Click += btnAddContact_Click_Add;
btnCancelRequest.Visible = false;
await _gatewayService.RefreshContactsForUser(_userInformationDto);
}
}
private void btnMessage_Click(object sender, EventArgs e)
{
DirectMessage frmDirectMessage = new DirectMessage(_gatewayService, _apiService, _userInformationDto);
Close();
frmDirectMessage.Show();
}
}
}

View File

@ -0,0 +1,144 @@
<?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>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEACAUAAAEACABZBAAAFgAAACgAAAAIAAAACgAAAAEACAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAA
AADy4bcA18WYAO/LcwDPyr0Aq6urALKysgC0sKcAgYGBAK+KLwDHwbEAp6enANvChQCrkE4AypQOAN3X
xwDV1dUAwsLCALy0oACCgX4AtYwnAL+rewCCdFQAnncVAJqUhwB5eXkAgICAAP///wAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAFBUWFxgYGBkREhMODw8PEAsMDQ4PDw8QBgcICQQEBAoAAQIDBAQEBQAAAAAAAAAAAA==
</value>
</data>
</root>

View File

@ -0,0 +1,114 @@
namespace qtc_net_client_2.Forms
{
partial class ProfileEdit
{
/// <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()
{
tbUsername = new TextBox();
lblUsername = new Label();
rtxtBio = new RichTextBox();
lblBio = new Label();
btnSave = new Button();
SuspendLayout();
//
// tbUsername
//
tbUsername.Location = new Point(76, 17);
tbUsername.Name = "tbUsername";
tbUsername.Size = new Size(257, 23);
tbUsername.TabIndex = 0;
//
// lblUsername
//
lblUsername.AutoSize = true;
lblUsername.Location = new Point(12, 20);
lblUsername.Name = "lblUsername";
lblUsername.Size = new Size(58, 15);
lblUsername.TabIndex = 1;
lblUsername.Text = "Username";
//
// rtxtBio
//
rtxtBio.Location = new Point(76, 46);
rtxtBio.Name = "rtxtBio";
rtxtBio.Size = new Size(257, 96);
rtxtBio.TabIndex = 2;
rtxtBio.Text = "";
//
// lblBio
//
lblBio.AutoSize = true;
lblBio.Location = new Point(47, 49);
lblBio.Name = "lblBio";
lblBio.Size = new Size(23, 15);
lblBio.TabIndex = 3;
lblBio.Text = "Bio";
//
// btnSave
//
btnSave.ForeColor = Color.Black;
btnSave.Location = new Point(76, 148);
btnSave.Name = "btnSave";
btnSave.Size = new Size(43, 23);
btnSave.TabIndex = 4;
btnSave.Text = "Save";
btnSave.UseVisualStyleBackColor = true;
btnSave.Click += btnSave_Click;
//
// frmProfileEdit
//
AutoScaleDimensions = new SizeF(6F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(345, 187);
Controls.Add(btnSave);
Controls.Add(lblBio);
Controls.Add(rtxtBio);
Controls.Add(lblUsername);
Controls.Add(tbUsername);
Font = new Font("Segoe UI Light", 9F);
ForeColor = SystemColors.ControlLight;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
Name = "frmProfileEdit";
StartPosition = FormStartPosition.CenterParent;
Text = "QtC.NET Client - Edit Profile";
Load += frmProfileEdit_Load;
ResumeLayout(false);
PerformLayout();
}
#endregion
private TextBox tbUsername;
private Label lblUsername;
private RichTextBox rtxtBio;
private Label lblBio;
private Button btnSave;
}
}

View File

@ -0,0 +1,59 @@
using QtCNETAPI.Dtos.User;
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 ProfileEdit : Form
{
IApiService _apiService;
public ProfileEdit(IApiService apiService)
{
_apiService = apiService;
InitializeComponent();
}
private void frmProfileEdit_Load(object sender, EventArgs e)
{
tbUsername.Text = _apiService.CurrentUser!.Username;
rtxtBio.Text = _apiService.CurrentUser!.Bio;
}
private async void btnSave_Click(object sender, EventArgs e)
{
if(!string.IsNullOrEmpty(tbUsername.Text) && (tbUsername.Text != _apiService.CurrentUser!.Username || rtxtBio.Text != _apiService.CurrentUser!.Bio))
{
// update user info
UserUpdateInformationDto userUpdateInformationDto = new UserUpdateInformationDto
{
Id = _apiService.CurrentUser.Id,
Username = tbUsername.Text,
Bio = rtxtBio.Text,
DateOfBirth = _apiService.CurrentUser.DateOfBirth
};
var res = await _apiService.UpdateUserInformationAsync(userUpdateInformationDto);
if (res.Success)
{
DialogResult = DialogResult.OK;
Close();
}
else
{
MessageBox.Show(res.Message, "Info Update Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
DialogResult = DialogResult.Cancel;
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,241 @@
namespace qtc_net_client_2.Forms
{
partial class Register
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Register));
pbLoginBanner = new PictureBox();
lblEmail = new Label();
tbEmail = new TextBox();
lblPassword = new Label();
tbPassword = new TextBox();
lblConPassword = new Label();
tbConPassword = new TextBox();
lblConEmail = new Label();
tbConEmail = new TextBox();
dtpDateOfBirth = new DateTimePicker();
lblDateOfBirth = new Label();
btnRegister = new Button();
cbAgreement = new CheckBox();
lblUsername = new Label();
tbUsername = new TextBox();
((System.ComponentModel.ISupportInitialize)pbLoginBanner).BeginInit();
SuspendLayout();
//
// pbLoginBanner
//
pbLoginBanner.Image = Properties.Resources.LoginBanner;
pbLoginBanner.Location = new Point(-2, -1);
pbLoginBanner.Name = "pbLoginBanner";
pbLoginBanner.Size = new Size(543, 99);
pbLoginBanner.SizeMode = PictureBoxSizeMode.StretchImage;
pbLoginBanner.TabIndex = 1;
pbLoginBanner.TabStop = false;
//
// lblEmail
//
lblEmail.AutoSize = true;
lblEmail.Font = new Font("Segoe UI Light", 9F);
lblEmail.ForeColor = SystemColors.ControlLight;
lblEmail.Location = new Point(29, 143);
lblEmail.Name = "lblEmail";
lblEmail.Size = new Size(33, 15);
lblEmail.TabIndex = 4;
lblEmail.Text = "Email";
//
// tbEmail
//
tbEmail.Location = new Point(68, 140);
tbEmail.Name = "tbEmail";
tbEmail.Size = new Size(461, 23);
tbEmail.TabIndex = 3;
//
// lblPassword
//
lblPassword.AutoSize = true;
lblPassword.Font = new Font("Segoe UI Light", 9F);
lblPassword.ForeColor = SystemColors.ControlLight;
lblPassword.Location = new Point(7, 172);
lblPassword.Name = "lblPassword";
lblPassword.Size = new Size(55, 15);
lblPassword.TabIndex = 6;
lblPassword.Text = "Password";
//
// tbPassword
//
tbPassword.Location = new Point(68, 169);
tbPassword.Name = "tbPassword";
tbPassword.PasswordChar = '*';
tbPassword.Size = new Size(461, 23);
tbPassword.TabIndex = 5;
//
// lblConPassword
//
lblConPassword.AutoSize = true;
lblConPassword.Font = new Font("Segoe UI Light", 6F);
lblConPassword.ForeColor = SystemColors.ControlLight;
lblConPassword.Location = new Point(2, 232);
lblConPassword.Name = "lblConPassword";
lblConPassword.Size = new Size(64, 11);
lblConPassword.TabIndex = 10;
lblConPassword.Text = "Confirm Password";
//
// tbConPassword
//
tbConPassword.Location = new Point(68, 227);
tbConPassword.Name = "tbConPassword";
tbConPassword.PasswordChar = '*';
tbConPassword.Size = new Size(461, 23);
tbConPassword.TabIndex = 9;
//
// lblConEmail
//
lblConEmail.AutoSize = true;
lblConEmail.Font = new Font("Segoe UI Light", 7F);
lblConEmail.ForeColor = SystemColors.ControlLight;
lblConEmail.Location = new Point(2, 203);
lblConEmail.Name = "lblConEmail";
lblConEmail.Size = new Size(63, 12);
lblConEmail.TabIndex = 8;
lblConEmail.Text = "Confirm Email";
//
// tbConEmail
//
tbConEmail.Location = new Point(68, 198);
tbConEmail.Name = "tbConEmail";
tbConEmail.Size = new Size(461, 23);
tbConEmail.TabIndex = 7;
//
// dtpDateOfBirth
//
dtpDateOfBirth.Location = new Point(68, 256);
dtpDateOfBirth.Name = "dtpDateOfBirth";
dtpDateOfBirth.Size = new Size(461, 23);
dtpDateOfBirth.TabIndex = 11;
//
// lblDateOfBirth
//
lblDateOfBirth.AutoSize = true;
lblDateOfBirth.Font = new Font("Segoe UI Light", 8F);
lblDateOfBirth.ForeColor = SystemColors.ControlLight;
lblDateOfBirth.Location = new Point(0, 260);
lblDateOfBirth.Name = "lblDateOfBirth";
lblDateOfBirth.Size = new Size(66, 13);
lblDateOfBirth.TabIndex = 12;
lblDateOfBirth.Text = "Date Of Birth";
//
// btnRegister
//
btnRegister.Location = new Point(68, 285);
btnRegister.Name = "btnRegister";
btnRegister.Size = new Size(75, 23);
btnRegister.TabIndex = 13;
btnRegister.Text = "Register";
btnRegister.UseVisualStyleBackColor = true;
btnRegister.Click += btnRegister_Click;
//
// cbAgreement
//
cbAgreement.AutoSize = true;
cbAgreement.Font = new Font("Segoe UI Light", 9F);
cbAgreement.ForeColor = SystemColors.ControlLight;
cbAgreement.Location = new Point(149, 288);
cbAgreement.Name = "cbAgreement";
cbAgreement.Size = new Size(356, 19);
cbAgreement.TabIndex = 14;
cbAgreement.Text = "By Registering, I Agree To Any Rules Or Terms This Server Imposes";
cbAgreement.UseVisualStyleBackColor = true;
//
// lblUsername
//
lblUsername.AutoSize = true;
lblUsername.Font = new Font("Segoe UI Light", 9F);
lblUsername.ForeColor = SystemColors.ControlLight;
lblUsername.Location = new Point(8, 114);
lblUsername.Name = "lblUsername";
lblUsername.Size = new Size(58, 15);
lblUsername.TabIndex = 16;
lblUsername.Text = "Username";
//
// tbUsername
//
tbUsername.Location = new Point(68, 111);
tbUsername.Name = "tbUsername";
tbUsername.Size = new Size(461, 23);
tbUsername.TabIndex = 15;
//
// frmRegister
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(537, 319);
Controls.Add(lblUsername);
Controls.Add(tbUsername);
Controls.Add(cbAgreement);
Controls.Add(btnRegister);
Controls.Add(lblDateOfBirth);
Controls.Add(dtpDateOfBirth);
Controls.Add(lblConPassword);
Controls.Add(tbConPassword);
Controls.Add(lblConEmail);
Controls.Add(tbConEmail);
Controls.Add(lblPassword);
Controls.Add(tbPassword);
Controls.Add(lblEmail);
Controls.Add(tbEmail);
Controls.Add(pbLoginBanner);
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
Name = "frmRegister";
StartPosition = FormStartPosition.CenterParent;
Text = "QtC.NET Client - Register";
((System.ComponentModel.ISupportInitialize)pbLoginBanner).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private PictureBox pbLoginBanner;
private Label lblEmail;
private TextBox tbEmail;
private Label lblPassword;
private TextBox tbPassword;
private Label lblConPassword;
private TextBox tbConPassword;
private Label lblConEmail;
private TextBox tbConEmail;
private DateTimePicker dtpDateOfBirth;
private Label lblDateOfBirth;
private Button btnRegister;
private CheckBox cbAgreement;
private Label lblUsername;
private TextBox tbUsername;
}
}

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 Register : Form
{
IApiService _apiService;
public Register(IApiService apiService)
{
_apiService = apiService;
InitializeComponent();
}
private async void btnRegister_Click(object sender, EventArgs e)
{
if( !string.IsNullOrEmpty(tbUsername.Text) &&
!string.IsNullOrEmpty(tbEmail.Text) &&
!string.IsNullOrEmpty(tbPassword.Text) &&
!string.IsNullOrEmpty(tbConEmail.Text) &&
!string.IsNullOrEmpty(tbConPassword.Text) &&
tbConEmail.Text == tbEmail.Text &&
tbConPassword.Text == tbPassword.Text &&
cbAgreement.Checked)
{
DisableControls();
var registerResult = await _apiService.RegisterAsync(new QtCNETAPI.Dtos.User.UserDto
{
Username = tbUsername.Text,
Email = tbEmail.Text,
Password = tbPassword.Text,
DateOfBirth = dtpDateOfBirth.Value
});
if(registerResult.Success)
{
DialogResult = DialogResult.OK;
Close();
} else
{
MessageBox.Show(registerResult.Message, "Registration Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
DialogResult = DialogResult.Cancel;
Close();
}
}
}
private void DisableControls()
{
tbUsername.Enabled = false;
tbEmail.Enabled = false;
tbConEmail.Enabled = false;
tbPassword.Enabled = false;
tbConPassword.Enabled = false;
dtpDateOfBirth.Enabled = false;
cbAgreement.Enabled = false;
}
}
}

View File

@ -0,0 +1,197 @@
<?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>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABMLAAATCwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAO24wAMtuMAFjbjABy24wAetuMAHHbjABW24wAL9uMAAzbjAAA24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA24wAANuMAADbjAAf24wAdNuMAMTbjADu24wA/duMAP/bjAD/24wA/9uMAPzbjADs24wAv9uM
AG3bjAAa24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAJ24wAYduMANXbjAD+24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/duMAM7bjABY24wABtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAA24wAEtuMAJPbjAD324wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPTbjACH24wADtuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMABDbjACf24wA/duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPzbjACT24wAC9uMAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAE24wAh9uMAP3bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAPrbjAB524wAAtuM
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAE3bjADw24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AOrbjABA24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAQ24wAvduMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAK/bjAAK24wAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMAFHbjAD324wA/9uM
AP/bjAD/24wA/9uLAP/biwD/24wA/9uMAP/bjAD/24wA/9uMAP/biwD/24wA/9uMAP/bjAD/24sA/9uL
AP/bjAD/24wA/9uMAP/bjAD/24wA8tuMAEPbjAAAAAAAAAAAAAAAAAAAAAAAANuMAADbjAAC24wAntuM
AP/bjAD/24wA/9uLAP/ckQz/4Jwj/9+YG//bjQP/3JEM/9yPB//biwD/3ZIN/96YGv/bjQP/24wA/9uM
Af/elRX/4Jwk/92TD//biwD/24wA/9uMAP/bjAD/24wAj9uMAADbjAAAAAAAAAAAAAAAAAAA24wAANuM
ABbbjADU24wA/9uMAP/biwD/4qM1//Tet//57dj/+OnR/+/Ok//y16f/5atG/92TD//y2Kr/9N63/96V
FP/bjAL/6Lhi//fnzf/47Nf/9uPB/+WrR//biwD/24wA/9uMAP/bjADH24wAD9uMAAAAAAAAAAAAAAAA
AADbjAAA24wAMtuMAO7bjAD/24wA/92TD//14b3/89mt/+SoP//w0Jj///////Parv/dkg3/4qIy//rw
3v/msFH/24wA/+OlOf/68N//7MJ5/+KkNf/uy47/+OrS/+CcJP/biwD/24wA/9uMAObbjAAm24wAAAAA
AAAAAAAAAAAAANuMAADbjABI24wA+duMAP/biwD/4qIy//nv3f/iozX/3pUX//LWpv/25cj/8tis/9uM
AP/ipDb/+ezY/+CcJ//aiQD/68F3//bjwv/ckAz/24oA/92RD//puWX/4Jwl/9uLAP/bjAD/24wA89uM
ADrbjAAAAAAAAAAAAAAAAAAA24wAANuMAFHbjAD824wA/9uKAP/jpzz/+e7b/+CdJ//bjAH/3ZMQ/+zD
fP/137v/240D/+KkNv/57Nj/4J0n/9qJAP/tx4X/89yz/9uNBP/bjAD/24wA/9qKAP/biwD/24wA/9uM
AP/bjAD324wAQtuMAAAAAAAAAAAAAAAAAADbjAAA24wATNuMAPrbjAD/24oA/+OnPP/57tv/4J0n/9uL
AP/aigD/7MJ6//Xfu//cjgX/5axI//rv3v/kqED/24sA/+3Hhf/z3LL/244E/9uMAP/bjAD/24sA/9uM
AP/bjAD/24wA/9uMAPXbjAA924wAAAAAAAAAAAAAAAAAANuMAADbjAA524wA8tuMAP/bigD/46Y6//nu
2//gnin/24sA/9qKAP/sw37/9N+4/9yRC//x0p7//v37//Tdtv/dkxD/7cWB//Tdtv/cjgX/24wA/9uM
Av/elRb/3JAK/9uMAP/bjAD/24wA69uMACzbjAAAAAAAAAAAAAAAAAAA24wAANuMAB7bjADd24wA/9uL
AP/gmyL/+e3Z/+i4Yv/bjAD/3ZMP//TdtP/w0Jj/24sA/+WuS//68eD/5KpD/9uLAP/otl3/+e3Y/+Gf
Kv/biwD/46c8//XiwP/iozT/24sA/9uMAP/bjADS24wAFduMAAAAAAAAAAAAAAAAAADbjAAA24wABtuM
AK/bjAD/24wA/9uNAv/sxH3/+vDg//LZq//14sH/+OvU/+KkNv/aigD/4Z4p//HUov/fmB3/24oA/96V
Fv/03bX/+OnQ//LWpv/57dn/8NGa/9yQCv/bjAD/24wA/9uMAKDbjAAC24wAAAAAAAAAAAAAAAAAANuM
AADbjAAA24wAZtuMAP3bjAD/24wA/9yPCf/mr0//7cmI/+zCef/hoCz/24wA/9uMAP/bjQL/3I8I/9uN
Af/bjAD/24sA/96XGf/pu2r/7sqK/+i1XP/dkg//24sA/9uMAP/bjAD624wAVtuMAADbjAAAAAAAAAAA
AAAAAAAAAAAAANuMAADbjAAc24wA09uMAP/bjAD/24wA/9qKAP/biwD/2ooA/9uLAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24sA/9qKAP/biwD/2ooA/9uMAP/bjAD/24wA/9uMAMfbjAAU24wAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjABr24wA+9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD324wAXNuM
AADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAA7bjACr24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AJ7bjAAJ24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuMACTbjADG24wA/9uM
AP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAC724wAHduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbjAAA24wAANuM
ACvbjADA24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD+24wAtduMACPbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADbjAAA24wAANuMABzbjACV24wA8duMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD/24wA7tuMAIvbjAAW24wAANuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAAbbjABG24wAqtuMAOnbjAD+24wA/9uMAP/bjAD/24wA/9uM
AP/bjAD924wA59uMAKTbjAA/24wABNuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuMAADbjAAH24wALtuMAGXbjACS24wAq9uM
ALPbjACq24wAj9uMAGHbjAAq24wABduMAADbjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24wAANuM
AADbjAAD24wABduMAALbjAAA24wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA//////////////////AH///AAf//AAB//gAAP/wAAB/4AAAP+AAAD/AA
AAfwAAAH4AAAB+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD8AAAB/AAAAf4AAAP+AAAD/wA
AB/+AAA//wAAf/+AAP//4AP///4///////8=
</value>
</data>
</root>

View File

@ -0,0 +1,85 @@
namespace qtc_net_client_2.Forms
{
partial class TokenJackpotSpinner
{
/// <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()
{
lblTokensWon = new Label();
btnClaim = new Button();
SuspendLayout();
//
// lblTokensWon
//
lblTokensWon.AutoSize = true;
lblTokensWon.Font = new Font("Segoe UI Black", 13F);
lblTokensWon.Location = new Point(137, 30);
lblTokensWon.Name = "lblTokensWon";
lblTokensWon.Size = new Size(137, 25);
lblTokensWon.TabIndex = 0;
lblTokensWon.Text = "0 Tokens Won";
lblTokensWon.TextAlign = ContentAlignment.MiddleCenter;
//
// btnClaim
//
btnClaim.Enabled = false;
btnClaim.Location = new Point(167, 76);
btnClaim.Name = "btnClaim";
btnClaim.Size = new Size(75, 23);
btnClaim.TabIndex = 1;
btnClaim.Text = "Claim";
btnClaim.UseVisualStyleBackColor = true;
btnClaim.Click += btnClaim_Click;
//
// TokenJackpotSpinner
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.DodgerBlue;
ClientSize = new Size(427, 125);
Controls.Add(btnClaim);
Controls.Add(lblTokensWon);
DoubleBuffered = true;
ForeColor = Color.Black;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
Name = "TokenJackpotSpinner";
StartPosition = FormStartPosition.CenterScreen;
Text = "QtC.NET Client - Daily Jackpot Spin";
FormClosing += TokenJackpotSpinner_FormClosing;
FormClosed += TokenJackpotSpinner_FormClosed;
Load += TokenJackpotSpinner_Load;
ResumeLayout(false);
PerformLayout();
}
#endregion
private Label lblTokensWon;
private Button btnClaim;
}
}

View File

@ -0,0 +1,77 @@
using qtc_net_client_2.Services;
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 TokenJackpotSpinner : Form
{
public int TokensWon { get; private set; }
private bool AllowClose = false;
private AudioService _audioService = new();
public TokenJackpotSpinner()
{
InitializeComponent();
}
private async void TokenJackpotSpinner_Load(object sender, EventArgs e)
{
btnClaim.Enabled = false;
AllowClose = false;
await StartSpinAnimation(lblTokensWon);
}
private void TokenJackpotSpinner_FormClosed(object sender, FormClosedEventArgs e)
{
DialogResult = DialogResult.OK;
Close();
}
private void TokenJackpotSpinner_FormClosing(object sender, FormClosingEventArgs e)
{
if (!AllowClose) e.Cancel = true;
}
private void btnClaim_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
Close();
}
public async Task StartSpinAnimation(Label label)
{
if (label.IsHandleCreated)
{
Random rnd = new Random();
_audioService.PlaySoundEffect("sndTokenSpinLoop");
while (_audioService.OutputDevice?.PlaybackState == NAudio.Wave.PlaybackState.Playing)
{
label.BeginInvoke(delegate () { label.Text = $"{rnd.Next(0, 300)} Tokens Won"; });
await Task.Delay(10);
}
var win = rnd.Next(0, 300);
label.BeginInvoke(delegate ()
{
label.Text = $"{win} Tokens Won";
btnClaim.Enabled = true;
_audioService.PlaySoundEffect("sndTokenWin");
});
_audioService.Dispose();
AllowClose = true;
TokensWon = win;
}
}
}
}

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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,60 @@
using qtc_net_client_2.ClientModel;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using System.Text.Json;
using System.Threading.Tasks;
namespace qtc_net_client_2
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static async Task Main()
{
Config clientConfig = new Config();
// find config file
if(!File.Exists("./config.json"))
{
// create it using default config model
string configJson = JsonSerializer.Serialize(clientConfig, options: new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText("./config.json", configJson);
} else
{
// use config in file
Config? fileConfig = JsonSerializer.Deserialize<Config>(File.ReadAllText("./config.json"));
if(fileConfig != null)
clientConfig = fileConfig;
}
// instantiate new ApiService and GatewayService for this session
// this will keep the gateway thread in the main thread (i think)
IApiService api = new ApiService(clientConfig.ApiEndpoint);
IGatewayService gateway = new GatewayService(clientConfig.GatewayEndpoint, api);
// ping api
var pingResult = api.PingServerAsync().GetAwaiter().GetResult();
if (!pingResult.Success)
{
MessageBox.Show("The API Specified In The Config Could Not Be Reached.\nCheck The URL Specified, Otherwise Contact The Server Admin.", "Uh Oh.", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(1);
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new Main(api, gateway));
// if application loop is exited, dispose everything
await gateway.DisposeAsync();
api = null;
gateway = null;
Environment.Exit(0);
}
}
}

View File

@ -0,0 +1,213 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace qtc_net_client_2.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("qtc_net_client_2.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap AcceptContactIcon {
get {
object obj = ResourceManager.GetObject("AcceptContactIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap AddContactIcon {
get {
object obj = ResourceManager.GetObject("AddContactIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap AwayIcon {
get {
object obj = ResourceManager.GetObject("AwayIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap CancelRequestIcon {
get {
object obj = ResourceManager.GetObject("CancelRequestIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap CurrencyIcon {
get {
object obj = ResourceManager.GetObject("CurrencyIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap DeclineContactIcon {
get {
object obj = ResourceManager.GetObject("DeclineContactIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap DefaultPfp {
get {
object obj = ResourceManager.GetObject("DefaultPfp", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap DNDIcon {
get {
object obj = ResourceManager.GetObject("DNDIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap LoginBanner {
get {
object obj = ResourceManager.GetObject("LoginBanner", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MessageIcon {
get {
object obj = ResourceManager.GetObject("MessageIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap OfflineIcon {
get {
object obj = ResourceManager.GetObject("OfflineIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap OnlineIcon {
get {
object obj = ResourceManager.GetObject("OnlineIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap RemoveContactIcon {
get {
object obj = ResourceManager.GetObject("RemoveContactIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] RequestSentIcon {
get {
object obj = ResourceManager.GetObject("RequestSentIcon", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap SendIcon {
get {
object obj = ResourceManager.GetObject("SendIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View File

@ -0,0 +1,166 @@
<?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>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="SendIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\SendIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="DefaultPfp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\DefaultPfp.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="OfflineIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\OfflineIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="OnlineIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\OnlineIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="CancelRequestIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\CancelRequestIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="DNDIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\DNDIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="AwayIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\AwayIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="AddContactIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\AddContactIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="RequestSentIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\RequestSentIcon.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="RemoveContactIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\RemoveContactIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MessageIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\MessageIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="LoginBanner" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\LoginBanner.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="DeclineContactIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\DeclineContactIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="AcceptContactIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\AcceptContactIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="CurrencyIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Icons\CurrencyIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,53 @@
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Media;
using System.Text;
using System.Threading.Tasks;
namespace qtc_net_client_2.Services
{
public class AudioService : IDisposable
{
public WaveOutEvent? OutputDevice { get; set; }
public AudioFileReader? AudioFileReader { get; set; }
public void PlaySoundEffect(string soundName)
{
if (!File.Exists($"./Sounds/{soundName}.wav")) return;
OutputDevice = new WaveOutEvent();
AudioFileReader = new AudioFileReader($"./Sounds/{soundName}.wav");
OutputDevice.Init(AudioFileReader);
OutputDevice.Play();
return;
}
public void PlaySoundLooped(string soundName, int loopCount)
{
if (!File.Exists($"./Sounds/{soundName}.wav")) return;
OutputDevice = new WaveOutEvent();
AudioFileReader = new AudioFileReader($"./Sounds/{soundName}.wav");
OutputDevice.Init(AudioFileReader);
OutputDevice.Play();
int i = 0;
OutputDevice.PlaybackStopped += (sender, args) =>
{
i++;
if (i == loopCount) { OutputDevice.PlaybackStopped += null; return; }
AudioFileReader.Seek(0, SeekOrigin.Begin);
OutputDevice.Play();
};
}
public void Dispose()
{
if (OutputDevice != null) { OutputDevice.Dispose(); OutputDevice = null; }
if (AudioFileReader != null) { AudioFileReader.Dispose(); AudioFileReader = null; }
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<RootNamespace>qtc_net_client_2</RootNamespace>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>QtCNETAppIconV2.ico</ApplicationIcon>
<AssemblyName>QtCNetChat</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Content Include="QtCNETAppIconV2.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NAudio" Version="2.2.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QtCNETAPI\QtCNETAPI.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Sounds\sndContactRequest.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Sounds\sndDirectMsg.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Sounds\sndMessage.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Sounds\sndSendClick.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Sounds\sndTokenJackpot.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Sounds\sndTokenSpinLoop.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Sounds\sndTokenWin.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>