Files
flutter/lib/state/experience_controller.dart
2026-05-05 11:58:38 +07:00

210 lines
6.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:latlong2/latlong.dart';
import '../models/experience_models.dart';
final experienceControllerProvider =
NotifierProvider<ExperienceController, ExperienceState>(
ExperienceController.new,
);
class ExperienceState {
const ExperienceState({
required this.experiences,
required this.selectedExperienceId,
required this.filterEmotion,
required this.profile,
});
final List<PlaceExperience> experiences;
final String? selectedExperienceId;
final ExperienceEmotion? filterEmotion;
final ExperienceAuthor profile;
List<PlaceExperience> get visibleExperiences {
final emotion = filterEmotion;
if (emotion == null) {
return experiences;
}
return experiences.where((item) => item.emotion == emotion).toList();
}
PlaceExperience? get selectedExperience {
for (final experience in experiences) {
if (experience.id == selectedExperienceId) {
return experience;
}
}
return visibleExperiences.isEmpty ? null : visibleExperiences.first;
}
ExperienceState copyWith({
List<PlaceExperience>? experiences,
String? selectedExperienceId,
bool clearSelection = false,
ExperienceEmotion? filterEmotion,
bool clearFilter = false,
ExperienceAuthor? profile,
}) {
return ExperienceState(
experiences: experiences ?? this.experiences,
selectedExperienceId: clearSelection
? null
: selectedExperienceId ?? this.selectedExperienceId,
filterEmotion: clearFilter ? null : filterEmotion ?? this.filterEmotion,
profile: profile ?? this.profile,
);
}
}
class ExperienceController extends Notifier<ExperienceState> {
@override
ExperienceState build() {
final experiences = _seedExperiences();
return ExperienceState(
experiences: experiences,
selectedExperienceId: experiences.first.id,
filterEmotion: null,
profile: const ExperienceAuthor(
name: 'Руслан',
facets: [
ProfileFacet(name: 'темп', value: 'спокойно, без очередей'),
ProfileFacet(name: 'еда', value: 'яркое блюдо важнее кухни'),
ProfileFacet(
name: 'контекст',
value: 'работа днем, прогулки вечером',
),
],
),
);
}
void selectExperience(String id) {
state = state.copyWith(selectedExperienceId: id);
}
void setEmotionFilter(ExperienceEmotion? emotion) {
if (emotion == null) {
state = state.copyWith(clearFilter: true);
return;
}
final nextVisible = state.experiences.firstWhere(
(item) => item.emotion == emotion,
orElse: () => state.experiences.first,
);
state = state.copyWith(
filterEmotion: emotion,
selectedExperienceId: nextVisible.id,
);
}
void addExperience({
required String placeName,
required String dishName,
required ExperienceEmotion emotion,
required LatLng coordinate,
required String context,
}) {
final experience = PlaceExperience(
id: 'local-${DateTime.now().microsecondsSinceEpoch}',
placeName: placeName.trim().isEmpty ? 'Новое место' : placeName.trim(),
neighborhood: 'рядом',
coordinate: coordinate,
emotion: emotion,
intensity: 3,
context: context.trim().isEmpty ? 'личная заметка' : context.trim(),
dish: DishSignal(
name: dishName.trim().isEmpty ? 'блюдо' : dishName.trim(),
reason: 'стоит проверить лично',
texture: 'новый сигнал',
),
author: state.profile,
createdLabel: 'сейчас',
);
state = state.copyWith(
experiences: [experience, ...state.experiences],
selectedExperienceId: experience.id,
clearFilter: true,
);
}
List<PlaceExperience> _seedExperiences() {
const author = ExperienceAuthor(
name: 'Mira',
facets: [
ProfileFacet(name: 'темп', value: 'медленно'),
ProfileFacet(name: 'еда', value: 'текстура'),
ProfileFacet(name: 'настроение', value: 'тихое внимание'),
],
);
return const [
PlaceExperience(
id: 'secret-garden',
placeName: 'Secret Garden',
neighborhood: 'District 1',
coordinate: LatLng(10.7752, 106.7009),
emotion: ExperienceEmotion.comfort,
intensity: 4,
context: 'крыша, зелень, хороший разговор',
dish: DishSignal(
name: 'caramelized pork clay pot',
reason: 'мягко собирает вечер',
texture: 'густой соус, рис, тепло',
),
author: author,
createdLabel: 'вчера',
),
PlaceExperience(
id: 'banh-mi-huynh-hoa',
placeName: 'Banh Mi Huynh Hoa',
neighborhood: 'District 1',
coordinate: LatLng(10.7716, 106.6920),
emotion: ExperienceEmotion.energy,
intensity: 5,
context: 'быстро, плотно, без церемоний',
dish: DishSignal(
name: 'banh mi dac biet',
reason: 'если хочется прямого удара вкуса',
texture: 'хруст, паштет, травы',
),
author: author,
createdLabel: '3 дня назад',
),
PlaceExperience(
id: 'the-workshop',
placeName: 'The Workshop',
neighborhood: 'District 1',
coordinate: LatLng(10.7740, 106.7042),
emotion: ExperienceEmotion.focus,
intensity: 4,
context: 'ноутбук, кофе, два часа ясности',
dish: DishSignal(
name: 'egg coffee',
reason: 'сладкая пауза между задачами',
texture: 'крем, горечь, плотность',
),
author: author,
createdLabel: 'на неделе',
),
PlaceExperience(
id: 'oc-dao',
placeName: 'Oc Dao',
neighborhood: 'District 1',
coordinate: LatLng(10.7607, 106.6898),
emotion: ExperienceEmotion.curiosity,
intensity: 5,
context: 'пробовать руками, спорить, заказывать еще',
dish: DishSignal(
name: 'grilled scallops',
reason: 'блюдо ведет сильнее, чем место',
texture: 'дым, масло, арахис',
),
author: author,
createdLabel: 'месяц назад',
),
];
}
}