|
1 | 1 | import React, { useState, useMemo } from 'react'; |
2 | | -import { ExternalLink, GitBranch, Calendar, Package, Bell, Search, X, RefreshCw, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Eye, EyeOff } from 'lucide-react'; |
| 2 | +import { ExternalLink, GitBranch, Calendar, Package, Bell, Search, X, RefreshCw, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Eye, EyeOff, Apple, Monitor, Terminal, Smartphone, Globe, Download } from 'lucide-react'; |
3 | 3 | import { Release } from '../types'; |
4 | 4 | import { useAppStore } from '../store/useAppStore'; |
5 | 5 | import { GitHubApiService } from '../services/githubApi'; |
@@ -288,15 +288,37 @@ export const ReleaseTimeline: React.FC = () => { |
288 | 288 | }; |
289 | 289 |
|
290 | 290 | const getPlatformIcon = (platform: string) => { |
291 | | - const iconMap: Record<string, string> = { |
292 | | - windows: 'fab fa-windows', |
293 | | - macos: 'fab fa-apple', |
294 | | - linux: 'fab fa-linux', |
295 | | - android: 'fab fa-android', |
296 | | - ios: 'fab fa-apple', |
297 | | - universal: 'fas fa-download' |
| 291 | + const platformLower = platform.toLowerCase(); |
| 292 | + |
| 293 | + switch (platformLower) { |
| 294 | + case 'windows': |
| 295 | + return Monitor; |
| 296 | + case 'macos': |
| 297 | + case 'mac': |
| 298 | + case 'ios': |
| 299 | + return Apple; |
| 300 | + case 'linux': |
| 301 | + return Terminal; |
| 302 | + case 'android': |
| 303 | + return Smartphone; |
| 304 | + case 'universal': |
| 305 | + default: |
| 306 | + return Download; |
| 307 | + } |
| 308 | + }; |
| 309 | + |
| 310 | + const getPlatformDisplayName = (platform: string) => { |
| 311 | + const platformLower = platform.toLowerCase(); |
| 312 | + const nameMap: Record<string, string> = { |
| 313 | + windows: 'Windows', |
| 314 | + macos: 'macOS', |
| 315 | + mac: 'macOS', |
| 316 | + linux: 'Linux', |
| 317 | + android: 'Android', |
| 318 | + ios: 'iOS', |
| 319 | + universal: 'Universal' |
298 | 320 | }; |
299 | | - return iconMap[platform] || 'fas fa-download'; |
| 321 | + return nameMap[platformLower] || platform; |
300 | 322 | }; |
301 | 323 |
|
302 | 324 | const getPlatformColor = (platform: string) => { |
@@ -338,9 +360,29 @@ export const ReleaseTimeline: React.FC = () => { |
338 | 360 | <p className="text-gray-500 dark:text-gray-400 mb-6 max-w-md mx-auto"> |
339 | 361 | {subscribedRepoCount === 0 |
340 | 362 | ? t('从仓库页面订阅仓库Release以在此查看更新。', 'Subscribe to repository releases from the Repositories tab to see updates here.') |
341 | | - : t(`您已订阅 ${subscribedRepoCount} 个仓库,但没有找到最近的Release。尝试同步以获取最新更新。`, `You're subscribed to ${subscribedRepoCount} repositories, but no recent releases were found. Try syncing to get the latest updates.`) |
| 363 | + : t(`您已订阅 ${subscribedRepoCount} 个仓库,但没有找到最近的Release。点击下方刷新按钮获取最新更新。`, `You're subscribed to ${subscribedRepoCount} repositories, but no recent releases were found. Click the refresh button below to get the latest updates.`) |
342 | 364 | } |
343 | 365 | </p> |
| 366 | + |
| 367 | + {/* 刷新按钮 - 在有订阅仓库时显示 */} |
| 368 | + {subscribedRepoCount > 0 && ( |
| 369 | + <div className="mb-6"> |
| 370 | + <button |
| 371 | + onClick={handleRefresh} |
| 372 | + disabled={isRefreshing} |
| 373 | + className="flex items-center space-x-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed mx-auto" |
| 374 | + > |
| 375 | + <RefreshCw className={`w-5 h-5 ${isRefreshing ? 'animate-spin' : ''}`} /> |
| 376 | + <span>{isRefreshing ? t('刷新中...', 'Refreshing...') : t('刷新Release', 'Refresh Releases')}</span> |
| 377 | + </button> |
| 378 | + {lastRefreshTime && ( |
| 379 | + <p className="text-sm text-gray-500 dark:text-gray-400 mt-2"> |
| 380 | + {t('上次刷新:', 'Last refresh:')} {formatDistanceToNow(new Date(lastRefreshTime), { addSuffix: true })} |
| 381 | + </p> |
| 382 | + )} |
| 383 | + </div> |
| 384 | + )} |
| 385 | + |
344 | 386 | {subscribedRepoCount === 0 && ( |
345 | 387 | <div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4 max-w-md mx-auto"> |
346 | 388 | <div className="flex items-center space-x-2 text-blue-700 dark:text-blue-300"> |
@@ -459,8 +501,8 @@ export const ReleaseTimeline: React.FC = () => { |
459 | 501 | : 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600' |
460 | 502 | }`} |
461 | 503 | > |
462 | | - <i className={`${getPlatformIcon(platform)} w-4 h-4`}></i> |
463 | | - <span className="capitalize">{platform}</span> |
| 504 | + {React.createElement(getPlatformIcon(platform), { className: "w-4 h-4" })} |
| 505 | + <span className="capitalize">{getPlatformDisplayName(platform)}</span> |
464 | 506 | </button> |
465 | 507 | ))} |
466 | 508 | {(searchQuery || selectedPlatforms.length > 0) && ( |
@@ -646,13 +688,16 @@ export const ReleaseTimeline: React.FC = () => { |
646 | 688 | }} |
647 | 689 | > |
648 | 690 | <div className="flex items-center space-x-1"> |
649 | | - {link.platforms.map((platform, pIndex) => ( |
650 | | - <i |
651 | | - key={pIndex} |
652 | | - className={`${getPlatformIcon(platform)} w-4 h-4 ${getPlatformColor(platform)}`} |
653 | | - title={platform} |
654 | | - ></i> |
655 | | - ))} |
| 691 | + {link.platforms.map((platform, pIndex) => { |
| 692 | + const IconComponent = getPlatformIcon(platform); |
| 693 | + return ( |
| 694 | + <IconComponent |
| 695 | + key={pIndex} |
| 696 | + className={`w-4 h-4 ${getPlatformColor(platform)}`} |
| 697 | + title={getPlatformDisplayName(platform)} |
| 698 | + /> |
| 699 | + ); |
| 700 | + })} |
656 | 701 | </div> |
657 | 702 | <span className="truncate max-w-32">{link.name}</span> |
658 | 703 | </a> |
@@ -733,13 +778,16 @@ export const ReleaseTimeline: React.FC = () => { |
733 | 778 | }} |
734 | 779 | > |
735 | 780 | <div className="flex items-center space-x-0.5"> |
736 | | - {link.platforms.map((platform, pIndex) => ( |
737 | | - <i |
738 | | - key={pIndex} |
739 | | - className={`${getPlatformIcon(platform)} w-3 h-3 ${getPlatformColor(platform)}`} |
740 | | - title={platform} |
741 | | - ></i> |
742 | | - ))} |
| 781 | + {link.platforms.map((platform, pIndex) => { |
| 782 | + const IconComponent = getPlatformIcon(platform); |
| 783 | + return ( |
| 784 | + <IconComponent |
| 785 | + key={pIndex} |
| 786 | + className={`w-3 h-3 ${getPlatformColor(platform)}`} |
| 787 | + title={getPlatformDisplayName(platform)} |
| 788 | + /> |
| 789 | + ); |
| 790 | + })} |
743 | 791 | </div> |
744 | 792 | <span className="text-xs text-gray-700 dark:text-gray-300 truncate max-w-16"> |
745 | 793 | {link.name.split('.').pop() || link.name} |
|
0 commit comments