checkpoint: focus seed 1.0.1 localized dev switch
This commit is contained in:
@@ -5,26 +5,34 @@ import 'reflection_entry.dart';
|
||||
class AppState {
|
||||
const AppState({
|
||||
required this.anchor,
|
||||
required this.anchorDateKey,
|
||||
required this.habits,
|
||||
required this.sessions,
|
||||
required this.reflections,
|
||||
});
|
||||
|
||||
final String anchor;
|
||||
final String anchorDateKey;
|
||||
final List<Habit> habits;
|
||||
final List<FocusSession> sessions;
|
||||
final List<ReflectionEntry> reflections;
|
||||
|
||||
bool get isFreshStart => habits.isEmpty && sessions.isEmpty && anchor.isEmpty;
|
||||
|
||||
String activeAnchor(String dateKey) {
|
||||
return anchorDateKey == dateKey ? anchor : '';
|
||||
}
|
||||
|
||||
AppState copyWith({
|
||||
String? anchor,
|
||||
String? anchorDateKey,
|
||||
List<Habit>? habits,
|
||||
List<FocusSession>? sessions,
|
||||
List<ReflectionEntry>? reflections,
|
||||
}) {
|
||||
return AppState(
|
||||
anchor: anchor ?? this.anchor,
|
||||
anchorDateKey: anchorDateKey ?? this.anchorDateKey,
|
||||
habits: habits ?? this.habits,
|
||||
sessions: sessions ?? this.sessions,
|
||||
reflections: reflections ?? this.reflections,
|
||||
@@ -33,8 +41,9 @@ class AppState {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'version': 1,
|
||||
'version': 2,
|
||||
'anchor': anchor,
|
||||
'anchorDateKey': anchorDateKey,
|
||||
'habits': habits.map((habit) => habit.toJson()).toList(),
|
||||
'sessions': sessions.map((session) => session.toJson()).toList(),
|
||||
'reflections':
|
||||
@@ -43,8 +52,12 @@ class AppState {
|
||||
}
|
||||
|
||||
static AppState fromJson(Map<String, dynamic> json) {
|
||||
final anchor = (json['anchor'] as String?) ?? '';
|
||||
return AppState(
|
||||
anchor: (json['anchor'] as String?) ?? '',
|
||||
anchor: anchor,
|
||||
anchorDateKey:
|
||||
(json['anchorDateKey'] as String?) ??
|
||||
(anchor.isEmpty ? '' : _currentDateKey()),
|
||||
habits: _jsonObjects(json['habits']).map(Habit.fromJson).toList(),
|
||||
sessions:
|
||||
_jsonObjects(json['sessions']).map(FocusSession.fromJson).toList(),
|
||||
@@ -62,26 +75,35 @@ class AppState {
|
||||
return value.whereType<Map<String, dynamic>>();
|
||||
}
|
||||
|
||||
static AppState starter() {
|
||||
static AppState starter({List<String>? defaultHabitTitles}) {
|
||||
final now = DateTime.now();
|
||||
final titles =
|
||||
defaultHabitTitles?.length == 3
|
||||
? defaultHabitTitles!
|
||||
: const [
|
||||
'Plan one clear task',
|
||||
'Focus for 25 minutes',
|
||||
'Write one-line reflection',
|
||||
];
|
||||
return AppState(
|
||||
anchor: '',
|
||||
anchorDateKey: _currentDateKey(now),
|
||||
habits: [
|
||||
Habit(
|
||||
id: 'habit-${now.microsecondsSinceEpoch}-1',
|
||||
title: 'Plan one clear task',
|
||||
title: titles[0],
|
||||
createdAt: now,
|
||||
completedDates: {},
|
||||
),
|
||||
Habit(
|
||||
id: 'habit-${now.microsecondsSinceEpoch}-2',
|
||||
title: 'Focus for 25 minutes',
|
||||
title: titles[1],
|
||||
createdAt: now,
|
||||
completedDates: {},
|
||||
),
|
||||
Habit(
|
||||
id: 'habit-${now.microsecondsSinceEpoch}-3',
|
||||
title: 'Write a one-line reflection',
|
||||
title: titles[2],
|
||||
createdAt: now,
|
||||
completedDates: {},
|
||||
),
|
||||
@@ -90,4 +112,11 @@ class AppState {
|
||||
reflections: const [],
|
||||
);
|
||||
}
|
||||
|
||||
static String _currentDateKey([DateTime? value]) {
|
||||
final date = value ?? DateTime.now();
|
||||
return '${date.year.toString().padLeft(4, '0')}-'
|
||||
'${date.month.toString().padLeft(2, '0')}-'
|
||||
'${date.day.toString().padLeft(2, '0')}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
enum AppearancePreference { system, light, dark }
|
||||
|
||||
AppearancePreference appearancePreferenceFromName(String? name) {
|
||||
return AppearancePreference.values.firstWhere(
|
||||
(preference) => preference.name == name,
|
||||
orElse: () => AppearancePreference.system,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import '../core/date_keys.dart';
|
||||
import 'app_state.dart';
|
||||
|
||||
enum GrowthStage { seed, sprout, leaves, flower }
|
||||
|
||||
GrowthStage growthStageFor(AppState state, String dateKey) {
|
||||
if (state.reflections.any((entry) => entry.dateKey == dateKey)) {
|
||||
return GrowthStage.flower;
|
||||
}
|
||||
final hasFocus = minutesForDate(state.sessions, dateKey) > 0;
|
||||
final hasHabit = state.habits.any(
|
||||
(habit) => habit.completedDates.contains(dateKey),
|
||||
);
|
||||
if (hasFocus || hasHabit) {
|
||||
return GrowthStage.leaves;
|
||||
}
|
||||
return state.activeAnchor(dateKey).isEmpty
|
||||
? GrowthStage.seed
|
||||
: GrowthStage.sprout;
|
||||
}
|
||||
|
||||
extension GrowthStagePresentation on GrowthStage {
|
||||
String get assetPath => switch (this) {
|
||||
GrowthStage.seed => 'assets/images/growth/growth-seed.png',
|
||||
GrowthStage.sprout => 'assets/images/growth/growth-sprout.png',
|
||||
GrowthStage.leaves => 'assets/images/growth/growth-leaves.png',
|
||||
GrowthStage.flower => 'assets/images/growth/growth-flower.png',
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum LanguagePreference { defaultLocale, english, chinese }
|
||||
|
||||
LanguagePreference languagePreferenceFromName(String? name) {
|
||||
return LanguagePreference.values.firstWhere(
|
||||
(preference) => preference.name == name,
|
||||
orElse: () => LanguagePreference.defaultLocale,
|
||||
);
|
||||
}
|
||||
|
||||
extension LanguagePreferenceLocale on LanguagePreference {
|
||||
Locale? get locale => switch (this) {
|
||||
LanguagePreference.defaultLocale => null,
|
||||
LanguagePreference.english => const Locale('en'),
|
||||
LanguagePreference.chinese => const Locale('zh'),
|
||||
};
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum Mood {
|
||||
calm('Calm', Icons.spa_outlined),
|
||||
clear('Clear', Icons.wb_sunny_outlined),
|
||||
proud('Proud', Icons.emoji_events_outlined),
|
||||
tired('Tired', Icons.bedtime_outlined);
|
||||
calm(Icons.spa_outlined),
|
||||
clear(Icons.wb_sunny_outlined),
|
||||
proud(Icons.emoji_events_outlined),
|
||||
tired(Icons.bedtime_outlined);
|
||||
|
||||
const Mood(this.label, this.icon);
|
||||
const Mood(this.icon);
|
||||
|
||||
final String label;
|
||||
final IconData icon;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import '../core/date_keys.dart';
|
||||
import 'app_state.dart';
|
||||
import 'growth_stage.dart';
|
||||
|
||||
class TodayProgress {
|
||||
const TodayProgress({
|
||||
required this.dateKey,
|
||||
required this.intention,
|
||||
required this.focusMinutes,
|
||||
required this.completedHabits,
|
||||
required this.totalHabits,
|
||||
required this.hasReflection,
|
||||
required this.growthStage,
|
||||
});
|
||||
|
||||
factory TodayProgress.fromState(AppState state, String dateKey) {
|
||||
return TodayProgress(
|
||||
dateKey: dateKey,
|
||||
intention: state.activeAnchor(dateKey),
|
||||
focusMinutes: minutesForDate(state.sessions, dateKey),
|
||||
completedHabits:
|
||||
state.habits
|
||||
.where((habit) => habit.completedDates.contains(dateKey))
|
||||
.length,
|
||||
totalHabits: state.habits.length,
|
||||
hasReflection: state.reflections.any((entry) => entry.dateKey == dateKey),
|
||||
growthStage: growthStageFor(state, dateKey),
|
||||
);
|
||||
}
|
||||
|
||||
final String dateKey;
|
||||
final String intention;
|
||||
final int focusMinutes;
|
||||
final int completedHabits;
|
||||
final int totalHabits;
|
||||
final bool hasReflection;
|
||||
final GrowthStage growthStage;
|
||||
|
||||
bool get hasIntention => intention.isNotEmpty;
|
||||
bool get hasFocus => focusMinutes > 0;
|
||||
bool get hasHabits => completedHabits > 0;
|
||||
int get completedSteps =>
|
||||
[
|
||||
hasIntention,
|
||||
hasFocus,
|
||||
hasHabits,
|
||||
hasReflection,
|
||||
].where((complete) => complete).length;
|
||||
}
|
||||
Reference in New Issue
Block a user