Files
ftdl/docs/plans/2026-03-04-enhance-download-ui-design.md
tech 6182c38366 docs: add design document for enhanced download UI
Add comprehensive design document for project aliases and
APK environment classification feature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 22:17:31 +08:00

5.8 KiB

Design: Enhanced Download UI Readability

Date: 2026-03-04 Status: Approved

Overview

Enhance the download UI to improve readability by:

  1. Displaying project aliases instead of directory names
  2. Classifying APKs by environment (dev, sandbox, product, other)
  3. Displaying APKs in a list format with commit ID, file size, and creation time
  4. Organizing APKs into tabs by environment

Requirements

Project Aliases

  • Store project display name mappings in lib/project-aliases.json
  • Example: FT外放构建, FT_DEV开发构建
  • Fallback: Display directory name if no alias exists

APK Classification

  • Categories: dev, sandbox, product, other
  • APK filename format: ft_[environment]_[version]_[commit_id].apk
  • Environment suffix is optional; if missing, default to product
  • Unmatched formats go to other tab

Display Format

  • List view with columns: Commit ID, File Size, Creation Time, Download
  • Always display all 4 tabs (dev, product, sandbox, other)
  • Commit filter applies globally to all tabs

Architecture

New Files

  • lib/project-aliases.json - Project name alias configuration
  • lib/apk-parser.ts - APK filename parsing utilities

Modified Files

  • lib/utils.ts - Add alias lookup utility
  • app/api/projects/route.ts - Return projects with aliases
  • app/api/apks/route.ts - Add environment metadata to response
  • app/download/SearchForm.tsx - Use project aliases in dropdown
  • app/download/ApkList.tsx - Replace card grid with tabbed list view

Data Flow

  1. Project Selection:

    • Client loads projects → API reads project-aliases.json → returns with display names
    • SearchForm displays displayName, submits name to API
  2. APK Fetching:

    • User selects project/version → client fetches APKs
    • API uses parseApkFilename() to extract environment
    • Returns enriched APKs with environment field
  3. Display:

    • Client receives APKs with environment field
    • Organizes into tabs (dev, product, sandbox, other)
    • Displays in list format

Data Structures

APK Parser Types

export type ApkEnvironment = 'dev' | 'sandbox' | 'product' | 'other';

export interface ParsedApkMetadata {
  environment: ApkEnvironment;
  commit: string | null;
  isValid: boolean;
}

Project Alias Config

{
  "FT": "外放构建",
  "FT_DEV": "开发构建"
}

Enriched APK Response

{
  name: string;
  environment: ApkEnvironment;
  commit: string | null;
  size: number;
  modifiedAt: string;  // ISO string
  downloadUrl: string;
}

Components

ApkList (Restructured)

  • Replaces grid of ApkCard with tabbed list view
  • Manages tab state (active tab, filtered APKs)
  • Handles commit filter globally
  • Delegates list display to ApkTable component

ApkTable (New)

  • Table with columns: Commit ID, File Size, Creation Time, Download
  • Styled with hover effects
  • Empty state handling per tab

SearchForm (Updated)

  • Uses getProjectDisplayName() for project dropdown
  • Maintains existing functionality

Error Handling

Project Alias Edge Cases

  • Missing alias file: Fallback to directory names
  • Project not in aliases: Display directory name
  • Malformed JSON: Log error, fallback to directory names

APK Filename Parsing Edge Cases

  • No environment in filename: Default to product
  • No commit ID: commit field is null
  • Unmatched format: environment: "other", still display
  • Invalid characters: Handle gracefully

UI Edge Cases

  • Empty tab: Display "暂无 APK 文件"
  • All tabs empty: Show helpful message
  • Loading state: Skeleton or spinner
  • API error: Display error message, allow retry

APK Filename Parsing Logic

Regex Pattern

const APK_PATTERN = /^ft_([a-z]+)?_([0-9_]+)_([a-f0-9]{7,})\.apk$/;
//                ^^^ ^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^
//                ft  env(opt) version   commit id

Parsing Rules

  1. Try to match standard pattern: ft_[env]_[version]_[commit].apk
  2. Extract environment if present, otherwise default to product
  3. If pattern doesn't match, assign environment: "other"
  4. Extract commit ID from filename
  5. Use file system metadata for size and modified time

Testing Strategy

Unit Tests

  • lib/__tests__/apk-parser.test.ts: Test filename parsing for all environments
  • lib/__tests__/project-aliases.test.ts: Test alias lookup logic

Integration Tests

  • API tests for /api/projects with alias data
  • API tests for /api/apks returning enriched metadata

Component Tests

  • ApkTable: Renders correct columns, handles empty states
  • ApkList: Tabs work correctly, filters apply globally
  • SearchForm: Displays project aliases correctly

Manual Testing Checklist

  • Projects display with aliases (FT → 外放构建)
  • Projects without aliases show directory names
  • APKs grouped correctly in 4 tabs
  • Product APKs (no env suffix) go to "product" tab
  • Dev/sandbox APKs go to correct tabs
  • Invalid format APKs go to "other" tab
  • Commit filter applies to all tabs
  • List shows Commit ID, File Size, Creation Time
  • Download button works correctly

Implementation Approach

Selected: Approach 2 - Shared Utility Functions + Client-side Organization

Why:

  • Clean separation of concerns
  • Reusable parsing utilities
  • Type-safe with TypeScript
  • Easier to unit test parsing logic
  • Client receives all APKs once, then handles tab organization
  • Performance overhead is minimal for this use case

Success Criteria

  1. Project aliases display correctly in dropdown
  2. APKs are correctly classified by environment
  3. All 4 tabs display correctly (dev, product, sandbox, other)
  4. List view shows correct columns (Commit ID, File Size, Creation Time)
  5. Commit filter works globally across all tabs
  6. Edge cases handled gracefully (missing aliases, invalid formats)
  7. All tests pass