Files
OwnCord/Client/OwnCord.Client/Controls/ToastControl.xaml.cs
T
jevb c1c25ed26c feat: implement full client UI from mockup — 10 phases, 331 tests
Client UI:
- Design system: Colors, Typography, Controls resource dictionaries
- Message actions: reply compose bar, hover edit/delete/reply buttons
- Rich content: code blocks, attachments, system messages, content parser
- Server strip: 72px sidebar with server icons, home button, add server
- Status picker: popup for changing online/idle/dnd/invisible status
- ConnectPage: server health check dots with auto-refresh
- User popup: profile card with banner, avatar, roles, member since
- Emoji picker: 6 categories, search, grid of Unicode emojis
- Settings overlay: full-screen with sidebar navigation
- Friends/DM view: sidebar + friends list with tabs (online/all/pending)
- Toast notifications: auto-dismiss after 3s with fade animation

Models & services:
- Attachment model added to Message, ApiMessage, ChatMessagePayload
- EditMessageAsync, DeleteMessageAsync, SendStatusChangeAsync APIs
- MessageContentParser (code blocks, inline code, bold, italic)
- EmojiData, ToastService, HealthStatusToBrushConverter

Server (from prior session):
- Voice room management, SFU, speaker detection
- ACME/TLS support, config improvements
- Protocol and schema updates

Tests: 331 passing (61 converter + 24 voice service + 34 voice VM +
41 parser + 9 edit/delete + existing)
2026-03-15 11:42:25 +01:00

86 lines
2.5 KiB
C#

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace OwnCord.Client.Controls;
public partial class ToastControl : UserControl
{
private DispatcherTimer? _autoDismissTimer;
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register(
nameof(Message),
typeof(string),
typeof(ToastControl),
new PropertyMetadata(string.Empty));
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register(
nameof(IsOpen),
typeof(bool),
typeof(ToastControl),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsOpenChanged));
public ToastControl()
{
InitializeComponent();
}
public string Message
{
get => (string)GetValue(MessageProperty);
set => SetValue(MessageProperty, value);
}
public bool IsOpen
{
get => (bool)GetValue(IsOpenProperty);
set => SetValue(IsOpenProperty, value);
}
private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ToastControl control)
control.HandleIsOpenChanged((bool)e.NewValue);
}
private void HandleIsOpenChanged(bool isOpen)
{
_autoDismissTimer?.Stop();
_autoDismissTimer = null;
if (isOpen)
{
// Fade in
var fadeIn = new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(200))
{
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut }
};
BeginAnimation(OpacityProperty, fadeIn);
// Auto-dismiss after 3 seconds
_autoDismissTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(3) };
_autoDismissTimer.Tick += OnAutoDismiss;
_autoDismissTimer.Start();
}
else
{
// Fade out
var fadeOut = new DoubleAnimation(1, 0, TimeSpan.FromMilliseconds(300))
{
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseIn }
};
BeginAnimation(OpacityProperty, fadeOut);
}
}
private void OnAutoDismiss(object? sender, EventArgs e)
{
_autoDismissTimer?.Stop();
_autoDismissTimer = null;
IsOpen = false;
}
}