Update: Mutli Channel Support
This commit is contained in:
@@ -5,26 +5,60 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
Title="Relay Client">
|
Title="Relay Client">
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*,Auto" Padding="12" RowSpacing="10">
|
<Grid RowDefinitions="Auto,*,Auto"
|
||||||
|
ColumnDefinitions="220,*"
|
||||||
|
Padding="12"
|
||||||
|
RowSpacing="10"
|
||||||
|
ColumnSpacing="10">
|
||||||
|
|
||||||
<Border Grid.Row="0" StrokeThickness="1" Padding="10">
|
<!-- Header -->
|
||||||
|
<Border Grid.Row="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Padding="10">
|
||||||
<VerticalStackLayout Spacing="4">
|
<VerticalStackLayout Spacing="4">
|
||||||
<Label x:Name="UserLabel"
|
<Label x:Name="UserLabel"
|
||||||
Text="Logged in as: Unknown"
|
Text="Logged in as: Unknown"
|
||||||
FontAttributes="Bold"
|
FontAttributes="Bold"
|
||||||
FontSize="18" />
|
FontSize="18" />
|
||||||
<Label Text="#general"
|
<Label x:Name="ChannelLabel"
|
||||||
|
Text="No channel selected"
|
||||||
FontSize="14" />
|
FontSize="14" />
|
||||||
</VerticalStackLayout>
|
</VerticalStackLayout>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Border Grid.Row="1" StrokeThickness="1" Padding="10">
|
<!-- Sidebar -->
|
||||||
<ScrollView x:Name="MessagesScrollView">
|
<Border Grid.Row="1"
|
||||||
<VerticalStackLayout x:Name="MessagesLayout" Spacing="8" />
|
Grid.Column="0"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Padding="10">
|
||||||
|
<ScrollView>
|
||||||
|
<VerticalStackLayout Spacing="8">
|
||||||
|
<Label Text="Channels"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
FontSize="16" />
|
||||||
|
<VerticalStackLayout x:Name="SidebarList"
|
||||||
|
Spacing="6" />
|
||||||
|
</VerticalStackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Grid Grid.Row="2" ColumnDefinitions="*,Auto" ColumnSpacing="10">
|
<!-- Messages -->
|
||||||
|
<Border Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Padding="10">
|
||||||
|
<ScrollView x:Name="MessagesScrollView">
|
||||||
|
<VerticalStackLayout x:Name="MessagesLayout"
|
||||||
|
Spacing="8" />
|
||||||
|
</ScrollView>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Input -->
|
||||||
|
<Grid Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
ColumnDefinitions="*,Auto"
|
||||||
|
ColumnSpacing="10">
|
||||||
<Entry x:Name="MessageEntry"
|
<Entry x:Name="MessageEntry"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Placeholder="Type a message..."
|
Placeholder="Type a message..."
|
||||||
|
|||||||
@@ -8,8 +8,13 @@ namespace RelayClient;
|
|||||||
public partial class MainPage : ContentPage
|
public partial class MainPage : ContentPage
|
||||||
{
|
{
|
||||||
private readonly string _username;
|
private readonly string _username;
|
||||||
private readonly WebSocket wsc;
|
private readonly WebSocket _wsc;
|
||||||
private string? _serverPublicKey;
|
private string? _serverPublicKey;
|
||||||
|
private string? _currentChannelId;
|
||||||
|
private string? _currentChannelName;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, List<ChatMessage>> _messagesByChannel = new();
|
||||||
|
private readonly List<ChannelItem> _channels = new();
|
||||||
|
|
||||||
public MainPage(string username)
|
public MainPage(string username)
|
||||||
{
|
{
|
||||||
@@ -25,15 +30,15 @@ public partial class MainPage : ContentPage
|
|||||||
KeyStorage.SavePublicKey(_username, keys.publicKey);
|
KeyStorage.SavePublicKey(_username, keys.publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
wsc = new WebSocket("ws://localhost:1337/");
|
_wsc = new WebSocket("ws://localhost:1337/");
|
||||||
|
|
||||||
wsc.OnMessage += WscOnMessage;
|
_wsc.OnMessage += WscOnMessage;
|
||||||
wsc.Connect();
|
_wsc.Connect();
|
||||||
|
|
||||||
var publicKey = KeyStorage.LoadPublicKey(_username);
|
var publicKey = KeyStorage.LoadPublicKey(_username);
|
||||||
wsc.Send($"REGISTER_KEY|{_username}|{publicKey}");
|
_wsc.Send($"REGISTER_KEY|{_username}|{publicKey}");
|
||||||
wsc.Send("GET_SERVER_KEY");
|
_wsc.Send("GET_SERVER_KEY");
|
||||||
wsc.Send($"GET_HISTORY|{_username}");
|
_wsc.Send("GET_CHANNELS");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendButton_OnClicked(object? sender, EventArgs e)
|
private void SendButton_OnClicked(object? sender, EventArgs e)
|
||||||
@@ -63,6 +68,7 @@ public partial class MainPage : ContentPage
|
|||||||
|
|
||||||
var payload = new SocketEncryptedMessage
|
var payload = new SocketEncryptedMessage
|
||||||
{
|
{
|
||||||
|
ChannelId = _currentChannelId!,
|
||||||
Type = "client_encrypted_chat",
|
Type = "client_encrypted_chat",
|
||||||
SenderUsername = _username,
|
SenderUsername = _username,
|
||||||
CipherText = encrypted.CipherText,
|
CipherText = encrypted.CipherText,
|
||||||
@@ -72,7 +78,7 @@ public partial class MainPage : ContentPage
|
|||||||
};
|
};
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(payload);
|
var json = JsonSerializer.Serialize(payload);
|
||||||
wsc.Send(json);
|
_wsc.Send(json);
|
||||||
|
|
||||||
Console.WriteLine($"[{_username}] sent encrypted message.");
|
Console.WriteLine($"[{_username}] sent encrypted message.");
|
||||||
|
|
||||||
@@ -100,6 +106,38 @@ public partial class MainPage : ContentPage
|
|||||||
|
|
||||||
var type = typeElement.GetString();
|
var type = typeElement.GetString();
|
||||||
|
|
||||||
|
if (type == "channel_list")
|
||||||
|
{
|
||||||
|
var channelList = JsonSerializer.Deserialize<SocketChannelList>(e.Data);
|
||||||
|
if (channelList is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_channels.Clear();
|
||||||
|
_channels.AddRange(channelList.Channels.OrderBy(c => c.CreatedAt));
|
||||||
|
|
||||||
|
var defaultChannel = _channels
|
||||||
|
.Where(c => c.Name.Equals("welcome", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.OrderBy(c => c.CreatedAt)
|
||||||
|
.FirstOrDefault()
|
||||||
|
?? _channels.OrderBy(c => c.CreatedAt).FirstOrDefault();
|
||||||
|
|
||||||
|
if (defaultChannel is not null)
|
||||||
|
{
|
||||||
|
_currentChannelId = defaultChannel.ChannelId;
|
||||||
|
_currentChannelName = defaultChannel.Name;
|
||||||
|
|
||||||
|
MainThread.BeginInvokeOnMainThread(() =>
|
||||||
|
{
|
||||||
|
ChannelLabel.Text = $"#{_currentChannelName}";
|
||||||
|
RenderChannelList();
|
||||||
|
});
|
||||||
|
|
||||||
|
_wsc.Send($"GET_HISTORY|{_username}|{_currentChannelId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (type == "server_public_key")
|
if (type == "server_public_key")
|
||||||
{
|
{
|
||||||
var serverKeyMessage = JsonSerializer.Deserialize<ServerPublicKeyMessage>(e.Data);
|
var serverKeyMessage = JsonSerializer.Deserialize<ServerPublicKeyMessage>(e.Data);
|
||||||
@@ -146,7 +184,80 @@ public partial class MainPage : ContentPage
|
|||||||
Timestamp = DateTime.Now
|
Timestamp = DateTime.Now
|
||||||
};
|
};
|
||||||
|
|
||||||
MainThread.BeginInvokeOnMainThread(async () =>
|
if (!_messagesByChannel.ContainsKey(payload.ChannelId))
|
||||||
|
{
|
||||||
|
_messagesByChannel[payload.ChannelId] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_messagesByChannel[payload.ChannelId].Add(message);
|
||||||
|
|
||||||
|
if (payload.ChannelId == _currentChannelId)
|
||||||
|
{
|
||||||
|
MainThread.BeginInvokeOnMainThread(() =>
|
||||||
|
{
|
||||||
|
RenderSingleMessage(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[{_username}] failed to process websocket message: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisappearing()
|
||||||
|
{
|
||||||
|
_wsc.OnMessage -= WscOnMessage;
|
||||||
|
_wsc.Close();
|
||||||
|
base.OnDisappearing();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderChannelList()
|
||||||
|
{
|
||||||
|
SidebarList.Children.Clear();
|
||||||
|
|
||||||
|
foreach (var channel in _channels.OrderBy(c => c.CreatedAt))
|
||||||
|
{
|
||||||
|
var button = new Button
|
||||||
|
{
|
||||||
|
Text = $"#{channel.Name}"
|
||||||
|
};
|
||||||
|
|
||||||
|
button.Clicked += (_, _) =>
|
||||||
|
{
|
||||||
|
_currentChannelId = channel.ChannelId;
|
||||||
|
_currentChannelName = channel.Name;
|
||||||
|
|
||||||
|
ChannelLabel.Text = $"#{_currentChannelName}";
|
||||||
|
RenderCurrentChannelMessages();
|
||||||
|
|
||||||
|
if (!_messagesByChannel.ContainsKey(channel.ChannelId))
|
||||||
|
{
|
||||||
|
_wsc.Send($"GET_HISTORY|{_username}|{channel.ChannelId}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SidebarList.Children.Add(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderCurrentChannelMessages()
|
||||||
|
{
|
||||||
|
MessagesLayout.Children.Clear();
|
||||||
|
|
||||||
|
if (_currentChannelId is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_messagesByChannel.TryGetValue(_currentChannelId, out var messages))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var message in messages.OrderBy(m => m.Timestamp))
|
||||||
|
{
|
||||||
|
RenderSingleMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RenderSingleMessage(ChatMessage message)
|
||||||
{
|
{
|
||||||
bool isOwnMessage = message.SenderUsername == _username;
|
bool isOwnMessage = message.SenderUsername == _username;
|
||||||
|
|
||||||
@@ -165,40 +276,14 @@ public partial class MainPage : ContentPage
|
|||||||
Spacing = 2,
|
Spacing = 2,
|
||||||
Children =
|
Children =
|
||||||
{
|
{
|
||||||
new Label
|
new Label { Text = message.SenderUsername, FontAttributes = FontAttributes.Bold, FontSize = 12 },
|
||||||
{
|
new Label { Text = message.Text, FontSize = 14 },
|
||||||
Text = message.SenderUsername,
|
new Label { Text = message.Timestamp.ToString("h:mm tt"), FontSize = 10 }
|
||||||
FontAttributes = FontAttributes.Bold,
|
|
||||||
FontSize = 12
|
|
||||||
},
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Text = message.Text,
|
|
||||||
FontSize = 14
|
|
||||||
},
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Text = message.Timestamp.ToString("h:mm tt"),
|
|
||||||
FontSize = 10
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MessagesLayout.Children.Add(bubble);
|
MessagesLayout.Children.Add(bubble);
|
||||||
await MessagesScrollView.ScrollToAsync(MessagesLayout, ScrollToPosition.End, true);
|
await MessagesScrollView.ScrollToAsync(MessagesLayout, ScrollToPosition.End, true);
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[{_username}] failed to process websocket message: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
|
||||||
{
|
|
||||||
wsc.OnMessage -= WscOnMessage;
|
|
||||||
wsc.Close();
|
|
||||||
base.OnDisappearing();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
RelayClient/Models/ChannelItem.cs
Normal file
8
RelayClient/Models/ChannelItem.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace RelayClient.Models;
|
||||||
|
|
||||||
|
public class ChannelItem
|
||||||
|
{
|
||||||
|
public required string ChannelId { get; set; }
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
7
RelayClient/Models/SocketChannelList.cs
Normal file
7
RelayClient/Models/SocketChannelList.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace RelayClient.Models;
|
||||||
|
|
||||||
|
public class SocketChannelList
|
||||||
|
{
|
||||||
|
public required string Type { get; set; }
|
||||||
|
public required List<ChannelItem> Channels { get; set; }
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ public class SocketEncryptedMessage
|
|||||||
public required string Type { get; set; }
|
public required string Type { get; set; }
|
||||||
public required string SenderUsername { get; set; }
|
public required string SenderUsername { get; set; }
|
||||||
public string? RecipientUsername { get; set; }
|
public string? RecipientUsername { get; set; }
|
||||||
|
public required string ChannelId { get; set; }
|
||||||
public required string CipherText { get; set; }
|
public required string CipherText { get; set; }
|
||||||
public required string Nonce { get; set; }
|
public required string Nonce { get; set; }
|
||||||
public required string Tag { get; set; }
|
public required string Tag { get; set; }
|
||||||
|
|||||||
8
RelayServer/Models/SocketChannelInfo.cs
Normal file
8
RelayServer/Models/SocketChannelInfo.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace RelayServer.Models;
|
||||||
|
|
||||||
|
public class SocketChannelInfo
|
||||||
|
{
|
||||||
|
public required string ChannelId { get; set; }
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
7
RelayServer/Models/SocketChannelList.cs
Normal file
7
RelayServer/Models/SocketChannelList.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace RelayServer.Models;
|
||||||
|
|
||||||
|
public class SocketChannelList
|
||||||
|
{
|
||||||
|
public required string Type { get; set; }
|
||||||
|
public required List<SocketChannelInfo> Channels { get; set; }
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ public class SocketEncryptedMessage
|
|||||||
{
|
{
|
||||||
public required string Type { get; set; }
|
public required string Type { get; set; }
|
||||||
public required string SenderUsername { get; set; }
|
public required string SenderUsername { get; set; }
|
||||||
public required string RecipientUsername { get; set; }
|
public string? RecipientUsername { get; set; }
|
||||||
|
public required string ChannelId { get; set; }
|
||||||
public required string CipherText { get; set; }
|
public required string CipherText { get; set; }
|
||||||
public required string Nonce { get; set; }
|
public required string Nonce { get; set; }
|
||||||
public required string Tag { get; set; }
|
public required string Tag { get; set; }
|
||||||
|
|||||||
@@ -75,18 +75,29 @@ var channel = await db.Create("channels", new Channels
|
|||||||
CreatedAt = DateTime.UtcNow
|
CreatedAt = DateTime.UtcNow
|
||||||
});
|
});
|
||||||
|
|
||||||
var channel2 = await db.Create("files", new Channels
|
var channel2 = await db.Create("channels", new Channels
|
||||||
{
|
{
|
||||||
Name = "files",
|
Name = "files",
|
||||||
CreatedAt = DateTime.UtcNow.Subtract(new TimeSpan(0, 4, 0, 0))
|
CreatedAt = DateTime.UtcNow.Subtract(new TimeSpan(0, 4, 0, 0))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var channel3 = await db.Create("channels", new Channels
|
||||||
|
{
|
||||||
|
Name = "welcome",
|
||||||
|
CreatedAt = DateTime.UtcNow.Subtract(new TimeSpan(1, 4, 4, 4))
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.WriteLine($"Channel created: {ToJsonString(channel)}");
|
||||||
Console.WriteLine($"Channel created: {ToJsonString(channel2)}");
|
Console.WriteLine($"Channel created: {ToJsonString(channel2)}");
|
||||||
|
Console.WriteLine($"Channel created: {ToJsonString(channel3)}");
|
||||||
|
|
||||||
var channelId = GetRecordId(channel.Id);
|
var channelId = GetRecordId(channel.Id);
|
||||||
|
var channelId2 = GetRecordId(channel2.Id);
|
||||||
|
var channelId3 = GetRecordId(channel3.Id);
|
||||||
|
|
||||||
Console.WriteLine($"Resolved channelId: {channelId}");
|
Console.WriteLine($"Resolved channelId: {channelId}");
|
||||||
ChatTest.DefaultChannelId = channelId;
|
Console.WriteLine($"Resolved channelId: {channelId2}");
|
||||||
|
Console.WriteLine($"Resolved channelId: {channelId3}");
|
||||||
|
|
||||||
var keyBase64 = cryptoService.GenerateKey();
|
var keyBase64 = cryptoService.GenerateKey();
|
||||||
var serverKeys = E2EeHelper.GenerateRsaKeyPair();
|
var serverKeys = E2EeHelper.GenerateRsaKeyPair();
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ public class ChatTest : WebSocketBehavior
|
|||||||
public static string? ServerPrivateKey { get; set; }
|
public static string? ServerPrivateKey { get; set; }
|
||||||
public static string? ChannelDbKey { get; set; }
|
public static string? ChannelDbKey { get; set; }
|
||||||
public static SurrealDb.Net.SurrealDbClient? Db { get; set; }
|
public static SurrealDb.Net.SurrealDbClient? Db { get; set; }
|
||||||
public static string? DefaultChannelId { get; set; }
|
|
||||||
|
|
||||||
protected override void OnMessage(MessageEventArgs e)
|
protected override void OnMessage(MessageEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -31,6 +30,12 @@ public class ChatTest : WebSocketBehavior
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg == "GET_CHANNELS")
|
||||||
|
{
|
||||||
|
HandleGetChannels();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.StartsWith("GET_HISTORY|"))
|
if (msg.StartsWith("GET_HISTORY|"))
|
||||||
{
|
{
|
||||||
HandleGetHistory(msg);
|
HandleGetHistory(msg);
|
||||||
@@ -76,6 +81,35 @@ public class ChatTest : WebSocketBehavior
|
|||||||
Send($"SERVER:REGISTERED_KEY:{username}");
|
Send($"SERVER:REGISTERED_KEY:{username}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleGetChannels()
|
||||||
|
{
|
||||||
|
if (Db is null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Db is not initialized.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var channels = Task.Run(async () => await Db.Select<Channels>("channels"))
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult()
|
||||||
|
.OrderBy(c => c.CreatedAt)
|
||||||
|
.Select(c => new SocketChannelInfo
|
||||||
|
{
|
||||||
|
ChannelId = GetRecordId(c.Id),
|
||||||
|
Name = c.Name,
|
||||||
|
CreatedAt = c.CreatedAt
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var payload = new SocketChannelList
|
||||||
|
{
|
||||||
|
Type = "channel_list",
|
||||||
|
Channels = channels
|
||||||
|
};
|
||||||
|
|
||||||
|
Send(JsonSerializer.Serialize(payload));
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleGetServerKey()
|
private void HandleGetServerKey()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(ServerPublicKey))
|
if (string.IsNullOrWhiteSpace(ServerPublicKey))
|
||||||
@@ -113,8 +147,7 @@ public class ChatTest : WebSocketBehavior
|
|||||||
if (ClientKeyService is null ||
|
if (ClientKeyService is null ||
|
||||||
Db is null ||
|
Db is null ||
|
||||||
string.IsNullOrWhiteSpace(ServerPrivateKey) ||
|
string.IsNullOrWhiteSpace(ServerPrivateKey) ||
|
||||||
string.IsNullOrWhiteSpace(ChannelDbKey) ||
|
string.IsNullOrWhiteSpace(ChannelDbKey))
|
||||||
string.IsNullOrWhiteSpace(DefaultChannelId))
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("Server crypto/database dependencies are not initialized.");
|
Console.WriteLine("Server crypto/database dependencies are not initialized.");
|
||||||
return;
|
return;
|
||||||
@@ -150,7 +183,7 @@ public class ChatTest : WebSocketBehavior
|
|||||||
var savedMessage = Task.Run(async () =>
|
var savedMessage = Task.Run(async () =>
|
||||||
await Db.Create("channel_messages", new ChannelMessages
|
await Db.Create("channel_messages", new ChannelMessages
|
||||||
{
|
{
|
||||||
ChannelId = DefaultChannelId,
|
ChannelId = clientPayload.ChannelId,
|
||||||
SenderUserId = $"users:{clientPayload.SenderUsername.ToLower()}",
|
SenderUserId = $"users:{clientPayload.SenderUsername.ToLower()}",
|
||||||
CipherText = dbEncrypted.cipherText,
|
CipherText = dbEncrypted.cipherText,
|
||||||
Nonce = dbEncrypted.nonce,
|
Nonce = dbEncrypted.nonce,
|
||||||
@@ -182,6 +215,7 @@ public class ChatTest : WebSocketBehavior
|
|||||||
Type = "encrypted_chat",
|
Type = "encrypted_chat",
|
||||||
SenderUsername = clientPayload.SenderUsername,
|
SenderUsername = clientPayload.SenderUsername,
|
||||||
RecipientUsername = client.Username,
|
RecipientUsername = client.Username,
|
||||||
|
ChannelId = clientPayload.ChannelId,
|
||||||
CipherText = encrypted.CipherText,
|
CipherText = encrypted.CipherText,
|
||||||
Nonce = encrypted.Nonce,
|
Nonce = encrypted.Nonce,
|
||||||
Tag = encrypted.Tag,
|
Tag = encrypted.Tag,
|
||||||
@@ -194,20 +228,20 @@ public class ChatTest : WebSocketBehavior
|
|||||||
|
|
||||||
private void HandleGetHistory(string msg)
|
private void HandleGetHistory(string msg)
|
||||||
{
|
{
|
||||||
var parts = msg.Split('|', 2);
|
var parts = msg.Split('|', 3);
|
||||||
|
|
||||||
if (parts.Length < 2)
|
if (parts.Length < 3)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Invalid GET_HISTORY payload.");
|
Console.WriteLine("Invalid GET_HISTORY payload.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var username = parts[1];
|
var username = parts[1];
|
||||||
|
var channelId = parts[2];
|
||||||
|
|
||||||
if (ClientKeyService is null ||
|
if (ClientKeyService is null ||
|
||||||
Db is null ||
|
Db is null ||
|
||||||
string.IsNullOrWhiteSpace(ChannelDbKey) ||
|
string.IsNullOrWhiteSpace(ChannelDbKey))
|
||||||
string.IsNullOrWhiteSpace(DefaultChannelId))
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("History dependencies are not initialized.");
|
Console.WriteLine("History dependencies are not initialized.");
|
||||||
return;
|
return;
|
||||||
@@ -228,7 +262,7 @@ public class ChatTest : WebSocketBehavior
|
|||||||
.GetResult();
|
.GetResult();
|
||||||
|
|
||||||
var channelMessages = allMessages
|
var channelMessages = allMessages
|
||||||
.Where(m => m.ChannelId == DefaultChannelId)
|
.Where(m => m.ChannelId == channelId)
|
||||||
.OrderBy(m => m.CreatedAt)
|
.OrderBy(m => m.CreatedAt)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -262,6 +296,7 @@ public class ChatTest : WebSocketBehavior
|
|||||||
Type = "encrypted_chat",
|
Type = "encrypted_chat",
|
||||||
SenderUsername = ExtractUsernameFromUserId(dbMessage.SenderUserId),
|
SenderUsername = ExtractUsernameFromUserId(dbMessage.SenderUserId),
|
||||||
RecipientUsername = username,
|
RecipientUsername = username,
|
||||||
|
ChannelId = channelId,
|
||||||
CipherText = encrypted.CipherText,
|
CipherText = encrypted.CipherText,
|
||||||
Nonce = encrypted.Nonce,
|
Nonce = encrypted.Nonce,
|
||||||
Tag = encrypted.Tag,
|
Tag = encrypted.Tag,
|
||||||
@@ -271,4 +306,21 @@ public class ChatTest : WebSocketBehavior
|
|||||||
Send(JsonSerializer.Serialize(outbound));
|
Send(JsonSerializer.Serialize(outbound));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetRecordId(object? id)
|
||||||
|
{
|
||||||
|
if (id is null)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(id);
|
||||||
|
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
|
||||||
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
var recordId = root.GetProperty("Id").GetString() ?? string.Empty;
|
||||||
|
var table = root.GetProperty("Table").GetString() ?? string.Empty;
|
||||||
|
|
||||||
|
return $"{table}:{recordId}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user