mirror of
https://github.com/Stone-Red-Code/Decho.git
synced 2026-09-04 08:56:14 +02:00
Add password support and UI for joining protected channels
This commit is contained in:
@@ -2,7 +2,7 @@ using System.Collections.ObjectModel;
|
||||
|
||||
namespace Decho.Models;
|
||||
|
||||
public sealed class ChannelModel(string id, string name, ObservableCollection<MessageModel> messages, string? topic = null, bool isPublic = true)
|
||||
public sealed class ChannelModel(string id, string name, ObservableCollection<MessageModel> messages, string? topic = null, bool isPublic = true, bool isProtected = false)
|
||||
{
|
||||
public string Id { get; } = id;
|
||||
|
||||
@@ -12,5 +12,7 @@ public sealed class ChannelModel(string id, string name, ObservableCollection<Me
|
||||
|
||||
public bool IsPublic { get; } = isPublic;
|
||||
|
||||
public bool IsProtected { get; } = isProtected;
|
||||
|
||||
public ObservableCollection<MessageModel> Messages { get; } = messages;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public sealed class ConnectionService : IDisposable
|
||||
await entry.Manager.SendMessageAsync(channelName, content);
|
||||
}
|
||||
|
||||
public async Task<List<MessageModel>> JoinChannelAsync(string serverUrl, string channelName)
|
||||
public async Task<List<MessageModel>> JoinChannelAsync(string serverUrl, string channelName, string? password = null)
|
||||
{
|
||||
if (!_connections.TryGetValue(serverUrl, out ServerConnection? entry))
|
||||
{
|
||||
@@ -166,7 +166,7 @@ public sealed class ConnectionService : IDisposable
|
||||
|
||||
if (entry.Manager.TrackChannel(channelName))
|
||||
{
|
||||
var outcome = await entry.Manager.JoinChannelAsync(channelName);
|
||||
var outcome = await entry.Manager.JoinChannelAsync(channelName, password);
|
||||
return outcome.History.Select(m => MessageModelFromDto(m, entry)).ToList();
|
||||
}
|
||||
|
||||
@@ -480,7 +480,8 @@ public sealed class ConnectionService : IDisposable
|
||||
dto.Name,
|
||||
[],
|
||||
dto.Topic,
|
||||
dto.IsPublic);
|
||||
dto.IsPublic,
|
||||
dto.IsProtected);
|
||||
}
|
||||
|
||||
internal static MessageModel MessageModelFromDto(MessageDto dto, ServerConnection entry)
|
||||
|
||||
@@ -28,6 +28,14 @@ public sealed class ChannelViewModel(ChannelModel model) : ViewModelBase
|
||||
|
||||
public bool IsPublic => Model.IsPublic;
|
||||
|
||||
public bool IsProtected => Model.IsProtected;
|
||||
|
||||
public bool IsLocked
|
||||
{
|
||||
get => field;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<MessageViewModel> Messages { get; } = new ObservableCollection<MessageViewModel>(
|
||||
model.Messages.Select(message => new MessageViewModel(message)));
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using EchoHub.Core.DTOs;
|
||||
using EchoHub.Core.Models;
|
||||
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Base;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
using System.Reactive;
|
||||
@@ -86,11 +87,11 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var box = MessageBoxManager.GetMessageBoxStandard(
|
||||
IMsBox<ButtonResult> box = MessageBoxManager.GetMessageBoxStandard(
|
||||
"Connection Failed",
|
||||
$"Could not connect to server:\n{ex.Message}",
|
||||
ButtonEnum.Ok);
|
||||
await box.ShowWindowDialogAsync(_mainWindow);
|
||||
_ = await box.ShowWindowDialogAsync(_mainWindow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +137,57 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
return await dialog.ShowDialog<ConnectDialogResult?>(_mainWindow);
|
||||
}
|
||||
|
||||
private async Task<string?> ShowPasswordPromptWindowAsync()
|
||||
{
|
||||
var window = new Avalonia.Controls.Window
|
||||
{
|
||||
Title = "Channel Password",
|
||||
Width = 320,
|
||||
Height = 180,
|
||||
WindowStartupLocation = Avalonia.Controls.WindowStartupLocation.CenterOwner,
|
||||
SizeToContent = Avalonia.Controls.SizeToContent.Height,
|
||||
CanResize = false,
|
||||
};
|
||||
|
||||
var passwordBox = new Avalonia.Controls.TextBox { Watermark = "Password", PasswordChar = '*' };
|
||||
string? result = null;
|
||||
|
||||
var joinBtn = new Avalonia.Controls.Button { Content = "Join", IsDefault = true };
|
||||
var cancelBtn = new Avalonia.Controls.Button { Content = "Cancel", IsCancel = true };
|
||||
|
||||
joinBtn.Click += (_, _) =>
|
||||
{
|
||||
result = passwordBox.Text;
|
||||
window.Close();
|
||||
};
|
||||
cancelBtn.Click += (_, _) => window.Close();
|
||||
|
||||
var buttons = new Avalonia.Controls.StackPanel
|
||||
{
|
||||
Orientation = Avalonia.Layout.Orientation.Horizontal,
|
||||
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right,
|
||||
Spacing = 8,
|
||||
Children = { cancelBtn, joinBtn },
|
||||
};
|
||||
|
||||
var panel = new Avalonia.Controls.StackPanel
|
||||
{
|
||||
Margin = new Avalonia.Thickness(12),
|
||||
Spacing = 8,
|
||||
Children =
|
||||
{
|
||||
new Avalonia.Controls.TextBlock { Text = "This channel is password protected." },
|
||||
new Avalonia.Controls.TextBlock { Text = "Enter the channel password:" },
|
||||
passwordBox,
|
||||
buttons,
|
||||
},
|
||||
};
|
||||
|
||||
window.Content = panel;
|
||||
await window.ShowDialog(_mainWindow!);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void WireCommandHandlerEvents()
|
||||
{
|
||||
_commandHandler.OnSetStatus += async (status, message) =>
|
||||
@@ -162,7 +214,7 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
return;
|
||||
}
|
||||
|
||||
List<MessageModel> channel = await ConnectionService.JoinChannelAsync(serverUrl, channelName);
|
||||
List<MessageModel> channel = await ConnectionService.JoinChannelAsync(serverUrl, channelName, password);
|
||||
EnsureChannelInList(serverUrl, channelName);
|
||||
ChannelModel? channelModel = FindChannel(serverUrl, channelName);
|
||||
if (channelModel is not null)
|
||||
@@ -456,10 +508,11 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
}
|
||||
|
||||
Sidebar.Servers.Insert(insertIndex, serverVm);
|
||||
if (serverVm.Channels.Count > 0)
|
||||
{
|
||||
serverVm.SelectedChannel = serverVm.Channels[0];
|
||||
}
|
||||
|
||||
ChannelViewModel? defaultChannel = serverVm.Channels.FirstOrDefault(c => c.Name == HubConstants.DefaultChannel);
|
||||
defaultChannel ??= serverVm.Channels.FirstOrDefault(c => c.IsPublic);
|
||||
defaultChannel ??= serverVm.Channels.FirstOrDefault();
|
||||
serverVm.SelectedChannel = defaultChannel;
|
||||
|
||||
StatusText = $"Connected to {server.Name}";
|
||||
});
|
||||
@@ -601,14 +654,26 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
Chat.Composer.SetCommandHandler(new CommandHandler());
|
||||
|
||||
if (channel.IsProtected && channel.Messages.Count == 0)
|
||||
{
|
||||
channel.IsLocked = true;
|
||||
Chat.Composer.IsConnected = false;
|
||||
}
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
string? password = null;
|
||||
while (true)
|
||||
{
|
||||
List<MessageModel> history = await ConnectionService.JoinChannelAsync(serverUrl, channel.Name);
|
||||
try
|
||||
{
|
||||
List<MessageModel> history = await ConnectionService.JoinChannelAsync(serverUrl, channel.Name, password);
|
||||
|
||||
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
channel.IsLocked = false;
|
||||
Chat.Composer.IsConnected = isServerConnected;
|
||||
|
||||
if (channel.Messages.Count == 0)
|
||||
{
|
||||
foreach (MessageModel msg in history)
|
||||
@@ -617,10 +682,32 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Channel might not be available
|
||||
|
||||
return;
|
||||
}
|
||||
catch (EchoHub.Client.Services.ChannelPasswordRequiredException)
|
||||
{
|
||||
string? pwd = await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
if (_mainWindow is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await ShowPasswordPromptWindowAsync();
|
||||
});
|
||||
|
||||
if (string.IsNullOrEmpty(pwd))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
password = pwd;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -697,11 +784,11 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
catch (Exception ex)
|
||||
{
|
||||
serverVm.IsConnecting = false;
|
||||
var box = MessageBoxManager.GetMessageBoxStandard(
|
||||
IMsBox<ButtonResult> box = MessageBoxManager.GetMessageBoxStandard(
|
||||
"Connection Failed",
|
||||
$"Could not connect to server:\n{ex.Message}",
|
||||
ButtonEnum.Ok);
|
||||
await box.ShowWindowDialogAsync(_mainWindow);
|
||||
_ = await box.ShowWindowDialogAsync(_mainWindow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user