stuff
Some checks failed
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Backend CI / test (push) Has been cancelled
Mobile App CI / test (push) Has been cancelled
Deploy Telegram Bot / Deploy Telegram Bot (push) Has been cancelled
Backend CI / build (push) Has been cancelled
Mobile App CI / build-android (push) Has been cancelled
Mobile App CI / build-ios (push) Has been cancelled
Some checks failed
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Backend CI / test (push) Has been cancelled
Mobile App CI / test (push) Has been cancelled
Deploy Telegram Bot / Deploy Telegram Bot (push) Has been cancelled
Backend CI / build (push) Has been cancelled
Mobile App CI / build-android (push) Has been cancelled
Mobile App CI / build-ios (push) Has been cancelled
This commit is contained in:
parent
a71f265a8e
commit
cb781741f3
13 changed files with 622 additions and 211 deletions
|
|
@ -194,19 +194,31 @@ class MatrixQuestionGenerator implements QuestionGenerator {
|
|||
// Generate stages for all cards
|
||||
final stages = _generateStages(selected, questionType);
|
||||
|
||||
// Create cards - always include original and translation for back side display
|
||||
// Create cards with explicit front and back side fields
|
||||
// Front side will show only relevant fields based on question type,
|
||||
// but back side needs both original and translation
|
||||
// Back side always shows original and translation
|
||||
final cards = selected.map((c) {
|
||||
// Always include original and translation for back side
|
||||
// Include image if available (for image-based question types)
|
||||
final frontImage = questionType.imageCards
|
||||
? (_imageIdToUrl(c.image) ?? c.image ?? c.id)
|
||||
: null;
|
||||
final frontImageUrl = frontImage != null && _isUuid(frontImage)
|
||||
? null
|
||||
: frontImage;
|
||||
|
||||
return MatrixCardDto(
|
||||
id: c.id,
|
||||
image: questionType.imageCards
|
||||
? (_imageIdToUrl(c.image) ?? c.image ?? c.id)
|
||||
: null,
|
||||
// Front side fields
|
||||
image: frontImage != null && _isUuid(frontImage) ? frontImage : null,
|
||||
imageUrl: frontImageUrl,
|
||||
original: c.original,
|
||||
translation: c.translation,
|
||||
// Back side fields - always include original and translation
|
||||
originalBack: c.original,
|
||||
translationBack: c.translation,
|
||||
imageBack: c.image != null && _isUuid(c.image!) ? c.image : null,
|
||||
imageBackUrl: c.image != null && !_isUuid(c.image!)
|
||||
? c.image
|
||||
: (c.image != null ? _imageIdToUrl(c.image) : null),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
|
|
@ -225,7 +237,8 @@ class MatrixQuestionGenerator implements QuestionGenerator {
|
|||
stages: stages,
|
||||
// Backward compatibility fields
|
||||
word: firstStage.targetWord,
|
||||
answer: firstStage.targetCardId,
|
||||
answer: firstStage
|
||||
.targetCardId, // Опциональное, но заполняем для совместимости
|
||||
text: 'Найди слово:',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -484,10 +484,14 @@ class TestManager {
|
|||
.map(
|
||||
(c) => <String, dynamic>{
|
||||
'id': c.id,
|
||||
// store image as objectId (MinIO) or filename
|
||||
// Front side fields
|
||||
'image': c.image,
|
||||
'original': c.original,
|
||||
'translation': c.translation,
|
||||
// Back side fields - always include original and translation
|
||||
'originalBack': c.original,
|
||||
'translationBack': c.translation,
|
||||
'imageBack': c.image,
|
||||
},
|
||||
)
|
||||
.toList();
|
||||
|
|
@ -503,16 +507,56 @@ class TestManager {
|
|||
questionJson['matrixSize'] = resolvedSize;
|
||||
} else if ((questionJson['answer'] as String?)?.isEmpty != false) {
|
||||
// If matrix cards exist but answer is missing, derive initial target
|
||||
// from the first card.
|
||||
final first = currentButtons.firstOrNull;
|
||||
if (first is Map<String, dynamic>) {
|
||||
final id = first['id']?.toString();
|
||||
final original = first['original']?.toString();
|
||||
if (id != null && id.isNotEmpty) {
|
||||
questionJson['answer'] = id;
|
||||
// from the first card or from stages.
|
||||
final stages = (uiDataForResponse['stages'] as List<dynamic>?) ?? [];
|
||||
if (stages.isNotEmpty) {
|
||||
// Извлекаем из stages[0]
|
||||
final firstStage = stages.first;
|
||||
if (firstStage is Map<String, dynamic>) {
|
||||
final targetCardId = firstStage['targetCardId']?.toString();
|
||||
final targetWord = firstStage['targetWord']?.toString();
|
||||
if (targetCardId != null && targetCardId.isNotEmpty) {
|
||||
questionJson['answer'] = targetCardId;
|
||||
}
|
||||
if (targetWord != null && targetWord.isNotEmpty) {
|
||||
questionJson['word'] = targetWord;
|
||||
}
|
||||
}
|
||||
if (original != null && original.isNotEmpty) {
|
||||
questionJson['word'] = original;
|
||||
} else {
|
||||
// Fallback: извлекаем из первого card
|
||||
final first = currentButtons.firstOrNull;
|
||||
if (first is Map<String, dynamic>) {
|
||||
final id = first['id']?.toString();
|
||||
final original = first['original']?.toString();
|
||||
if (id != null && id.isNotEmpty) {
|
||||
questionJson['answer'] = id;
|
||||
}
|
||||
if (original != null && original.isNotEmpty) {
|
||||
questionJson['word'] = original;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Также проверяем word и answer из stages, если они пусты из БД
|
||||
if ((questionJson['word'] as String?)?.isEmpty != false ||
|
||||
(questionJson['answer'] as String?)?.isEmpty != false) {
|
||||
final stages = (uiDataForResponse['stages'] as List<dynamic>?) ?? [];
|
||||
if (stages.isNotEmpty) {
|
||||
final firstStage = stages.first;
|
||||
if (firstStage is Map<String, dynamic>) {
|
||||
if ((questionJson['word'] as String?)?.isEmpty != false) {
|
||||
final targetWord = firstStage['targetWord']?.toString();
|
||||
if (targetWord != null && targetWord.isNotEmpty) {
|
||||
questionJson['word'] = targetWord;
|
||||
}
|
||||
}
|
||||
if ((questionJson['answer'] as String?)?.isEmpty != false) {
|
||||
final targetCardId = firstStage['targetCardId']?.toString();
|
||||
if (targetCardId != null && targetCardId.isNotEmpty) {
|
||||
questionJson['answer'] = targetCardId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -523,19 +567,33 @@ class TestManager {
|
|||
// All images now use the same cardImagesBucket
|
||||
final updatedButtons = await Future.wait(
|
||||
(questionJson['buttons'] as List<dynamic>? ?? []).map((button) async {
|
||||
if (button is Map<String, dynamic> && button['image'] != null) {
|
||||
if (button is Map<String, dynamic>) {
|
||||
final buttonMap = Map<String, dynamic>.from(button);
|
||||
final imageValue = buttonMap['image']?.toString();
|
||||
|
||||
// Convert image to presigned URL
|
||||
final imageUrl = await _imageValueToApiUrl(
|
||||
imageValue,
|
||||
packId: packId,
|
||||
);
|
||||
|
||||
if (imageUrl != null) {
|
||||
buttonMap['imageUrl'] = imageUrl;
|
||||
// Convert front image to presigned URL
|
||||
if (buttonMap['image'] != null) {
|
||||
final imageValue = buttonMap['image']?.toString();
|
||||
final imageUrl = await _imageValueToApiUrl(
|
||||
imageValue,
|
||||
packId: packId,
|
||||
);
|
||||
if (imageUrl != null) {
|
||||
buttonMap['imageUrl'] = imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back image to presigned URL (for matrix cards)
|
||||
if (buttonMap['imageBack'] != null) {
|
||||
final imageBackValue = buttonMap['imageBack']?.toString();
|
||||
final imageBackUrl = await _imageValueToApiUrl(
|
||||
imageBackValue,
|
||||
packId: packId,
|
||||
);
|
||||
if (imageBackUrl != null) {
|
||||
buttonMap['imageBackUrl'] = imageBackUrl;
|
||||
}
|
||||
}
|
||||
|
||||
return buttonMap;
|
||||
}
|
||||
return button;
|
||||
|
|
@ -834,8 +892,8 @@ class TestManager {
|
|||
final questionJson = question.toJson();
|
||||
|
||||
// Extract key fields
|
||||
final word = questionJson['word'] as String? ?? '';
|
||||
final answer = questionJson['answer'] as String? ?? '';
|
||||
var word = questionJson['word'] as String? ?? '';
|
||||
var answer = questionJson['answer'] as String? ?? '';
|
||||
final buttons = questionJson['buttons'] as List<dynamic>? ?? [];
|
||||
|
||||
// UI data (image, text, audio, template)
|
||||
|
|
@ -852,6 +910,29 @@ class TestManager {
|
|||
if (questionJson['stages'] != null)
|
||||
uiData['stages'] = questionJson['stages'];
|
||||
|
||||
// Для матричных вопросов извлекаем word и answer из stages[0] если они пусты
|
||||
if (question.questionType == TestQuestionType.matrix) {
|
||||
final stages = (questionJson['stages'] as List<dynamic>?) ?? [];
|
||||
if (stages.isNotEmpty) {
|
||||
final firstStage = stages.first;
|
||||
if (firstStage is Map<String, dynamic>) {
|
||||
if (word.isEmpty) {
|
||||
final targetWord = firstStage['targetWord']?.toString() ?? '';
|
||||
if (targetWord.isNotEmpty) {
|
||||
word = targetWord;
|
||||
}
|
||||
}
|
||||
if (answer.isEmpty) {
|
||||
final targetCardId =
|
||||
firstStage['targetCardId']?.toString() ?? '';
|
||||
if (targetCardId.isNotEmpty) {
|
||||
answer = targetCardId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final questionCompanion = TestQuestionsCompanion.insert(
|
||||
testId: testId,
|
||||
orderIndex: drift.Value(orderIndex++),
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class MatrixTestQuestionBody extends AbstractTestQuestion {
|
|||
/// DEPRECATED: Use stages[0].targetCardId instead.
|
||||
/// Kept for backward compatibility.
|
||||
@JsonKey(defaultValue: '')
|
||||
final String answer;
|
||||
final String? answer;
|
||||
|
||||
/// Question text (e.g., "Найди слово:")
|
||||
final String? text;
|
||||
|
|
@ -40,7 +40,7 @@ class MatrixTestQuestionBody extends AbstractTestQuestion {
|
|||
required this.matrixSize,
|
||||
required this.cards,
|
||||
this.stages = const [],
|
||||
required this.answer,
|
||||
this.answer,
|
||||
required super.word,
|
||||
this.text,
|
||||
super.questionType = TestQuestionType.matrix,
|
||||
|
|
@ -80,6 +80,10 @@ class MatrixCardDto {
|
|||
final String? imageUrl; // Presigned URL (for display)
|
||||
final String? original;
|
||||
final String? translation;
|
||||
final String? originalBack; // Original on back side
|
||||
final String? translationBack; // Translation on back side
|
||||
final String? imageBack; // Object ID in MinIO for back side (for admin)
|
||||
final String? imageBackUrl; // Presigned URL for back side (for display)
|
||||
|
||||
const MatrixCardDto({
|
||||
required this.id,
|
||||
|
|
@ -87,6 +91,10 @@ class MatrixCardDto {
|
|||
this.imageUrl,
|
||||
this.original,
|
||||
this.translation,
|
||||
this.originalBack,
|
||||
this.translationBack,
|
||||
this.imageBack,
|
||||
this.imageBackUrl,
|
||||
});
|
||||
|
||||
factory MatrixCardDto.fromJson(Map<String, dynamic> json) =>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ abstract class _$MatrixTestQuestionBodyCWProxy {
|
|||
|
||||
MatrixTestQuestionBody stages(List<MatrixStageDto> stages);
|
||||
|
||||
MatrixTestQuestionBody answer(String answer);
|
||||
MatrixTestQuestionBody answer(String? answer);
|
||||
|
||||
MatrixTestQuestionBody word(String word);
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ abstract class _$MatrixTestQuestionBodyCWProxy {
|
|||
int matrixSize,
|
||||
List<MatrixCardDto> cards,
|
||||
List<MatrixStageDto> stages,
|
||||
String answer,
|
||||
String? answer,
|
||||
String word,
|
||||
String? text,
|
||||
TestQuestionType questionType,
|
||||
|
|
@ -65,7 +65,7 @@ class _$MatrixTestQuestionBodyCWProxyImpl
|
|||
call(stages: stages);
|
||||
|
||||
@override
|
||||
MatrixTestQuestionBody answer(String answer) => call(answer: answer);
|
||||
MatrixTestQuestionBody answer(String? answer) => call(answer: answer);
|
||||
|
||||
@override
|
||||
MatrixTestQuestionBody word(String word) => call(word: word);
|
||||
|
|
@ -113,10 +113,10 @@ class _$MatrixTestQuestionBodyCWProxyImpl
|
|||
? _value.stages
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: stages as List<MatrixStageDto>,
|
||||
answer: answer == const $CopyWithPlaceholder() || answer == null
|
||||
answer: answer == const $CopyWithPlaceholder()
|
||||
? _value.answer
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: answer as String,
|
||||
: answer as String?,
|
||||
word: word == const $CopyWithPlaceholder() || word == null
|
||||
? _value.word
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -231,6 +231,14 @@ abstract class _$MatrixCardDtoCWProxy {
|
|||
|
||||
MatrixCardDto translation(String? translation);
|
||||
|
||||
MatrixCardDto originalBack(String? originalBack);
|
||||
|
||||
MatrixCardDto translationBack(String? translationBack);
|
||||
|
||||
MatrixCardDto imageBack(String? imageBack);
|
||||
|
||||
MatrixCardDto imageBackUrl(String? imageBackUrl);
|
||||
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `MatrixCardDto(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
|
|
@ -244,6 +252,10 @@ abstract class _$MatrixCardDtoCWProxy {
|
|||
String? imageUrl,
|
||||
String? original,
|
||||
String? translation,
|
||||
String? originalBack,
|
||||
String? translationBack,
|
||||
String? imageBack,
|
||||
String? imageBackUrl,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -270,6 +282,21 @@ class _$MatrixCardDtoCWProxyImpl implements _$MatrixCardDtoCWProxy {
|
|||
MatrixCardDto translation(String? translation) =>
|
||||
call(translation: translation);
|
||||
|
||||
@override
|
||||
MatrixCardDto originalBack(String? originalBack) =>
|
||||
call(originalBack: originalBack);
|
||||
|
||||
@override
|
||||
MatrixCardDto translationBack(String? translationBack) =>
|
||||
call(translationBack: translationBack);
|
||||
|
||||
@override
|
||||
MatrixCardDto imageBack(String? imageBack) => call(imageBack: imageBack);
|
||||
|
||||
@override
|
||||
MatrixCardDto imageBackUrl(String? imageBackUrl) =>
|
||||
call(imageBackUrl: imageBackUrl);
|
||||
|
||||
@override
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `MatrixCardDto(...).copyWith.fieldName(value)`.
|
||||
|
|
@ -284,6 +311,10 @@ class _$MatrixCardDtoCWProxyImpl implements _$MatrixCardDtoCWProxy {
|
|||
Object? imageUrl = const $CopyWithPlaceholder(),
|
||||
Object? original = const $CopyWithPlaceholder(),
|
||||
Object? translation = const $CopyWithPlaceholder(),
|
||||
Object? originalBack = const $CopyWithPlaceholder(),
|
||||
Object? translationBack = const $CopyWithPlaceholder(),
|
||||
Object? imageBack = const $CopyWithPlaceholder(),
|
||||
Object? imageBackUrl = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return MatrixCardDto(
|
||||
id: id == const $CopyWithPlaceholder() || id == null
|
||||
|
|
@ -306,6 +337,22 @@ class _$MatrixCardDtoCWProxyImpl implements _$MatrixCardDtoCWProxy {
|
|||
? _value.translation
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: translation as String?,
|
||||
originalBack: originalBack == const $CopyWithPlaceholder()
|
||||
? _value.originalBack
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: originalBack as String?,
|
||||
translationBack: translationBack == const $CopyWithPlaceholder()
|
||||
? _value.translationBack
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: translationBack as String?,
|
||||
imageBack: imageBack == const $CopyWithPlaceholder()
|
||||
? _value.imageBack
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: imageBack as String?,
|
||||
imageBackUrl: imageBackUrl == const $CopyWithPlaceholder()
|
||||
? _value.imageBackUrl
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: imageBackUrl as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -353,7 +400,7 @@ Map<String, dynamic> _$MatrixTestQuestionBodyToJson(
|
|||
'matrixSize': instance.matrixSize,
|
||||
'buttons': instance.cards.map((e) => e.toJson()).toList(),
|
||||
'stages': instance.stages.map((e) => e.toJson()).toList(),
|
||||
'answer': instance.answer,
|
||||
'answer': ?instance.answer,
|
||||
'text': ?instance.text,
|
||||
};
|
||||
|
||||
|
|
@ -386,6 +433,10 @@ MatrixCardDto _$MatrixCardDtoFromJson(Map<String, dynamic> json) =>
|
|||
imageUrl: json['imageUrl'] as String?,
|
||||
original: json['original'] as String?,
|
||||
translation: json['translation'] as String?,
|
||||
originalBack: json['originalBack'] as String?,
|
||||
translationBack: json['translationBack'] as String?,
|
||||
imageBack: json['imageBack'] as String?,
|
||||
imageBackUrl: json['imageBackUrl'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MatrixCardDtoToJson(MatrixCardDto instance) =>
|
||||
|
|
@ -395,4 +446,8 @@ Map<String, dynamic> _$MatrixCardDtoToJson(MatrixCardDto instance) =>
|
|||
'imageUrl': instance.imageUrl,
|
||||
'original': instance.original,
|
||||
'translation': instance.translation,
|
||||
'originalBack': instance.originalBack,
|
||||
'translationBack': instance.translationBack,
|
||||
'imageBack': instance.imageBack,
|
||||
'imageBackUrl': instance.imageBackUrl,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -121,6 +121,9 @@ abstract class MatchPair with _$MatchPair {
|
|||
/// The question is multi-step on a single screen: after each correct selection
|
||||
/// the chosen card flips and a new target is shown from stages array until
|
||||
/// all cards are processed.
|
||||
///
|
||||
/// Note: Each stage has its own targetWord. For backward compatibility,
|
||||
/// use stages[0].targetWord if a single word is needed.
|
||||
@freezed
|
||||
abstract class MatrixQuestion with _$MatrixQuestion {
|
||||
const factory MatrixQuestion({
|
||||
|
|
@ -131,7 +134,6 @@ abstract class MatrixQuestion with _$MatrixQuestion {
|
|||
// DEPRECATED: Use stages[0] instead. Kept for backward compatibility.
|
||||
@Deprecated('Use stages[0] instead') String? initialTargetCardId,
|
||||
@Deprecated('Use stages[0] instead') String? initialTargetWord,
|
||||
required String word,
|
||||
String? text,
|
||||
@Default('matrix') String type,
|
||||
}) = _MatrixQuestion;
|
||||
|
|
@ -157,8 +159,13 @@ abstract class MatrixCard with _$MatrixCard {
|
|||
const factory MatrixCard({
|
||||
required String id,
|
||||
String? image,
|
||||
String? imageUrl,
|
||||
String? original,
|
||||
String? translation,
|
||||
String? originalBack,
|
||||
String? translationBack,
|
||||
String? imageBack,
|
||||
String? imageBackUrl,
|
||||
}) = _MatrixCard;
|
||||
|
||||
factory MatrixCard.fromJson(Map<String, dynamic> json) =>
|
||||
|
|
|
|||
|
|
@ -2268,7 +2268,7 @@ as String,
|
|||
mixin _$MatrixQuestion {
|
||||
|
||||
String get id; int get matrixSize; List<MatrixCard> get cards; List<MatrixStage> get stages;// DEPRECATED: Use stages[0] instead. Kept for backward compatibility.
|
||||
@Deprecated('Use stages[0] instead') String? get initialTargetCardId;@Deprecated('Use stages[0] instead') String? get initialTargetWord; String get word; String? get text; String get type;
|
||||
@Deprecated('Use stages[0] instead') String? get initialTargetCardId;@Deprecated('Use stages[0] instead') String? get initialTargetWord; String? get text; String get type;
|
||||
/// Create a copy of MatrixQuestion
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
|
|
@ -2281,16 +2281,16 @@ $MatrixQuestionCopyWith<MatrixQuestion> get copyWith => _$MatrixQuestionCopyWith
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MatrixQuestion&&(identical(other.id, id) || other.id == id)&&(identical(other.matrixSize, matrixSize) || other.matrixSize == matrixSize)&&const DeepCollectionEquality().equals(other.cards, cards)&&const DeepCollectionEquality().equals(other.stages, stages)&&(identical(other.initialTargetCardId, initialTargetCardId) || other.initialTargetCardId == initialTargetCardId)&&(identical(other.initialTargetWord, initialTargetWord) || other.initialTargetWord == initialTargetWord)&&(identical(other.word, word) || other.word == word)&&(identical(other.text, text) || other.text == text)&&(identical(other.type, type) || other.type == type));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MatrixQuestion&&(identical(other.id, id) || other.id == id)&&(identical(other.matrixSize, matrixSize) || other.matrixSize == matrixSize)&&const DeepCollectionEquality().equals(other.cards, cards)&&const DeepCollectionEquality().equals(other.stages, stages)&&(identical(other.initialTargetCardId, initialTargetCardId) || other.initialTargetCardId == initialTargetCardId)&&(identical(other.initialTargetWord, initialTargetWord) || other.initialTargetWord == initialTargetWord)&&(identical(other.text, text) || other.text == text)&&(identical(other.type, type) || other.type == type));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,matrixSize,const DeepCollectionEquality().hash(cards),const DeepCollectionEquality().hash(stages),initialTargetCardId,initialTargetWord,word,text,type);
|
||||
int get hashCode => Object.hash(runtimeType,id,matrixSize,const DeepCollectionEquality().hash(cards),const DeepCollectionEquality().hash(stages),initialTargetCardId,initialTargetWord,text,type);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MatrixQuestion(id: $id, matrixSize: $matrixSize, cards: $cards, stages: $stages, initialTargetCardId: $initialTargetCardId, initialTargetWord: $initialTargetWord, word: $word, text: $text, type: $type)';
|
||||
return 'MatrixQuestion(id: $id, matrixSize: $matrixSize, cards: $cards, stages: $stages, initialTargetCardId: $initialTargetCardId, initialTargetWord: $initialTargetWord, text: $text, type: $type)';
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2301,7 +2301,7 @@ abstract mixin class $MatrixQuestionCopyWith<$Res> {
|
|||
factory $MatrixQuestionCopyWith(MatrixQuestion value, $Res Function(MatrixQuestion) _then) = _$MatrixQuestionCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages,@Deprecated('Use stages[0] instead') String? initialTargetCardId,@Deprecated('Use stages[0] instead') String? initialTargetWord, String word, String? text, String type
|
||||
String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages,@Deprecated('Use stages[0] instead') String? initialTargetCardId,@Deprecated('Use stages[0] instead') String? initialTargetWord, String? text, String type
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -2318,7 +2318,7 @@ class _$MatrixQuestionCopyWithImpl<$Res>
|
|||
|
||||
/// Create a copy of MatrixQuestion
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? matrixSize = null,Object? cards = null,Object? stages = null,Object? initialTargetCardId = freezed,Object? initialTargetWord = freezed,Object? word = null,Object? text = freezed,Object? type = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? matrixSize = null,Object? cards = null,Object? stages = null,Object? initialTargetCardId = freezed,Object? initialTargetWord = freezed,Object? text = freezed,Object? type = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,matrixSize: null == matrixSize ? _self.matrixSize : matrixSize // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -2326,8 +2326,7 @@ as int,cards: null == cards ? _self.cards : cards // ignore: cast_nullable_to_no
|
|||
as List<MatrixCard>,stages: null == stages ? _self.stages : stages // ignore: cast_nullable_to_non_nullable
|
||||
as List<MatrixStage>,initialTargetCardId: freezed == initialTargetCardId ? _self.initialTargetCardId : initialTargetCardId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,initialTargetWord: freezed == initialTargetWord ? _self.initialTargetWord : initialTargetWord // ignore: cast_nullable_to_non_nullable
|
||||
as String?,word: null == word ? _self.word : word // ignore: cast_nullable_to_non_nullable
|
||||
as String,text: freezed == text ? _self.text : text // ignore: cast_nullable_to_non_nullable
|
||||
as String?,text: freezed == text ? _self.text : text // ignore: cast_nullable_to_non_nullable
|
||||
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
|
|
@ -2414,10 +2413,10 @@ return $default(_that);case _:
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages, @Deprecated('Use stages[0] instead') String? initialTargetCardId, @Deprecated('Use stages[0] instead') String? initialTargetWord, String word, String? text, String type)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages, @Deprecated('Use stages[0] instead') String? initialTargetCardId, @Deprecated('Use stages[0] instead') String? initialTargetWord, String? text, String type)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MatrixQuestion() when $default != null:
|
||||
return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initialTargetCardId,_that.initialTargetWord,_that.word,_that.text,_that.type);case _:
|
||||
return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initialTargetCardId,_that.initialTargetWord,_that.text,_that.type);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
|
|
@ -2435,10 +2434,10 @@ return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initial
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages, @Deprecated('Use stages[0] instead') String? initialTargetCardId, @Deprecated('Use stages[0] instead') String? initialTargetWord, String word, String? text, String type) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages, @Deprecated('Use stages[0] instead') String? initialTargetCardId, @Deprecated('Use stages[0] instead') String? initialTargetWord, String? text, String type) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MatrixQuestion():
|
||||
return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initialTargetCardId,_that.initialTargetWord,_that.word,_that.text,_that.type);case _:
|
||||
return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initialTargetCardId,_that.initialTargetWord,_that.text,_that.type);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
|
|
@ -2455,10 +2454,10 @@ return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initial
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages, @Deprecated('Use stages[0] instead') String? initialTargetCardId, @Deprecated('Use stages[0] instead') String? initialTargetWord, String word, String? text, String type)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages, @Deprecated('Use stages[0] instead') String? initialTargetCardId, @Deprecated('Use stages[0] instead') String? initialTargetWord, String? text, String type)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MatrixQuestion() when $default != null:
|
||||
return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initialTargetCardId,_that.initialTargetWord,_that.word,_that.text,_that.type);case _:
|
||||
return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initialTargetCardId,_that.initialTargetWord,_that.text,_that.type);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
|
|
@ -2470,7 +2469,7 @@ return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initial
|
|||
@JsonSerializable()
|
||||
|
||||
class _MatrixQuestion implements MatrixQuestion {
|
||||
const _MatrixQuestion({required this.id, required this.matrixSize, required final List<MatrixCard> cards, final List<MatrixStage> stages = const [], @Deprecated('Use stages[0] instead') this.initialTargetCardId, @Deprecated('Use stages[0] instead') this.initialTargetWord, required this.word, this.text, this.type = 'matrix'}): _cards = cards,_stages = stages;
|
||||
const _MatrixQuestion({required this.id, required this.matrixSize, required final List<MatrixCard> cards, final List<MatrixStage> stages = const [], @Deprecated('Use stages[0] instead') this.initialTargetCardId, @Deprecated('Use stages[0] instead') this.initialTargetWord, this.text, this.type = 'matrix'}): _cards = cards,_stages = stages;
|
||||
factory _MatrixQuestion.fromJson(Map<String, dynamic> json) => _$MatrixQuestionFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
|
|
@ -2492,7 +2491,6 @@ class _MatrixQuestion implements MatrixQuestion {
|
|||
// DEPRECATED: Use stages[0] instead. Kept for backward compatibility.
|
||||
@override@Deprecated('Use stages[0] instead') final String? initialTargetCardId;
|
||||
@override@Deprecated('Use stages[0] instead') final String? initialTargetWord;
|
||||
@override final String word;
|
||||
@override final String? text;
|
||||
@override@JsonKey() final String type;
|
||||
|
||||
|
|
@ -2509,16 +2507,16 @@ Map<String, dynamic> toJson() {
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MatrixQuestion&&(identical(other.id, id) || other.id == id)&&(identical(other.matrixSize, matrixSize) || other.matrixSize == matrixSize)&&const DeepCollectionEquality().equals(other._cards, _cards)&&const DeepCollectionEquality().equals(other._stages, _stages)&&(identical(other.initialTargetCardId, initialTargetCardId) || other.initialTargetCardId == initialTargetCardId)&&(identical(other.initialTargetWord, initialTargetWord) || other.initialTargetWord == initialTargetWord)&&(identical(other.word, word) || other.word == word)&&(identical(other.text, text) || other.text == text)&&(identical(other.type, type) || other.type == type));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MatrixQuestion&&(identical(other.id, id) || other.id == id)&&(identical(other.matrixSize, matrixSize) || other.matrixSize == matrixSize)&&const DeepCollectionEquality().equals(other._cards, _cards)&&const DeepCollectionEquality().equals(other._stages, _stages)&&(identical(other.initialTargetCardId, initialTargetCardId) || other.initialTargetCardId == initialTargetCardId)&&(identical(other.initialTargetWord, initialTargetWord) || other.initialTargetWord == initialTargetWord)&&(identical(other.text, text) || other.text == text)&&(identical(other.type, type) || other.type == type));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,matrixSize,const DeepCollectionEquality().hash(_cards),const DeepCollectionEquality().hash(_stages),initialTargetCardId,initialTargetWord,word,text,type);
|
||||
int get hashCode => Object.hash(runtimeType,id,matrixSize,const DeepCollectionEquality().hash(_cards),const DeepCollectionEquality().hash(_stages),initialTargetCardId,initialTargetWord,text,type);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MatrixQuestion(id: $id, matrixSize: $matrixSize, cards: $cards, stages: $stages, initialTargetCardId: $initialTargetCardId, initialTargetWord: $initialTargetWord, word: $word, text: $text, type: $type)';
|
||||
return 'MatrixQuestion(id: $id, matrixSize: $matrixSize, cards: $cards, stages: $stages, initialTargetCardId: $initialTargetCardId, initialTargetWord: $initialTargetWord, text: $text, type: $type)';
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2529,7 +2527,7 @@ abstract mixin class _$MatrixQuestionCopyWith<$Res> implements $MatrixQuestionCo
|
|||
factory _$MatrixQuestionCopyWith(_MatrixQuestion value, $Res Function(_MatrixQuestion) _then) = __$MatrixQuestionCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages,@Deprecated('Use stages[0] instead') String? initialTargetCardId,@Deprecated('Use stages[0] instead') String? initialTargetWord, String word, String? text, String type
|
||||
String id, int matrixSize, List<MatrixCard> cards, List<MatrixStage> stages,@Deprecated('Use stages[0] instead') String? initialTargetCardId,@Deprecated('Use stages[0] instead') String? initialTargetWord, String? text, String type
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -2546,7 +2544,7 @@ class __$MatrixQuestionCopyWithImpl<$Res>
|
|||
|
||||
/// Create a copy of MatrixQuestion
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? matrixSize = null,Object? cards = null,Object? stages = null,Object? initialTargetCardId = freezed,Object? initialTargetWord = freezed,Object? word = null,Object? text = freezed,Object? type = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? matrixSize = null,Object? cards = null,Object? stages = null,Object? initialTargetCardId = freezed,Object? initialTargetWord = freezed,Object? text = freezed,Object? type = null,}) {
|
||||
return _then(_MatrixQuestion(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,matrixSize: null == matrixSize ? _self.matrixSize : matrixSize // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -2554,8 +2552,7 @@ as int,cards: null == cards ? _self._cards : cards // ignore: cast_nullable_to_n
|
|||
as List<MatrixCard>,stages: null == stages ? _self._stages : stages // ignore: cast_nullable_to_non_nullable
|
||||
as List<MatrixStage>,initialTargetCardId: freezed == initialTargetCardId ? _self.initialTargetCardId : initialTargetCardId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,initialTargetWord: freezed == initialTargetWord ? _self.initialTargetWord : initialTargetWord // ignore: cast_nullable_to_non_nullable
|
||||
as String?,word: null == word ? _self.word : word // ignore: cast_nullable_to_non_nullable
|
||||
as String,text: freezed == text ? _self.text : text // ignore: cast_nullable_to_non_nullable
|
||||
as String?,text: freezed == text ? _self.text : text // ignore: cast_nullable_to_non_nullable
|
||||
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
|
|
@ -2837,7 +2834,7 @@ as String?,
|
|||
/// @nodoc
|
||||
mixin _$MatrixCard {
|
||||
|
||||
String get id; String? get image; String? get original; String? get translation;
|
||||
String get id; String? get image; String? get imageUrl; String? get original; String? get translation; String? get originalBack; String? get translationBack; String? get imageBack; String? get imageBackUrl;
|
||||
/// Create a copy of MatrixCard
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
|
|
@ -2850,16 +2847,16 @@ $MatrixCardCopyWith<MatrixCard> get copyWith => _$MatrixCardCopyWithImpl<MatrixC
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MatrixCard&&(identical(other.id, id) || other.id == id)&&(identical(other.image, image) || other.image == image)&&(identical(other.original, original) || other.original == original)&&(identical(other.translation, translation) || other.translation == translation));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MatrixCard&&(identical(other.id, id) || other.id == id)&&(identical(other.image, image) || other.image == image)&&(identical(other.imageUrl, imageUrl) || other.imageUrl == imageUrl)&&(identical(other.original, original) || other.original == original)&&(identical(other.translation, translation) || other.translation == translation)&&(identical(other.originalBack, originalBack) || other.originalBack == originalBack)&&(identical(other.translationBack, translationBack) || other.translationBack == translationBack)&&(identical(other.imageBack, imageBack) || other.imageBack == imageBack)&&(identical(other.imageBackUrl, imageBackUrl) || other.imageBackUrl == imageBackUrl));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,image,original,translation);
|
||||
int get hashCode => Object.hash(runtimeType,id,image,imageUrl,original,translation,originalBack,translationBack,imageBack,imageBackUrl);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MatrixCard(id: $id, image: $image, original: $original, translation: $translation)';
|
||||
return 'MatrixCard(id: $id, image: $image, imageUrl: $imageUrl, original: $original, translation: $translation, originalBack: $originalBack, translationBack: $translationBack, imageBack: $imageBack, imageBackUrl: $imageBackUrl)';
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2870,7 +2867,7 @@ abstract mixin class $MatrixCardCopyWith<$Res> {
|
|||
factory $MatrixCardCopyWith(MatrixCard value, $Res Function(MatrixCard) _then) = _$MatrixCardCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String? image, String? original, String? translation
|
||||
String id, String? image, String? imageUrl, String? original, String? translation, String? originalBack, String? translationBack, String? imageBack, String? imageBackUrl
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -2887,12 +2884,17 @@ class _$MatrixCardCopyWithImpl<$Res>
|
|||
|
||||
/// Create a copy of MatrixCard
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? image = freezed,Object? original = freezed,Object? translation = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? image = freezed,Object? imageUrl = freezed,Object? original = freezed,Object? translation = freezed,Object? originalBack = freezed,Object? translationBack = freezed,Object? imageBack = freezed,Object? imageBackUrl = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
|
||||
as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,original: freezed == original ? _self.original : original // ignore: cast_nullable_to_non_nullable
|
||||
as String?,translation: freezed == translation ? _self.translation : translation // ignore: cast_nullable_to_non_nullable
|
||||
as String?,originalBack: freezed == originalBack ? _self.originalBack : originalBack // ignore: cast_nullable_to_non_nullable
|
||||
as String?,translationBack: freezed == translationBack ? _self.translationBack : translationBack // ignore: cast_nullable_to_non_nullable
|
||||
as String?,imageBack: freezed == imageBack ? _self.imageBack : imageBack // ignore: cast_nullable_to_non_nullable
|
||||
as String?,imageBackUrl: freezed == imageBackUrl ? _self.imageBackUrl : imageBackUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
|
@ -2978,10 +2980,10 @@ return $default(_that);case _:
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? image, String? original, String? translation)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? image, String? imageUrl, String? original, String? translation, String? originalBack, String? translationBack, String? imageBack, String? imageBackUrl)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MatrixCard() when $default != null:
|
||||
return $default(_that.id,_that.image,_that.original,_that.translation);case _:
|
||||
return $default(_that.id,_that.image,_that.imageUrl,_that.original,_that.translation,_that.originalBack,_that.translationBack,_that.imageBack,_that.imageBackUrl);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
|
|
@ -2999,10 +3001,10 @@ return $default(_that.id,_that.image,_that.original,_that.translation);case _:
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? image, String? original, String? translation) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? image, String? imageUrl, String? original, String? translation, String? originalBack, String? translationBack, String? imageBack, String? imageBackUrl) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MatrixCard():
|
||||
return $default(_that.id,_that.image,_that.original,_that.translation);case _:
|
||||
return $default(_that.id,_that.image,_that.imageUrl,_that.original,_that.translation,_that.originalBack,_that.translationBack,_that.imageBack,_that.imageBackUrl);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
|
|
@ -3019,10 +3021,10 @@ return $default(_that.id,_that.image,_that.original,_that.translation);case _:
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? image, String? original, String? translation)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? image, String? imageUrl, String? original, String? translation, String? originalBack, String? translationBack, String? imageBack, String? imageBackUrl)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MatrixCard() when $default != null:
|
||||
return $default(_that.id,_that.image,_that.original,_that.translation);case _:
|
||||
return $default(_that.id,_that.image,_that.imageUrl,_that.original,_that.translation,_that.originalBack,_that.translationBack,_that.imageBack,_that.imageBackUrl);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
|
|
@ -3034,13 +3036,18 @@ return $default(_that.id,_that.image,_that.original,_that.translation);case _:
|
|||
@JsonSerializable()
|
||||
|
||||
class _MatrixCard implements MatrixCard {
|
||||
const _MatrixCard({required this.id, this.image, this.original, this.translation});
|
||||
const _MatrixCard({required this.id, this.image, this.imageUrl, this.original, this.translation, this.originalBack, this.translationBack, this.imageBack, this.imageBackUrl});
|
||||
factory _MatrixCard.fromJson(Map<String, dynamic> json) => _$MatrixCardFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final String? image;
|
||||
@override final String? imageUrl;
|
||||
@override final String? original;
|
||||
@override final String? translation;
|
||||
@override final String? originalBack;
|
||||
@override final String? translationBack;
|
||||
@override final String? imageBack;
|
||||
@override final String? imageBackUrl;
|
||||
|
||||
/// Create a copy of MatrixCard
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
|
@ -3055,16 +3062,16 @@ Map<String, dynamic> toJson() {
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MatrixCard&&(identical(other.id, id) || other.id == id)&&(identical(other.image, image) || other.image == image)&&(identical(other.original, original) || other.original == original)&&(identical(other.translation, translation) || other.translation == translation));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MatrixCard&&(identical(other.id, id) || other.id == id)&&(identical(other.image, image) || other.image == image)&&(identical(other.imageUrl, imageUrl) || other.imageUrl == imageUrl)&&(identical(other.original, original) || other.original == original)&&(identical(other.translation, translation) || other.translation == translation)&&(identical(other.originalBack, originalBack) || other.originalBack == originalBack)&&(identical(other.translationBack, translationBack) || other.translationBack == translationBack)&&(identical(other.imageBack, imageBack) || other.imageBack == imageBack)&&(identical(other.imageBackUrl, imageBackUrl) || other.imageBackUrl == imageBackUrl));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,image,original,translation);
|
||||
int get hashCode => Object.hash(runtimeType,id,image,imageUrl,original,translation,originalBack,translationBack,imageBack,imageBackUrl);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MatrixCard(id: $id, image: $image, original: $original, translation: $translation)';
|
||||
return 'MatrixCard(id: $id, image: $image, imageUrl: $imageUrl, original: $original, translation: $translation, originalBack: $originalBack, translationBack: $translationBack, imageBack: $imageBack, imageBackUrl: $imageBackUrl)';
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -3075,7 +3082,7 @@ abstract mixin class _$MatrixCardCopyWith<$Res> implements $MatrixCardCopyWith<$
|
|||
factory _$MatrixCardCopyWith(_MatrixCard value, $Res Function(_MatrixCard) _then) = __$MatrixCardCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String? image, String? original, String? translation
|
||||
String id, String? image, String? imageUrl, String? original, String? translation, String? originalBack, String? translationBack, String? imageBack, String? imageBackUrl
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -3092,12 +3099,17 @@ class __$MatrixCardCopyWithImpl<$Res>
|
|||
|
||||
/// Create a copy of MatrixCard
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? image = freezed,Object? original = freezed,Object? translation = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? image = freezed,Object? imageUrl = freezed,Object? original = freezed,Object? translation = freezed,Object? originalBack = freezed,Object? translationBack = freezed,Object? imageBack = freezed,Object? imageBackUrl = freezed,}) {
|
||||
return _then(_MatrixCard(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
|
||||
as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,original: freezed == original ? _self.original : original // ignore: cast_nullable_to_non_nullable
|
||||
as String?,translation: freezed == translation ? _self.translation : translation // ignore: cast_nullable_to_non_nullable
|
||||
as String?,originalBack: freezed == originalBack ? _self.originalBack : originalBack // ignore: cast_nullable_to_non_nullable
|
||||
as String?,translationBack: freezed == translationBack ? _self.translationBack : translationBack // ignore: cast_nullable_to_non_nullable
|
||||
as String?,imageBack: freezed == imageBack ? _self.imageBack : imageBack // ignore: cast_nullable_to_non_nullable
|
||||
as String?,imageBackUrl: freezed == imageBackUrl ? _self.imageBackUrl : imageBackUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,7 +205,6 @@ _MatrixQuestion _$MatrixQuestionFromJson(Map<String, dynamic> json) =>
|
|||
const [],
|
||||
initialTargetCardId: json['initialTargetCardId'] as String?,
|
||||
initialTargetWord: json['initialTargetWord'] as String?,
|
||||
word: json['word'] as String,
|
||||
text: json['text'] as String?,
|
||||
type: json['type'] as String? ?? 'matrix',
|
||||
);
|
||||
|
|
@ -218,7 +217,6 @@ Map<String, dynamic> _$MatrixQuestionToJson(_MatrixQuestion instance) =>
|
|||
'stages': instance.stages,
|
||||
'initialTargetCardId': instance.initialTargetCardId,
|
||||
'initialTargetWord': instance.initialTargetWord,
|
||||
'word': instance.word,
|
||||
'text': instance.text,
|
||||
'type': instance.type,
|
||||
};
|
||||
|
|
@ -239,16 +237,26 @@ Map<String, dynamic> _$MatrixStageToJson(_MatrixStage instance) =>
|
|||
_MatrixCard _$MatrixCardFromJson(Map<String, dynamic> json) => _MatrixCard(
|
||||
id: json['id'] as String,
|
||||
image: json['image'] as String?,
|
||||
imageUrl: json['imageUrl'] as String?,
|
||||
original: json['original'] as String?,
|
||||
translation: json['translation'] as String?,
|
||||
originalBack: json['originalBack'] as String?,
|
||||
translationBack: json['translationBack'] as String?,
|
||||
imageBack: json['imageBack'] as String?,
|
||||
imageBackUrl: json['imageBackUrl'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MatrixCardToJson(_MatrixCard instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'image': instance.image,
|
||||
'imageUrl': instance.imageUrl,
|
||||
'original': instance.original,
|
||||
'translation': instance.translation,
|
||||
'originalBack': instance.originalBack,
|
||||
'translationBack': instance.translationBack,
|
||||
'imageBack': instance.imageBack,
|
||||
'imageBackUrl': instance.imageBackUrl,
|
||||
};
|
||||
|
||||
_QuestionResult _$QuestionResultFromJson(Map<String, dynamic> json) =>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class GameSessionManager {
|
|||
GameSessionManager();
|
||||
|
||||
GameSessionResult? _currentResult;
|
||||
final Map<String, QuestionResult> _questionResults = {};
|
||||
final Map<String, List<QuestionResult>> _questionResults = {};
|
||||
Timer? _questionTimer;
|
||||
DateTime? _sessionStartTime;
|
||||
DateTime? _currentQuestionStartTime;
|
||||
|
|
@ -21,7 +21,7 @@ class GameSessionManager {
|
|||
GameSessionResult? get currentResult => _currentResult;
|
||||
|
||||
/// All question results for current session
|
||||
Map<String, QuestionResult> get questionResults =>
|
||||
Map<String, List<QuestionResult>> get questionResults =>
|
||||
Map.unmodifiable(_questionResults);
|
||||
|
||||
/// Elapsed time since session start
|
||||
|
|
@ -50,11 +50,53 @@ class GameSessionManager {
|
|||
// Pre-populate question results with placeholders
|
||||
for (final question in questions) {
|
||||
final questionId = _getQuestionId(question);
|
||||
_questionResults[questionId] = QuestionResult(
|
||||
questionId: questionId,
|
||||
word: _getQuestionWord(question),
|
||||
isCorrect: false,
|
||||
timeSpent: Duration.zero,
|
||||
|
||||
question.when(
|
||||
multipleChoice: (_) {
|
||||
// Для обычных: список с одним элементом
|
||||
_questionResults[questionId] = [
|
||||
QuestionResult(
|
||||
questionId: questionId,
|
||||
word: _getQuestionWord(question),
|
||||
isCorrect: false,
|
||||
timeSpent: Duration.zero,
|
||||
)
|
||||
];
|
||||
},
|
||||
inputLetters: (_) {
|
||||
// Для обычных: список с одним элементом
|
||||
_questionResults[questionId] = [
|
||||
QuestionResult(
|
||||
questionId: questionId,
|
||||
word: _getQuestionWord(question),
|
||||
isCorrect: false,
|
||||
timeSpent: Duration.zero,
|
||||
)
|
||||
];
|
||||
},
|
||||
match: (_) {
|
||||
// Для обычных: список с одним элементом
|
||||
_questionResults[questionId] = [
|
||||
QuestionResult(
|
||||
questionId: questionId,
|
||||
word: _getQuestionWord(question),
|
||||
isCorrect: false,
|
||||
timeSpent: Duration.zero,
|
||||
)
|
||||
];
|
||||
},
|
||||
matrix: (q) {
|
||||
// Для матричных: список placeholders для каждого stage
|
||||
_questionResults[questionId] = q.stages.map((stage) =>
|
||||
QuestionResult(
|
||||
questionId: questionId,
|
||||
word: stage.targetWord,
|
||||
isCorrect: false,
|
||||
timeSpent: Duration.zero,
|
||||
selectedAnswer: stage.targetCardId, // Для идентификации
|
||||
)
|
||||
).toList();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -74,31 +116,69 @@ class GameSessionManager {
|
|||
/// Submit answer for current question and move to next
|
||||
void submitAnswer(String questionId, GameQuestion question, dynamic answer) {
|
||||
final timeSpent = _calculateTimeSpent();
|
||||
final isCorrect = _validateAnswer(question, answer);
|
||||
|
||||
log(
|
||||
'Answer submitted for question $questionId: correct=$isCorrect, time=${timeSpent.inMilliseconds}ms',
|
||||
'Answer submitted for question $questionId, time=${timeSpent.inMilliseconds}ms',
|
||||
name: 'GameSessionManager',
|
||||
);
|
||||
|
||||
final selectedAnswer = answer is String ? answer : null;
|
||||
final selectedAnswers = switch (answer) {
|
||||
List<String> v => v,
|
||||
MatrixImageSelectAnswer v => v.correctCardIdsInOrder,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
final result = QuestionResult(
|
||||
questionId: questionId,
|
||||
word: _getQuestionWord(question),
|
||||
isCorrect: isCorrect,
|
||||
timeSpent: timeSpent,
|
||||
selectedAnswer: selectedAnswer,
|
||||
selectedAnswers: selectedAnswers,
|
||||
answeredAt: DateTime.now(),
|
||||
// Для матричных вопросов создаем список результатов (по одному на stage)
|
||||
var isMatrixProcessed = false;
|
||||
question.when(
|
||||
matrix: (q) {
|
||||
if (answer is MatrixImageSelectAnswer) {
|
||||
final results = <QuestionResult>[];
|
||||
final timePerStageMs = timeSpent.inMilliseconds / q.stages.length;
|
||||
final timePerStage = Duration(milliseconds: timePerStageMs.round());
|
||||
|
||||
for (int i = 0; i < q.stages.length; i++) {
|
||||
final stage = q.stages[i];
|
||||
final selectedCardId = answer.correctCardIdsInOrder[i];
|
||||
final isStageCorrect = selectedCardId == stage.targetCardId;
|
||||
|
||||
results.add(QuestionResult(
|
||||
questionId: questionId, // Оригинальный ID (без _stage_)
|
||||
word: stage.targetWord,
|
||||
isCorrect: isStageCorrect,
|
||||
timeSpent: timePerStage,
|
||||
selectedAnswer: selectedCardId,
|
||||
selectedAnswers: answer.correctCardIdsInOrder,
|
||||
answeredAt: DateTime.now(),
|
||||
));
|
||||
}
|
||||
|
||||
_questionResults[questionId] = results;
|
||||
isMatrixProcessed = true;
|
||||
}
|
||||
},
|
||||
multipleChoice: (_) {},
|
||||
inputLetters: (_) {},
|
||||
match: (_) {},
|
||||
);
|
||||
|
||||
if (!isMatrixProcessed) {
|
||||
// Обычные вопросы: список с одним элементом
|
||||
final isCorrect = _validateAnswer(question, answer);
|
||||
final selectedAnswer = answer is String ? answer : null;
|
||||
final selectedAnswers = switch (answer) {
|
||||
List<String> v => v,
|
||||
MatrixImageSelectAnswer v => v.correctCardIdsInOrder,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
final result = QuestionResult(
|
||||
questionId: questionId,
|
||||
word: _getQuestionWord(question),
|
||||
isCorrect: isCorrect,
|
||||
timeSpent: timeSpent,
|
||||
selectedAnswer: selectedAnswer,
|
||||
selectedAnswers: selectedAnswers,
|
||||
answeredAt: DateTime.now(),
|
||||
);
|
||||
|
||||
_questionResults[questionId] = [result];
|
||||
}
|
||||
|
||||
_questionResults[questionId] = result;
|
||||
_questionTimer?.cancel();
|
||||
_currentQuestionStartTime = null;
|
||||
}
|
||||
|
|
@ -108,14 +188,20 @@ class GameSessionManager {
|
|||
/// Complete the current game session
|
||||
GameSessionResult completeSession(String testId) {
|
||||
final totalTime = _calculateSessionTime();
|
||||
final correctAnswers = _questionResults.values
|
||||
|
||||
// Все результаты из всех списков
|
||||
final allResults = _questionResults.values
|
||||
.expand((results) => results)
|
||||
.toList();
|
||||
|
||||
final correctAnswers = allResults
|
||||
.where((r) => r.isCorrect)
|
||||
.length;
|
||||
final totalQuestions = _questionResults.length;
|
||||
final totalQuestions = allResults.length; // Количество stages
|
||||
|
||||
_currentResult = GameSessionResult(
|
||||
testId: testId,
|
||||
questionResults: _questionResults.values.toList(),
|
||||
questionResults: allResults, // Плоский список
|
||||
totalTime: totalTime,
|
||||
correctAnswers: correctAnswers,
|
||||
totalQuestions: totalQuestions,
|
||||
|
|
@ -136,21 +222,51 @@ class GameSessionManager {
|
|||
log('Question skipped: $questionId', name: 'GameSessionManager');
|
||||
|
||||
final timeSpent = _calculateTimeSpent();
|
||||
final existingResult = _questionResults[questionId];
|
||||
final results = _questionResults[questionId];
|
||||
|
||||
if (existingResult != null) {
|
||||
_questionResults[questionId] = existingResult.copyWith(
|
||||
timeSpent: existingResult.timeSpent + timeSpent,
|
||||
);
|
||||
if (results != null && results.isNotEmpty) {
|
||||
final timePerResultMs = timeSpent.inMilliseconds / results.length;
|
||||
final timePerResult = Duration(milliseconds: timePerResultMs.round());
|
||||
_questionResults[questionId] = results
|
||||
.map((r) => r.copyWith(timeSpent: r.timeSpent + timePerResult))
|
||||
.toList();
|
||||
}
|
||||
|
||||
_questionTimer?.cancel();
|
||||
_currentQuestionStartTime = null;
|
||||
}
|
||||
|
||||
/// Get result for specific question
|
||||
/// Get result for specific question (returns first result for backward compatibility)
|
||||
QuestionResult? getQuestionResult(String questionId) {
|
||||
return _questionResults[questionId];
|
||||
final results = _questionResults[questionId];
|
||||
return results?.isNotEmpty == true ? results!.first : null;
|
||||
}
|
||||
|
||||
/// Get result for specific stage of matrix question
|
||||
QuestionResult? getQuestionResultByStage(
|
||||
String questionId,
|
||||
String word,
|
||||
String? selectedAnswer,
|
||||
) {
|
||||
final results = _questionResults[questionId];
|
||||
if (results == null || results.isEmpty) return null;
|
||||
|
||||
try {
|
||||
return results.firstWhere(
|
||||
(r) => r.word == word && r.selectedAnswer == selectedAnswer,
|
||||
);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if all stages of matrix question are correct
|
||||
bool isMatrixQuestionCorrect(String questionId) {
|
||||
final results = _questionResults[questionId];
|
||||
if (results == null || results.isEmpty) return false;
|
||||
|
||||
// Все stages должны быть правильными
|
||||
return results.every((r) => r.isCorrect);
|
||||
}
|
||||
|
||||
/// Check if session is active
|
||||
|
|
@ -160,10 +276,14 @@ class GameSessionManager {
|
|||
Map<String, dynamic> getSessionStats() {
|
||||
if (!isSessionActive) return {};
|
||||
|
||||
final answeredQuestions = _questionResults.values
|
||||
final allResults = _questionResults.values
|
||||
.expand((results) => results)
|
||||
.toList();
|
||||
|
||||
final answeredQuestions = allResults
|
||||
.where((r) => r.answeredAt != null)
|
||||
.length;
|
||||
final correctAnswers = _questionResults.values
|
||||
final correctAnswers = allResults
|
||||
.where((r) => r.isCorrect)
|
||||
.length;
|
||||
final totalTime = _calculateSessionTime();
|
||||
|
|
@ -171,7 +291,7 @@ class GameSessionManager {
|
|||
return {
|
||||
'answeredQuestions': answeredQuestions,
|
||||
'correctAnswers': correctAnswers,
|
||||
'totalQuestions': _questionResults.length,
|
||||
'totalQuestions': allResults.length, // Количество stages
|
||||
'accuracy': answeredQuestions > 0
|
||||
? correctAnswers / answeredQuestions
|
||||
: 0.0,
|
||||
|
|
@ -206,7 +326,7 @@ class GameSessionManager {
|
|||
multipleChoice: (q) => q.word,
|
||||
inputLetters: (q) => q.word,
|
||||
match: (q) => q.word,
|
||||
matrix: (q) => q.word,
|
||||
matrix: (q) => q.stages.isNotEmpty ? q.stages.first.targetWord : '',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -283,13 +403,25 @@ class GameSessionManager {
|
|||
// attempts. Progression to the next question is handled by
|
||||
// TestsStateManager (it should advance even if isCorrect == false).
|
||||
if (answer is MatrixImageSelectAnswer) {
|
||||
return answer.correctCardIdsInOrder.length == question.cards.length &&
|
||||
answer.wrongAttempts == 0;
|
||||
// Проверяем, что все stages пройдены
|
||||
if (answer.correctCardIdsInOrder.length != question.stages.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Проверяем, что все stages правильные
|
||||
for (int i = 0; i < question.stages.length; i++) {
|
||||
if (answer.correctCardIdsInOrder[i] != question.stages[i].targetCardId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем, что не было ошибок
|
||||
return answer.wrongAttempts == 0;
|
||||
}
|
||||
|
||||
// Backward/compat: if someone submits just the correct ids list.
|
||||
if (answer is List<String>) {
|
||||
return answer.length == question.cards.length;
|
||||
return answer.length == question.stages.length;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class TestsState with _$TestsState {
|
|||
required List<GameQuestion> questions,
|
||||
required int currentQuestionIndex,
|
||||
required GameSessionResult? currentResult,
|
||||
required Map<String, QuestionResult> questionResults,
|
||||
required Map<String, List<QuestionResult>> questionResults,
|
||||
required bool isAnswerSubmitted,
|
||||
required bool isCorrect,
|
||||
Duration? answerFeedbackDelay,
|
||||
|
|
@ -168,8 +168,13 @@ class TestsStateManager extends StateManager<TestsState> {
|
|||
// Submit answer to session manager
|
||||
_gameSessionManager.submitAnswer(questionId, currentQuestion, answer);
|
||||
|
||||
final isCorrect =
|
||||
_gameSessionManager.getQuestionResult(questionId)?.isCorrect ?? false;
|
||||
// Для матричных вопросов проверяем все stages, для остальных - первый результат
|
||||
final isCorrect = currentQuestion.when(
|
||||
multipleChoice: (_) => _gameSessionManager.getQuestionResult(questionId)?.isCorrect ?? false,
|
||||
inputLetters: (_) => _gameSessionManager.getQuestionResult(questionId)?.isCorrect ?? false,
|
||||
match: (_) => _gameSessionManager.getQuestionResult(questionId)?.isCorrect ?? false,
|
||||
matrix: (_) => _gameSessionManager.isMatrixQuestionCorrect(questionId),
|
||||
);
|
||||
|
||||
// Play sound/haptics based on settings
|
||||
if (_settings.enableSounds) {
|
||||
|
|
@ -246,8 +251,8 @@ class TestsStateManager extends StateManager<TestsState> {
|
|||
final questionId = _getQuestionId(currentQuestion);
|
||||
|
||||
_gameSessionManager.submitAnswer(questionId, currentQuestion, answer);
|
||||
final isCorrect =
|
||||
_gameSessionManager.getQuestionResult(questionId)?.isCorrect ?? false;
|
||||
// Матричный вопрос считается правильным, если все stages правильные
|
||||
final isCorrect = _gameSessionManager.isMatrixQuestionCorrect(questionId);
|
||||
|
||||
// Completion feedback (always positive UX)
|
||||
if (_settings.enableSounds) {
|
||||
|
|
@ -540,9 +545,16 @@ class TestsStateManager extends StateManager<TestsState> {
|
|||
.map(
|
||||
(c) => MatrixCard(
|
||||
id: c.id,
|
||||
image: c.imageUrl ?? c.image, // Use presigned URL if available
|
||||
// Front side fields
|
||||
image: c.image,
|
||||
imageUrl: c.imageUrl, // Presigned URL for display
|
||||
original: c.original,
|
||||
translation: c.translation,
|
||||
// Back side fields
|
||||
originalBack: c.originalBack,
|
||||
translationBack: c.translationBack,
|
||||
imageBack: c.imageBack,
|
||||
imageBackUrl: c.imageBackUrl,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
|
@ -558,13 +570,12 @@ class TestsStateManager extends StateManager<TestsState> {
|
|||
)
|
||||
.toList();
|
||||
|
||||
// Use stages if available, otherwise fallback to deprecated fields
|
||||
final initialTargetCardId = stages.isNotEmpty
|
||||
? stages.first.targetCardId
|
||||
: question.answer;
|
||||
final initialTargetWord = stages.isNotEmpty
|
||||
? stages.first.targetWord
|
||||
: question.word;
|
||||
// Ensure stages are not empty
|
||||
assert(stages.isNotEmpty, 'Matrix question must have at least one stage');
|
||||
|
||||
// Use stages for deprecated fields (for backward compatibility)
|
||||
final initialTargetCardId = stages.first.targetCardId;
|
||||
final initialTargetWord = stages.first.targetWord;
|
||||
|
||||
questions.add(
|
||||
GameQuestion.matrix(
|
||||
|
|
@ -575,7 +586,6 @@ class TestsStateManager extends StateManager<TestsState> {
|
|||
stages: stages,
|
||||
initialTargetCardId: initialTargetCardId,
|
||||
initialTargetWord: initialTargetWord,
|
||||
word: question.word,
|
||||
text: question.text,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -12,11 +12,17 @@ part of 'tests_state_manager.dart';
|
|||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$TestsState {
|
||||
mixin _$TestsState implements DiagnosticableTreeMixin {
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'TestsState'))
|
||||
;
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
|
|
@ -28,7 +34,7 @@ bool operator ==(Object other) {
|
|||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'TestsState()';
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +140,7 @@ return gameSessionCompleted(_that);case _:
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? loading,TResult Function( List<TestDto> tests, String packId)? loaded,TResult Function( String message)? error,TResult Function( TestDto test, List<GameQuestion> questions)? gameSessionPreparing,TResult Function( TestDto test, List<GameQuestion> questions, int currentQuestionIndex, GameSessionResult? currentResult, Map<String, QuestionResult> questionResults, bool isAnswerSubmitted, bool isCorrect, Duration? answerFeedbackDelay)? gameSessionActive,TResult Function( TestDto test, GameSessionResult result)? gameSessionCompleted,required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? loading,TResult Function( List<TestDto> tests, String packId)? loaded,TResult Function( String message)? error,TResult Function( TestDto test, List<GameQuestion> questions)? gameSessionPreparing,TResult Function( TestDto test, List<GameQuestion> questions, int currentQuestionIndex, GameSessionResult? currentResult, Map<String, List<QuestionResult>> questionResults, bool isAnswerSubmitted, bool isCorrect, Duration? answerFeedbackDelay)? gameSessionActive,TResult Function( TestDto test, GameSessionResult result)? gameSessionCompleted,required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Loading() when loading != null:
|
||||
return loading();case _Loaded() when loaded != null:
|
||||
|
|
@ -160,7 +166,7 @@ return gameSessionCompleted(_that.test,_that.result);case _:
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() loading,required TResult Function( List<TestDto> tests, String packId) loaded,required TResult Function( String message) error,required TResult Function( TestDto test, List<GameQuestion> questions) gameSessionPreparing,required TResult Function( TestDto test, List<GameQuestion> questions, int currentQuestionIndex, GameSessionResult? currentResult, Map<String, QuestionResult> questionResults, bool isAnswerSubmitted, bool isCorrect, Duration? answerFeedbackDelay) gameSessionActive,required TResult Function( TestDto test, GameSessionResult result) gameSessionCompleted,}) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() loading,required TResult Function( List<TestDto> tests, String packId) loaded,required TResult Function( String message) error,required TResult Function( TestDto test, List<GameQuestion> questions) gameSessionPreparing,required TResult Function( TestDto test, List<GameQuestion> questions, int currentQuestionIndex, GameSessionResult? currentResult, Map<String, List<QuestionResult>> questionResults, bool isAnswerSubmitted, bool isCorrect, Duration? answerFeedbackDelay) gameSessionActive,required TResult Function( TestDto test, GameSessionResult result) gameSessionCompleted,}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Loading():
|
||||
return loading();case _Loaded():
|
||||
|
|
@ -185,7 +191,7 @@ return gameSessionCompleted(_that.test,_that.result);case _:
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? loading,TResult? Function( List<TestDto> tests, String packId)? loaded,TResult? Function( String message)? error,TResult? Function( TestDto test, List<GameQuestion> questions)? gameSessionPreparing,TResult? Function( TestDto test, List<GameQuestion> questions, int currentQuestionIndex, GameSessionResult? currentResult, Map<String, QuestionResult> questionResults, bool isAnswerSubmitted, bool isCorrect, Duration? answerFeedbackDelay)? gameSessionActive,TResult? Function( TestDto test, GameSessionResult result)? gameSessionCompleted,}) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? loading,TResult? Function( List<TestDto> tests, String packId)? loaded,TResult? Function( String message)? error,TResult? Function( TestDto test, List<GameQuestion> questions)? gameSessionPreparing,TResult? Function( TestDto test, List<GameQuestion> questions, int currentQuestionIndex, GameSessionResult? currentResult, Map<String, List<QuestionResult>> questionResults, bool isAnswerSubmitted, bool isCorrect, Duration? answerFeedbackDelay)? gameSessionActive,TResult? Function( TestDto test, GameSessionResult result)? gameSessionCompleted,}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Loading() when loading != null:
|
||||
return loading();case _Loaded() when loaded != null:
|
||||
|
|
@ -204,7 +210,7 @@ return gameSessionCompleted(_that.test,_that.result);case _:
|
|||
/// @nodoc
|
||||
|
||||
|
||||
class _Loading implements TestsState {
|
||||
class _Loading with DiagnosticableTreeMixin implements TestsState {
|
||||
const _Loading();
|
||||
|
||||
|
||||
|
|
@ -212,6 +218,12 @@ class _Loading implements TestsState {
|
|||
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'TestsState.loading'))
|
||||
;
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
|
|
@ -223,7 +235,7 @@ bool operator ==(Object other) {
|
|||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'TestsState.loading()';
|
||||
}
|
||||
|
||||
|
|
@ -236,7 +248,7 @@ String toString() {
|
|||
/// @nodoc
|
||||
|
||||
|
||||
class _Loaded implements TestsState {
|
||||
class _Loaded with DiagnosticableTreeMixin implements TestsState {
|
||||
const _Loaded({required final List<TestDto> tests, required this.packId}): _tests = tests;
|
||||
|
||||
|
||||
|
|
@ -256,6 +268,12 @@ class _Loaded implements TestsState {
|
|||
_$LoadedCopyWith<_Loaded> get copyWith => __$LoadedCopyWithImpl<_Loaded>(this, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'TestsState.loaded'))
|
||||
..add(DiagnosticsProperty('tests', tests))..add(DiagnosticsProperty('packId', packId));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
|
|
@ -267,7 +285,7 @@ bool operator ==(Object other) {
|
|||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_tests),packId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'TestsState.loaded(tests: $tests, packId: $packId)';
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +328,7 @@ as String,
|
|||
/// @nodoc
|
||||
|
||||
|
||||
class _Error implements TestsState {
|
||||
class _Error with DiagnosticableTreeMixin implements TestsState {
|
||||
const _Error(this.message);
|
||||
|
||||
|
||||
|
|
@ -323,6 +341,12 @@ class _Error implements TestsState {
|
|||
_$ErrorCopyWith<_Error> get copyWith => __$ErrorCopyWithImpl<_Error>(this, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'TestsState.error'))
|
||||
..add(DiagnosticsProperty('message', message));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
|
|
@ -334,7 +358,7 @@ bool operator ==(Object other) {
|
|||
int get hashCode => Object.hash(runtimeType,message);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'TestsState.error(message: $message)';
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +400,7 @@ as String,
|
|||
/// @nodoc
|
||||
|
||||
|
||||
class _GameSessionPreparing implements TestsState {
|
||||
class _GameSessionPreparing with DiagnosticableTreeMixin implements TestsState {
|
||||
const _GameSessionPreparing({required this.test, required final List<GameQuestion> questions}): _questions = questions;
|
||||
|
||||
|
||||
|
|
@ -396,6 +420,12 @@ class _GameSessionPreparing implements TestsState {
|
|||
_$GameSessionPreparingCopyWith<_GameSessionPreparing> get copyWith => __$GameSessionPreparingCopyWithImpl<_GameSessionPreparing>(this, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'TestsState.gameSessionPreparing'))
|
||||
..add(DiagnosticsProperty('test', test))..add(DiagnosticsProperty('questions', questions));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
|
|
@ -407,7 +437,7 @@ bool operator ==(Object other) {
|
|||
int get hashCode => Object.hash(runtimeType,test,const DeepCollectionEquality().hash(_questions));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'TestsState.gameSessionPreparing(test: $test, questions: $questions)';
|
||||
}
|
||||
|
||||
|
|
@ -450,8 +480,8 @@ as List<GameQuestion>,
|
|||
/// @nodoc
|
||||
|
||||
|
||||
class _GameSessionActive implements TestsState {
|
||||
const _GameSessionActive({required this.test, required final List<GameQuestion> questions, required this.currentQuestionIndex, required this.currentResult, required final Map<String, QuestionResult> questionResults, required this.isAnswerSubmitted, required this.isCorrect, this.answerFeedbackDelay}): _questions = questions,_questionResults = questionResults;
|
||||
class _GameSessionActive with DiagnosticableTreeMixin implements TestsState {
|
||||
const _GameSessionActive({required this.test, required final List<GameQuestion> questions, required this.currentQuestionIndex, required this.currentResult, required final Map<String, List<QuestionResult>> questionResults, required this.isAnswerSubmitted, required this.isCorrect, this.answerFeedbackDelay}): _questions = questions,_questionResults = questionResults;
|
||||
|
||||
|
||||
final TestDto test;
|
||||
|
|
@ -464,8 +494,8 @@ class _GameSessionActive implements TestsState {
|
|||
|
||||
final int currentQuestionIndex;
|
||||
final GameSessionResult? currentResult;
|
||||
final Map<String, QuestionResult> _questionResults;
|
||||
Map<String, QuestionResult> get questionResults {
|
||||
final Map<String, List<QuestionResult>> _questionResults;
|
||||
Map<String, List<QuestionResult>> get questionResults {
|
||||
if (_questionResults is EqualUnmodifiableMapView) return _questionResults;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_questionResults);
|
||||
|
|
@ -482,6 +512,12 @@ class _GameSessionActive implements TestsState {
|
|||
_$GameSessionActiveCopyWith<_GameSessionActive> get copyWith => __$GameSessionActiveCopyWithImpl<_GameSessionActive>(this, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'TestsState.gameSessionActive'))
|
||||
..add(DiagnosticsProperty('test', test))..add(DiagnosticsProperty('questions', questions))..add(DiagnosticsProperty('currentQuestionIndex', currentQuestionIndex))..add(DiagnosticsProperty('currentResult', currentResult))..add(DiagnosticsProperty('questionResults', questionResults))..add(DiagnosticsProperty('isAnswerSubmitted', isAnswerSubmitted))..add(DiagnosticsProperty('isCorrect', isCorrect))..add(DiagnosticsProperty('answerFeedbackDelay', answerFeedbackDelay));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
|
|
@ -493,7 +529,7 @@ bool operator ==(Object other) {
|
|||
int get hashCode => Object.hash(runtimeType,test,const DeepCollectionEquality().hash(_questions),currentQuestionIndex,currentResult,const DeepCollectionEquality().hash(_questionResults),isAnswerSubmitted,isCorrect,answerFeedbackDelay);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'TestsState.gameSessionActive(test: $test, questions: $questions, currentQuestionIndex: $currentQuestionIndex, currentResult: $currentResult, questionResults: $questionResults, isAnswerSubmitted: $isAnswerSubmitted, isCorrect: $isCorrect, answerFeedbackDelay: $answerFeedbackDelay)';
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +541,7 @@ abstract mixin class _$GameSessionActiveCopyWith<$Res> implements $TestsStateCop
|
|||
factory _$GameSessionActiveCopyWith(_GameSessionActive value, $Res Function(_GameSessionActive) _then) = __$GameSessionActiveCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
TestDto test, List<GameQuestion> questions, int currentQuestionIndex, GameSessionResult? currentResult, Map<String, QuestionResult> questionResults, bool isAnswerSubmitted, bool isCorrect, Duration? answerFeedbackDelay
|
||||
TestDto test, List<GameQuestion> questions, int currentQuestionIndex, GameSessionResult? currentResult, Map<String, List<QuestionResult>> questionResults, bool isAnswerSubmitted, bool isCorrect, Duration? answerFeedbackDelay
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -529,7 +565,7 @@ as TestDto,questions: null == questions ? _self._questions : questions // ignore
|
|||
as List<GameQuestion>,currentQuestionIndex: null == currentQuestionIndex ? _self.currentQuestionIndex : currentQuestionIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int,currentResult: freezed == currentResult ? _self.currentResult : currentResult // ignore: cast_nullable_to_non_nullable
|
||||
as GameSessionResult?,questionResults: null == questionResults ? _self._questionResults : questionResults // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, QuestionResult>,isAnswerSubmitted: null == isAnswerSubmitted ? _self.isAnswerSubmitted : isAnswerSubmitted // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, List<QuestionResult>>,isAnswerSubmitted: null == isAnswerSubmitted ? _self.isAnswerSubmitted : isAnswerSubmitted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isCorrect: null == isCorrect ? _self.isCorrect : isCorrect // ignore: cast_nullable_to_non_nullable
|
||||
as bool,answerFeedbackDelay: freezed == answerFeedbackDelay ? _self.answerFeedbackDelay : answerFeedbackDelay // ignore: cast_nullable_to_non_nullable
|
||||
as Duration?,
|
||||
|
|
@ -554,7 +590,7 @@ $GameSessionResultCopyWith<$Res>? get currentResult {
|
|||
/// @nodoc
|
||||
|
||||
|
||||
class _GameSessionCompleted implements TestsState {
|
||||
class _GameSessionCompleted with DiagnosticableTreeMixin implements TestsState {
|
||||
const _GameSessionCompleted({required this.test, required this.result});
|
||||
|
||||
|
||||
|
|
@ -568,6 +604,12 @@ class _GameSessionCompleted implements TestsState {
|
|||
_$GameSessionCompletedCopyWith<_GameSessionCompleted> get copyWith => __$GameSessionCompletedCopyWithImpl<_GameSessionCompleted>(this, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'TestsState.gameSessionCompleted'))
|
||||
..add(DiagnosticsProperty('test', test))..add(DiagnosticsProperty('result', result));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
|
|
@ -579,7 +621,7 @@ bool operator ==(Object other) {
|
|||
int get hashCode => Object.hash(runtimeType,test,result);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'TestsState.gameSessionCompleted(test: $test, result: $result)';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ class _GamePageState extends State<GamePage> {
|
|||
int currentQuestionIndex,
|
||||
bool isAnswerSubmitted,
|
||||
bool isCorrect,
|
||||
Map<String, QuestionResult> questionResults,
|
||||
Map<String, List<QuestionResult>> questionResults,
|
||||
Duration sessionElapsed,
|
||||
) {
|
||||
final theme = Theme.of(context);
|
||||
|
|
@ -296,6 +296,7 @@ class _GamePageState extends State<GamePage> {
|
|||
currentQuestion: currentQuestionIndex,
|
||||
totalQuestions: questions.length,
|
||||
correctAnswers: questionResults.values
|
||||
.expand((results) => results)
|
||||
.where((r) => r.isCorrect)
|
||||
.length,
|
||||
timeElapsed: sessionElapsed,
|
||||
|
|
@ -889,11 +890,11 @@ class _GamePageState extends State<GamePage> {
|
|||
|
||||
String? _getSelectedAnswerForMultipleChoice(
|
||||
MultipleChoiceQuestion question,
|
||||
Map<String, QuestionResult> questionResults,
|
||||
Map<String, List<QuestionResult>> questionResults,
|
||||
) {
|
||||
final questionId = question.id;
|
||||
final result = questionResults[questionId];
|
||||
return result?.selectedAnswer;
|
||||
final results = questionResults[questionId];
|
||||
return results?.isNotEmpty == true ? results!.first.selectedAnswer : null;
|
||||
}
|
||||
|
||||
Color _scoreColor(ColorScheme colorScheme, int score) {
|
||||
|
|
|
|||
|
|
@ -127,54 +127,72 @@ final mockMatrixQuestion = MatrixQuestion(
|
|||
image: 'https://via.placeholder.com/100x100?text=1',
|
||||
original: 'Apple',
|
||||
translation: 'Яблоко',
|
||||
originalBack: 'Apple',
|
||||
translationBack: 'Яблоко',
|
||||
),
|
||||
MatrixCard(
|
||||
id: 'card2',
|
||||
image: 'https://via.placeholder.com/100x100?text=2',
|
||||
original: 'Orange',
|
||||
translation: 'Апельсин',
|
||||
originalBack: 'Orange',
|
||||
translationBack: 'Апельсин',
|
||||
),
|
||||
MatrixCard(
|
||||
id: 'card3',
|
||||
image: 'https://via.placeholder.com/100x100?text=3',
|
||||
original: 'Banana',
|
||||
translation: 'Банан',
|
||||
originalBack: 'Banana',
|
||||
translationBack: 'Банан',
|
||||
),
|
||||
MatrixCard(
|
||||
id: 'card4',
|
||||
image: 'https://via.placeholder.com/100x100?text=4',
|
||||
original: 'Grape',
|
||||
translation: 'Виноград',
|
||||
originalBack: 'Grape',
|
||||
translationBack: 'Виноград',
|
||||
),
|
||||
MatrixCard(
|
||||
id: 'card5',
|
||||
image: 'https://via.placeholder.com/100x100?text=5',
|
||||
original: 'Cherry',
|
||||
translation: 'Вишня',
|
||||
originalBack: 'Cherry',
|
||||
translationBack: 'Вишня',
|
||||
),
|
||||
MatrixCard(
|
||||
id: 'card6',
|
||||
image: 'https://via.placeholder.com/100x100?text=6',
|
||||
original: 'Strawberry',
|
||||
translation: 'Клубника',
|
||||
originalBack: 'Strawberry',
|
||||
translationBack: 'Клубника',
|
||||
),
|
||||
MatrixCard(
|
||||
id: 'card7',
|
||||
image: 'https://via.placeholder.com/100x100?text=7',
|
||||
original: 'Watermelon',
|
||||
translation: 'Арбуз',
|
||||
originalBack: 'Watermelon',
|
||||
translationBack: 'Арбуз',
|
||||
),
|
||||
MatrixCard(
|
||||
id: 'card8',
|
||||
image: 'https://via.placeholder.com/100x100?text=8',
|
||||
original: 'Pineapple',
|
||||
translation: 'Ананас',
|
||||
originalBack: 'Pineapple',
|
||||
translationBack: 'Ананас',
|
||||
),
|
||||
MatrixCard(
|
||||
id: 'card9',
|
||||
image: 'https://via.placeholder.com/100x100?text=9',
|
||||
original: 'Mango',
|
||||
translation: 'Манго',
|
||||
originalBack: 'Mango',
|
||||
translationBack: 'Манго',
|
||||
),
|
||||
],
|
||||
stages: [
|
||||
|
|
@ -194,6 +212,6 @@ final mockMatrixQuestion = MatrixQuestion(
|
|||
targetAudio: null,
|
||||
),
|
||||
],
|
||||
word: 'apple',
|
||||
initialTargetWord: 'Apple',
|
||||
type: 'matrix',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -547,49 +547,7 @@ class _MatrixFlipCard extends StatelessWidget {
|
|||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(21.r),
|
||||
child: isBack
|
||||
? Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.identity()..rotateY(math.pi),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24.w),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (card.original != null)
|
||||
Text(
|
||||
card.original!,
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (card.original != null &&
|
||||
card.translation != null)
|
||||
SizedBox(height: 12.h),
|
||||
if (card.translation != null)
|
||||
Text(
|
||||
card.translation!,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: colorScheme.onSurface.withOpacity(
|
||||
0.5,
|
||||
),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
? _buildCardBack(card, colorScheme)
|
||||
: _buildCardFront(card, colorScheme),
|
||||
),
|
||||
),
|
||||
|
|
@ -600,7 +558,9 @@ class _MatrixFlipCard extends StatelessWidget {
|
|||
}
|
||||
|
||||
Widget _buildCardFront(MatrixCard card, ColorScheme colorScheme) {
|
||||
final hasImage = card.image != null && card.image!.isNotEmpty;
|
||||
// Front side: use only front fields
|
||||
final frontImage = card.imageUrl ?? card.image;
|
||||
final hasImage = frontImage != null && frontImage.isNotEmpty;
|
||||
final hasOriginal = card.original != null && card.original!.isNotEmpty;
|
||||
final hasTranslation =
|
||||
card.translation != null && card.translation!.isNotEmpty;
|
||||
|
|
@ -648,7 +608,7 @@ class _MatrixFlipCard extends StatelessWidget {
|
|||
// If only image (no text), show image full size
|
||||
if (hasImage && !hasText) {
|
||||
return Image.network(
|
||||
card.image!,
|
||||
frontImage!,
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
|
|
@ -701,7 +661,7 @@ class _MatrixFlipCard extends StatelessWidget {
|
|||
),
|
||||
Expanded(
|
||||
child: Image.network(
|
||||
card.image!,
|
||||
frontImage!,
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
|
|
@ -746,4 +706,68 @@ class _MatrixFlipCard extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCardBack(MatrixCard card, ColorScheme colorScheme) {
|
||||
// Back side: use only back fields
|
||||
final backImage = card.imageBackUrl ?? card.imageBack;
|
||||
final hasImage = backImage != null && backImage.isNotEmpty;
|
||||
final hasOriginal = card.originalBack != null && card.originalBack!.isNotEmpty;
|
||||
final hasTranslation =
|
||||
card.translationBack != null && card.translationBack!.isNotEmpty;
|
||||
final hasText = hasOriginal || hasTranslation;
|
||||
|
||||
return Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.identity()..rotateY(math.pi),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24.w),
|
||||
child: hasImage && !hasText
|
||||
? Image.network(
|
||||
backImage!,
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Center(
|
||||
child: Icon(
|
||||
Icons.image_not_supported,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (hasOriginal)
|
||||
Text(
|
||||
card.originalBack!,
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (hasOriginal && hasTranslation) SizedBox(height: 12.h),
|
||||
if (hasTranslation)
|
||||
Text(
|
||||
card.translationBack!,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: colorScheme.onSurface.withOpacity(0.5),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue