fix: hide queue attachment action for messages without attachments
Derive attachment presence from saved queue inputs and only show the preview action when attachments exist. Cover queue metadata and button visibility with regression tests. Release note: Queued messages without attachments no longer show the View attachments button.
This commit is contained in:
@@ -64,7 +64,7 @@
|
||||
| 草稿图片,发送原图 | 打开原图;已有 OCR 结果时可查看 | 提取图片文字、移除 |
|
||||
| 草稿图片,发送提取文字 | 查看解析结果、打开原图 | 改为发送原图、使用当前设置重新提取文字、移除 |
|
||||
| 历史消息附件 | 查看已保存结果、打开原件 | 需要重解析或改变发送方式时先添加到当前可编辑草稿;不修改原消息 |
|
||||
| 已入队附件 | 查看入队时冻结的结果与发送方式 | 修改整条输入使用“恢复到草稿”,按 L-H7 合并;不直接修改队列附件 |
|
||||
| 已入队附件 | 仅有附件时显示“查看附件”,查看入队时冻结的结果与发送方式;无附件时隐藏入口 | 修改整条输入使用“恢复到草稿”,按 L-H7 合并;不直接修改队列附件 |
|
||||
| 知识文档 | 查看解析结果、打开来源、管理分块 | 使用当前设置重新解析;失败时使用重试,不再并列一个同义重解析按钮;来源同步仍属于来源级操作 |
|
||||
| 设置测试结果 | 查看正文、图片、详情及原件 | 重新测试、关闭;没有加入草稿、知识库或发送操作 |
|
||||
|
||||
|
||||
@@ -4618,6 +4618,22 @@ describe('AssistantDatabase', () => {
|
||||
}
|
||||
])
|
||||
|
||||
for (const [payload, hasAttachments] of [
|
||||
[{ prompt: 'text' }, false],
|
||||
[{ input: { attachments: [] } }, false],
|
||||
[{ input: { attachments: [{ id: 'attachment' }] } }, true],
|
||||
[{ attachments: [{ id: 'attachment' }] }, true]
|
||||
] as const) {
|
||||
const queued = initial.enqueueConversationUserInput({
|
||||
conversationId,
|
||||
label: 'Attachment visibility',
|
||||
payloadJson: JSON.stringify(payload)
|
||||
})
|
||||
expect(queued.hasAttachments).toBe(hasAttachments)
|
||||
expect(initial.listConversationQueueItems(conversationId)[0]?.hasAttachments).toBe(hasAttachments)
|
||||
initial.cancelConversationQueueItem(queued.id)
|
||||
}
|
||||
|
||||
const first = initial.enqueueConversationUserInput({
|
||||
conversationId,
|
||||
label: '第一条消息',
|
||||
|
||||
@@ -782,7 +782,18 @@ function toSchedule(row: ScheduleWithTaskRow): AssistantSchedule {
|
||||
function toConversationQueueItem(
|
||||
row: ConversationQueueRow
|
||||
): ConversationQueueItem {
|
||||
let hasAttachments = false
|
||||
if (row.source === 'user' && row.payload_json) {
|
||||
try {
|
||||
const payload = JSON.parse(row.payload_json)
|
||||
const input = payload?.input ?? payload
|
||||
hasAttachments = Array.isArray(input?.attachments) && input.attachments.length > 0
|
||||
} catch {
|
||||
// Invalid queued payloads are reported when previewed or dispatched.
|
||||
}
|
||||
}
|
||||
return {
|
||||
hasAttachments,
|
||||
id: row.id,
|
||||
conversationId: row.conversation_id,
|
||||
source: row.source,
|
||||
|
||||
@@ -311,6 +311,7 @@ function sameConversationQueueItems(
|
||||
item.id === candidate.id &&
|
||||
item.conversationId === candidate.conversationId &&
|
||||
item.source === candidate.source &&
|
||||
item.hasAttachments === candidate.hasAttachments &&
|
||||
item.label === candidate.label &&
|
||||
item.createdAt === candidate.createdAt &&
|
||||
item.scheduleRunId === candidate.scheduleRunId &&
|
||||
|
||||
@@ -81,6 +81,19 @@ describe('ConversationInputQueue', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it.each([undefined, false, true])('shows attachment preview only for attached inputs (%s)', (hasAttachments) => {
|
||||
render(
|
||||
<ConversationInputQueue
|
||||
items={[{ ...items[1]!, hasAttachments }]}
|
||||
onError={vi.fn()}
|
||||
onInterruptAndRun={vi.fn()}
|
||||
onRemove={vi.fn()}
|
||||
running={false}
|
||||
/>
|
||||
)
|
||||
expect(screen.queryByTitle('查看附件') !== null).toBe(hasAttachments === true)
|
||||
})
|
||||
|
||||
it('uses run-now wording while idle and reports action errors', async () => {
|
||||
const onError = vi.fn()
|
||||
render(
|
||||
|
||||
@@ -93,7 +93,7 @@ export const ConversationInputQueue = memo(
|
||||
{item.error && <small className="knowledge-inline-error" role="alert">{item.error}</small>}
|
||||
</span>
|
||||
<span className="conversation-input-queue__actions">
|
||||
{item.source === 'user' && <button type="button" aria-label={`查看入队附件:${item.label}`} title="查看附件" onClick={() => setViewing(item.id)}><Paperclip aria-hidden="true" size={13} /><span>查看附件</span></button>}
|
||||
{item.source === 'user' && item.hasAttachments && <button type="button" aria-label={`查看入队附件:${item.label}`} title="查看附件" onClick={() => setViewing(item.id)}><Paperclip aria-hidden="true" size={13} /><span>查看附件</span></button>}
|
||||
{onRestore && item.source === 'user' && <button type="button" disabled={pendingAction !== undefined} aria-label={`恢复到草稿:${item.label}`} title="恢复到草稿" onClick={() => void runAction(item.id, 'restore', () => onRestore(item.id))}><Undo2 aria-hidden="true" size={13} /><span>恢复到草稿</span></button>}
|
||||
<button
|
||||
aria-label={t(
|
||||
|
||||
@@ -200,6 +200,7 @@ export type ConversationAttachment = z.infer<
|
||||
|
||||
export const conversationQueueItemSchema = z
|
||||
.object({
|
||||
hasAttachments: z.boolean().optional(),
|
||||
error: z.string().max(1000).optional(),
|
||||
id: assistantIdSchema,
|
||||
conversationId: assistantIdSchema,
|
||||
|
||||
Reference in New Issue
Block a user