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
|
// Generate stages for all cards
|
||||||
final stages = _generateStages(selected, questionType);
|
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,
|
// 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) {
|
final cards = selected.map((c) {
|
||||||
// Always include original and translation for back side
|
final frontImage = questionType.imageCards
|
||||||
// Include image if available (for image-based question types)
|
? (_imageIdToUrl(c.image) ?? c.image ?? c.id)
|
||||||
|
: null;
|
||||||
|
final frontImageUrl = frontImage != null && _isUuid(frontImage)
|
||||||
|
? null
|
||||||
|
: frontImage;
|
||||||
|
|
||||||
return MatrixCardDto(
|
return MatrixCardDto(
|
||||||
id: c.id,
|
id: c.id,
|
||||||
image: questionType.imageCards
|
// Front side fields
|
||||||
? (_imageIdToUrl(c.image) ?? c.image ?? c.id)
|
image: frontImage != null && _isUuid(frontImage) ? frontImage : null,
|
||||||
: null,
|
imageUrl: frontImageUrl,
|
||||||
original: c.original,
|
original: c.original,
|
||||||
translation: c.translation,
|
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();
|
}).toList();
|
||||||
|
|
||||||
|
|
@ -225,7 +237,8 @@ class MatrixQuestionGenerator implements QuestionGenerator {
|
||||||
stages: stages,
|
stages: stages,
|
||||||
// Backward compatibility fields
|
// Backward compatibility fields
|
||||||
word: firstStage.targetWord,
|
word: firstStage.targetWord,
|
||||||
answer: firstStage.targetCardId,
|
answer: firstStage
|
||||||
|
.targetCardId, // Опциональное, но заполняем для совместимости
|
||||||
text: 'Найди слово:',
|
text: 'Найди слово:',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -484,10 +484,14 @@ class TestManager {
|
||||||
.map(
|
.map(
|
||||||
(c) => <String, dynamic>{
|
(c) => <String, dynamic>{
|
||||||
'id': c.id,
|
'id': c.id,
|
||||||
// store image as objectId (MinIO) or filename
|
// Front side fields
|
||||||
'image': c.image,
|
'image': c.image,
|
||||||
'original': c.original,
|
'original': c.original,
|
||||||
'translation': c.translation,
|
'translation': c.translation,
|
||||||
|
// Back side fields - always include original and translation
|
||||||
|
'originalBack': c.original,
|
||||||
|
'translationBack': c.translation,
|
||||||
|
'imageBack': c.image,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
@ -503,16 +507,56 @@ class TestManager {
|
||||||
questionJson['matrixSize'] = resolvedSize;
|
questionJson['matrixSize'] = resolvedSize;
|
||||||
} else if ((questionJson['answer'] as String?)?.isEmpty != false) {
|
} else if ((questionJson['answer'] as String?)?.isEmpty != false) {
|
||||||
// If matrix cards exist but answer is missing, derive initial target
|
// If matrix cards exist but answer is missing, derive initial target
|
||||||
// from the first card.
|
// from the first card or from stages.
|
||||||
final first = currentButtons.firstOrNull;
|
final stages = (uiDataForResponse['stages'] as List<dynamic>?) ?? [];
|
||||||
if (first is Map<String, dynamic>) {
|
if (stages.isNotEmpty) {
|
||||||
final id = first['id']?.toString();
|
// Извлекаем из stages[0]
|
||||||
final original = first['original']?.toString();
|
final firstStage = stages.first;
|
||||||
if (id != null && id.isNotEmpty) {
|
if (firstStage is Map<String, dynamic>) {
|
||||||
questionJson['answer'] = id;
|
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) {
|
} else {
|
||||||
questionJson['word'] = original;
|
// 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
|
// All images now use the same cardImagesBucket
|
||||||
final updatedButtons = await Future.wait(
|
final updatedButtons = await Future.wait(
|
||||||
(questionJson['buttons'] as List<dynamic>? ?? []).map((button) async {
|
(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 buttonMap = Map<String, dynamic>.from(button);
|
||||||
final imageValue = buttonMap['image']?.toString();
|
|
||||||
|
|
||||||
// Convert image to presigned URL
|
// Convert front image to presigned URL
|
||||||
final imageUrl = await _imageValueToApiUrl(
|
if (buttonMap['image'] != null) {
|
||||||
imageValue,
|
final imageValue = buttonMap['image']?.toString();
|
||||||
packId: packId,
|
final imageUrl = await _imageValueToApiUrl(
|
||||||
);
|
imageValue,
|
||||||
|
packId: packId,
|
||||||
if (imageUrl != null) {
|
);
|
||||||
buttonMap['imageUrl'] = imageUrl;
|
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 buttonMap;
|
||||||
}
|
}
|
||||||
return button;
|
return button;
|
||||||
|
|
@ -834,8 +892,8 @@ class TestManager {
|
||||||
final questionJson = question.toJson();
|
final questionJson = question.toJson();
|
||||||
|
|
||||||
// Extract key fields
|
// Extract key fields
|
||||||
final word = questionJson['word'] as String? ?? '';
|
var word = questionJson['word'] as String? ?? '';
|
||||||
final answer = questionJson['answer'] as String? ?? '';
|
var answer = questionJson['answer'] as String? ?? '';
|
||||||
final buttons = questionJson['buttons'] as List<dynamic>? ?? [];
|
final buttons = questionJson['buttons'] as List<dynamic>? ?? [];
|
||||||
|
|
||||||
// UI data (image, text, audio, template)
|
// UI data (image, text, audio, template)
|
||||||
|
|
@ -852,6 +910,29 @@ class TestManager {
|
||||||
if (questionJson['stages'] != null)
|
if (questionJson['stages'] != null)
|
||||||
uiData['stages'] = questionJson['stages'];
|
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(
|
final questionCompanion = TestQuestionsCompanion.insert(
|
||||||
testId: testId,
|
testId: testId,
|
||||||
orderIndex: drift.Value(orderIndex++),
|
orderIndex: drift.Value(orderIndex++),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class MatrixTestQuestionBody extends AbstractTestQuestion {
|
||||||
/// DEPRECATED: Use stages[0].targetCardId instead.
|
/// DEPRECATED: Use stages[0].targetCardId instead.
|
||||||
/// Kept for backward compatibility.
|
/// Kept for backward compatibility.
|
||||||
@JsonKey(defaultValue: '')
|
@JsonKey(defaultValue: '')
|
||||||
final String answer;
|
final String? answer;
|
||||||
|
|
||||||
/// Question text (e.g., "Найди слово:")
|
/// Question text (e.g., "Найди слово:")
|
||||||
final String? text;
|
final String? text;
|
||||||
|
|
@ -40,7 +40,7 @@ class MatrixTestQuestionBody extends AbstractTestQuestion {
|
||||||
required this.matrixSize,
|
required this.matrixSize,
|
||||||
required this.cards,
|
required this.cards,
|
||||||
this.stages = const [],
|
this.stages = const [],
|
||||||
required this.answer,
|
this.answer,
|
||||||
required super.word,
|
required super.word,
|
||||||
this.text,
|
this.text,
|
||||||
super.questionType = TestQuestionType.matrix,
|
super.questionType = TestQuestionType.matrix,
|
||||||
|
|
@ -80,6 +80,10 @@ class MatrixCardDto {
|
||||||
final String? imageUrl; // Presigned URL (for display)
|
final String? imageUrl; // Presigned URL (for display)
|
||||||
final String? original;
|
final String? original;
|
||||||
final String? translation;
|
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({
|
const MatrixCardDto({
|
||||||
required this.id,
|
required this.id,
|
||||||
|
|
@ -87,6 +91,10 @@ class MatrixCardDto {
|
||||||
this.imageUrl,
|
this.imageUrl,
|
||||||
this.original,
|
this.original,
|
||||||
this.translation,
|
this.translation,
|
||||||
|
this.originalBack,
|
||||||
|
this.translationBack,
|
||||||
|
this.imageBack,
|
||||||
|
this.imageBackUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory MatrixCardDto.fromJson(Map<String, dynamic> json) =>
|
factory MatrixCardDto.fromJson(Map<String, dynamic> json) =>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ abstract class _$MatrixTestQuestionBodyCWProxy {
|
||||||
|
|
||||||
MatrixTestQuestionBody stages(List<MatrixStageDto> stages);
|
MatrixTestQuestionBody stages(List<MatrixStageDto> stages);
|
||||||
|
|
||||||
MatrixTestQuestionBody answer(String answer);
|
MatrixTestQuestionBody answer(String? answer);
|
||||||
|
|
||||||
MatrixTestQuestionBody word(String word);
|
MatrixTestQuestionBody word(String word);
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ abstract class _$MatrixTestQuestionBodyCWProxy {
|
||||||
int matrixSize,
|
int matrixSize,
|
||||||
List<MatrixCardDto> cards,
|
List<MatrixCardDto> cards,
|
||||||
List<MatrixStageDto> stages,
|
List<MatrixStageDto> stages,
|
||||||
String answer,
|
String? answer,
|
||||||
String word,
|
String word,
|
||||||
String? text,
|
String? text,
|
||||||
TestQuestionType questionType,
|
TestQuestionType questionType,
|
||||||
|
|
@ -65,7 +65,7 @@ class _$MatrixTestQuestionBodyCWProxyImpl
|
||||||
call(stages: stages);
|
call(stages: stages);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MatrixTestQuestionBody answer(String answer) => call(answer: answer);
|
MatrixTestQuestionBody answer(String? answer) => call(answer: answer);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MatrixTestQuestionBody word(String word) => call(word: word);
|
MatrixTestQuestionBody word(String word) => call(word: word);
|
||||||
|
|
@ -113,10 +113,10 @@ class _$MatrixTestQuestionBodyCWProxyImpl
|
||||||
? _value.stages
|
? _value.stages
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: stages as List<MatrixStageDto>,
|
: stages as List<MatrixStageDto>,
|
||||||
answer: answer == const $CopyWithPlaceholder() || answer == null
|
answer: answer == const $CopyWithPlaceholder()
|
||||||
? _value.answer
|
? _value.answer
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: answer as String,
|
: answer as String?,
|
||||||
word: word == const $CopyWithPlaceholder() || word == null
|
word: word == const $CopyWithPlaceholder() || word == null
|
||||||
? _value.word
|
? _value.word
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -231,6 +231,14 @@ abstract class _$MatrixCardDtoCWProxy {
|
||||||
|
|
||||||
MatrixCardDto translation(String? translation);
|
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.
|
/// 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)`.
|
/// 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? imageUrl,
|
||||||
String? original,
|
String? original,
|
||||||
String? translation,
|
String? translation,
|
||||||
|
String? originalBack,
|
||||||
|
String? translationBack,
|
||||||
|
String? imageBack,
|
||||||
|
String? imageBackUrl,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,6 +282,21 @@ class _$MatrixCardDtoCWProxyImpl implements _$MatrixCardDtoCWProxy {
|
||||||
MatrixCardDto translation(String? translation) =>
|
MatrixCardDto translation(String? translation) =>
|
||||||
call(translation: 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
|
@override
|
||||||
/// Creates a new instance with the provided field values.
|
/// 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)`.
|
/// 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? imageUrl = const $CopyWithPlaceholder(),
|
||||||
Object? original = const $CopyWithPlaceholder(),
|
Object? original = const $CopyWithPlaceholder(),
|
||||||
Object? translation = const $CopyWithPlaceholder(),
|
Object? translation = const $CopyWithPlaceholder(),
|
||||||
|
Object? originalBack = const $CopyWithPlaceholder(),
|
||||||
|
Object? translationBack = const $CopyWithPlaceholder(),
|
||||||
|
Object? imageBack = const $CopyWithPlaceholder(),
|
||||||
|
Object? imageBackUrl = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return MatrixCardDto(
|
return MatrixCardDto(
|
||||||
id: id == const $CopyWithPlaceholder() || id == null
|
id: id == const $CopyWithPlaceholder() || id == null
|
||||||
|
|
@ -306,6 +337,22 @@ class _$MatrixCardDtoCWProxyImpl implements _$MatrixCardDtoCWProxy {
|
||||||
? _value.translation
|
? _value.translation
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: translation as String?,
|
: 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,
|
'matrixSize': instance.matrixSize,
|
||||||
'buttons': instance.cards.map((e) => e.toJson()).toList(),
|
'buttons': instance.cards.map((e) => e.toJson()).toList(),
|
||||||
'stages': instance.stages.map((e) => e.toJson()).toList(),
|
'stages': instance.stages.map((e) => e.toJson()).toList(),
|
||||||
'answer': instance.answer,
|
'answer': ?instance.answer,
|
||||||
'text': ?instance.text,
|
'text': ?instance.text,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -386,6 +433,10 @@ MatrixCardDto _$MatrixCardDtoFromJson(Map<String, dynamic> json) =>
|
||||||
imageUrl: json['imageUrl'] as String?,
|
imageUrl: json['imageUrl'] as String?,
|
||||||
original: json['original'] as String?,
|
original: json['original'] as String?,
|
||||||
translation: json['translation'] 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) =>
|
Map<String, dynamic> _$MatrixCardDtoToJson(MatrixCardDto instance) =>
|
||||||
|
|
@ -395,4 +446,8 @@ Map<String, dynamic> _$MatrixCardDtoToJson(MatrixCardDto instance) =>
|
||||||
'imageUrl': instance.imageUrl,
|
'imageUrl': instance.imageUrl,
|
||||||
'original': instance.original,
|
'original': instance.original,
|
||||||
'translation': instance.translation,
|
'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 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
|
/// the chosen card flips and a new target is shown from stages array until
|
||||||
/// all cards are processed.
|
/// 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
|
@freezed
|
||||||
abstract class MatrixQuestion with _$MatrixQuestion {
|
abstract class MatrixQuestion with _$MatrixQuestion {
|
||||||
const factory 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. Kept for backward compatibility.
|
||||||
@Deprecated('Use stages[0] instead') String? initialTargetCardId,
|
@Deprecated('Use stages[0] instead') String? initialTargetCardId,
|
||||||
@Deprecated('Use stages[0] instead') String? initialTargetWord,
|
@Deprecated('Use stages[0] instead') String? initialTargetWord,
|
||||||
required String word,
|
|
||||||
String? text,
|
String? text,
|
||||||
@Default('matrix') String type,
|
@Default('matrix') String type,
|
||||||
}) = _MatrixQuestion;
|
}) = _MatrixQuestion;
|
||||||
|
|
@ -157,8 +159,13 @@ abstract class MatrixCard with _$MatrixCard {
|
||||||
const factory MatrixCard({
|
const factory MatrixCard({
|
||||||
required String id,
|
required String id,
|
||||||
String? image,
|
String? image,
|
||||||
|
String? imageUrl,
|
||||||
String? original,
|
String? original,
|
||||||
String? translation,
|
String? translation,
|
||||||
|
String? originalBack,
|
||||||
|
String? translationBack,
|
||||||
|
String? imageBack,
|
||||||
|
String? imageBackUrl,
|
||||||
}) = _MatrixCard;
|
}) = _MatrixCard;
|
||||||
|
|
||||||
factory MatrixCard.fromJson(Map<String, dynamic> json) =>
|
factory MatrixCard.fromJson(Map<String, dynamic> json) =>
|
||||||
|
|
|
||||||
|
|
@ -2268,7 +2268,7 @@ as String,
|
||||||
mixin _$MatrixQuestion {
|
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.
|
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
|
/// Create a copy of MatrixQuestion
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
@ -2281,16 +2281,16 @@ $MatrixQuestionCopyWith<MatrixQuestion> get copyWith => _$MatrixQuestionCopyWith
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
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)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@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
|
@override
|
||||||
String toString() {
|
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;
|
factory $MatrixQuestionCopyWith(MatrixQuestion value, $Res Function(MatrixQuestion) _then) = _$MatrixQuestionCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$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
|
/// Create a copy of MatrixQuestion
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// 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(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
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
|
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<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 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?,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?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
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) {
|
switch (_that) {
|
||||||
case _MatrixQuestion() when $default != null:
|
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();
|
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) {
|
switch (_that) {
|
||||||
case _MatrixQuestion():
|
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');
|
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) {
|
switch (_that) {
|
||||||
case _MatrixQuestion() when $default != null:
|
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;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -2470,7 +2469,7 @@ return $default(_that.id,_that.matrixSize,_that.cards,_that.stages,_that.initial
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _MatrixQuestion implements MatrixQuestion {
|
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);
|
factory _MatrixQuestion.fromJson(Map<String, dynamic> json) => _$MatrixQuestionFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
|
|
@ -2492,7 +2491,6 @@ class _MatrixQuestion implements MatrixQuestion {
|
||||||
// DEPRECATED: Use stages[0] instead. Kept for backward compatibility.
|
// 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? initialTargetCardId;
|
||||||
@override@Deprecated('Use stages[0] instead') final String? initialTargetWord;
|
@override@Deprecated('Use stages[0] instead') final String? initialTargetWord;
|
||||||
@override final String word;
|
|
||||||
@override final String? text;
|
@override final String? text;
|
||||||
@override@JsonKey() final String type;
|
@override@JsonKey() final String type;
|
||||||
|
|
||||||
|
|
@ -2509,16 +2507,16 @@ Map<String, dynamic> toJson() {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
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)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@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
|
@override
|
||||||
String toString() {
|
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;
|
factory _$MatrixQuestionCopyWith(_MatrixQuestion value, $Res Function(_MatrixQuestion) _then) = __$MatrixQuestionCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$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
|
/// Create a copy of MatrixQuestion
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// 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(
|
return _then(_MatrixQuestion(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
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
|
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<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 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?,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?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
));
|
));
|
||||||
|
|
@ -2837,7 +2834,7 @@ as String?,
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$MatrixCard {
|
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
|
/// Create a copy of MatrixCard
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
@ -2850,16 +2847,16 @@ $MatrixCardCopyWith<MatrixCard> get copyWith => _$MatrixCardCopyWithImpl<MatrixC
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
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)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@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
|
@override
|
||||||
String toString() {
|
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;
|
factory $MatrixCardCopyWith(MatrixCard value, $Res Function(MatrixCard) _then) = _$MatrixCardCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$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
|
/// Create a copy of MatrixCard
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// 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(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
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,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?,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?,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?,
|
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) {
|
switch (_that) {
|
||||||
case _MatrixCard() when $default != null:
|
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();
|
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) {
|
switch (_that) {
|
||||||
case _MatrixCard():
|
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');
|
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) {
|
switch (_that) {
|
||||||
case _MatrixCard() when $default != null:
|
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;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -3034,13 +3036,18 @@ return $default(_that.id,_that.image,_that.original,_that.translation);case _:
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _MatrixCard implements MatrixCard {
|
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);
|
factory _MatrixCard.fromJson(Map<String, dynamic> json) => _$MatrixCardFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@override final String? image;
|
@override final String? image;
|
||||||
|
@override final String? imageUrl;
|
||||||
@override final String? original;
|
@override final String? original;
|
||||||
@override final String? translation;
|
@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
|
/// Create a copy of MatrixCard
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|
@ -3055,16 +3062,16 @@ Map<String, dynamic> toJson() {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
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)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@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
|
@override
|
||||||
String toString() {
|
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;
|
factory _$MatrixCardCopyWith(_MatrixCard value, $Res Function(_MatrixCard) _then) = __$MatrixCardCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$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
|
/// Create a copy of MatrixCard
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// 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(
|
return _then(_MatrixCard(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
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,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?,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?,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?,
|
as String?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,6 @@ _MatrixQuestion _$MatrixQuestionFromJson(Map<String, dynamic> json) =>
|
||||||
const [],
|
const [],
|
||||||
initialTargetCardId: json['initialTargetCardId'] as String?,
|
initialTargetCardId: json['initialTargetCardId'] as String?,
|
||||||
initialTargetWord: json['initialTargetWord'] as String?,
|
initialTargetWord: json['initialTargetWord'] as String?,
|
||||||
word: json['word'] as String,
|
|
||||||
text: json['text'] as String?,
|
text: json['text'] as String?,
|
||||||
type: json['type'] as String? ?? 'matrix',
|
type: json['type'] as String? ?? 'matrix',
|
||||||
);
|
);
|
||||||
|
|
@ -218,7 +217,6 @@ Map<String, dynamic> _$MatrixQuestionToJson(_MatrixQuestion instance) =>
|
||||||
'stages': instance.stages,
|
'stages': instance.stages,
|
||||||
'initialTargetCardId': instance.initialTargetCardId,
|
'initialTargetCardId': instance.initialTargetCardId,
|
||||||
'initialTargetWord': instance.initialTargetWord,
|
'initialTargetWord': instance.initialTargetWord,
|
||||||
'word': instance.word,
|
|
||||||
'text': instance.text,
|
'text': instance.text,
|
||||||
'type': instance.type,
|
'type': instance.type,
|
||||||
};
|
};
|
||||||
|
|
@ -239,16 +237,26 @@ Map<String, dynamic> _$MatrixStageToJson(_MatrixStage instance) =>
|
||||||
_MatrixCard _$MatrixCardFromJson(Map<String, dynamic> json) => _MatrixCard(
|
_MatrixCard _$MatrixCardFromJson(Map<String, dynamic> json) => _MatrixCard(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
image: json['image'] as String?,
|
image: json['image'] as String?,
|
||||||
|
imageUrl: json['imageUrl'] as String?,
|
||||||
original: json['original'] as String?,
|
original: json['original'] as String?,
|
||||||
translation: json['translation'] 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) =>
|
Map<String, dynamic> _$MatrixCardToJson(_MatrixCard instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'image': instance.image,
|
'image': instance.image,
|
||||||
|
'imageUrl': instance.imageUrl,
|
||||||
'original': instance.original,
|
'original': instance.original,
|
||||||
'translation': instance.translation,
|
'translation': instance.translation,
|
||||||
|
'originalBack': instance.originalBack,
|
||||||
|
'translationBack': instance.translationBack,
|
||||||
|
'imageBack': instance.imageBack,
|
||||||
|
'imageBackUrl': instance.imageBackUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
_QuestionResult _$QuestionResultFromJson(Map<String, dynamic> json) =>
|
_QuestionResult _$QuestionResultFromJson(Map<String, dynamic> json) =>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ class GameSessionManager {
|
||||||
GameSessionManager();
|
GameSessionManager();
|
||||||
|
|
||||||
GameSessionResult? _currentResult;
|
GameSessionResult? _currentResult;
|
||||||
final Map<String, QuestionResult> _questionResults = {};
|
final Map<String, List<QuestionResult>> _questionResults = {};
|
||||||
Timer? _questionTimer;
|
Timer? _questionTimer;
|
||||||
DateTime? _sessionStartTime;
|
DateTime? _sessionStartTime;
|
||||||
DateTime? _currentQuestionStartTime;
|
DateTime? _currentQuestionStartTime;
|
||||||
|
|
@ -21,7 +21,7 @@ class GameSessionManager {
|
||||||
GameSessionResult? get currentResult => _currentResult;
|
GameSessionResult? get currentResult => _currentResult;
|
||||||
|
|
||||||
/// All question results for current session
|
/// All question results for current session
|
||||||
Map<String, QuestionResult> get questionResults =>
|
Map<String, List<QuestionResult>> get questionResults =>
|
||||||
Map.unmodifiable(_questionResults);
|
Map.unmodifiable(_questionResults);
|
||||||
|
|
||||||
/// Elapsed time since session start
|
/// Elapsed time since session start
|
||||||
|
|
@ -50,11 +50,53 @@ class GameSessionManager {
|
||||||
// Pre-populate question results with placeholders
|
// Pre-populate question results with placeholders
|
||||||
for (final question in questions) {
|
for (final question in questions) {
|
||||||
final questionId = _getQuestionId(question);
|
final questionId = _getQuestionId(question);
|
||||||
_questionResults[questionId] = QuestionResult(
|
|
||||||
questionId: questionId,
|
question.when(
|
||||||
word: _getQuestionWord(question),
|
multipleChoice: (_) {
|
||||||
isCorrect: false,
|
// Для обычных: список с одним элементом
|
||||||
timeSpent: Duration.zero,
|
_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
|
/// Submit answer for current question and move to next
|
||||||
void submitAnswer(String questionId, GameQuestion question, dynamic answer) {
|
void submitAnswer(String questionId, GameQuestion question, dynamic answer) {
|
||||||
final timeSpent = _calculateTimeSpent();
|
final timeSpent = _calculateTimeSpent();
|
||||||
final isCorrect = _validateAnswer(question, answer);
|
|
||||||
|
|
||||||
log(
|
log(
|
||||||
'Answer submitted for question $questionId: correct=$isCorrect, time=${timeSpent.inMilliseconds}ms',
|
'Answer submitted for question $questionId, time=${timeSpent.inMilliseconds}ms',
|
||||||
name: 'GameSessionManager',
|
name: 'GameSessionManager',
|
||||||
);
|
);
|
||||||
|
|
||||||
final selectedAnswer = answer is String ? answer : null;
|
// Для матричных вопросов создаем список результатов (по одному на stage)
|
||||||
final selectedAnswers = switch (answer) {
|
var isMatrixProcessed = false;
|
||||||
List<String> v => v,
|
question.when(
|
||||||
MatrixImageSelectAnswer v => v.correctCardIdsInOrder,
|
matrix: (q) {
|
||||||
_ => null,
|
if (answer is MatrixImageSelectAnswer) {
|
||||||
};
|
final results = <QuestionResult>[];
|
||||||
|
final timePerStageMs = timeSpent.inMilliseconds / q.stages.length;
|
||||||
final result = QuestionResult(
|
final timePerStage = Duration(milliseconds: timePerStageMs.round());
|
||||||
questionId: questionId,
|
|
||||||
word: _getQuestionWord(question),
|
for (int i = 0; i < q.stages.length; i++) {
|
||||||
isCorrect: isCorrect,
|
final stage = q.stages[i];
|
||||||
timeSpent: timeSpent,
|
final selectedCardId = answer.correctCardIdsInOrder[i];
|
||||||
selectedAnswer: selectedAnswer,
|
final isStageCorrect = selectedCardId == stage.targetCardId;
|
||||||
selectedAnswers: selectedAnswers,
|
|
||||||
answeredAt: DateTime.now(),
|
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();
|
_questionTimer?.cancel();
|
||||||
_currentQuestionStartTime = null;
|
_currentQuestionStartTime = null;
|
||||||
}
|
}
|
||||||
|
|
@ -108,14 +188,20 @@ class GameSessionManager {
|
||||||
/// Complete the current game session
|
/// Complete the current game session
|
||||||
GameSessionResult completeSession(String testId) {
|
GameSessionResult completeSession(String testId) {
|
||||||
final totalTime = _calculateSessionTime();
|
final totalTime = _calculateSessionTime();
|
||||||
final correctAnswers = _questionResults.values
|
|
||||||
|
// Все результаты из всех списков
|
||||||
|
final allResults = _questionResults.values
|
||||||
|
.expand((results) => results)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final correctAnswers = allResults
|
||||||
.where((r) => r.isCorrect)
|
.where((r) => r.isCorrect)
|
||||||
.length;
|
.length;
|
||||||
final totalQuestions = _questionResults.length;
|
final totalQuestions = allResults.length; // Количество stages
|
||||||
|
|
||||||
_currentResult = GameSessionResult(
|
_currentResult = GameSessionResult(
|
||||||
testId: testId,
|
testId: testId,
|
||||||
questionResults: _questionResults.values.toList(),
|
questionResults: allResults, // Плоский список
|
||||||
totalTime: totalTime,
|
totalTime: totalTime,
|
||||||
correctAnswers: correctAnswers,
|
correctAnswers: correctAnswers,
|
||||||
totalQuestions: totalQuestions,
|
totalQuestions: totalQuestions,
|
||||||
|
|
@ -136,21 +222,51 @@ class GameSessionManager {
|
||||||
log('Question skipped: $questionId', name: 'GameSessionManager');
|
log('Question skipped: $questionId', name: 'GameSessionManager');
|
||||||
|
|
||||||
final timeSpent = _calculateTimeSpent();
|
final timeSpent = _calculateTimeSpent();
|
||||||
final existingResult = _questionResults[questionId];
|
final results = _questionResults[questionId];
|
||||||
|
|
||||||
if (existingResult != null) {
|
if (results != null && results.isNotEmpty) {
|
||||||
_questionResults[questionId] = existingResult.copyWith(
|
final timePerResultMs = timeSpent.inMilliseconds / results.length;
|
||||||
timeSpent: existingResult.timeSpent + timeSpent,
|
final timePerResult = Duration(milliseconds: timePerResultMs.round());
|
||||||
);
|
_questionResults[questionId] = results
|
||||||
|
.map((r) => r.copyWith(timeSpent: r.timeSpent + timePerResult))
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
_questionTimer?.cancel();
|
_questionTimer?.cancel();
|
||||||
_currentQuestionStartTime = null;
|
_currentQuestionStartTime = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get result for specific question
|
/// Get result for specific question (returns first result for backward compatibility)
|
||||||
QuestionResult? getQuestionResult(String questionId) {
|
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
|
/// Check if session is active
|
||||||
|
|
@ -160,10 +276,14 @@ class GameSessionManager {
|
||||||
Map<String, dynamic> getSessionStats() {
|
Map<String, dynamic> getSessionStats() {
|
||||||
if (!isSessionActive) return {};
|
if (!isSessionActive) return {};
|
||||||
|
|
||||||
final answeredQuestions = _questionResults.values
|
final allResults = _questionResults.values
|
||||||
|
.expand((results) => results)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final answeredQuestions = allResults
|
||||||
.where((r) => r.answeredAt != null)
|
.where((r) => r.answeredAt != null)
|
||||||
.length;
|
.length;
|
||||||
final correctAnswers = _questionResults.values
|
final correctAnswers = allResults
|
||||||
.where((r) => r.isCorrect)
|
.where((r) => r.isCorrect)
|
||||||
.length;
|
.length;
|
||||||
final totalTime = _calculateSessionTime();
|
final totalTime = _calculateSessionTime();
|
||||||
|
|
@ -171,7 +291,7 @@ class GameSessionManager {
|
||||||
return {
|
return {
|
||||||
'answeredQuestions': answeredQuestions,
|
'answeredQuestions': answeredQuestions,
|
||||||
'correctAnswers': correctAnswers,
|
'correctAnswers': correctAnswers,
|
||||||
'totalQuestions': _questionResults.length,
|
'totalQuestions': allResults.length, // Количество stages
|
||||||
'accuracy': answeredQuestions > 0
|
'accuracy': answeredQuestions > 0
|
||||||
? correctAnswers / answeredQuestions
|
? correctAnswers / answeredQuestions
|
||||||
: 0.0,
|
: 0.0,
|
||||||
|
|
@ -206,7 +326,7 @@ class GameSessionManager {
|
||||||
multipleChoice: (q) => q.word,
|
multipleChoice: (q) => q.word,
|
||||||
inputLetters: (q) => q.word,
|
inputLetters: (q) => q.word,
|
||||||
match: (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
|
// attempts. Progression to the next question is handled by
|
||||||
// TestsStateManager (it should advance even if isCorrect == false).
|
// TestsStateManager (it should advance even if isCorrect == false).
|
||||||
if (answer is MatrixImageSelectAnswer) {
|
if (answer is MatrixImageSelectAnswer) {
|
||||||
return answer.correctCardIdsInOrder.length == question.cards.length &&
|
// Проверяем, что все stages пройдены
|
||||||
answer.wrongAttempts == 0;
|
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.
|
// Backward/compat: if someone submits just the correct ids list.
|
||||||
if (answer is List<String>) {
|
if (answer is List<String>) {
|
||||||
return answer.length == question.cards.length;
|
return answer.length == question.stages.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class TestsState with _$TestsState {
|
||||||
required List<GameQuestion> questions,
|
required List<GameQuestion> questions,
|
||||||
required int currentQuestionIndex,
|
required int currentQuestionIndex,
|
||||||
required GameSessionResult? currentResult,
|
required GameSessionResult? currentResult,
|
||||||
required Map<String, QuestionResult> questionResults,
|
required Map<String, List<QuestionResult>> questionResults,
|
||||||
required bool isAnswerSubmitted,
|
required bool isAnswerSubmitted,
|
||||||
required bool isCorrect,
|
required bool isCorrect,
|
||||||
Duration? answerFeedbackDelay,
|
Duration? answerFeedbackDelay,
|
||||||
|
|
@ -168,8 +168,13 @@ class TestsStateManager extends StateManager<TestsState> {
|
||||||
// Submit answer to session manager
|
// Submit answer to session manager
|
||||||
_gameSessionManager.submitAnswer(questionId, currentQuestion, answer);
|
_gameSessionManager.submitAnswer(questionId, currentQuestion, answer);
|
||||||
|
|
||||||
final isCorrect =
|
// Для матричных вопросов проверяем все stages, для остальных - первый результат
|
||||||
_gameSessionManager.getQuestionResult(questionId)?.isCorrect ?? false;
|
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
|
// Play sound/haptics based on settings
|
||||||
if (_settings.enableSounds) {
|
if (_settings.enableSounds) {
|
||||||
|
|
@ -246,8 +251,8 @@ class TestsStateManager extends StateManager<TestsState> {
|
||||||
final questionId = _getQuestionId(currentQuestion);
|
final questionId = _getQuestionId(currentQuestion);
|
||||||
|
|
||||||
_gameSessionManager.submitAnswer(questionId, currentQuestion, answer);
|
_gameSessionManager.submitAnswer(questionId, currentQuestion, answer);
|
||||||
final isCorrect =
|
// Матричный вопрос считается правильным, если все stages правильные
|
||||||
_gameSessionManager.getQuestionResult(questionId)?.isCorrect ?? false;
|
final isCorrect = _gameSessionManager.isMatrixQuestionCorrect(questionId);
|
||||||
|
|
||||||
// Completion feedback (always positive UX)
|
// Completion feedback (always positive UX)
|
||||||
if (_settings.enableSounds) {
|
if (_settings.enableSounds) {
|
||||||
|
|
@ -540,9 +545,16 @@ class TestsStateManager extends StateManager<TestsState> {
|
||||||
.map(
|
.map(
|
||||||
(c) => MatrixCard(
|
(c) => MatrixCard(
|
||||||
id: c.id,
|
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,
|
original: c.original,
|
||||||
translation: c.translation,
|
translation: c.translation,
|
||||||
|
// Back side fields
|
||||||
|
originalBack: c.originalBack,
|
||||||
|
translationBack: c.translationBack,
|
||||||
|
imageBack: c.imageBack,
|
||||||
|
imageBackUrl: c.imageBackUrl,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
@ -558,13 +570,12 @@ class TestsStateManager extends StateManager<TestsState> {
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// Use stages if available, otherwise fallback to deprecated fields
|
// Ensure stages are not empty
|
||||||
final initialTargetCardId = stages.isNotEmpty
|
assert(stages.isNotEmpty, 'Matrix question must have at least one stage');
|
||||||
? stages.first.targetCardId
|
|
||||||
: question.answer;
|
// Use stages for deprecated fields (for backward compatibility)
|
||||||
final initialTargetWord = stages.isNotEmpty
|
final initialTargetCardId = stages.first.targetCardId;
|
||||||
? stages.first.targetWord
|
final initialTargetWord = stages.first.targetWord;
|
||||||
: question.word;
|
|
||||||
|
|
||||||
questions.add(
|
questions.add(
|
||||||
GameQuestion.matrix(
|
GameQuestion.matrix(
|
||||||
|
|
@ -575,7 +586,6 @@ class TestsStateManager extends StateManager<TestsState> {
|
||||||
stages: stages,
|
stages: stages,
|
||||||
initialTargetCardId: initialTargetCardId,
|
initialTargetCardId: initialTargetCardId,
|
||||||
initialTargetWord: initialTargetWord,
|
initialTargetWord: initialTargetWord,
|
||||||
word: question.word,
|
|
||||||
text: question.text,
|
text: question.text,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,17 @@ part of 'tests_state_manager.dart';
|
||||||
// dart format off
|
// dart format off
|
||||||
T _$identity<T>(T value) => value;
|
T _$identity<T>(T value) => value;
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$TestsState {
|
mixin _$TestsState implements DiagnosticableTreeMixin {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
properties
|
||||||
|
..add(DiagnosticsProperty('type', 'TestsState'))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
|
|
@ -28,7 +34,7 @@ bool operator ==(Object other) {
|
||||||
int get hashCode => runtimeType.hashCode;
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||||
return 'TestsState()';
|
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) {
|
switch (_that) {
|
||||||
case _Loading() when loading != null:
|
case _Loading() when loading != null:
|
||||||
return loading();case _Loaded() when loaded != 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) {
|
switch (_that) {
|
||||||
case _Loading():
|
case _Loading():
|
||||||
return loading();case _Loaded():
|
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) {
|
switch (_that) {
|
||||||
case _Loading() when loading != null:
|
case _Loading() when loading != null:
|
||||||
return loading();case _Loaded() when loaded != null:
|
return loading();case _Loaded() when loaded != null:
|
||||||
|
|
@ -204,7 +210,7 @@ return gameSessionCompleted(_that.test,_that.result);case _:
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
class _Loading implements TestsState {
|
class _Loading with DiagnosticableTreeMixin implements TestsState {
|
||||||
const _Loading();
|
const _Loading();
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -212,6 +218,12 @@ class _Loading implements TestsState {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
properties
|
||||||
|
..add(DiagnosticsProperty('type', 'TestsState.loading'))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
|
|
@ -223,7 +235,7 @@ bool operator ==(Object other) {
|
||||||
int get hashCode => runtimeType.hashCode;
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||||
return 'TestsState.loading()';
|
return 'TestsState.loading()';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,7 +248,7 @@ String toString() {
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
class _Loaded implements TestsState {
|
class _Loaded with DiagnosticableTreeMixin implements TestsState {
|
||||||
const _Loaded({required final List<TestDto> tests, required this.packId}): _tests = tests;
|
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);
|
_$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
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
|
|
@ -267,7 +285,7 @@ bool operator ==(Object other) {
|
||||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_tests),packId);
|
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_tests),packId);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||||
return 'TestsState.loaded(tests: $tests, packId: $packId)';
|
return 'TestsState.loaded(tests: $tests, packId: $packId)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,7 +328,7 @@ as String,
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
class _Error implements TestsState {
|
class _Error with DiagnosticableTreeMixin implements TestsState {
|
||||||
const _Error(this.message);
|
const _Error(this.message);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -323,6 +341,12 @@ class _Error implements TestsState {
|
||||||
_$ErrorCopyWith<_Error> get copyWith => __$ErrorCopyWithImpl<_Error>(this, _$identity);
|
_$ErrorCopyWith<_Error> get copyWith => __$ErrorCopyWithImpl<_Error>(this, _$identity);
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
properties
|
||||||
|
..add(DiagnosticsProperty('type', 'TestsState.error'))
|
||||||
|
..add(DiagnosticsProperty('message', message));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
|
|
@ -334,7 +358,7 @@ bool operator ==(Object other) {
|
||||||
int get hashCode => Object.hash(runtimeType,message);
|
int get hashCode => Object.hash(runtimeType,message);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||||
return 'TestsState.error(message: $message)';
|
return 'TestsState.error(message: $message)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,7 +400,7 @@ as String,
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
class _GameSessionPreparing implements TestsState {
|
class _GameSessionPreparing with DiagnosticableTreeMixin implements TestsState {
|
||||||
const _GameSessionPreparing({required this.test, required final List<GameQuestion> questions}): _questions = questions;
|
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);
|
_$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
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
|
|
@ -407,7 +437,7 @@ bool operator ==(Object other) {
|
||||||
int get hashCode => Object.hash(runtimeType,test,const DeepCollectionEquality().hash(_questions));
|
int get hashCode => Object.hash(runtimeType,test,const DeepCollectionEquality().hash(_questions));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||||
return 'TestsState.gameSessionPreparing(test: $test, questions: $questions)';
|
return 'TestsState.gameSessionPreparing(test: $test, questions: $questions)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -450,8 +480,8 @@ as List<GameQuestion>,
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
class _GameSessionActive implements TestsState {
|
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, QuestionResult> questionResults, required this.isAnswerSubmitted, required this.isCorrect, this.answerFeedbackDelay}): _questions = questions,_questionResults = questionResults;
|
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;
|
final TestDto test;
|
||||||
|
|
@ -464,8 +494,8 @@ class _GameSessionActive implements TestsState {
|
||||||
|
|
||||||
final int currentQuestionIndex;
|
final int currentQuestionIndex;
|
||||||
final GameSessionResult? currentResult;
|
final GameSessionResult? currentResult;
|
||||||
final Map<String, QuestionResult> _questionResults;
|
final Map<String, List<QuestionResult>> _questionResults;
|
||||||
Map<String, QuestionResult> get questionResults {
|
Map<String, List<QuestionResult>> get questionResults {
|
||||||
if (_questionResults is EqualUnmodifiableMapView) return _questionResults;
|
if (_questionResults is EqualUnmodifiableMapView) return _questionResults;
|
||||||
// ignore: implicit_dynamic_type
|
// ignore: implicit_dynamic_type
|
||||||
return EqualUnmodifiableMapView(_questionResults);
|
return EqualUnmodifiableMapView(_questionResults);
|
||||||
|
|
@ -482,6 +512,12 @@ class _GameSessionActive implements TestsState {
|
||||||
_$GameSessionActiveCopyWith<_GameSessionActive> get copyWith => __$GameSessionActiveCopyWithImpl<_GameSessionActive>(this, _$identity);
|
_$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
|
@override
|
||||||
bool operator ==(Object other) {
|
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);
|
int get hashCode => Object.hash(runtimeType,test,const DeepCollectionEquality().hash(_questions),currentQuestionIndex,currentResult,const DeepCollectionEquality().hash(_questionResults),isAnswerSubmitted,isCorrect,answerFeedbackDelay);
|
||||||
|
|
||||||
@override
|
@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)';
|
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;
|
factory _$GameSessionActiveCopyWith(_GameSessionActive value, $Res Function(_GameSessionActive) _then) = __$GameSessionActiveCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$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 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 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 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,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 bool,answerFeedbackDelay: freezed == answerFeedbackDelay ? _self.answerFeedbackDelay : answerFeedbackDelay // ignore: cast_nullable_to_non_nullable
|
||||||
as Duration?,
|
as Duration?,
|
||||||
|
|
@ -554,7 +590,7 @@ $GameSessionResultCopyWith<$Res>? get currentResult {
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
class _GameSessionCompleted implements TestsState {
|
class _GameSessionCompleted with DiagnosticableTreeMixin implements TestsState {
|
||||||
const _GameSessionCompleted({required this.test, required this.result});
|
const _GameSessionCompleted({required this.test, required this.result});
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -568,6 +604,12 @@ class _GameSessionCompleted implements TestsState {
|
||||||
_$GameSessionCompletedCopyWith<_GameSessionCompleted> get copyWith => __$GameSessionCompletedCopyWithImpl<_GameSessionCompleted>(this, _$identity);
|
_$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
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
|
|
@ -579,7 +621,7 @@ bool operator ==(Object other) {
|
||||||
int get hashCode => Object.hash(runtimeType,test,result);
|
int get hashCode => Object.hash(runtimeType,test,result);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||||
return 'TestsState.gameSessionCompleted(test: $test, result: $result)';
|
return 'TestsState.gameSessionCompleted(test: $test, result: $result)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ class _GamePageState extends State<GamePage> {
|
||||||
int currentQuestionIndex,
|
int currentQuestionIndex,
|
||||||
bool isAnswerSubmitted,
|
bool isAnswerSubmitted,
|
||||||
bool isCorrect,
|
bool isCorrect,
|
||||||
Map<String, QuestionResult> questionResults,
|
Map<String, List<QuestionResult>> questionResults,
|
||||||
Duration sessionElapsed,
|
Duration sessionElapsed,
|
||||||
) {
|
) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
@ -296,6 +296,7 @@ class _GamePageState extends State<GamePage> {
|
||||||
currentQuestion: currentQuestionIndex,
|
currentQuestion: currentQuestionIndex,
|
||||||
totalQuestions: questions.length,
|
totalQuestions: questions.length,
|
||||||
correctAnswers: questionResults.values
|
correctAnswers: questionResults.values
|
||||||
|
.expand((results) => results)
|
||||||
.where((r) => r.isCorrect)
|
.where((r) => r.isCorrect)
|
||||||
.length,
|
.length,
|
||||||
timeElapsed: sessionElapsed,
|
timeElapsed: sessionElapsed,
|
||||||
|
|
@ -889,11 +890,11 @@ class _GamePageState extends State<GamePage> {
|
||||||
|
|
||||||
String? _getSelectedAnswerForMultipleChoice(
|
String? _getSelectedAnswerForMultipleChoice(
|
||||||
MultipleChoiceQuestion question,
|
MultipleChoiceQuestion question,
|
||||||
Map<String, QuestionResult> questionResults,
|
Map<String, List<QuestionResult>> questionResults,
|
||||||
) {
|
) {
|
||||||
final questionId = question.id;
|
final questionId = question.id;
|
||||||
final result = questionResults[questionId];
|
final results = questionResults[questionId];
|
||||||
return result?.selectedAnswer;
|
return results?.isNotEmpty == true ? results!.first.selectedAnswer : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _scoreColor(ColorScheme colorScheme, int score) {
|
Color _scoreColor(ColorScheme colorScheme, int score) {
|
||||||
|
|
|
||||||
|
|
@ -127,54 +127,72 @@ final mockMatrixQuestion = MatrixQuestion(
|
||||||
image: 'https://via.placeholder.com/100x100?text=1',
|
image: 'https://via.placeholder.com/100x100?text=1',
|
||||||
original: 'Apple',
|
original: 'Apple',
|
||||||
translation: 'Яблоко',
|
translation: 'Яблоко',
|
||||||
|
originalBack: 'Apple',
|
||||||
|
translationBack: 'Яблоко',
|
||||||
),
|
),
|
||||||
MatrixCard(
|
MatrixCard(
|
||||||
id: 'card2',
|
id: 'card2',
|
||||||
image: 'https://via.placeholder.com/100x100?text=2',
|
image: 'https://via.placeholder.com/100x100?text=2',
|
||||||
original: 'Orange',
|
original: 'Orange',
|
||||||
translation: 'Апельсин',
|
translation: 'Апельсин',
|
||||||
|
originalBack: 'Orange',
|
||||||
|
translationBack: 'Апельсин',
|
||||||
),
|
),
|
||||||
MatrixCard(
|
MatrixCard(
|
||||||
id: 'card3',
|
id: 'card3',
|
||||||
image: 'https://via.placeholder.com/100x100?text=3',
|
image: 'https://via.placeholder.com/100x100?text=3',
|
||||||
original: 'Banana',
|
original: 'Banana',
|
||||||
translation: 'Банан',
|
translation: 'Банан',
|
||||||
|
originalBack: 'Banana',
|
||||||
|
translationBack: 'Банан',
|
||||||
),
|
),
|
||||||
MatrixCard(
|
MatrixCard(
|
||||||
id: 'card4',
|
id: 'card4',
|
||||||
image: 'https://via.placeholder.com/100x100?text=4',
|
image: 'https://via.placeholder.com/100x100?text=4',
|
||||||
original: 'Grape',
|
original: 'Grape',
|
||||||
translation: 'Виноград',
|
translation: 'Виноград',
|
||||||
|
originalBack: 'Grape',
|
||||||
|
translationBack: 'Виноград',
|
||||||
),
|
),
|
||||||
MatrixCard(
|
MatrixCard(
|
||||||
id: 'card5',
|
id: 'card5',
|
||||||
image: 'https://via.placeholder.com/100x100?text=5',
|
image: 'https://via.placeholder.com/100x100?text=5',
|
||||||
original: 'Cherry',
|
original: 'Cherry',
|
||||||
translation: 'Вишня',
|
translation: 'Вишня',
|
||||||
|
originalBack: 'Cherry',
|
||||||
|
translationBack: 'Вишня',
|
||||||
),
|
),
|
||||||
MatrixCard(
|
MatrixCard(
|
||||||
id: 'card6',
|
id: 'card6',
|
||||||
image: 'https://via.placeholder.com/100x100?text=6',
|
image: 'https://via.placeholder.com/100x100?text=6',
|
||||||
original: 'Strawberry',
|
original: 'Strawberry',
|
||||||
translation: 'Клубника',
|
translation: 'Клубника',
|
||||||
|
originalBack: 'Strawberry',
|
||||||
|
translationBack: 'Клубника',
|
||||||
),
|
),
|
||||||
MatrixCard(
|
MatrixCard(
|
||||||
id: 'card7',
|
id: 'card7',
|
||||||
image: 'https://via.placeholder.com/100x100?text=7',
|
image: 'https://via.placeholder.com/100x100?text=7',
|
||||||
original: 'Watermelon',
|
original: 'Watermelon',
|
||||||
translation: 'Арбуз',
|
translation: 'Арбуз',
|
||||||
|
originalBack: 'Watermelon',
|
||||||
|
translationBack: 'Арбуз',
|
||||||
),
|
),
|
||||||
MatrixCard(
|
MatrixCard(
|
||||||
id: 'card8',
|
id: 'card8',
|
||||||
image: 'https://via.placeholder.com/100x100?text=8',
|
image: 'https://via.placeholder.com/100x100?text=8',
|
||||||
original: 'Pineapple',
|
original: 'Pineapple',
|
||||||
translation: 'Ананас',
|
translation: 'Ананас',
|
||||||
|
originalBack: 'Pineapple',
|
||||||
|
translationBack: 'Ананас',
|
||||||
),
|
),
|
||||||
MatrixCard(
|
MatrixCard(
|
||||||
id: 'card9',
|
id: 'card9',
|
||||||
image: 'https://via.placeholder.com/100x100?text=9',
|
image: 'https://via.placeholder.com/100x100?text=9',
|
||||||
original: 'Mango',
|
original: 'Mango',
|
||||||
translation: 'Манго',
|
translation: 'Манго',
|
||||||
|
originalBack: 'Mango',
|
||||||
|
translationBack: 'Манго',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
stages: [
|
stages: [
|
||||||
|
|
@ -194,6 +212,6 @@ final mockMatrixQuestion = MatrixQuestion(
|
||||||
targetAudio: null,
|
targetAudio: null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
word: 'apple',
|
initialTargetWord: 'Apple',
|
||||||
type: 'matrix',
|
type: 'matrix',
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -547,49 +547,7 @@ class _MatrixFlipCard extends StatelessWidget {
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(21.r),
|
borderRadius: BorderRadius.circular(21.r),
|
||||||
child: isBack
|
child: isBack
|
||||||
? Transform(
|
? _buildCardBack(card, colorScheme)
|
||||||
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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: _buildCardFront(card, colorScheme),
|
: _buildCardFront(card, colorScheme),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -600,7 +558,9 @@ class _MatrixFlipCard extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCardFront(MatrixCard card, ColorScheme colorScheme) {
|
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 hasOriginal = card.original != null && card.original!.isNotEmpty;
|
||||||
final hasTranslation =
|
final hasTranslation =
|
||||||
card.translation != null && card.translation!.isNotEmpty;
|
card.translation != null && card.translation!.isNotEmpty;
|
||||||
|
|
@ -648,7 +608,7 @@ class _MatrixFlipCard extends StatelessWidget {
|
||||||
// If only image (no text), show image full size
|
// If only image (no text), show image full size
|
||||||
if (hasImage && !hasText) {
|
if (hasImage && !hasText) {
|
||||||
return Image.network(
|
return Image.network(
|
||||||
card.image!,
|
frontImage!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
errorBuilder: (context, error, stackTrace) {
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
|
@ -701,7 +661,7 @@ class _MatrixFlipCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
card.image!,
|
frontImage!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
errorBuilder: (context, error, stackTrace) {
|
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