Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cd9c4b9
docs: add UI fixes design spec
UGing265 May 19, 2026
4493a6d
docs: add implementation plan for ChatBox UI fixes
UGing265 May 19, 2026
db912f0
docs: remove message alignment from UI fixes spec and plan
UGing265 May 19, 2026
26cb9e4
chore: add .worktrees to gitignore
UGing265 May 19, 2026
fe6a9cb
feat: add IsDraft property to ChatMessage
UGing265 May 19, 2026
5770113
feat: add avatar converters for initials fallback and first letter ex…
UGing265 May 19, 2026
717d140
feat: update MainWindow.xaml avatar fallback with initials
UGing265 May 19, 2026
2d33913
feat: add pending images panel to MainWindow.xaml
UGing265 May 19, 2026
d450d96
feat: implement image staging logic with pending panel
UGing265 May 19, 2026
1ad72b0
feat: replace hardcoded 'Me' with _displayName in message sender
UGing265 May 19, 2026
877cec7
Merge feature/chatbox-ui-fixes: avatar fallback, image staging, usern…
UGing265 May 19, 2026
2e626e7
feat: show draft images inline in chat list with dashed border
UGing265 May 19, 2026
79f0503
Merge feature/chatbox-inline-drafts: inline draft images in chat
UGing265 May 19, 2026
1b991e4
feat: add MessageReaction entity for emoji reactions
UGing265 May 19, 2026
20b859b
feat: add reaction handling to ChatServer
UGing265 May 19, 2026
8fb68ba
feat: add emoji reaction popup UI to messages
UGing265 May 19, 2026
c274863
feat: wire up reaction sends and REACTION_UPDATE handling
UGing265 May 19, 2026
02bc231
feat: add emoji reaction popup and manager infrastructure
UGing265 May 20, 2026
0c462c5
feat(ui): add emoji reaction bar, draft image staging, lightbox, imag…
UGing265 May 20, 2026
4504d74
docs(plans): add implementation plans for file upload draft, staging …
UGing265 May 20, 2026
61c4ee3
docs: add bugfix plan for ChatBox UI bugs (reaction, wrap, scroll, la…
UGing265 May 20, 2026
55e54d0
perf(ui): replace List with ObservableCollection and parallelize imag…
UGing265 May 20, 2026
23876ac
fix(ui): use TextRange to read RichTextBox content for placeholder an…
UGing265 May 20, 2026
9f7c466
fix(ui): enable VirtualizingStackPanel on message list to fix scrollb…
UGing265 May 20, 2026
8342fe0
fix(ui): fix draft image thumbnail stretch using UniformToFill with C…
UGing265 May 20, 2026
f25ba9f
fix(ui): fix reaction button DataContext by walking visual tree to Li…
UGing265 May 20, 2026
344ecaa
fix(ui): scroll fixes and reaction refresh for emoji reactions
UGing265 May 20, 2026
0f4c2d1
feat(ci): add GitHub Actions workflow for building ChatBox client
UGing265 May 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/build-client.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Build ChatBox Client

on:
push:
branches: [ "main", "master" ]
pull_request:
branches: [ "main", "master" ]
workflow_dispatch: # Cho phép bấm chạy thủ công trên GitHub

jobs:
build:
runs-on: windows-latest # Ứng dụng WPF bắt buộc phải build trên môi trường Windows

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x' # Cài đặt .NET 10

- name: Restore dependencies
run: dotnet restore ChatBox.Client/ChatBox.Client.csproj

- name: Build and Publish Client
run: dotnet publish ChatBox.Client/ChatBox.Client.csproj -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false -o ./publish_exe

- name: Upload Artifact (ChatBox.Client.exe)
uses: actions/upload-artifact@v4
with:
name: ChatBox-Client-Windows
path: ./publish_exe/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,4 @@ $RECYCLE.BIN/

# Vim temporary swap files
*.swp
.worktrees/
75 changes: 75 additions & 0 deletions ChatBox.Client/Behaviors/SmoothScrollBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace ChatBox.Client.Behaviors
{
public static class SmoothScrollBehavior
{
public static readonly DependencyProperty EnableSmoothScrollProperty =
DependencyProperty.RegisterAttached(
"EnableSmoothScroll",
typeof(bool),
typeof(SmoothScrollBehavior),
new PropertyMetadata(false, OnEnableSmoothScrollChanged));

public static bool GetEnableSmoothScroll(DependencyObject obj) =>
(bool)obj.GetValue(EnableSmoothScrollProperty);

public static void SetEnableSmoothScroll(DependencyObject obj, bool value) =>
obj.SetValue(EnableSmoothScrollProperty, value);

private static void OnEnableSmoothScrollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ScrollViewer scrollViewer)
{
if ((bool)e.NewValue)
{
scrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;
}
else
{
scrollViewer.PreviewMouseWheel -= OnPreviewMouseWheel;
}
}
}

public static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (sender is DependencyObject dobj)
{
var scrollViewer = dobj as ScrollViewer;
if (scrollViewer == null)
scrollViewer = FindVisualChild<ScrollViewer>(dobj);

if (scrollViewer != null)
{
double step = 38.0;
double targetOffset = scrollViewer.VerticalOffset - (System.Math.Sign(e.Delta) * step);

if (targetOffset < 0) targetOffset = 0;
if (targetOffset > scrollViewer.ScrollableHeight) targetOffset = scrollViewer.ScrollableHeight;

scrollViewer.ScrollToVerticalOffset(targetOffset);
e.Handled = true;
}
}
}

private static T? FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T t)
return t;

T? childOfChild = FindVisualChild<T>(child);

Check warning on line 68 in ChatBox.Client/Behaviors/SmoothScrollBehavior.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'obj' in 'T? SmoothScrollBehavior.FindVisualChild<T>(DependencyObject obj)'.
if (childOfChild != null)
return childOfChild;
}
return null;
}
}
}
22 changes: 22 additions & 0 deletions ChatBox.Client/Converters/AvatarInitialsVisibilityConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace ChatBox.Client.Converters
{
public class AvatarInitialsVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string base64 && !string.IsNullOrEmpty(base64))
return Visibility.Collapsed;
return Visibility.Visible;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
24 changes: 24 additions & 0 deletions ChatBox.Client/Converters/FirstLetterConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace ChatBox.Client.Converters
{
public class FirstLetterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string name && !string.IsNullOrEmpty(name))
{
char initial = char.ToUpper(name[0]);
return initial.ToString();
}
return "?";
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Loading
Loading