chore: guard DevMenu in release build, clean up Phase/TODO comments

- DevMenu: kReleaseModeのときonTap=nullでリリースビルドから完全無効化
- Phase N マーカーを全ファイルから削除(実装済みのため歴史的コメントを除去)
- analysis_cache_service TODOを具体的な記述に改善

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ponshu Developer 2026-04-05 03:02:01 +09:00
parent 426697403e
commit b6163e8efe
16 changed files with 24 additions and 31 deletions

View File

@ -40,7 +40,7 @@ class SakeItem extends HiveObject {
@HiveField(25) @HiveField(25)
ItemType? _itemType; ItemType? _itemType;
// --- Phase 1: Draft Mode Fields (Fields 26-27) --- // --- Draft Mode Fields (Fields 26-27) ---
@HiveField(26) @HiveField(26)
bool? _isPendingAnalysis; // true = AI解析待ち, false/null = bool? _isPendingAnalysis; // true = AI解析待ち, false/null =
@ -110,7 +110,7 @@ class SakeItem extends HiveObject {
Gamification? gamification, Gamification? gamification,
Metadata? metadata, Metadata? metadata,
ItemType? itemType, ItemType? itemType,
// Phase 1: Draft Mode params // Draft Mode params
bool? isPendingAnalysis, bool? isPendingAnalysis,
String? draftPhotoPath, String? draftPhotoPath,
// Legacy params for migration compatibility (optional) // Legacy params for migration compatibility (optional)
@ -216,7 +216,7 @@ class SakeItem extends HiveObject {
_itemType = val; _itemType = val;
} }
// Phase 1: Draft Mode Getters/Setters // Draft Mode Getters/Setters
bool get isPendingAnalysis => _isPendingAnalysis ?? false; bool get isPendingAnalysis => _isPendingAnalysis ?? false;
set isPendingAnalysis(bool val) { set isPendingAnalysis(bool val) {
@ -274,7 +274,7 @@ class SakeItem extends HiveObject {
String? riceVariety, String? riceVariety,
String? yeast, String? yeast,
String? manufacturingYearMonth, String? manufacturingYearMonth,
// Phase 1: Draft Mode // Draft Mode
bool? isPendingAnalysis, bool? isPendingAnalysis,
String? draftPhotoPath, String? draftPhotoPath,
}) { }) {
@ -322,7 +322,7 @@ class SakeItem extends HiveObject {
aiConfidence: confidenceScore, aiConfidence: confidenceScore,
), ),
itemType: itemType ?? this.itemType, itemType: itemType ?? this.itemType,
// Phase 1: Draft Mode // Draft Mode
isPendingAnalysis: isPendingAnalysis ?? this.isPendingAnalysis, isPendingAnalysis: isPendingAnalysis ?? this.isPendingAnalysis,
draftPhotoPath: draftPhotoPath ?? this.draftPhotoPath, draftPhotoPath: draftPhotoPath ?? this.draftPhotoPath,
); );

View File

@ -11,7 +11,7 @@ class DisplayModeNotifier extends Notifier<String> {
@override @override
String build() { String build() {
// Phase 1 Optimization: Access box directly // SplashScreenで開済みのboxに直接アクセス
_box = Hive.box<UserProfile>('user_profile'); _box = Hive.box<UserProfile>('user_profile');
_profile = _box.get('current_user') ?? UserProfile(createdAt: DateTime.now()); _profile = _box.get('current_user') ?? UserProfile(createdAt: DateTime.now());
return _profile.displayMode; return _profile.displayMode;

View File

@ -54,7 +54,7 @@ class MenuOrderedIdsNotifier extends Notifier<List<String>> {
} }
} }
// 4. PDF Settings Providers (Phase 4) // 4. PDF Settings Providers
// Note: We use Notifiers for complex logic, but simple StateProviders (Riverpod 2.0 style) are fine here. // Note: We use Notifiers for complex logic, but simple StateProviders (Riverpod 2.0 style) are fine here.
// Actually, Riverpod recommended is Notifier, but StateProvider is still available. // Actually, Riverpod recommended is Notifier, but StateProvider is still available.
// Let's use simple class-based Notifiers for 2.0 strictness if needed, or simple State for brevity. // Let's use simple class-based Notifiers for 2.0 strictness if needed, or simple State for brevity.

View File

