메시지 좋아요 싫어요 토글 동작 수정
Some checks failed
Release Gate / gate (push) Has been cancelled

채팅 메시지 피드백 버튼 로직을 shared feedback 상태 기반으로 다시 구성해 좋아요와 싫어요가 모두 즉시 반응하고 상호배타적으로 동작하도록 정리했다.

좋아요는 활성 색상과 chip 배경으로 상태가 확실히 보이게 바꾸고, 싫어요를 다시 누르면 null 상태로 정상 해제되도록 수정했다.

README와 DEVELOPMENT 문서를 2026-04-06 18:09 (KST) 기준으로 갱신했고 dotnet build 검증에서 경고 0 / 오류 0을 확인했다.
This commit is contained in:
2026-04-06 17:56:53 +09:00
parent 606ecbe6cd
commit 94dc325df4
3 changed files with 120 additions and 72 deletions

View File

@@ -11,87 +11,76 @@ namespace AxCopilot.Views;
public partial class ChatWindow
{
/// <summary>좋아요/싫어요 토글 피드백 버튼 (상태 영구 저장)</summary>
private Button CreateFeedbackButton(string outline, string filled, string tooltip,
Brush normalColor, Brush activeColor, ChatMessage? message = null, string feedbackType = "",
Action? resetSibling = null, Action<Action>? registerReset = null)
/// <summary>좋아요/싫어요 피드백 버튼을 생성합니다.</summary>
private Button CreateFeedbackButton(
string iconGlyph,
string tooltip,
Brush normalColor,
Brush activeColor,
Func<bool> isActive,
Action toggle)
{
var hoverBrush = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
var isActive = message?.Feedback == feedbackType;
var activeBackground = TryFindResource("ItemHoverBackground") as Brush
?? new SolidColorBrush(Color.FromArgb(18, 255, 255, 255));
var icon = new TextBlock
{
Text = isActive ? filled : outline,
Text = iconGlyph,
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 12,
Foreground = isActive ? activeColor : normalColor,
Foreground = normalColor,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
RenderTransformOrigin = new Point(0.5, 0.5),
RenderTransform = new ScaleTransform(1, 1)
};
var chip = new Border
{
Background = Brushes.Transparent,
BorderBrush = Brushes.Transparent,
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8),
Padding = new Thickness(6, 4, 6, 4),
Margin = new Thickness(0, 0, 4, 0),
Child = icon
};
var btn = new Button
{
Content = icon,
Content = chip,
Background = Brushes.Transparent,
BorderThickness = new Thickness(0),
Cursor = Cursors.Hand,
Padding = new Thickness(6, 4, 6, 4),
Margin = new Thickness(0, 0, 4, 0),
Padding = new Thickness(0),
ToolTip = tooltip
};
registerReset?.Invoke(() =>
void RefreshVisual()
{
isActive = false;
icon.Text = outline;
icon.Foreground = normalColor;
});
var active = isActive();
icon.Foreground = active ? activeColor : normalColor;
chip.Background = active ? activeBackground : Brushes.Transparent;
chip.BorderBrush = active ? activeColor : Brushes.Transparent;
}
btn.MouseEnter += (_, _) => { if (!isActive) icon.Foreground = hoverBrush; };
btn.MouseLeave += (_, _) => { if (!isActive) icon.Foreground = normalColor; };
RefreshVisual();
btn.MouseEnter += (_, _) =>
{
if (!isActive())
icon.Foreground = hoverBrush;
};
btn.MouseLeave += (_, _) =>
{
if (!isActive())
icon.Foreground = normalColor;
};
btn.Click += (_, _) =>
{
isActive = !isActive;
icon.Text = isActive ? filled : outline;
icon.Foreground = isActive ? activeColor : normalColor;
if (isActive)
{
resetSibling?.Invoke();
}
if (message != null)
{
try
{
var feedback = isActive ? feedbackType : null;
var session = ChatSession;
if (session != null)
{
lock (_convLock)
{
session.UpdateMessageFeedback(_activeTab, message, feedback, _storage);
_currentConversation = session.CurrentConversation;
}
}
else
{
message.Feedback = feedback;
ChatConversation? conv;
lock (_convLock)
{
conv = _currentConversation;
}
if (conv != null)
{
_storage.Save(conv);
}
}
}
catch
{
}
}
toggle();
RefreshVisual();
var scale = (ScaleTransform)icon.RenderTransform;
var bounce = new DoubleAnimation(1.3, 1.0, TimeSpan.FromMilliseconds(250))
@@ -106,23 +95,77 @@ public partial class ChatWindow
scale.BeginAnimation(ScaleTransform.ScaleXProperty, bounce);
scale.BeginAnimation(ScaleTransform.ScaleYProperty, bounce);
};
return btn;
}
/// <summary>좋아요/싫어요 버튼을 상호 배타로 연결하여 추가</summary>
/// <summary>좋아요/싫어요 버튼을 상호배타 토글로 추가합니다.</summary>
private void AddLinkedFeedbackButtons(StackPanel actionBar, Brush btnColor, ChatMessage? message)
{
Action? resetLikeAction = null;
Action? resetDislikeAction = null;
string? currentFeedback = message?.Feedback;
var likeBtn = CreateFeedbackButton("\uE8E1", "\uEB51", "좋아요", btnColor,
new SolidColorBrush(Color.FromRgb(0x38, 0xA1, 0x69)), message, "like",
resetSibling: () => resetDislikeAction?.Invoke(),
registerReset: reset => resetLikeAction = reset);
var dislikeBtn = CreateFeedbackButton("\uE8E0", "\uEB50", "싫어요", btnColor,
new SolidColorBrush(Color.FromRgb(0xE5, 0x3E, 0x3E)), message, "dislike",
resetSibling: () => resetLikeAction?.Invoke(),
registerReset: reset => resetDislikeAction = reset);
void PersistFeedback()
{
if (message == null)
return;
try
{
var feedback = string.IsNullOrWhiteSpace(currentFeedback) ? null : currentFeedback;
var session = ChatSession;
if (session != null)
{
lock (_convLock)
{
session.UpdateMessageFeedback(_activeTab, message, feedback, _storage);
_currentConversation = session.CurrentConversation;
}
}
else
{
message.Feedback = feedback;
ChatConversation? conv;
lock (_convLock)
{
conv = _currentConversation;
}
if (conv != null)
_storage.Save(conv);
}
}
catch
{
}
}
var likeBtn = CreateFeedbackButton(
"\uE8E1",
"좋아요",
btnColor,
new SolidColorBrush(Color.FromRgb(0x38, 0xA1, 0x69)),
() => string.Equals(currentFeedback, "like", StringComparison.OrdinalIgnoreCase),
() =>
{
currentFeedback = string.Equals(currentFeedback, "like", StringComparison.OrdinalIgnoreCase)
? null
: "like";
PersistFeedback();
});
var dislikeBtn = CreateFeedbackButton(
"\uE8E0",
"싫어요",
btnColor,
new SolidColorBrush(Color.FromRgb(0xE5, 0x3E, 0x3E)),
() => string.Equals(currentFeedback, "dislike", StringComparison.OrdinalIgnoreCase),
() =>
{
currentFeedback = string.Equals(currentFeedback, "dislike", StringComparison.OrdinalIgnoreCase)
? null
: "dislike";
PersistFeedback();
});
actionBar.Children.Add(likeBtn);
actionBar.Children.Add(dislikeBtn);
@@ -157,7 +200,7 @@ public partial class ChatWindow
return new TextBlock
{
Text = string.Join(" · ", parts),
Text = string.Join(" ", parts),
FontSize = 9.75,
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
HorizontalAlignment = HorizontalAlignment.Left,
@@ -386,7 +429,7 @@ public partial class ChatWindow
}
catch (Exception ex)
{
Services.LogService.Debug($"대화 저장 실패: {ex.Message}");
Services.LogService.Debug($"편집 저장 실패: {ex.Message}");
}
RefreshConversationList();