├── .gitignore ├── Project.Chat.Client.sln ├── Project.Chat.Common ├── Models │ ├── Message.cs │ ├── Topic.cs │ └── User.cs ├── PacketUtils.cs ├── Packets │ ├── CreateTopicPacket.cs │ ├── ErrorPacket.cs │ ├── LoginAuthPacket.cs │ ├── LoginSuccessPacket.cs │ ├── Packet.cs │ ├── PacketWrapper.cs │ ├── RegisterAuthPacket.cs │ ├── SendMessagePacket.cs │ ├── ShowTopicPacket.cs │ ├── TopicHistoryPacket.cs │ └── TopicsListPacket.cs └── Project.Chat.Common.csproj ├── Project.Chat.Server ├── AppServer.cs ├── Client.cs ├── Project.Chat.Server.csproj ├── Repository │ ├── FileBasedMessageRepository.cs │ ├── FileBasedTopicRepository.cs │ ├── FileBasedUserRepository.cs │ ├── IMessageRepository.cs │ ├── ITopicRepository.cs │ └── IUserRepository.cs └── Util │ └── PasswordChecker.cs ├── Project.Chat ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── Converters │ ├── Assembly.cs │ ├── AutoScrollBehavior.cs │ ├── BoolToColumnIndexConverter.cs │ ├── BoolToColumnIndexInverseConverter.cs │ ├── BoolToHorizontalAlignmentConverter.cs │ ├── BoolToMarginConverter.cs │ ├── BoolToTextAlignmentConverter.cs │ └── MessageSentByUserVisibilityConverter.cs ├── Project.Chat.csproj ├── Project.Chat.csproj.user ├── Screens │ ├── LoginRegisterWindow.xaml │ ├── LoginRegisterWindow.xaml.cs │ ├── MainWindow.xaml │ └── MainWindow.xaml.cs ├── SocketClient.cs ├── Store.cs ├── modals │ ├── CreateTopicModal.xaml │ ├── CreateTopicModal.xaml.cs │ ├── ErrorModal.xaml │ └── ErrorModal.xaml.cs └── viewModels │ ├── CreateTopicViewModel.cs │ ├── LoginRegisterViewModel.cs │ └── MainViewModel.cs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 |  2 | # Ignore Visual Studio temporary files, build results, and 3 | # files generated by popular Visual Studio add-ons. 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | 8 | # User-specific files 9 | *.rsuser 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # Mono Auto Generated Files 16 | mono_crash.* 17 | 18 | # VS Code directories 19 | .vscode/ 20 | .history/ 21 | 22 | # Rider directories 23 | .idea/ 24 | 25 | # Backup & report files from converting an old project file 26 | _UpgradeReport_Files/ 27 | Backup*/ 28 | UpgradeLog*.XML 29 | UpgradeLog*.htm 30 | 31 | # SQL Server files 32 | *.mdf 33 | *.ldf 34 | *.ndf 35 | 36 | # Business Intelligence projects 37 | *.rdl.data 38 | *.bim.layout 39 | *.bim_*.settings 40 | *.rptproj.rsuser 41 | 42 | # Microsoft Fakes 43 | FakesAssemblies/ 44 | 45 | # Node.js 46 | node_modules/ 47 | npm-debug.log 48 | yarn-error.log 49 | 50 | # JetBrains Rider 51 | .idea/ 52 | *.sln.iml 53 | 54 | # Others 55 | *.class 56 | *.jar 57 | *.war 58 | *.ear 59 | hs_err_pid* 60 | *.log 61 | *.stackdump 62 | *.dmp 63 | 64 | # Windows image file caches 65 | Thumbs.db 66 | ehthumbs.db 67 | 68 | # Folder config file 69 | Desktop.ini 70 | 71 | # Recycle Bin used on file shares 72 | $RECYCLE.BIN/ 73 | 74 | # VS Code directories 75 | .vscode/ 76 | .vscode/* 77 | .vscode/cpptools/ 78 | 79 | # Rider directories 80 | .idea/ 81 | .idea/* 82 | 83 | # Rider files 84 | .idea/ 85 | 86 | # Rider files (system) 87 | .idea_modules/ 88 | 89 | # Windows image file caches 90 | Thumbs.db 91 | ehthumbs.db 92 | 93 | # Folder config file 94 | Desktop.ini 95 | 96 | # Recycle Bin used on file shares 97 | $RECYCLE.BIN/ 98 | 99 | # Temporary files 100 | *.tmp 101 | *.temp 102 | 103 | # Temporary files for the Git index 104 | .git/index.lock 105 | 106 | # Build folders (can be generated if needed) 107 | _build/ 108 | -------------------------------------------------------------------------------- /Project.Chat.Client.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.10.34928.147 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.Chat", "Project.Chat\Project.Chat.csproj", "{643928E4-21B8-41F6-8E94-844D62AF455A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.Chat.Server", "Project.Chat.Server\Project.Chat.Server.csproj", "{E77ADD56-6CE2-4226-BF7E-C35AB44E49F8}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.Chat.Common", "Project.Chat.Common\Project.Chat.Common.csproj", "{D977046A-E3D3-483B-BC9D-D24DB51F05ED}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{78BC34ED-AA7B-4EF5-BC1B-1C7A49630A57}" 13 | ProjectSection(SolutionItems) = preProject 14 | .gitignore = .gitignore 15 | README.md = README.md 16 | EndProjectSection 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {643928E4-21B8-41F6-8E94-844D62AF455A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {643928E4-21B8-41F6-8E94-844D62AF455A}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {643928E4-21B8-41F6-8E94-844D62AF455A}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {643928E4-21B8-41F6-8E94-844D62AF455A}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {E77ADD56-6CE2-4226-BF7E-C35AB44E49F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {E77ADD56-6CE2-4226-BF7E-C35AB44E49F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {E77ADD56-6CE2-4226-BF7E-C35AB44E49F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {E77ADD56-6CE2-4226-BF7E-C35AB44E49F8}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {D977046A-E3D3-483B-BC9D-D24DB51F05ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {D977046A-E3D3-483B-BC9D-D24DB51F05ED}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {D977046A-E3D3-483B-BC9D-D24DB51F05ED}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {D977046A-E3D3-483B-BC9D-D24DB51F05ED}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {020EE99E-F6D3-47DA-9524-562A0E8B0698} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /Project.Chat.Common/Models/Message.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.Json.Serialization; 7 | using System.Threading.Tasks; 8 | 9 | namespace Project.Chat.Common.Models 10 | { 11 | [Serializable] 12 | public class Message 13 | { 14 | public Guid Id { get; set; } 15 | public Topic Topic { get; set; } 16 | public User Sender { get; set; } 17 | public DateTime Time { get; set; } 18 | public string Content { get; set; } 19 | 20 | [JsonIgnore] 21 | public bool IsSentByCurrentUser { get; set; } 22 | 23 | [JsonIgnore] 24 | public bool IsNotSentByCurrentUser => !IsSentByCurrentUser; 25 | 26 | [JsonIgnore] 27 | public string FormatedTime => Time.ToString("HH:mm:ss - dd/MM/yyyy"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Project.Chat.Common/Models/Topic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Project.Chat.Common.Models 8 | { 9 | [Serializable] 10 | public class Topic 11 | { 12 | 13 | public Guid Id { get; set; } 14 | public string Name { get; set; } 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Project.Chat.Common/Models/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Serialization; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Common.Model 9 | { 10 | [Serializable] 11 | public class User 12 | { 13 | 14 | public Guid Id { get; set; } 15 | public string Username { get; set; } 16 | public string Password { get; set; } 17 | public string AvatarUrl { get; set; } 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Project.Chat.Common/PacketUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization.Formatters.Binary; 5 | using System.Text; 6 | using System.Text.Json; 7 | using System.Threading.Tasks; 8 | 9 | namespace Project.Chat.Common 10 | { 11 | public static class PacketUtils 12 | { 13 | public static byte[] Serialize(T obj) 14 | { 15 | if (obj == null) return null; 16 | Console.WriteLine("Sending: " + JsonSerializer.Serialize(obj)); 17 | return JsonSerializer.SerializeToUtf8Bytes(obj); 18 | } 19 | 20 | public static T Deserialize(byte[] data) 21 | { 22 | if (data == null) return default(T); 23 | //Console.WriteLine("Received: " + Encoding.Default.GetString(data)); 24 | return JsonSerializer.Deserialize(data); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/CreateTopicPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Project.Chat.Common.Packets 8 | { 9 | [Serializable] 10 | public class CreateTopicPacket : Packet 11 | { 12 | 13 | public string Name { get; set; } 14 | 15 | public int Id => 5; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/ErrorPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Project.Chat.Common.Packets 8 | { 9 | [Serializable] 10 | public class ErrorPacket : Packet 11 | { 12 | public string Message { get; set; } 13 | 14 | public int Id => 2; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/LoginAuthPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Common.Packets 9 | { 10 | [Serializable] 11 | public class LoginAuthPacket : Packet 12 | { 13 | public string Username { get; set; } 14 | public string Password { get; set; } 15 | 16 | public int Id => 1; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/LoginSuccessPacket.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Common.Packets 9 | { 10 | [Serializable] 11 | public class LoginSuccessPacket : Packet 12 | { 13 | public User User { get; set; } 14 | 15 | public int Id => 4; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/Packet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Project.Chat.Common.Packets 8 | { 9 | public interface Packet 10 | { 11 | public int Id { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/PacketWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Project.Chat.Common.Packets 8 | { 9 | [Serializable] 10 | public class PacketWrapper 11 | { 12 | public int PacketId { get; set; } 13 | public byte[] Data { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/RegisterAuthPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Project.Chat.Common.Packets 8 | { 9 | [Serializable] 10 | public class RegisterAuthPacket : Packet 11 | { 12 | 13 | public string Username { get; set; } 14 | public string Password { get; set; } 15 | 16 | public int Id => 3; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/SendMessagePacket.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Models; 2 | 3 | namespace Project.Chat.Common.Packets 4 | { 5 | [Serializable] 6 | public class SendMessagePacket : Packet 7 | { 8 | public Topic Topic { get; set; } 9 | 10 | public string Content { get; set; } 11 | 12 | public int Id => 7; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/ShowTopicPacket.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Common.Packets 9 | { 10 | [Serializable] 11 | public class ShowTopicPacket : Packet 12 | { 13 | 14 | public Topic Topic { get; set; } 15 | 16 | public int Id => 8; 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/TopicHistoryPacket.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Common.Packets 9 | { 10 | [Serializable] 11 | public class TopicHistoryPacket : Packet 12 | { 13 | 14 | public Topic Topic { get; set; } 15 | 16 | public List Messages { get; set; } 17 | 18 | public int Id => 9; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Project.Chat.Common/Packets/TopicsListPacket.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Common.Packets 9 | { 10 | [Serializable] 11 | public class TopicsListPacket : Packet 12 | { 13 | 14 | public List Topics { get; set; } 15 | 16 | public int Id => 6; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Project.Chat.Common/Project.Chat.Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Project.Chat.Server/AppServer.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Packets; 2 | using Project.Chat.Common; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using Project.Chat.Server; 6 | using Project.Chat.Server.Repository; 7 | using Project.Chat.Common.Model; 8 | 9 | public class AppServer 10 | { 11 | 12 | private static List connectedClients = new List(); 13 | private static IUserRepository userRepository; 14 | private static ITopicRepository topicRepository; 15 | private static IMessageRepository messageRepository; 16 | 17 | public static User SystemUser = new User() 18 | { 19 | Username = "System", 20 | AvatarUrl = "https://gravatar.com/avatar/system", 21 | Id = Guid.NewGuid() 22 | }; 23 | 24 | static void Main(string[] args) 25 | { 26 | int port = 12345; 27 | IPAddress ipAddress = IPAddress.Any; 28 | TcpListener listener = new TcpListener(ipAddress, port); 29 | 30 | userRepository = new FileBasedUserRepository("./users.json"); 31 | topicRepository = new FileBasedTopicRepository("./topics.json"); 32 | messageRepository = new FileBasedMessageRepository("./messages.json"); 33 | 34 | listener.Start(); 35 | Console.WriteLine("Serveur de socket démarré..."); 36 | 37 | while (true) 38 | { 39 | // Accepter une nouvelle connexion 40 | TcpClient client = listener.AcceptTcpClient(); 41 | Console.WriteLine("Client connecté..."); 42 | 43 | // Créer un nouveau thread pour gérer la connexion 44 | Thread clientThread = new Thread(() => HandleClient(client)); 45 | clientThread.Start(); 46 | } 47 | } 48 | 49 | public static void AddClient(Client clientHandler) 50 | { 51 | lock (connectedClients) 52 | { 53 | connectedClients.Add(clientHandler); 54 | } 55 | } 56 | 57 | public static void RemoveClient(Client clientHandler) 58 | { 59 | lock (connectedClients) 60 | { 61 | connectedClients.Remove(clientHandler); 62 | } 63 | } 64 | 65 | public static List GetConnectedClients() 66 | { 67 | lock (connectedClients) 68 | { 69 | return new List(connectedClients); 70 | } 71 | } 72 | 73 | static void HandleClient(TcpClient tpcClient) 74 | { 75 | var client = new Client(tpcClient, userRepository, topicRepository, messageRepository); 76 | AddClient(client); 77 | NetworkStream stream = tpcClient.GetStream(); 78 | byte[] buffer = new byte[4096 * 10]; 79 | int bytesRead; 80 | 81 | try 82 | { 83 | while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0) 84 | { 85 | byte[] data = new byte[bytesRead]; 86 | Array.Copy(buffer, data, bytesRead); 87 | PacketWrapper packet = PacketUtils.Deserialize(data); 88 | Console.WriteLine($"Reçu paquet avec ID: {packet.PacketId}"); 89 | 90 | client.HandlePacket(packet); 91 | } 92 | } 93 | catch (Exception e) 94 | { 95 | Console.WriteLine($"Erreur: {e.Message}"); 96 | } 97 | finally 98 | { 99 | stream.Close(); 100 | tpcClient.Close(); 101 | RemoveClient(client); 102 | Console.WriteLine("Client déconnecté..."); 103 | } 104 | } 105 | public static void BroadcastPacket(T packet) where T : Packet 106 | { 107 | connectedClients.ForEach(client => { 108 | client.SendPacket(packet); 109 | }); 110 | } 111 | } -------------------------------------------------------------------------------- /Project.Chat.Server/Client.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Packets; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Project.Chat.Common; 9 | using Project.Chat.Server.Repository; 10 | using Project.Chat.Common.Model; 11 | using Project.Chat.Server.Util; 12 | using Project.Chat.Common.Models; 13 | using System.Text.Json; 14 | 15 | namespace Project.Chat.Server 16 | { 17 | public class Client 18 | { 19 | private TcpClient tcpClient; 20 | private NetworkStream stream; 21 | public string clientId { get; private set; } 22 | 23 | private User user; 24 | private IUserRepository userRepository; 25 | private ITopicRepository topicRepository; 26 | private IMessageRepository messageRepository; 27 | 28 | private bool IsConnected => user != null; 29 | 30 | public Client(TcpClient client, 31 | IUserRepository userRepository, 32 | ITopicRepository topicRepository, 33 | IMessageRepository messageRepository) 34 | { 35 | tcpClient = client; 36 | stream = tcpClient.GetStream(); 37 | clientId = Guid.NewGuid().ToString(); 38 | this.userRepository = userRepository; 39 | this.topicRepository = topicRepository; 40 | this.messageRepository = messageRepository; 41 | } 42 | 43 | public void SendPacket(T packet) where T : Packet 44 | { 45 | PacketWrapper wrapper = new PacketWrapper 46 | { 47 | PacketId = packet.Id, 48 | Data = PacketUtils.Serialize(packet) 49 | }; 50 | byte[] data = PacketUtils.Serialize(wrapper); 51 | stream.Write(data, 0, data.Length); 52 | } 53 | 54 | public void HandlePacket(PacketWrapper packet) 55 | { 56 | switch (packet.PacketId) 57 | { 58 | case 1: 59 | LoginAuthPacket loginAuth = PacketUtils.Deserialize(packet.Data); 60 | HandleLoginAuth(loginAuth); 61 | break; 62 | case 3: 63 | RegisterAuthPacket registerAuthPacket = PacketUtils.Deserialize(packet.Data); 64 | HandleRegisterAuth(registerAuthPacket); 65 | break; 66 | case 5: 67 | CreateTopicPacket createTopic = PacketUtils.Deserialize(packet.Data); 68 | HandleCreateTopic(createTopic); 69 | break; 70 | case 7: 71 | SendMessagePacket sentMessage = PacketUtils.Deserialize(packet.Data); 72 | HandleSendMessage(sentMessage); 73 | break; 74 | case 8: 75 | ShowTopicPacket showTopic = PacketUtils.Deserialize(packet.Data); 76 | HandleShowTopic(showTopic); 77 | break; 78 | } 79 | } 80 | 81 | private async void TrackToken(String symbol, Topic topic) 82 | { 83 | string url = "https://tokenstracker.iambluedev.workers.dev/?symbol=" + symbol; 84 | using (HttpClient client = new HttpClient()) 85 | { 86 | try 87 | { 88 | HttpResponseMessage response = await client.GetAsync(url); 89 | response.EnsureSuccessStatusCode(); 90 | string responseBody = await response.Content.ReadAsStringAsync(); 91 | 92 | CryptoData cryptoData = JsonSerializer.Deserialize(responseBody); 93 | 94 | Console.WriteLine(responseBody); 95 | 96 | SystemNotify($"Name: {cryptoData.name}" + 97 | $"\nSymbol: {cryptoData.symbo}" + 98 | $"\nCirculating Supply: {cryptoData.circulating_supply}" + 99 | $"\nTotal Supply: {cryptoData.total_supply}" + 100 | $"\nPrice: {cryptoData.price}" 101 | , topic); 102 | } 103 | catch (HttpRequestException e) 104 | { 105 | Console.WriteLine($"Request error: {e.Message}"); 106 | SystemNotify($"Le token {symbol} n'existe pas", topic); 107 | } 108 | } 109 | } 110 | 111 | private void SystemNotify(string content, Topic topic) 112 | { 113 | var message = new Message() 114 | { 115 | Id = Guid.NewGuid(), 116 | Topic = topic, 117 | Content = content, 118 | Sender = AppServer.SystemUser, 119 | Time = DateTime.Now 120 | }; 121 | 122 | messageRepository.Save(message); 123 | 124 | AppServer.BroadcastPacket(new TopicHistoryPacket() 125 | { 126 | Topic = topic, 127 | Messages = [message] 128 | }); 129 | } 130 | 131 | private void HandleSendMessage(SendMessagePacket packet) 132 | { 133 | Console.WriteLine("[" + packet.Topic.Name + "] " + user.Username + ":" + packet.Content); 134 | 135 | if(packet.Content.StartsWith("/track")) { 136 | var parts = packet.Content.Split(" "); 137 | 138 | if(parts.Length == 2) 139 | { 140 | var token = parts[1]; 141 | TrackToken(token.Trim(), packet.Topic); 142 | } else 143 | { 144 | SystemNotify("La commande entrée n'est pas correcte", packet.Topic); 145 | } 146 | } 147 | 148 | var message = new Message() 149 | { 150 | Id = Guid.NewGuid(), 151 | Topic = packet.Topic, 152 | Content = packet.Content, 153 | Sender = user, 154 | Time = DateTime.Now 155 | }; 156 | 157 | messageRepository.Save(message); 158 | 159 | AppServer.BroadcastPacket(new TopicHistoryPacket() 160 | { 161 | Topic = packet.Topic, 162 | Messages = [message] 163 | }); 164 | } 165 | 166 | private void HandleShowTopic(ShowTopicPacket packet) 167 | { 168 | var messages = messageRepository.ByTopic(packet.Topic); 169 | 170 | 171 | SendPacket(new TopicHistoryPacket() 172 | { 173 | Topic = packet.Topic, 174 | Messages = messages 175 | }); 176 | } 177 | 178 | private void HandleLoginAuth(LoginAuthPacket packet) 179 | { 180 | Console.WriteLine($"Login attempt: {packet.Username}"); 181 | 182 | var user = userRepository.ByUsername(packet.Username); 183 | 184 | if (user != null) 185 | { 186 | bool isPasswordCorrect = PasswordChecker.CheckPassword(user.Password, packet.Password); 187 | Console.WriteLine("Mot de passe correct: " + isPasswordCorrect); 188 | 189 | if (isPasswordCorrect) 190 | { 191 | this.user = user; 192 | 193 | user.Password = null; 194 | 195 | SendPacket(new LoginSuccessPacket() 196 | { 197 | User = user, 198 | }); 199 | 200 | SendPacket(new TopicsListPacket() 201 | { 202 | Topics = topicRepository.All() 203 | }); 204 | } else 205 | { 206 | SendPacket(new ErrorPacket() 207 | { 208 | Message = "Mot de passe incorrect" 209 | }); 210 | } 211 | } 212 | else 213 | { 214 | Console.WriteLine("Utilisateur non trouvé."); 215 | 216 | SendPacket(new ErrorPacket() 217 | { 218 | Message = "Utilisateur n'existe pas" 219 | }); 220 | } 221 | } 222 | 223 | private void HandleRegisterAuth(RegisterAuthPacket registerAuthPacket) 224 | { 225 | Console.WriteLine("Register " + registerAuthPacket.Username); 226 | 227 | User user = userRepository.ByUsername(registerAuthPacket.Username); 228 | if(user != null) 229 | { 230 | Console.WriteLine("Utilisateur existe déjà."); 231 | 232 | SendPacket(new ErrorPacket() 233 | { 234 | Message = "Username déjà utilisé" 235 | }); 236 | } else 237 | { 238 | User newUser = new User 239 | { 240 | Id = Guid.NewGuid(), 241 | Username = registerAuthPacket.Username, 242 | Password = PasswordChecker.HashPassword(registerAuthPacket.Password), 243 | AvatarUrl = "https://www.gravatar.com/avatar/?d=identicon" 244 | }; 245 | 246 | userRepository.Save(newUser); 247 | 248 | Console.WriteLine("Utilisateur enregistré avec id " + newUser.Id); 249 | 250 | SendPacket(new ErrorPacket() 251 | { 252 | Message = "Vous pouvez désormais vous connecter !" 253 | }); 254 | } 255 | } 256 | 257 | private void HandleCreateTopic(CreateTopicPacket packet) 258 | { 259 | Console.WriteLine("Create topic " + packet.Name); 260 | 261 | Topic topic = topicRepository.ByName(packet.Name); 262 | if (topic != null) 263 | { 264 | Console.WriteLine("Topic existe déjà."); 265 | 266 | SendPacket(new ErrorPacket() 267 | { 268 | Message = "Topic déjà présent" 269 | }); 270 | } 271 | else 272 | { 273 | Topic newTopic = new Topic 274 | { 275 | Id = Guid.NewGuid(), 276 | Name = packet.Name, 277 | }; 278 | 279 | topicRepository.Save(newTopic); 280 | 281 | Console.WriteLine("Topic enregistré avec id " + newTopic.Id); 282 | 283 | AppServer.BroadcastPacket(new TopicsListPacket() 284 | { 285 | Topics = topicRepository.All() 286 | }); 287 | } 288 | } 289 | } 290 | 291 | [Serializable] 292 | public class CryptoData 293 | { 294 | public string name { get; set; } 295 | public string symbo { get; set; } 296 | public decimal circulating_supply { get; set; } 297 | public decimal total_supply { get; set; } 298 | public decimal price { get; set; } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /Project.Chat.Server/Project.Chat.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Project.Chat.Server/Repository/FileBasedMessageRepository.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Models; 2 | using System.Text.Json; 3 | 4 | namespace Project.Chat.Server.Repository 5 | { 6 | public class FileBasedMessageRepository : IMessageRepository 7 | { 8 | 9 | private readonly string filePath; 10 | private List messages; 11 | 12 | public FileBasedMessageRepository(string filePath) 13 | { 14 | this.filePath = filePath; 15 | LoadMessages(); 16 | } 17 | 18 | private void LoadMessages() 19 | { 20 | if (File.Exists(filePath)) 21 | { 22 | string json = File.ReadAllText(filePath); 23 | messages = JsonSerializer.Deserialize>(json); 24 | } 25 | else 26 | { 27 | messages = new List(); 28 | } 29 | } 30 | 31 | private void SaveMessages() 32 | { 33 | string json = JsonSerializer.Serialize(messages, new JsonSerializerOptions { WriteIndented = true }); 34 | File.WriteAllText(filePath, json); 35 | } 36 | 37 | public void Save(Message message) 38 | { 39 | messages.Add(message); 40 | SaveMessages(); 41 | } 42 | 43 | public List ByTopic(Topic topic) 44 | { 45 | return messages.FindAll(u => u.Topic.Id.Equals(topic.Id)) 46 | .OrderBy(x => x.Time) 47 | .ToList(); 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Project.Chat.Server/Repository/FileBasedTopicRepository.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Model; 2 | using Project.Chat.Common.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.Json; 8 | using System.Threading.Tasks; 9 | 10 | namespace Project.Chat.Server.Repository 11 | { 12 | public class FileBasedTopicRepository : ITopicRepository 13 | { 14 | 15 | private readonly string filePath; 16 | private List topics; 17 | 18 | public FileBasedTopicRepository(string filePath) 19 | { 20 | this.filePath = filePath; 21 | LoadTopics(); 22 | } 23 | 24 | private void LoadTopics() 25 | { 26 | if (File.Exists(filePath)) 27 | { 28 | string json = File.ReadAllText(filePath); 29 | topics = JsonSerializer.Deserialize>(json); 30 | } 31 | else 32 | { 33 | topics = new List(); 34 | } 35 | } 36 | 37 | private void SaveTopics() 38 | { 39 | string json = JsonSerializer.Serialize(topics, new JsonSerializerOptions { WriteIndented = true }); 40 | File.WriteAllText(filePath, json); 41 | } 42 | 43 | public List All() 44 | { 45 | return topics; 46 | } 47 | 48 | public void Save(Topic topic) 49 | { 50 | topics.Add(topic); 51 | SaveTopics(); 52 | } 53 | 54 | public Topic ByName(string name) 55 | { 56 | return topics.FirstOrDefault(u => u.Name.Equals(name)); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Project.Chat.Server/Repository/FileBasedUserRepository.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text.Json; 7 | 8 | namespace Project.Chat.Server.Repository 9 | { 10 | public class FileBasedUserRepository : IUserRepository 11 | { 12 | private readonly string filePath; 13 | private List users; 14 | 15 | public FileBasedUserRepository(string filePath) 16 | { 17 | this.filePath = filePath; 18 | LoadUsers(); 19 | } 20 | 21 | private void LoadUsers() 22 | { 23 | if (File.Exists(filePath)) 24 | { 25 | string json = File.ReadAllText(filePath); 26 | users = JsonSerializer.Deserialize>(json); 27 | } 28 | else 29 | { 30 | users = new List(); 31 | } 32 | } 33 | 34 | private void SaveUsers() 35 | { 36 | string json = JsonSerializer.Serialize(users, new JsonSerializerOptions { WriteIndented = true }); 37 | File.WriteAllText(filePath, json); 38 | } 39 | 40 | public User ByUsername(string username) 41 | { 42 | return users.FirstOrDefault(u => u.Username.Equals(username)); 43 | } 44 | 45 | public void Save(User user) 46 | { 47 | users.Add(user); 48 | SaveUsers(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Project.Chat.Server/Repository/IMessageRepository.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Server.Repository 9 | { 10 | public interface IMessageRepository 11 | { 12 | List ByTopic(Topic topic); 13 | 14 | void Save(Message message); 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Project.Chat.Server/Repository/ITopicRepository.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Server.Repository 9 | { 10 | public interface ITopicRepository 11 | { 12 | List All(); 13 | 14 | Topic ByName(string name); 15 | 16 | void Save(Topic topic); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Project.Chat.Server/Repository/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.Common.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Project.Chat.Server.Repository 9 | { 10 | public interface IUserRepository 11 | { 12 | User ByUsername(string username); 13 | 14 | void Save(User user); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Project.Chat.Server/Util/PasswordChecker.cs: -------------------------------------------------------------------------------- 1 | using DevOne.Security.Cryptography.BCrypt; 2 | 3 | namespace Project.Chat.Server.Util 4 | { 5 | public class PasswordChecker 6 | { 7 | 8 | public static bool CheckPassword(string bcryptPassword, string password) 9 | { 10 | return BCryptHelper.CheckPassword(password, bcryptPassword); 11 | } 12 | 13 | public static string HashPassword(string password) 14 | { 15 | return BCryptHelper.HashPassword(password, BCryptHelper.GenerateSalt(12)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Project.Chat/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Project.Chat/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Project.Chat.modals; 2 | using Project.Chat.viewModels; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Runtime.InteropServices; 6 | using System.Windows; 7 | 8 | namespace Project.Chat 9 | { 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | /// 14 | public partial class App : Application 15 | { 16 | private static SocketClient _socketClient; 17 | public static SocketClient SocketClient { get { return _socketClient; } } 18 | 19 | protected override async void OnStartup(StartupEventArgs e) 20 | { 21 | base.OnStartup(e); 22 | 23 | _socketClient = new SocketClient("localhost", 12345); 24 | bool isConnected = await _socketClient.ConnectAsync(); 25 | 26 | if (!isConnected) 27 | { 28 | ShowErrorModal("Failed to connect to the server at localhost:12345."); 29 | } 30 | else 31 | { 32 | // Continue with application startup 33 | LoginRegisterWindow loginRegisterWindow = new LoginRegisterWindow(); 34 | loginRegisterWindow.Show(); 35 | 36 | _socketClient.OnConnected += (s, e) => { 37 | MainWindow mainWindow = new MainWindow(); 38 | mainWindow.Show(); 39 | 40 | loginRegisterWindow.Close(); 41 | }; 42 | } 43 | } 44 | 45 | private void ShowErrorModal(string errorMessage) 46 | { 47 | ErrorModal errorModal = new ErrorModal(errorMessage); 48 | errorModal.ShowDialog(); 49 | Shutdown(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Project.Chat/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /Project.Chat/Converters/Assembly.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Project.Chat.Converters 8 | { 9 | internal class Assembly 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Project.Chat/Converters/AutoScrollBehavior.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Controls; 7 | using System.Windows; 8 | 9 | // https://stackoverflow.com/a/35312386 10 | 11 | namespace Project.Chat 12 | { 13 | public static class AutoScrollBehavior 14 | { 15 | public static readonly DependencyProperty AutoScrollProperty = 16 | DependencyProperty.RegisterAttached("AutoScroll", typeof(bool), typeof(AutoScrollBehavior), new PropertyMetadata(false, AutoScrollPropertyChanged)); 17 | 18 | 19 | public static void AutoScrollPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 20 | { 21 | var scrollViewer = obj as ScrollViewer; 22 | if (scrollViewer != null && (bool)args.NewValue) 23 | { 24 | scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged; 25 | scrollViewer.ScrollToEnd(); 26 | } 27 | else 28 | { 29 | scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged; 30 | } 31 | } 32 | 33 | private static void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) 34 | { 35 | // Only scroll to bottom when the extent changed. Otherwise you can't scroll up 36 | if (e.ExtentHeightChange != 0) 37 | { 38 | var scrollViewer = sender as ScrollViewer; 39 | scrollViewer?.ScrollToBottom(); 40 | } 41 | } 42 | 43 | public static bool GetAutoScroll(DependencyObject obj) 44 | { 45 | return (bool)obj.GetValue(AutoScrollProperty); 46 | } 47 | 48 | public static void SetAutoScroll(DependencyObject obj, bool value) 49 | { 50 | obj.SetValue(AutoScrollProperty, value); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Project.Chat/Converters/BoolToColumnIndexConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | 9 | namespace Project.Chat 10 | { 11 | public class BoolToColumnIndexConverter : IValueConverter 12 | { 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if (value is bool isSentByCurrentUser) 16 | { 17 | return isSentByCurrentUser ? 1 : 0; 18 | } 19 | return 0; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Project.Chat/Converters/BoolToColumnIndexInverseConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | 9 | namespace Project.Chat 10 | { 11 | public class BoolToColumnIndexInverseConverter : IValueConverter 12 | { 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if (value is bool isSentByCurrentUser) 16 | { 17 | return isSentByCurrentUser ? 0 : 1; 18 | } 19 | return 1; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Project.Chat/Converters/BoolToHorizontalAlignmentConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | using System.Windows; 9 | 10 | namespace Project.Chat 11 | { 12 | public class BoolToHorizontalAlignmentConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is bool isSentByCurrentUser) 17 | { 18 | return isSentByCurrentUser ? HorizontalAlignment.Right : HorizontalAlignment.Left; 19 | } 20 | return HorizontalAlignment.Left; 21 | } 22 | 23 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 24 | { 25 | throw new NotImplementedException(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Project.Chat/Converters/BoolToMarginConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | using System.Windows; 9 | 10 | namespace Project.Chat 11 | { 12 | public class BoolToMarginConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is bool isSentByCurrentUser) 17 | { 18 | return isSentByCurrentUser ? new Thickness(50, 0, 0, 0) : new Thickness(0, 0, 50, 0); 19 | } 20 | return new Thickness(0, 0, 50, 0); 21 | } 22 | 23 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 24 | { 25 | throw new NotImplementedException(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Project.Chat/Converters/BoolToTextAlignmentConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | using System.Windows; 9 | 10 | namespace Project.Chat 11 | { 12 | public class BoolToTextAlignmentConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is bool isSentByCurrentUser) 17 | { 18 | return isSentByCurrentUser ? TextAlignment.Right : TextAlignment.Left; 19 | } 20 | return TextAlignment.Left; 21 | } 22 | 23 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 24 | { 25 | throw new NotImplementedException(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Project.Chat/Converters/MessageSentByUserVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace Project.Chat 11 | { 12 | public class MessageSentByUserVisibilityConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is bool isSentByCurrentUser) 17 | { 18 | return isSentByCurrentUser ? Visibility.Visible : Visibility.Hidden; 19 | } 20 | 21 | return Visibility.Hidden; 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Project.Chat/Project.Chat.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0-windows 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Project.Chat/Project.Chat.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Designer 7 | 8 | 9 | 10 | 11 | Code 12 | 13 | 14 | Code 15 | 16 | 17 | Code 18 | 19 | 20 | 21 | 22 | Designer 23 | 24 | 25 | Designer 26 | 27 | 28 | Designer 29 | 30 | 31 | Designer 32 | 33 | 34 | -------------------------------------------------------------------------------- /Project.Chat/Screens/LoginRegisterWindow.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 |