Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ jobs:
COMMON="--build-name=${{ needs.version.outputs.train }} \
--build-number=${{ needs.version.outputs.code }} \
--dart-define=DPIP_LABEL=${{ needs.version.outputs.label }} \
--dart-define=DPIP_CODE=${{ needs.version.outputs.code }}"
--dart-define=DPIP_CODE=${{ needs.version.outputs.code }} \
--dart-define=DPIP_TRAIN=${{ needs.version.outputs.train }}"
if [ "${{ matrix.platform }}" = "android" ]; then
mise exec -- flutter build apk --release $COMMON
mise exec -- flutter build appbundle --release $COMMON
Expand Down
27 changes: 8 additions & 19 deletions lib/app/theme/app_gold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,19 @@ import 'package:flutter/material.dart';
@immutable
class AppGold {
const AppGold({
required this.fillStart,
required this.fillEnd,
required this.fill,
required this.ink,
required this.badge,
required this.onBadge,
required this.edge,
required this.glow,
});

/// Card gradient, top-left → bottom-right. Two stops of the same hue at
/// different lightness: a metal reads as a *sheen*, and a sheen is a
/// gradient across one hue, never a blend of two.
final Color fillStart;
final Color fillEnd;
/// The card's flat fill — one stop now, not a gradient: the support card
/// sits on the same tonal plane as its neighbours, and the ranking is
/// carried by the gold colour alone.
final Color fill;

/// Title and body ink on [fillStart]/[fillEnd].
/// Title and body ink on [fill].
final Color ink;

/// The filled circular badge, and the mark inside it — the strongest
Expand All @@ -59,34 +56,26 @@ class AppGold {
/// Hairline along the card's edge, catching the light at the top.
final Color edge;

/// The cast under the card. Warm, not grey: a neutral drop shadow makes gold
/// look printed on, a gold one makes it look lit.
final Color glow;

/// Champagne on white: the fill has to be pale enough for dark ink, so the
/// *ink* carries the metal — a deep bronze reads as gold leaf where a bright
/// yellow would read as a highlighter.
static AppGold get light => AppGold(
fillStart: const Color(0xFFFDF2D0).vision,
fillEnd: const Color(0xFFF3D89A).vision,
fill: const Color(0xFFFDF2D0).vision,
ink: const Color(0xFF4A3208).vision,
badge: const Color(0xFF87610F).vision,
onBadge: const Color(0xFFFFF8E6).vision,
edge: const Color(0x33A9822B).vision,
glow: const Color(0x2E8A6A1F).vision,
);

/// Deep amber on near-black: the fill carries the metal here, because a pale
/// champagne on a dark page reads as plain cream. The ink lifts to a light
/// gold so it stays legible on it.
static AppGold get dark => AppGold(
fillStart: const Color(0xFF4A3811).vision,
fillEnd: const Color(0xFF2E230C).vision,
fill: const Color(0xFF4A3811).vision,
ink: const Color(0xFFF7DFA5).vision,
badge: const Color(0xFFE8C46A).vision,
onBadge: const Color(0xFF3A2A06).vision,
edge: const Color(0x40E8C46A).vision,
glow: const Color(0x33C9A34A).vision,
);

/// The palette for the ambient theme.
Expand Down
16 changes: 7 additions & 9 deletions lib/core/build_info.g.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
// GENERATED — do not edit by hand. Written by tool/gen_build_info.sh (run by the
// git hooks in .githooks/; set up once with tool/setup.sh). Holds what git knows
// about this build, so a debug build can name itself without CI's --dart-define.
//
// What is *committed* here is a stub, on purpose. The file is `skip-worktree`
// locally so a regenerated copy never dirties the tree — which also means a
// real value written here would be frozen at whoever last cleared that flag,
// and a clone that has not run tool/setup.sh would confidently report someone
// else's build. Empty values fall back to the platform's own version instead.
library;

/// Short git commit hash of HEAD at generation time ('unknown' outside a repo).
const String kGitCommit = 'unknown';
const String kGitCommit = '31da8b50';

/// The label tool/version.sh derives for HEAD — '26w33b', '26.1'. Empty when
/// git could not answer, in which case the platform's own version is used.
const String kBuildLabel = '';
const String kBuildLabel = '26w34a';

/// The ordinal that goes with it; 0 when git could not answer.
const int kBuildCode = 0;
const int kBuildCode = 426000335;

/// The train number version.sh derives — '26.1'. Apple is told this and
/// never the label; the More page version card shows it as the big number.
const String kBuildTrain = '26.1';
33 changes: 32 additions & 1 deletion lib/core/version/app_build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,48 @@ abstract final class AppBuild {
/// What CI stamped in, empty on a local build.
static const String _definedLabel = String.fromEnvironment('DPIP_LABEL');
static const int _definedCode = int.fromEnvironment('DPIP_CODE');
static const String _definedTrain = String.fromEnvironment('DPIP_TRAIN');

/// What the git hooks wrote, empty outside a repository.
static String get _generatedLabel => kBuildLabel;
static int get _generatedCode => kBuildCode;

/// The train number this build rides — the release a snapshot is heading
/// toward, e.g. `26.1`. Apple is told this and never the label. The More
/// page version card shows it as the big number, above the label.
static String get train => _train;

/// The version the platform itself records for this build — what the OS
/// shows under Settings → app. For a local debug run that is the pubspec
/// placeholder (`26.1.0`); CI stamps `--build-name` on iOS and `DPIP_LABEL`
/// on Android, so a published build reports the train (`26.1`) instead.
/// The version card prints it as the release's fine-print line.
static String? get platformVersion => _platformVersion;

static String get _bestLabel =>
_definedLabel.isNotEmpty ? _definedLabel : _generatedLabel;
static int get _bestCode => _definedCode > 0 ? _definedCode : _generatedCode;

static String? _label;
static int? _code;
static String? _platformVersion;
static String _train = _definedTrain.isNotEmpty ? _definedTrain : kBuildTrain;

/// Reads the platform's own version, for the builds CI did not stamp.
///
/// Called once at bootstrap. Safe to call again; safe to skip, in which case
/// [label] falls back to whatever was defined and [code] to 0.
static Future<void> ensureLoaded() async {
if (_label != null) return;
String platformVersion = '';
Comment on lines 74 to +76

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

performance · low
ensureLoaded 方法目前的冪等性檢查依賴於 _label != null。如果 _bestLabel 為空或 _bestCode 不大於 0(例如在某些開發環境下),_label 將保持為 null,導致每次呼叫 ensureLoaded 都會重新執行非同步的 PackageInfo.fromPlatform()。建議使用一個獨立的布林值(如 _isLoaded)來標記載入狀態,以避免重複的 IO 操作。

Suggestion:

Suggested change
static Future<void> ensureLoaded() async {
if (_label != null) return;
String platformVersion = '';
static bool _isLoaded = false;
static Future<void> ensureLoaded() async {
if (_isLoaded) return;
String platformVersion = '';
try {
final info = await PackageInfo.fromPlatform();
platformVersion = info.version;
} on Object {
// A version readout is never worth failing a launch over. The platform
// version line simply stays empty for that build.
}
_platformVersion = platformVersion;
if (_bestLabel.isNotEmpty && _bestCode > 0) {
_label = _bestLabel;
_code = _bestCode;
}
_isLoaded = true;
}

try {
final info = await PackageInfo.fromPlatform();
platformVersion = info.version;
} on Object {
// A version readout is never worth failing a launch over. The platform
// version line simply stays empty for that build.
}
_platformVersion = platformVersion;
if (_bestLabel.isNotEmpty && _bestCode > 0) {
_label = _bestLabel;
_code = _bestCode;
Expand Down Expand Up @@ -99,8 +123,15 @@ abstract final class AppBuild {
}

/// Test seam — sets both halves directly.
static void debugSet({required String label, required int code}) {
static void debugSet({
required String label,
required int code,
String? train,
String? platformVersion,
}) {
_label = label;
_code = code;
if (train != null) _train = train;
if (platformVersion != null) _platformVersion = platformVersion;
}
}
Loading
Loading