Files
Task_Reporter/openspec/changes/archive/2025-12-08-fix-chat-ux-issues/design.md
egg 599802b818 feat: Add Chat UX improvements with notifications and @mention support
- Add ActionBar component with expandable toolbar for mobile
- Add @mention functionality with autocomplete dropdown
- Add browser notification system (push, sound, vibration)
- Add NotificationSettings modal for user preferences
- Add mention badges on room list cards
- Add ReportPreview with Markdown rendering and copy/download
- Add message copy functionality with hover actions
- Add backend mentions field to messages with Alembic migration
- Add lots field to rooms, remove templates
- Optimize WebSocket database session handling
- Various UX polish (animations, accessibility)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 08:20:37 +08:00

4.7 KiB

Design: Fix Chat UX Issues

Technical Design

1. 發文者顯示名稱 (Sender Display Name)

後端改動

Schema 變更 (app/modules/realtime/schemas.py):

class MessageResponse(BaseModel):
    message_id: str
    room_id: str
    sender_id: str
    sender_display_name: Optional[str] = None  # 新增欄位
    content: str
    # ... 其他欄位

訊息查詢修改 (app/modules/realtime/services/message_service.py):

from app.modules.auth.models import User

# 在 get_messages() 中 JOIN users 表
messages = (
    db.query(Message, User.display_name)
    .outerjoin(User, Message.sender_id == User.user_id)
    .filter(Message.room_id == room_id)
    .order_by(desc(Message.created_at))
    .limit(limit)
    .all()
)

# 轉換為 response
for msg, display_name in messages:
    msg_response = MessageResponse.from_orm(msg)
    msg_response.sender_display_name = display_name or msg.sender_id

WebSocket 廣播修改 (app/modules/realtime/schemas.py):

class MessageBroadcast(BaseModel):
    type: str = "message"
    message_id: str
    sender_id: str
    sender_display_name: Optional[str] = None  # 新增
    # ... 其他欄位

前端改動

RoomDetail.tsx:

// 將 message.sender_id 改為
{message.sender_display_name || message.sender_id}

2. 統一時區為 GMT+8

前端工具函數 (frontend/src/utils/datetime.ts)

/**
 * 格式化日期時間為 GMT+8 (台灣時間)
 */
export function formatDateTimeGMT8(date: Date | string): string {
  const d = typeof date === 'string' ? new Date(date) : date
  return d.toLocaleString('zh-TW', {
    timeZone: 'Asia/Taipei',
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  })
}

/**
 * 格式化時間為 GMT+8 (僅時:分)
 */
export function formatTimeGMT8(date: Date | string): string {
  const d = typeof date === 'string' ? new Date(date) : date
  return d.toLocaleString('zh-TW', {
    timeZone: 'Asia/Taipei',
    hour: '2-digit',
    minute: '2-digit',
  })
}

使用位置

  • RoomDetail.tsx - 訊息時間
  • RoomList.tsx - 房間最後更新時間
  • Reports.tsx - 報告生成時間

3. AI 報告生成問題修復

新增健康檢查端點 (app/modules/report_generation/router.py)

@router.get("/health", response_model=schemas.HealthCheckResponse)
async def check_dify_health():
    """檢查 DIFY AI 服務連線狀態"""
    if not settings.DIFY_API_KEY:
        return {"status": "error", "message": "DIFY_API_KEY 未設定"}

    try:
        # 測試 API 連線
        result = await dify_service.test_connection()
        return {"status": "ok", "message": "AI 服務正常"}
    except Exception as e:
        return {"status": "error", "message": str(e)}

啟動時檢查 (app/main.py)

@app.on_event("startup")
async def startup_event():
    # 檢查 DIFY API Key
    if not settings.DIFY_API_KEY:
        logger.warning("DIFY_API_KEY not configured - AI report generation will be unavailable")

前端改善 (frontend/src/hooks/useReports.ts)

// 新增輪詢直到報告完成或失敗
const pollReportStatus = async (reportId: string) => {
  const maxAttempts = 60  // 最多輪詢 2 分鐘
  let attempts = 0

  while (attempts < maxAttempts) {
    const status = await api.get(`/rooms/${roomId}/reports/${reportId}`)
    if (status.data.status === 'completed' || status.data.status === 'failed') {
      return status.data
    }
    await new Promise(resolve => setTimeout(resolve, 2000))
    attempts++
  }

  throw new Error('報告生成超時')
}

Data Flow

Message Flow with Display Name

1. User sends message via WebSocket
2. Backend creates message in DB
3. Backend queries User table for sender's display_name
4. Backend broadcasts MessageBroadcast with sender_display_name
5. Frontend displays sender_display_name in chat bubble

Report Generation Flow (Fixed)

1. User clicks "Generate Report"
2. Frontend: POST /api/rooms/{id}/reports/generate
3. Backend: Creates report record (status=pending)
4. Backend: Returns report_id immediately
5. Frontend: Starts polling GET /api/rooms/{id}/reports/{report_id}
6. Backend: Background task updates status (collecting_data → generating_content → assembling_document → completed)
7. Backend: Broadcasts WebSocket updates for each status change
8. Frontend: Updates UI based on poll response OR WebSocket message
9. If completed: Enable download button
10. If failed: Show error message

Database Changes

無資料庫結構變更。僅新增 JOIN 查詢。

Configuration Changes

無新增設定項目。僅改善現有 DIFY_API_KEY 的檢查機制。