feat(mobile): UI-01 theme system, shared components and navigation
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/// 相册时区感知的时间展示(P3 计划 Task 4)。
|
||||
/// 相册时区感知的时间展示与多语言展示回退(P3 计划 Task 4;UI-01 §4.1)。
|
||||
///
|
||||
/// 契约:startAt/endAt 按 UTC 存储,「展示按 timezone」。Dart 核心库无
|
||||
/// IANA 时区数据库,引入 `timezone` 包(内嵌 tzdata)做墙钟换算,字符串
|
||||
@@ -10,6 +10,8 @@ import 'package:intl/intl.dart';
|
||||
import 'package:timezone/data/latest.dart' as tzdata;
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
import 'models.dart';
|
||||
|
||||
bool _tzInitialized = false;
|
||||
|
||||
/// 初始化内嵌 tzdata(幂等;首次调用后为空操作)。
|
||||
@@ -73,14 +75,56 @@ DateTime albumWallClockToUtc(DateTime wallClock, String timeZone) {
|
||||
wallClock.minute,
|
||||
);
|
||||
}
|
||||
return tz
|
||||
.TZDateTime(
|
||||
location,
|
||||
wallClock.year,
|
||||
wallClock.month,
|
||||
wallClock.day,
|
||||
wallClock.hour,
|
||||
wallClock.minute,
|
||||
)
|
||||
.toUtc();
|
||||
return tz.TZDateTime(
|
||||
location,
|
||||
wallClock.year,
|
||||
wallClock.month,
|
||||
wallClock.day,
|
||||
wallClock.hour,
|
||||
wallClock.minute,
|
||||
).toUtc();
|
||||
}
|
||||
|
||||
// ── 多语言展示回退链(UI-01 §4.1)────────────────────────────────────────
|
||||
//
|
||||
// 展示顺序:界面语言(语言前缀匹配,App locale `zh`/`en` vs 契约
|
||||
// `zh-CN`/`en`)→ 相册默认语言 → 首个条目 → 相册 ID 兜底。
|
||||
|
||||
/// 展示回退链:界面语言条目优先,其余按「默认语言在前」排列。
|
||||
List<AlbumTranslation> albumDisplayTranslations(
|
||||
AlbumSummary album,
|
||||
String localeName,
|
||||
) {
|
||||
final lang = localeName.split('-').first.toLowerCase();
|
||||
AlbumTranslation? langMatch;
|
||||
for (final t in album.translations) {
|
||||
final l = t.locale.toLowerCase();
|
||||
if (l == localeName.toLowerCase() || l.split('-').first == lang) {
|
||||
langMatch = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int byDefault(AlbumTranslation a, AlbumTranslation b) =>
|
||||
(a.locale == album.defaultLocale ? 0 : 1).compareTo(
|
||||
b.locale == album.defaultLocale ? 0 : 1,
|
||||
);
|
||||
if (langMatch != null) {
|
||||
final rest = album.translations.where((t) => t != langMatch).toList()
|
||||
..sort(byDefault);
|
||||
return [langMatch, ...rest];
|
||||
}
|
||||
return [...album.translations]..sort(byDefault);
|
||||
}
|
||||
|
||||
/// 展示标题(UI-01 §4.1 回退链;无翻译时相册 ID 兜底)。
|
||||
String albumDisplayTitle(AlbumSummary album, String localeName) =>
|
||||
albumDisplayTranslations(album, localeName).firstOrNull?.title ?? album.id;
|
||||
|
||||
/// 展示场地(同一回退链上首个非空场地;全空返回 null,不占一整行空白)。
|
||||
String? albumDisplayVenue(AlbumSummary album, String localeName) {
|
||||
for (final t in albumDisplayTranslations(album, localeName)) {
|
||||
final venue = t.venue?.trim();
|
||||
if (venue != null && venue.isNotEmpty) return venue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user