fluffychat/lib/pangea/practice_activities/practice_target.dart
2026-01-20 11:39:48 -05:00

93 lines
3 KiB
Dart

import 'package:flutter/foundation.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
/// Picks which tokens to do activities on and what types of activities to do
/// Caches result so that we don't have to recompute it
/// Most importantly, we can't do this in the state of a message widget because the state is disposed of and recreated
/// If we decided that the first token should have a hidden word listening, we need to remember that
/// Otherwise, the user might leave the chat, return, and see a different word hidden
class PracticeTarget {
/// this is the tokens involved in the activity
/// for most, this will be a single token
final List<PangeaToken> tokens;
/// this is the type of activity to do on the tokens
final ActivityTypeEnum activityType;
/// this is only defined for morphId activities
final MorphFeaturesEnum? morphFeature;
PracticeTarget({
required this.tokens,
required this.activityType,
this.morphFeature,
}) {
if (ActivityTypeEnum.morphId == activityType && morphFeature == null) {
throw Exception("morphFeature must be defined for morphId activities");
}
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is PracticeTarget &&
listEquals(other.tokens, tokens) &&
other.activityType == activityType &&
other.morphFeature == morphFeature;
}
@override
int get hashCode =>
tokens.hashCode ^ activityType.hashCode ^ morphFeature.hashCode;
static PracticeTarget fromJson(Map<String, dynamic> json) {
final type = ActivityTypeEnum.values.firstWhereOrNull(
(v) => json['activityType'] == v.name,
);
if (type == null) {
throw Exception(
"ActivityTypeEnum ${json['activityType']} not found in enum",
);
}
return PracticeTarget(
tokens:
(json['tokens'] as List).map((e) => PangeaToken.fromJson(e)).toList(),
activityType: type,
morphFeature: json['morphFeature'] == null
? null
: MorphFeaturesEnumExtension.fromString(json['morphFeature']),
);
}
Map<String, dynamic> toJson() {
return {
'tokens': tokens.map((e) => e.toJson()).toList(),
'activityType': activityType.name,
'morphFeature': morphFeature?.name,
};
}
//unique condensed deterministic key for local storage
String get storageKey {
return tokens.map((e) => e.text.content).join() +
activityType.name +
(morphFeature?.name ?? "");
}
ConstructIdentifier targetTokenConstructID(PangeaToken token) {
final defaultID = token.vocabConstructID;
final ConstructIdentifier? cId = morphFeature == null
? defaultID
: token.morphIdByFeature(morphFeature!);
return cId ?? defaultID;
}
}