mirror of
https://github.com/Stone-Red-Code/Decho.git
synced 2026-09-04 00:46:11 +02:00
Add online users panel
This commit is contained in:
@@ -204,9 +204,9 @@ public sealed class ConnectionService : IDisposable
|
|||||||
|
|
||||||
if (password is not null)
|
if (password is not null)
|
||||||
{
|
{
|
||||||
var salt = RoomCrypto.GenerateSalt();
|
byte[] salt = RoomCrypto.GenerateSalt();
|
||||||
var derived = RoomCrypto.DeriveKeys(password, salt);
|
RoomCrypto.DerivedKeys derived = RoomCrypto.DeriveKeys(password, salt);
|
||||||
var roomKey = RoomCrypto.GenerateRoomKey();
|
byte[] roomKey = RoomCrypto.GenerateRoomKey();
|
||||||
wirePassword = derived.AuthKeyHex;
|
wirePassword = derived.AuthKeyHex;
|
||||||
saltB64 = Convert.ToBase64String(salt);
|
saltB64 = Convert.ToBase64String(salt);
|
||||||
wrappedKey = RoomCrypto.WrapRoomKey(roomKey, derived.KeyEncryptionKey);
|
wrappedKey = RoomCrypto.WrapRoomKey(roomKey, derived.KeyEncryptionKey);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
using EchoHub.Core.DTOs;
|
||||||
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Reactive;
|
||||||
|
|
||||||
namespace Decho.ViewModels;
|
namespace Decho.ViewModels;
|
||||||
|
|
||||||
@@ -10,6 +13,8 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
private set => this.RaiseAndSetIfChanged(ref field, value);
|
private set => this.RaiseAndSetIfChanged(ref field, value);
|
||||||
} = [];
|
} = [];
|
||||||
|
|
||||||
|
public ObservableCollection<UserViewModel> OnlineUsers { get; } = [];
|
||||||
|
|
||||||
public string ChannelTitle
|
public string ChannelTitle
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -32,6 +37,20 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
private set => this.RaiseAndSetIfChanged(ref field, value);
|
private set => this.RaiseAndSetIfChanged(ref field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ShowOnlineUsers
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> ToggleUsersPanelCommand { get; }
|
||||||
|
|
||||||
|
public string OnlineUserCount
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||||
|
} = string.Empty;
|
||||||
|
|
||||||
public MessageComposerViewModel Composer { get; }
|
public MessageComposerViewModel Composer { get; }
|
||||||
|
|
||||||
public string CurrentServerUrl { get; private set; } = string.Empty;
|
public string CurrentServerUrl { get; private set; } = string.Empty;
|
||||||
@@ -41,6 +60,12 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
public ChatViewModel()
|
public ChatViewModel()
|
||||||
{
|
{
|
||||||
Composer = new MessageComposerViewModel();
|
Composer = new MessageComposerViewModel();
|
||||||
|
ToggleUsersPanelCommand = ReactiveCommand.Create(ToggleUsersPanel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleUsersPanel()
|
||||||
|
{
|
||||||
|
ShowOnlineUsers = !ShowOnlineUsers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetChannel(ChannelViewModel? channel, string serverUrl = "", bool isServerConnected = true)
|
public void SetChannel(ChannelViewModel? channel, string serverUrl = "", bool isServerConnected = true)
|
||||||
@@ -53,6 +78,9 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
HasTopic = false;
|
HasTopic = false;
|
||||||
CurrentChannelName = string.Empty;
|
CurrentChannelName = string.Empty;
|
||||||
CurrentServerUrl = string.Empty;
|
CurrentServerUrl = string.Empty;
|
||||||
|
ShowOnlineUsers = false;
|
||||||
|
OnlineUsers.Clear();
|
||||||
|
OnlineUserCount = string.Empty;
|
||||||
Composer.SetServer(string.Empty, isServerConnected);
|
Composer.SetServer(string.Empty, isServerConnected);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -64,6 +92,45 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
CurrentChannelName = channel.Name;
|
CurrentChannelName = channel.Name;
|
||||||
CurrentServerUrl = serverUrl;
|
CurrentServerUrl = serverUrl;
|
||||||
Composer.SetServer(serverUrl, isServerConnected);
|
Composer.SetServer(serverUrl, isServerConnected);
|
||||||
|
|
||||||
|
if (!isServerConnected)
|
||||||
|
{
|
||||||
|
ShowOnlineUsers = false;
|
||||||
|
OnlineUsers.Clear();
|
||||||
|
OnlineUserCount = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetOnlineUsers(List<UserPresenceDto> users)
|
||||||
|
{
|
||||||
|
OnlineUsers.Clear();
|
||||||
|
foreach (UserPresenceDto user in users)
|
||||||
|
{
|
||||||
|
OnlineUsers.Add(new UserViewModel(user));
|
||||||
|
}
|
||||||
|
OnlineUserCount = $"{users.Count}";
|
||||||
|
ShowOnlineUsers = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddOnlineUser(UserPresenceDto user)
|
||||||
|
{
|
||||||
|
if (OnlineUsers.All(u => u.Username != user.Username))
|
||||||
|
{
|
||||||
|
OnlineUsers.Add(new UserViewModel(user));
|
||||||
|
OnlineUserCount = $"{OnlineUsers.Count}";
|
||||||
|
}
|
||||||
|
ShowOnlineUsers = OnlineUsers.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveOnlineUser(string username)
|
||||||
|
{
|
||||||
|
UserViewModel? user = OnlineUsers.FirstOrDefault(u => u.Username == username);
|
||||||
|
if (user is not null)
|
||||||
|
{
|
||||||
|
_ = OnlineUsers.Remove(user);
|
||||||
|
OnlineUserCount = $"{OnlineUsers.Count}";
|
||||||
|
}
|
||||||
|
ShowOnlineUsers = OnlineUsers.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearMessages()
|
public void ClearMessages()
|
||||||
@@ -72,5 +139,7 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
ChannelTitle = "Select a channel";
|
ChannelTitle = "Select a channel";
|
||||||
ChannelTopic = null;
|
ChannelTopic = null;
|
||||||
HasTopic = false;
|
HasTopic = false;
|
||||||
|
OnlineUsers.Clear();
|
||||||
|
OnlineUserCount = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -584,6 +584,24 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ConnectionService.UserJoined += (serverUrl, channelName, username) =>
|
||||||
|
{
|
||||||
|
if (string.Equals(channelName, Chat.CurrentChannelName, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(serverUrl, Chat.CurrentServerUrl, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
_ = RefreshOnlineUsersAsync(serverUrl, channelName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionService.UserLeft += (serverUrl, channelName) =>
|
||||||
|
{
|
||||||
|
if (string.Equals(channelName, Chat.CurrentChannelName, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(serverUrl, Chat.CurrentServerUrl, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
_ = RefreshOnlineUsersAsync(serverUrl, channelName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ConnectionService.ErrorOccurred += (serverUrl, error) =>
|
ConnectionService.ErrorOccurred += (serverUrl, error) =>
|
||||||
{
|
{
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||||
@@ -593,6 +611,22 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RefreshOnlineUsersAsync(string serverUrl, string channelName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<UserPresenceDto> users = await ConnectionService.GetOnlineUsersAsync(serverUrl, channelName);
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
Chat.SetOnlineUsers(users);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// silently ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string?> HandleCommandAsync(string commandText)
|
private async Task<string?> HandleCommandAsync(string commandText)
|
||||||
{
|
{
|
||||||
CommandResult result = await _commandHandler.HandleAsync(commandText);
|
CommandResult result = await _commandHandler.HandleAsync(commandText);
|
||||||
@@ -663,11 +697,17 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
|
|
||||||
private async Task HandleCreateChannelRequested(ServerViewModel server)
|
private async Task HandleCreateChannelRequested(ServerViewModel server)
|
||||||
{
|
{
|
||||||
if (_mainWindow is null) return;
|
if (_mainWindow is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CreateChannelWindow dialog = new CreateChannelWindow();
|
CreateChannelWindow dialog = new CreateChannelWindow();
|
||||||
bool? result = await dialog.ShowDialog<bool?>(_mainWindow);
|
bool? result = await dialog.ShowDialog<bool?>(_mainWindow);
|
||||||
if (result != true) return;
|
if (result != true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -716,7 +756,10 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
|
|
||||||
private async Task HandleDeleteChannelRequested(ServerViewModel server)
|
private async Task HandleDeleteChannelRequested(ServerViewModel server)
|
||||||
{
|
{
|
||||||
if (_mainWindow is null) return;
|
if (_mainWindow is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string channelName = Chat.CurrentChannelName;
|
string channelName = Chat.CurrentChannelName;
|
||||||
if (string.IsNullOrEmpty(channelName) || !string.Equals(Chat.CurrentServerUrl, server.ServerUrl, StringComparison.OrdinalIgnoreCase))
|
if (string.IsNullOrEmpty(channelName) || !string.Equals(Chat.CurrentServerUrl, server.ServerUrl, StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -738,7 +781,10 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
$"Are you sure you want to delete #{channelName}?\nThis will remove all messages permanently.",
|
$"Are you sure you want to delete #{channelName}?\nThis will remove all messages permanently.",
|
||||||
ButtonEnum.YesNo);
|
ButtonEnum.YesNo);
|
||||||
ButtonResult confirm = await confirmBox.ShowWindowDialogAsync(_mainWindow);
|
ButtonResult confirm = await confirmBox.ShowWindowDialogAsync(_mainWindow);
|
||||||
if (confirm != ButtonResult.Yes) return;
|
if (confirm != ButtonResult.Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -814,6 +860,7 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_ = RefreshOnlineUsersAsync(serverUrl, channel.Name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (EchoHub.Client.Services.ChannelPasswordRequiredException)
|
catch (EchoHub.Client.Services.ChannelPasswordRequiredException)
|
||||||
@@ -830,6 +877,12 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
|
|
||||||
if (string.IsNullOrEmpty(pwd))
|
if (string.IsNullOrEmpty(pwd))
|
||||||
{
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
Chat.OnlineUsers.Clear();
|
||||||
|
Chat.ShowOnlineUsers = false;
|
||||||
|
Chat.OnlineUserCount = string.Empty;
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,6 +890,12 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
Chat.OnlineUsers.Clear();
|
||||||
|
Chat.ShowOnlineUsers = false;
|
||||||
|
Chat.OnlineUserCount = string.Empty;
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
using EchoHub.Core.DTOs;
|
||||||
|
using EchoHub.Core.Models;
|
||||||
|
|
||||||
|
namespace Decho.ViewModels;
|
||||||
|
|
||||||
|
public sealed class UserViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
public string Username { get; }
|
||||||
|
public string DisplayName { get; }
|
||||||
|
public string? NicknameColor { get; }
|
||||||
|
public string? StatusMessage { get; }
|
||||||
|
public ServerRole Role { get; }
|
||||||
|
|
||||||
|
public UserStatus Status
|
||||||
|
{
|
||||||
|
get => _status;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_status == value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_status = value;
|
||||||
|
this.RaisePropertyChanged();
|
||||||
|
this.RaisePropertyChanged(nameof(StatusColor));
|
||||||
|
this.RaisePropertyChanged(nameof(StatusText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserStatus _status;
|
||||||
|
|
||||||
|
public string FullName => DisplayName ?? Username;
|
||||||
|
|
||||||
|
public IBrush? DisplayColor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(NicknameColor))
|
||||||
|
{
|
||||||
|
return Brushes.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new SolidColorBrush(Color.Parse(NicknameColor));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusColor => Status switch
|
||||||
|
{
|
||||||
|
UserStatus.Online => "#4CAF50",
|
||||||
|
UserStatus.Away => "#FF9800",
|
||||||
|
UserStatus.DoNotDisturb => "#E53935",
|
||||||
|
UserStatus.Invisible => "#9E9E9E",
|
||||||
|
_ => "#9E9E9E",
|
||||||
|
};
|
||||||
|
|
||||||
|
public string StatusText => Status switch
|
||||||
|
{
|
||||||
|
UserStatus.Online => "Online",
|
||||||
|
UserStatus.Away => "Away",
|
||||||
|
UserStatus.DoNotDisturb => "Do Not Disturb",
|
||||||
|
UserStatus.Invisible => "Invisible",
|
||||||
|
_ => "Offline",
|
||||||
|
};
|
||||||
|
|
||||||
|
public UserViewModel(UserPresenceDto dto)
|
||||||
|
{
|
||||||
|
Username = dto.Username;
|
||||||
|
DisplayName = dto.DisplayName ?? dto.Username;
|
||||||
|
NicknameColor = dto.NicknameColor;
|
||||||
|
StatusMessage = dto.StatusMessage;
|
||||||
|
Role = dto.Role;
|
||||||
|
_status = dto.Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,33 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:Decho.ViewModels"
|
xmlns:vm="using:Decho.ViewModels"
|
||||||
xmlns:views="using:Decho.Views"
|
xmlns:views="using:Decho.Views"
|
||||||
x:Class="Decho.Views.ChatView"
|
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
||||||
x:DataType="vm:ChatViewModel">
|
x:Class="Decho.Views.ChatView"
|
||||||
<Grid RowDefinitions="Auto, *, Auto" Margin="10">
|
x:DataType="vm:ChatViewModel">
|
||||||
<TextBlock FontWeight="Bold"
|
<Grid RowDefinitions="Auto, Auto, *, Auto" Margin="10">
|
||||||
FontSize="16"
|
<Grid ColumnDefinitions="*, Auto">
|
||||||
Margin="0 0 0 8"
|
<StackPanel Orientation="Horizontal">
|
||||||
Text="{Binding ChannelTitle}" />
|
<TextBlock FontWeight="Bold"
|
||||||
<views:MessageListView Grid.Row="1" />
|
FontSize="16"
|
||||||
<views:MessageComposerView Grid.Row="2"
|
Margin="0 0 4 4"
|
||||||
DataContext="{Binding Composer}" />
|
Text="{Binding ChannelTitle}" />
|
||||||
</Grid>
|
<TextBlock Text="{Binding ChannelTopic}"
|
||||||
|
FontSize="12"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Opacity="0.7" />
|
||||||
|
</StackPanel>
|
||||||
|
<Button i:Attached.Icon="fa-users"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="-10"
|
||||||
|
Background="Transparent"
|
||||||
|
Foreground="{DynamicResource UiTheme08}"
|
||||||
|
Command="{Binding ToggleUsersPanelCommand}"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right" />
|
||||||
|
</Grid>
|
||||||
|
<views:MessageListView Grid.Row="2" />
|
||||||
|
<views:MessageComposerView Grid.Row="3"
|
||||||
|
DataContext="{Binding Composer}" />
|
||||||
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public partial class CreateChannelWindow : Window
|
|||||||
string name = ChannelName.Text?.Trim() ?? string.Empty;
|
string name = ChannelName.Text?.Trim() ?? string.Empty;
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
ChannelName.Focus();
|
_ = ChannelName.Focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<vm:MainWindowViewModel/>
|
<vm:MainWindowViewModel/>
|
||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto, *" ColumnDefinitions="Auto, *">
|
<Grid RowDefinitions="Auto, *" ColumnDefinitions="Auto, *, Auto">
|
||||||
<DockPanel HorizontalAlignment="Stretch">
|
<DockPanel HorizontalAlignment="Stretch">
|
||||||
<TextBlock FontSize="20"
|
<TextBlock FontSize="20"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
@@ -44,5 +44,8 @@
|
|||||||
<views:ChatView Grid.RowSpan="2"
|
<views:ChatView Grid.RowSpan="2"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
DataContext="{Binding Chat}" />
|
DataContext="{Binding Chat}" />
|
||||||
|
<views:OnlineUsersView Grid.RowSpan="2"
|
||||||
|
Grid.Column="2"
|
||||||
|
DataContext="{Binding Chat}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:Decho.ViewModels"
|
||||||
|
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
||||||
|
x:Class="Decho.Views.OnlineUsersView"
|
||||||
|
x:DataType="vm:ChatViewModel">
|
||||||
|
<Border Background="{DynamicResource UiTheme00}"
|
||||||
|
Width="200"
|
||||||
|
IsVisible="{Binding ShowOnlineUsers}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontWeight="Bold"
|
||||||
|
FontSize="13"
|
||||||
|
Margin="8 6"
|
||||||
|
Text="{Binding OnlineUserCount, StringFormat='Online — {0}'}" />
|
||||||
|
<ItemsControl ItemsSource="{Binding OnlineUsers}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:UserViewModel">
|
||||||
|
<Grid ColumnDefinitions="Auto, *" Margin="8 6">
|
||||||
|
<Ellipse Width="8" Height="8"
|
||||||
|
Fill="{Binding StatusColor}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="0 0 6 0" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Text="{Binding FullName}"
|
||||||
|
Foreground="{Binding DisplayColor}"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="12"
|
||||||
|
Cursor="Hand"
|
||||||
|
PointerPressed="OnUserNamePointerPressed" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
using Decho.ViewModels;
|
||||||
|
|
||||||
|
using EchoHub.Core.DTOs;
|
||||||
|
|
||||||
|
namespace Decho.Views;
|
||||||
|
|
||||||
|
public partial class OnlineUsersView : UserControl
|
||||||
|
{
|
||||||
|
public OnlineUsersView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnUserNamePointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not TextBlock textBlock || textBlock.DataContext is not UserViewModel user)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TopLevel.GetTopLevel(this) is not Window parent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.DataContext is not MainWindowViewModel mainVm)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string serverUrl = mainVm.Chat.CurrentServerUrl;
|
||||||
|
UserProfileDto? profile = await mainVm.ConnectionService.GetUserProfileAsync(serverUrl, user.Username);
|
||||||
|
if (profile is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileWindow dialog = new ProfileWindow(profile);
|
||||||
|
await dialog.ShowDialog(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,13 +49,13 @@
|
|||||||
IsVisible="{Binding IsConnected}" />
|
IsVisible="{Binding IsConnected}" />
|
||||||
<MenuItem Header="Remove Server"
|
<MenuItem Header="Remove Server"
|
||||||
Command="{Binding RemoveCommand}" />
|
Command="{Binding RemoveCommand}" />
|
||||||
<Separator />
|
<Separator IsVisible="{Binding IsConnected}" />
|
||||||
<MenuItem Header="Create Channel"
|
<MenuItem Header="Create Channel"
|
||||||
Command="{Binding CreateChannelCommand}"
|
Command="{Binding CreateChannelCommand}"
|
||||||
IsVisible="{Binding IsConnected}" />
|
IsVisible="{Binding IsConnected}" />
|
||||||
<MenuItem Header="Delete Channel"
|
<MenuItem Header="Delete Channel"
|
||||||
Command="{Binding DeleteChannelCommand}"
|
Command="{Binding DeleteChannelCommand}"
|
||||||
IsVisible="{Binding IsConnected}" />
|
IsVisible="{Binding IsConnected}" />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</Button.ContextMenu>
|
</Button.ContextMenu>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user