You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fluffychat/lib/pangea/models/choreo_record.dart

208 lines
6.0 KiB
Dart

import 'dart:convert';
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
import 'it_step.dart';
/// this class lives within a [PangeaIGCEvent]
/// it always has a [RepresentationEvent] parent
/// These live as separate event so that anyone can add and edit grammar checks
/// to a representation
/// It represents the real-time changes to a text
/// TODO - start saving senderL2Code in choreoRecord to be able better decide the useType
class ChoreoRecord {
/// ordered versions of the representation, with first being original and last
/// being the final sent text
/// there is not a 1-to-1 map from steps to matches
List<ChoreoRecordStep> choreoSteps;
// String current;
List<PangeaMatch> openMatches;
ChoreoRecord({
required this.choreoSteps,
required this.openMatches,
// required this.current,
});
factory ChoreoRecord.fromJson(Map<String, dynamic> json) {
final stepsRaw = json[_stepsKey];
return ChoreoRecord(
choreoSteps: (jsonDecode(stepsRaw ?? "[]") as Iterable)
.map((e) {
return ChoreoRecordStep.fromJson(e);
})
.toList()
.cast<ChoreoRecordStep>(),
openMatches: (jsonDecode(json[_openMatchesKey] ?? "[]") as Iterable)
.map((e) {
return PangeaMatch.fromJson(e);
})
.toList()
.cast<PangeaMatch>(),
// current: json[_currentKey],
);
}
static const _stepsKey = "stps";
static const _openMatchesKey = "mtchs";
// static const _currentKey = "crnt";
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data[_stepsKey] = jsonEncode(choreoSteps.map((e) => e.toJson()).toList());
data[_openMatchesKey] =
jsonEncode(openMatches.map((e) => e.toJson()).toList());
// data[_currentKey] = current;
return data;
}
addRecord(String text, {PangeaMatch? match, ITStep? step}) {
if (match != null && step != null) {
throw Exception("match and step should not both be defined");
}
choreoSteps.add(
ChoreoRecordStep(
text: text,
acceptedOrIgnoredMatch: match,
itStep: step,
),
);
}
bool get hasAcceptedMatches => choreoSteps.any(
(element) =>
element.acceptedOrIgnoredMatch?.status ==
PangeaMatchStatus.accepted,
);
bool get hasIgnoredMatches => choreoSteps.any(
(element) =>
element.acceptedOrIgnoredMatch?.status == PangeaMatchStatus.ignored,
);
// bool get includedIT => choreoSteps.any((step) {
// return step.acceptedOrIgnoredMatch?.status ==
// PangeaMatchStatus.accepted &&
// (step.acceptedOrIgnoredMatch?.isITStart ?? false);
// });
bool get includedIT => choreoSteps.any((step) {
return step.acceptedOrIgnoredMatch?.status ==
PangeaMatchStatus.accepted &&
(step.acceptedOrIgnoredMatch?.isOutOfTargetMatch ?? false);
});
bool get includedIGC => choreoSteps.any((step) {
return step.acceptedOrIgnoredMatch?.status ==
PangeaMatchStatus.accepted &&
(step.acceptedOrIgnoredMatch?.isGrammarMatch ?? false);
});
static ChoreoRecord get newRecord => ChoreoRecord(
choreoSteps: [],
openMatches: [],
);
List<ITStep> get itSteps =>
choreoSteps.where((e) => e.itStep != null).map((e) => e.itStep!).toList();
String get finalMessage =>
choreoSteps.isNotEmpty ? choreoSteps.last.text : "";
}
/// A new ChoreoRecordStep is saved in the following cases:
/// 1) before every system-provided text is accepted, if final text is different
/// from last step
/// 2) on the acceptance of system-provided text
/// 3) on message send, if final text is different from last step
/// 4) on the acceptance of an it step
/// 5) on the start of it
///
/// Example 1:
/// the user types "hey ther"
/// IGC suggests "there"
/// user accepts "there" correction
/// text is now "hey there"
/// A step is made for the original input 'hey there' and a step is made for system suggestion
///
/// Example 2:
/// user write "hi friend"
/// a step is made for the original input 'hi friend'
/// the user selects IT and a step is made
/// the user chooses "hola" and a step is saved
/// adds "amigo" and a step saved
class ChoreoRecordStep {
/// text after changes have been made
String text;
/// all matches throughout edit process,
/// including those open, accepted and ignored
/// last step in list may contain open
PangeaMatch? acceptedOrIgnoredMatch;
ITStep? itStep;
ChoreoRecordStep({
required this.text,
this.acceptedOrIgnoredMatch,
this.itStep,
}) {
if (itStep != null && acceptedOrIgnoredMatch != null) {
throw Exception(
"itStep and acceptedOrIgnoredMatch should not both be defined",
);
}
}
factory ChoreoRecordStep.fromJson(Map<String, dynamic> json) {
return ChoreoRecordStep(
text: json[_textKey],
acceptedOrIgnoredMatch: json[_acceptedOrIgnoredMatchKey] != null
? PangeaMatch.fromJson(json[_acceptedOrIgnoredMatchKey])
: null,
itStep: json[_stepKey] != null ? ITStep.fromJson(json[_stepKey]) : null,
);
}
static const _textKey = "txt";
static const _acceptedOrIgnoredMatchKey = "mtch";
static const _stepKey = "stp";
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data[_textKey] = text;
data[_acceptedOrIgnoredMatchKey] = acceptedOrIgnoredMatch?.toJson();
data[_stepKey] = itStep?.toJson();
return data;
}
}
// Example flow
// hello my name is Jordan! Im typing a message
// igc called, step saved with matchIndex = null
// click Im and fixed
// hello my name is Jordan! I'm typing a message
// igc called, step saved with matchIndex equal to i'm index
// hello my name is Jordan! I'm typing a message
// igc called, step saved with matchIndex = null
// class ITStepRecord {
// List<ContinuanceRecord> allContinuances;
// /// continuances that were clicked but not selected
// List<int> clicked;
// ITStepRecord({required this.continuances});
// }
// class ContinuanceRecord {}