mirror of
https://github.com/Stone-Red-Code/Decho.git
synced 2026-09-04 08:56:14 +02:00
Code cleanup
This commit is contained in:
@@ -35,86 +35,6 @@ public sealed class ConnectionService : IDisposable
|
||||
private readonly Dictionary<string, ServerConnection> _connections = new(StringComparer.OrdinalIgnoreCase);
|
||||
internal IReadOnlyDictionary<string, ServerConnection> Connections => _connections;
|
||||
|
||||
private async Task<ServerModel> ConnectCoreAsync(ConnectDialogResult dialogResult)
|
||||
{
|
||||
ConnectionManager conn = new ConnectionManager();
|
||||
|
||||
ConnectResult result;
|
||||
try
|
||||
{
|
||||
result = await conn.ConnectAsync(dialogResult, _ => { });
|
||||
}
|
||||
catch
|
||||
{
|
||||
await conn.DisposeAsync();
|
||||
throw;
|
||||
}
|
||||
|
||||
LoginResponse login = result.Login;
|
||||
UserModel userModel = new UserModel(login.Username, login.DisplayName ?? login.Username, login.NicknameColor);
|
||||
|
||||
ObservableCollection<ChannelModel> channels = [];
|
||||
ServerModel serverModel = new ServerModel(
|
||||
Guid.NewGuid().ToString("N"),
|
||||
new Uri(dialogResult.ServerUrl).Host,
|
||||
channels,
|
||||
dialogResult.ServerUrl,
|
||||
isConnected: true,
|
||||
connectedUser: login.Username);
|
||||
|
||||
ServerConnection serverEntry = new ServerConnection(conn, conn.Api!, serverModel, userModel);
|
||||
|
||||
foreach (ChannelDto ch in result.Channels)
|
||||
{
|
||||
ChannelModel channelModel = ChannelModelFromDto(ch);
|
||||
channels.Add(channelModel);
|
||||
}
|
||||
|
||||
WireConnectionEvents(serverEntry, conn);
|
||||
|
||||
_connections[dialogResult.ServerUrl] = serverEntry;
|
||||
SaveRefreshToken(dialogResult.ServerUrl, dialogResult.RememberMe);
|
||||
ServerAdded?.Invoke(serverModel);
|
||||
|
||||
AutoJoinRemainingChannels(dialogResult.ServerUrl, result.Channels);
|
||||
|
||||
return serverModel;
|
||||
}
|
||||
|
||||
private async void AutoJoinRemainingChannels(string serverUrl, List<ChannelDto> channels)
|
||||
{
|
||||
ClientConfig config = ConfigManager.Load();
|
||||
List<string> leftChannels = config.SavedServers
|
||||
.FirstOrDefault(s => string.Equals(s.Url, serverUrl, StringComparison.OrdinalIgnoreCase))
|
||||
?.LeftChannels ?? [];
|
||||
|
||||
foreach (ChannelDto ch in channels)
|
||||
{
|
||||
if (string.Equals(ch.Name, HubConstants.DefaultChannel, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (leftChannels.Contains(ch.Name, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ = await JoinChannelAsync(serverUrl, ch.Name);
|
||||
}
|
||||
catch (EchoHub.Client.Services.ChannelPasswordRequiredException)
|
||||
{
|
||||
// protected channel — join stays manual
|
||||
}
|
||||
catch
|
||||
{
|
||||
// skip channels we can't join
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ServerModel> ConnectAsync(string serverUrl, string username, string password, bool isRegister, bool rememberMe)
|
||||
{
|
||||
ConnectDialogResult dialogResult = new ConnectDialogResult(
|
||||
@@ -129,24 +49,6 @@ public sealed class ConnectionService : IDisposable
|
||||
_ = await ConnectCoreAsync(dialogResult);
|
||||
}
|
||||
|
||||
private async Task<ServerConnection?> CleanupConnectionAsync(string serverUrl)
|
||||
{
|
||||
if (!_connections.TryGetValue(serverUrl, out ServerConnection? entry))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
entry.Server.IsConnected = false;
|
||||
entry.Server.IsConnecting = false;
|
||||
|
||||
await entry.Manager.CleanupAsync();
|
||||
entry.ApiClient.Dispose();
|
||||
await entry.Manager.DisposeAsync();
|
||||
|
||||
_ = _connections.Remove(serverUrl);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync(string serverUrl)
|
||||
{
|
||||
ServerConnection? entry = await CleanupConnectionAsync(serverUrl);
|
||||
@@ -163,26 +65,6 @@ public sealed class ConnectionService : IDisposable
|
||||
ServerRemoved?.Invoke(serverUrl);
|
||||
}
|
||||
|
||||
private static void ModifyConfig(string serverUrl, Action<ClientConfig, SavedServer?> action)
|
||||
{
|
||||
ClientConfig config = ConfigManager.Load();
|
||||
SavedServer? saved = config.SavedServers.FirstOrDefault(s =>
|
||||
string.Equals(s.Url, serverUrl, StringComparison.OrdinalIgnoreCase));
|
||||
action(config, saved);
|
||||
ConfigManager.Save(config);
|
||||
}
|
||||
|
||||
private static void RemoveServerFromConfig(string serverUrl)
|
||||
{
|
||||
ModifyConfig(serverUrl, (config, saved) =>
|
||||
{
|
||||
if (saved is not null)
|
||||
{
|
||||
_ = config.SavedServers.Remove(saved);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendMessageAsync(string serverUrl, string channelName, string content)
|
||||
{
|
||||
if (!_connections.TryGetValue(serverUrl, out ServerConnection? entry))
|
||||
@@ -256,17 +138,6 @@ public sealed class ConnectionService : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveFromLeftChannels(string serverUrl, string channelName)
|
||||
{
|
||||
ClientConfig config = ConfigManager.Load();
|
||||
SavedServer? saved = config.SavedServers
|
||||
.FirstOrDefault(s => string.Equals(s.Url, serverUrl, StringComparison.OrdinalIgnoreCase));
|
||||
if (saved is not null && saved.LeftChannels.Remove(channelName))
|
||||
{
|
||||
ConfigManager.Save(config);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<MessageModel>> GetHistoryAsync(string serverUrl, string channelName, int count = HubConstants.DefaultHistoryCount, int offset = 0)
|
||||
{
|
||||
if (!_connections.TryGetValue(serverUrl, out ServerConnection? entry))
|
||||
@@ -591,6 +462,135 @@ public sealed class ConnectionService : IDisposable
|
||||
return _connections.TryGetValue(serverUrl, out ServerConnection? conn) ? conn : null;
|
||||
}
|
||||
|
||||
private static void ModifyConfig(string serverUrl, Action<ClientConfig, SavedServer?> action)
|
||||
{
|
||||
ClientConfig config = ConfigManager.Load();
|
||||
SavedServer? saved = config.SavedServers.FirstOrDefault(s =>
|
||||
string.Equals(s.Url, serverUrl, StringComparison.OrdinalIgnoreCase));
|
||||
action(config, saved);
|
||||
ConfigManager.Save(config);
|
||||
}
|
||||
|
||||
private static void RemoveServerFromConfig(string serverUrl)
|
||||
{
|
||||
ModifyConfig(serverUrl, (config, saved) =>
|
||||
{
|
||||
if (saved is not null)
|
||||
{
|
||||
_ = config.SavedServers.Remove(saved);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void RemoveFromLeftChannels(string serverUrl, string channelName)
|
||||
{
|
||||
ClientConfig config = ConfigManager.Load();
|
||||
SavedServer? saved = config.SavedServers
|
||||
.FirstOrDefault(s => string.Equals(s.Url, serverUrl, StringComparison.OrdinalIgnoreCase));
|
||||
if (saved is not null && saved.LeftChannels.Remove(channelName))
|
||||
{
|
||||
ConfigManager.Save(config);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ServerModel> ConnectCoreAsync(ConnectDialogResult dialogResult)
|
||||
{
|
||||
ConnectionManager conn = new ConnectionManager();
|
||||
|
||||
ConnectResult result;
|
||||
try
|
||||
{
|
||||
result = await conn.ConnectAsync(dialogResult, _ => { });
|
||||
}
|
||||
catch
|
||||
{
|
||||
await conn.DisposeAsync();
|
||||
throw;
|
||||
}
|
||||
|
||||
LoginResponse login = result.Login;
|
||||
UserModel userModel = new UserModel(login.Username, login.DisplayName ?? login.Username, login.NicknameColor);
|
||||
|
||||
ObservableCollection<ChannelModel> channels = [];
|
||||
ServerModel serverModel = new ServerModel(
|
||||
Guid.NewGuid().ToString("N"),
|
||||
new Uri(dialogResult.ServerUrl).Host,
|
||||
channels,
|
||||
dialogResult.ServerUrl,
|
||||
isConnected: true,
|
||||
connectedUser: login.Username);
|
||||
|
||||
ServerConnection serverEntry = new ServerConnection(conn, conn.Api!, serverModel, userModel);
|
||||
|
||||
foreach (ChannelDto ch in result.Channels)
|
||||
{
|
||||
ChannelModel channelModel = ChannelModelFromDto(ch);
|
||||
channels.Add(channelModel);
|
||||
}
|
||||
|
||||
WireConnectionEvents(serverEntry, conn);
|
||||
|
||||
_connections[dialogResult.ServerUrl] = serverEntry;
|
||||
SaveRefreshToken(dialogResult.ServerUrl, dialogResult.RememberMe);
|
||||
ServerAdded?.Invoke(serverModel);
|
||||
|
||||
AutoJoinRemainingChannels(dialogResult.ServerUrl, result.Channels);
|
||||
|
||||
return serverModel;
|
||||
}
|
||||
|
||||
private async void AutoJoinRemainingChannels(string serverUrl, List<ChannelDto> channels)
|
||||
{
|
||||
ClientConfig config = ConfigManager.Load();
|
||||
List<string> leftChannels = config.SavedServers
|
||||
.FirstOrDefault(s => string.Equals(s.Url, serverUrl, StringComparison.OrdinalIgnoreCase))
|
||||
?.LeftChannels ?? [];
|
||||
|
||||
foreach (ChannelDto ch in channels)
|
||||
{
|
||||
if (string.Equals(ch.Name, HubConstants.DefaultChannel, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (leftChannels.Contains(ch.Name, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ = await JoinChannelAsync(serverUrl, ch.Name);
|
||||
}
|
||||
catch (EchoHub.Client.Services.ChannelPasswordRequiredException)
|
||||
{
|
||||
// protected channel — join stays manual
|
||||
}
|
||||
catch
|
||||
{
|
||||
// skip channels we can't join
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ServerConnection?> CleanupConnectionAsync(string serverUrl)
|
||||
{
|
||||
if (!_connections.TryGetValue(serverUrl, out ServerConnection? entry))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
entry.Server.IsConnected = false;
|
||||
entry.Server.IsConnecting = false;
|
||||
|
||||
await entry.Manager.CleanupAsync();
|
||||
entry.ApiClient.Dispose();
|
||||
await entry.Manager.DisposeAsync();
|
||||
|
||||
_ = _connections.Remove(serverUrl);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private void SaveRefreshToken(string serverUrl, bool rememberMe)
|
||||
{
|
||||
if (!rememberMe)
|
||||
|
||||
@@ -3,29 +3,27 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace Decho.ViewModels;
|
||||
|
||||
public sealed class AutocompleteController : ViewModelBase
|
||||
public sealed class AutocompleteController(IEnumerable<AutocompleteProvider> providers) : ViewModelBase
|
||||
{
|
||||
private readonly List<AutocompleteProvider> _providers;
|
||||
|
||||
private static readonly Regex TriggerPattern = new(@"([@#])(\w*)$", RegexOptions.Compiled);
|
||||
|
||||
private readonly List<AutocompleteProvider> _providers = providers.ToList();
|
||||
public ObservableCollection<string> FilteredItems { get; } = [];
|
||||
|
||||
public bool ShowPopup
|
||||
{
|
||||
get => field;
|
||||
get;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
}
|
||||
|
||||
public int SelectedIndex
|
||||
{
|
||||
get => field;
|
||||
get;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
}
|
||||
|
||||
public string FilterText
|
||||
{
|
||||
get => field;
|
||||
get;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
}
|
||||
|
||||
@@ -33,11 +31,6 @@ public sealed class AutocompleteController : ViewModelBase
|
||||
|
||||
public int TriggerCharIndex { get; private set; }
|
||||
|
||||
public AutocompleteController(IEnumerable<AutocompleteProvider> providers)
|
||||
{
|
||||
_providers = providers.ToList();
|
||||
}
|
||||
|
||||
public void Update(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) || _providers.Count == 0)
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
namespace Decho.ViewModels;
|
||||
|
||||
public sealed class AutocompleteProvider
|
||||
public sealed class AutocompleteProvider(char trigger, Func<IEnumerable<string>> itemsSource, string insertPrefix, int maxResults = 10)
|
||||
{
|
||||
public char Trigger { get; }
|
||||
public Func<IEnumerable<string>> ItemsSource { get; }
|
||||
public Func<string, string, bool> Filter { get; }
|
||||
public Func<string, string> FormatInsertion { get; }
|
||||
public int MaxResults { get; }
|
||||
|
||||
public AutocompleteProvider(
|
||||
char trigger,
|
||||
Func<IEnumerable<string>> itemsSource,
|
||||
string insertPrefix,
|
||||
int maxResults = 10)
|
||||
{
|
||||
Trigger = trigger;
|
||||
ItemsSource = itemsSource;
|
||||
Filter = (item, filter) => item.StartsWith(filter, StringComparison.OrdinalIgnoreCase);
|
||||
FormatInsertion = item => $"{insertPrefix}{item} ";
|
||||
MaxResults = maxResults;
|
||||
}
|
||||
public char Trigger { get; } = trigger;
|
||||
public Func<IEnumerable<string>> ItemsSource { get; } = itemsSource;
|
||||
public Func<string, string, bool> Filter { get; } = (item, filter) => item.StartsWith(filter, StringComparison.OrdinalIgnoreCase);
|
||||
public Func<string, string> FormatInsertion { get; } = item => $"{insertPrefix}{item} ";
|
||||
public int MaxResults { get; } = maxResults;
|
||||
}
|
||||
@@ -34,13 +34,13 @@ public sealed class ChannelViewModel(ChannelModel model) : ViewModelBase
|
||||
|
||||
public bool IsLocked
|
||||
{
|
||||
get => field;
|
||||
get;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
}
|
||||
|
||||
public int UnreadCount
|
||||
{
|
||||
get => field;
|
||||
get;
|
||||
set
|
||||
{
|
||||
if (field == value)
|
||||
@@ -60,7 +60,7 @@ public sealed class ChannelViewModel(ChannelModel model) : ViewModelBase
|
||||
|
||||
public int MentionCount
|
||||
{
|
||||
get => field;
|
||||
get;
|
||||
set
|
||||
{
|
||||
if (field == value)
|
||||
|
||||
@@ -39,7 +39,7 @@ public sealed class ChatViewModel : ViewModelBase
|
||||
|
||||
public bool ShowOnlineUsers
|
||||
{
|
||||
get => field;
|
||||
get;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public sealed class ChatViewModel : ViewModelBase
|
||||
|
||||
public string OnlineUserCount
|
||||
{
|
||||
get => field;
|
||||
get;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
} = string.Empty;
|
||||
|
||||
@@ -63,11 +63,6 @@ public sealed class ChatViewModel : ViewModelBase
|
||||
ToggleUsersPanelCommand = ReactiveCommand.Create(ToggleUsersPanel);
|
||||
}
|
||||
|
||||
private void ToggleUsersPanel()
|
||||
{
|
||||
ShowOnlineUsers = !ShowOnlineUsers;
|
||||
}
|
||||
|
||||
public void SetChannel(ChannelViewModel? channel, string serverUrl = "", bool isServerConnected = true)
|
||||
{
|
||||
if (channel is null)
|
||||
@@ -145,4 +140,9 @@ public sealed class ChatViewModel : ViewModelBase
|
||||
OnlineUsers.Clear();
|
||||
OnlineUserCount = string.Empty;
|
||||
}
|
||||
|
||||
private void ToggleUsersPanel()
|
||||
{
|
||||
ShowOnlineUsers = !ShowOnlineUsers;
|
||||
}
|
||||
}
|
||||
@@ -1059,5 +1059,4 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
return Chat.CurrentServerUrl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,10 +13,9 @@ public sealed class MessageComposerViewModel : ViewModelBase
|
||||
|
||||
public event Action<string, string>? FileUploadRequested;
|
||||
|
||||
private CommandHandler? _commandHandler;
|
||||
|
||||
private readonly ObservableCollection<UserViewModel> _onlineUsers = [];
|
||||
private readonly ObservableCollection<string> _channelNames = [];
|
||||
private CommandHandler? _commandHandler;
|
||||
|
||||
public string Draft
|
||||
{
|
||||
|
||||
@@ -5,13 +5,14 @@ using EchoHub.Core.Models;
|
||||
|
||||
namespace Decho.ViewModels;
|
||||
|
||||
public sealed class UserViewModel : ViewModelBase
|
||||
public sealed class UserViewModel(UserPresenceDto dto) : ViewModelBase
|
||||
{
|
||||
public string Username { get; }
|
||||
public string DisplayName { get; }
|
||||
public string? NicknameColor { get; }
|
||||
public string? StatusMessage { get; }
|
||||
public ServerRole Role { get; }
|
||||
private UserStatus _status = dto.Status;
|
||||
public string Username { get; } = dto.Username;
|
||||
public string DisplayName { get; } = dto.DisplayName ?? dto.Username;
|
||||
public string? NicknameColor { get; } = dto.NicknameColor;
|
||||
public string? StatusMessage { get; } = dto.StatusMessage;
|
||||
public ServerRole Role { get; } = dto.Role;
|
||||
|
||||
public UserStatus Status
|
||||
{
|
||||
@@ -30,8 +31,6 @@ public sealed class UserViewModel : ViewModelBase
|
||||
}
|
||||
}
|
||||
|
||||
private UserStatus _status;
|
||||
|
||||
public string FullName => DisplayName ?? Username;
|
||||
|
||||
public IBrush? DisplayColor
|
||||
@@ -71,14 +70,4 @@ public sealed class UserViewModel : ViewModelBase
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,4 +147,4 @@ public sealed class ConnectDialogResult
|
||||
public bool RememberMe { get; set; }
|
||||
public bool IsSavedSession { get; set; }
|
||||
public string? SavedRefreshToken { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -29,4 +29,4 @@ public partial class CreateChannelWindow : Window
|
||||
ResultPassword = string.IsNullOrWhiteSpace(ChannelPassword.Text) ? null : ChannelPassword.Text;
|
||||
Close(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Decho.Views;
|
||||
|
||||
public partial class MessageItemView : UserControl
|
||||
{
|
||||
private static readonly Regex MentionRegex = new(@"@(\w+)", RegexOptions.Compiled);
|
||||
private CancellationTokenSource? _loadCts;
|
||||
private string? _loadedMessageId;
|
||||
|
||||
@@ -25,8 +26,6 @@ public partial class MessageItemView : UserControl
|
||||
DataContextChanged += OnDataContextChanged;
|
||||
}
|
||||
|
||||
private static readonly Regex MentionRegex = new(@"@(\w+)", RegexOptions.Compiled);
|
||||
|
||||
private void OnDataContextChanged(object? sender, EventArgs args)
|
||||
{
|
||||
if (DataContext is MessageViewModel newMsg && newMsg.Model.Id == _loadedMessageId)
|
||||
|
||||
@@ -40,4 +40,4 @@ public partial class OnlineUsersView : UserControl
|
||||
ProfileWindow dialog = new ProfileWindow(profile);
|
||||
await dialog.ShowDialog(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,11 +51,6 @@ public sealed partial class ProfileWindow : Window
|
||||
LastSeenText.Text = profile.LastSeenAt.ToString("g");
|
||||
}
|
||||
|
||||
private void OnCloseClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private static string FormatStatus(UserStatus status)
|
||||
{
|
||||
return status switch
|
||||
@@ -90,4 +85,9 @@ public sealed partial class ProfileWindow : Window
|
||||
_ => role.ToString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
@@ -15,4 +15,4 @@ internal static class ViewExtensions
|
||||
{
|
||||
return TopLevel.GetTopLevel(control)?.DataContext as MainWindowViewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user