@ -28,7 +28,7 @@ class UserProfileNotifier extends Notifier<UserProfile> {
@override @override
UserProfile build() { UserProfile build() {
// Phase 1 Optimization: Access box directly as it's guaranteed to be open by SplashScreen // SplashScreenで開済みのboxに直接アクセス
_box = Hive.box<UserProfile>('user_profile'); _box = Hive.box<UserProfile>('user_profile');
// Return existing profile or create default // Return existing profile or create default

View File

@ -217,7 +217,7 @@ class _CameraScreenState extends ConsumerState<CameraScreen> with SingleTickerPr
final imagePath = compressedPath; final imagePath = compressedPath;
// Save to Gallery (Public) - Phase 4: Data Safety // Save to Gallery (Public)
try { try {
await Gal.putImage(imagePath); await Gal.putImage(imagePath);
debugPrint('Saved to Gallery: $imagePath'); debugPrint('Saved to Gallery: $imagePath');

View File

@ -21,9 +21,6 @@ class DevMenuScreen extends ConsumerWidget {
), ),
body: ListView( body: ListView(
children: [ children: [
// NOTE: (SoulScreen)
// UI実験
const ListTile( const ListTile(
leading: Icon(LucideIcons.flaskConical), leading: Icon(LucideIcons.flaskConical),
title: Text('UI実験'), title: Text('UI実験'),

View File

@ -143,7 +143,7 @@ class HomeScreen extends ConsumerWidget {
body: SafeArea( body: SafeArea(
child: Column( child: Column(
children: [ children: [
// Phase 1: Draft通知バナー // Draft通知バナー
const PendingAnalysisBanner(), const PendingAnalysisBanner(),
if (!isMenuMode && hasItems) if (!isMenuMode && hasItems)

View File

@ -41,7 +41,7 @@ class PdfPreviewScreen extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
// Phase 4: Watch new PDF Settings // PDF Settings
final isPortrait = ref.watch(pdfIsPortraitProvider); final isPortrait = ref.watch(pdfIsPortraitProvider);
final density = ref.watch(pdfDensityProvider); final density = ref.watch(pdfDensityProvider);

View File

@ -83,7 +83,7 @@ class _SakeDetailScreenState extends ConsumerState<SakeDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appColors = Theme.of(context).extension<AppColors>()!; final appColors = Theme.of(context).extension<AppColors>()!;
// (Phase 1-8 Enhanced) //
final allSakeAsync = ref.watch(allSakeItemsProvider); final allSakeAsync = ref.watch(allSakeItemsProvider);
final allSake = allSakeAsync.asData?.value ?? []; final allSake = allSakeAsync.asData?.value ?? [];
@ -209,7 +209,7 @@ class _SakeDetailScreenState extends ConsumerState<SakeDetailScreen> {
const SizedBox(height: 48), const SizedBox(height: 48),
// Related Items 3D Carousel (Phase 1-8 Enhanced) // Related Items 3D Carousel
if (_sake.itemType != ItemType.set) ...[ // Hide for Set Items if (_sake.itemType != ItemType.set) ...[ // Hide for Set Items
Row( Row(
children: [ children: [
@ -273,7 +273,7 @@ class _SakeDetailScreenState extends ConsumerState<SakeDetailScreen> {
), ),
), ),
// Phase 2-3: Business Pricing Section (Extracted) // Business Pricing Section
SliverToBoxAdapter( SliverToBoxAdapter(
child: SakePricingSection( child: SakePricingSection(
sake: _sake, sake: _sake,

View File

@ -128,9 +128,7 @@ class AnalysisCacheService {
/// - 30 /// - 30
/// - /// -
static Future<void> cleanupExpired() async { static Future<void> cleanupExpired() async {
// TODO: Phase 3 // TODO: 30
// -
// - 30
} }
// ============================================ // ============================================

View File

@ -6,7 +6,7 @@ import 'gemini_service.dart';
/// Draft /// Draft
/// ///
/// Phase 1: ///
/// ///
/// ///
/// 使: /// 使:

View File

@ -20,7 +20,7 @@ class GeminiService {
/// ///
Future<SakeAnalysisResult> analyzeSakeLabel(List<String> imagePaths, {bool forceRefresh = false}) async { Future<SakeAnalysisResult> analyzeSakeLabel(List<String> imagePaths, {bool forceRefresh = false}) async {
// Force use of client-side prompt to ensure Schema consistency (Phase 1 Fix) //
const prompt = ''' const prompt = '''
JSON形式で情報を抽出してください JSON形式で情報を抽出してください
@ -151,7 +151,7 @@ $extractedText
} }
_lastApiCallTime = DateTime.now(); _lastApiCallTime = DateTime.now();
// 2. Base64変換 (Phase 4: Images already compressed at capture) // 2. Base64変換
List<String> base64Images = []; List<String> base64Images = [];
for (final path in imagePaths) { for (final path in imagePaths) {
// Read already-compressed images directly (compressed at capture time) // Read already-compressed images directly (compressed at capture time)
@ -203,7 +203,7 @@ $extractedText
final result = SakeAnalysisResult.fromJson(data); final result = SakeAnalysisResult.fromJson(data);
// Phase 3 Validation: Check for Schema Compliance //
if (result.tasteStats.isEmpty || if (result.tasteStats.isEmpty ||
result.tasteStats.values.every((v) => v == 0)) { result.tasteStats.values.every((v) => v == 0)) {
debugPrint('⚠️ WARNING: AI returned empty or zero taste stats. This item will not form a valid chart.'); debugPrint('⚠️ WARNING: AI returned empty or zero taste stats. This item will not form a valid chart.');
@ -299,7 +299,7 @@ $extractedText
// Prepare Content // Prepare Content
final contentParts = <Part>[TextPart(promptText)]; final contentParts = <Part>[TextPart(promptText)];
for (var path in imagePaths) { for (var path in imagePaths) {
// Phase 4: Images already compressed at capture time //
final bytes = await File(path).readAsBytes(); final bytes = await File(path).readAsBytes();
contentParts.add(DataPart('image/jpeg', bytes)); contentParts.add(DataPart('image/jpeg', bytes));
} }

View File

@ -3,7 +3,6 @@ import 'package:flutter/foundation.dart';
/// ///
/// ///
/// Phase 1:
/// - / /// - /
/// - /// -
class NetworkService { class NetworkService {

View File

@ -6,7 +6,6 @@ import '../screens/pending_analysis_screen.dart';
/// Draft /// Draft
/// ///
/// Phase 1:
/// Draftがある場合にのみ表示されます /// Draftがある場合にのみ表示されます
/// ///
/// [PendingAnalysisScreen] /// [PendingAnalysisScreen]

View File

@ -34,7 +34,7 @@ class _SakeDetailSpecsState extends State<SakeDetailSpecs> {
late final TextEditingController _manufacturingController; late final TextEditingController _manufacturingController;
// Unused in UI currently but reserved // Unused in UI currently but reserved
// TODO: Phase X UIを追加する予定 // TODO: UIを追加する予定
/* /*
late final TextEditingController _sweetnessController; late final TextEditingController _sweetnessController;
late final TextEditingController _bodyController; late final TextEditingController _bodyController;

View File

@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lucide_icons/lucide_icons.dart'; import 'package:lucide_icons/lucide_icons.dart';
@ -72,7 +73,7 @@ class _OtherSettingsSectionState extends ConsumerState<OtherSettingsSection> {
leading: Icon(LucideIcons.info, color: appColors.iconDefault), leading: Icon(LucideIcons.info, color: appColors.iconDefault),
title: Text('アプリバージョン', style: TextStyle(color: appColors.textPrimary)), title: Text('アプリバージョン', style: TextStyle(color: appColors.textPrimary)),
subtitle: Text(_appVersion, style: TextStyle(color: appColors.textSecondary)), subtitle: Text(_appVersion, style: TextStyle(color: appColors.textSecondary)),
onTap: () { onTap: kReleaseMode ? null : () {
setState(() { setState(() {
_devTapCount++; _devTapCount++;
if (_devTapCount >= AppConstants.devModeTapCount) { if (_devTapCount >= AppConstants.devModeTapCount) {
@ -83,8 +84,7 @@ class _OtherSettingsSectionState extends ConsumerState<OtherSettingsSection> {
MaterialPageRoute(builder: (context) => const DevMenuScreen()), MaterialPageRoute(builder: (context) => const DevMenuScreen()),
); );
} else { } else {
// Optional: Small feedback HapticFeedback.lightImpact();
HapticFeedback.lightImpact();
} }
}); });
}, },