fix
Some checks are pending
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

This commit is contained in:
Dmitry 2026-01-10 20:29:56 +03:00
parent 99100deb20
commit a71f265a8e
8 changed files with 174 additions and 150 deletions

View file

@ -37,6 +37,11 @@ class UserScopeContainer extends ChildScopeContainer<UserScopeParent>
implements UserScope { implements UserScope {
UserScopeContainer({required super.parent}); UserScopeContainer({required super.parent});
@override
List<Set<AsyncDep<dynamic>>> get initializeQueue => [
{purchaseEventBroadcasterDep},
];
// State Manager for user state // State Manager for user state
late final userStateManagerDep = dep(() => UserStateManager()); late final userStateManagerDep = dep(() => UserStateManager());

View file

@ -231,14 +231,31 @@ class GameSessionManager {
if (answer is! String) return false; if (answer is! String) return false;
// Remove spaces from answer // Remove spaces from answer
final answerNoSpaces = answer.replaceAll(' ', '').toLowerCase(); final answerNoSpaces = answer.replaceAll(' ', '');
final correctNoSpaces = question.correctAnswer.replaceAll(' ', '').toLowerCase();
final template = question.template;
final expectedAnswer = StringBuffer();
int answerLetterIndex = 0;
for (int i = 0; i < template.length && answerLetterIndex < answerNoSpaces.length; i++) {
final templateChar = template[i];
if (templateChar == '_' || templateChar == '|') {
// This is a slot - use the corresponding letter from correct answer
final correctChar = answerNoSpaces[answerLetterIndex];
if (templateChar == '|') {
// Uppercase slot - expect uppercase
expectedAnswer.write(correctChar.toUpperCase());
} else {
// Lowercase slot - expect lowercase
expectedAnswer.write(correctChar.toLowerCase());
}
answerLetterIndex++;
}
// Skip other characters in template (visible letters, spaces)
}
// If lengths don't match, answer is wrong
if (answerNoSpaces.length != correctNoSpaces.length) return false;
// Compare user answer with expected answer (case-sensitive for uppercase slots) // Compare user answer with expected answer (case-sensitive for uppercase slots)
return answerNoSpaces == question.correctAnswer; return answerNoSpaces.toLowerCase() == expectedAnswer.toString().toLowerCase();
} }
bool _validateMatch(MatchQuestion question, dynamic answer) { bool _validateMatch(MatchQuestion question, dynamic answer) {

View file

@ -825,7 +825,7 @@ class _CardSide extends StatelessWidget {
'Нет мнемоники', 'Нет мнемоники',
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w700,
color: colorScheme.onSurface.withOpacity(0.38), color: colorScheme.onSurface.withOpacity(0.38),
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,

View file

@ -398,24 +398,27 @@ class _CardSide extends StatelessWidget {
return Hero( return Hero(
tag: 'card_${card.id}', tag: 'card_${card.id}',
child: Container( child: Material(
width: cardWidth, color: Colors.transparent,
height: cardHeight, child: Container(
decoration: BoxDecoration( width: cardWidth,
color: colorScheme.surface, height: cardHeight,
borderRadius: BorderRadius.circular(24), decoration: BoxDecoration(
border: Border.all(color: packColor, width: 3), color: colorScheme.surface,
boxShadow: [ borderRadius: BorderRadius.circular(24),
BoxShadow( border: Border.all(color: packColor, width: 3),
color: colorScheme.shadow.withOpacity(0.3), boxShadow: [
blurRadius: 20, BoxShadow(
offset: const Offset(0, 10), color: colorScheme.shadow.withOpacity(0.3),
), blurRadius: 20,
], offset: const Offset(0, 10),
), ),
child: ClipRRect( ],
borderRadius: BorderRadius.circular(21), ),
child: isFront ? _buildFrontLayout() : _buildBackLayout(), child: ClipRRect(
borderRadius: BorderRadius.circular(21),
child: isFront ? _buildFrontLayout() : _buildBackLayout(),
),
), ),
), ),
); );
@ -622,7 +625,7 @@ class _CardSide extends StatelessWidget {
card.original!, card.original!,
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w700,
color: colorScheme.onSurface, color: colorScheme.onSurface,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,

View file

@ -301,7 +301,7 @@ class AnswerOptions extends StatelessWidget {
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: textColor, color: textColor,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, fontWeight: FontWeight.bold,
), ),
child: Text( child: Text(
text, text,
@ -320,9 +320,7 @@ class AnswerOptions extends StatelessWidget {
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
style: Theme.of(context).textTheme.bodyLarge!.copyWith( style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: textColor, color: textColor,
fontWeight: isSelected || (isAnswerSubmitted && isCorrectOption) fontWeight: FontWeight.bold,
? FontWeight.w600
: FontWeight.normal,
fontSize: isSelected ? 17 : 16, fontSize: isSelected ? 17 : 16,
), ),
child: Text( child: Text(

View file

@ -142,7 +142,7 @@ class _InputLettersWidgetState extends State<InputLettersWidget> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// Image display (if available) // Image display (if available)
if (widget.question.image != null) ...[ if (widget.question.image != null && false) ...[
SizedBox( SizedBox(
height: imageHeight, height: imageHeight,
child: Center( child: Center(

View file

@ -192,107 +192,105 @@ class _MatrixWidgetState extends State<MatrixWidget>
final targetPaddingH = 20.w; final targetPaddingH = 20.w;
final targetPaddingV = 16.h; final targetPaddingV = 16.h;
return Expanded( return Column(
child: Column( mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Expanded( Expanded(
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
// Calculate the grid size needed // Calculate the grid size needed
final availableWidth = constraints.maxWidth; final availableWidth = constraints.maxWidth;
final availableHeight = constraints.maxHeight; final availableHeight = constraints.maxHeight;
// Calculate grid dimensions including spacing // Calculate grid dimensions including spacing
final totalSpacingWidth = (size - 1) * cellSpacing; final totalSpacingWidth = (size - 1) * cellSpacing;
final totalSpacingHeight = (size - 1) * cellSpacing; final totalSpacingHeight = (size - 1) * cellSpacing;
final gridWidthNeeded = availableWidth - totalSpacingWidth; final gridWidthNeeded = availableWidth - totalSpacingWidth;
final gridHeightNeeded = availableHeight - totalSpacingHeight; final gridHeightNeeded = availableHeight - totalSpacingHeight;
// Calculate cell size based on available space // Calculate cell size based on available space
final cellWidthByWidth = gridWidthNeeded / size; final cellWidthByWidth = gridWidthNeeded / size;
final cellHeightByHeight = gridHeightNeeded / size; final cellHeightByHeight = gridHeightNeeded / size;
// Use the smaller dimension to ensure everything fits // Use the smaller dimension to ensure everything fits
final cellSize = final cellSize =
math.min(cellWidthByWidth, cellHeightByHeight); math.min(cellWidthByWidth, cellHeightByHeight);
// Calculate actual grid size // Calculate actual grid size
final actualGridWidth = final actualGridWidth =
cellSize * size + totalSpacingWidth; cellSize * size + totalSpacingWidth;
final actualGridHeight = final actualGridHeight =
cellSize * size + totalSpacingHeight; cellSize * size + totalSpacingHeight;
// Calculate scale to fit if needed // Calculate scale to fit if needed
final scaleX = availableWidth / actualGridWidth; final scaleX = availableWidth / actualGridWidth;
final scaleY = availableHeight / actualGridHeight; final scaleY = availableHeight / actualGridHeight;
final scale = math.min(1.0, math.min(scaleX, scaleY)); final scale = math.min(1.0, math.min(scaleX, scaleY));
return Center( return Center(
child: Transform.scale( child: Transform.scale(
scale: scale, scale: scale,
child: SizedBox( child: SizedBox(
width: actualGridWidth, width: actualGridWidth,
height: actualGridHeight, height: actualGridHeight,
child: GridView.builder( child: GridView.builder(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: size, crossAxisCount: size,
crossAxisSpacing: cellSpacing, crossAxisSpacing: cellSpacing,
mainAxisSpacing: cellSpacing, mainAxisSpacing: cellSpacing,
childAspectRatio: 1.0, childAspectRatio: 1.0,
),
itemCount: total,
itemBuilder: (context, index) {
final card = slots[index];
return _buildSlot(context, card);
},
), ),
itemCount: total,
itemBuilder: (context, index) {
final card = slots[index];
return _buildSlot(context, card);
},
), ),
), ),
); ),
}, );
},
),
),
SizedBox(height: spacingBetween),
Align(
alignment: Alignment.center,
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(
horizontal: targetPaddingH,
vertical: targetPaddingV,
),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(24.r),
border: Border.all(color: colorScheme.primary, width: 3),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (widget.question.text != null &&
widget.question.text!.isNotEmpty)
Text(
widget.question.text!,
style: textTheme.labelLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
if (widget.question.text != null &&
widget.question.text!.isNotEmpty)
SizedBox(height: 8.h),
_buildTargetContent(context),
],
), ),
), ),
SizedBox(height: spacingBetween), ),
Align( ],
alignment: Alignment.center, );
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(
horizontal: targetPaddingH,
vertical: targetPaddingV,
),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(24.r),
border: Border.all(color: colorScheme.primary, width: 3),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (widget.question.text != null &&
widget.question.text!.isNotEmpty)
Text(
widget.question.text!,
style: textTheme.labelLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
if (widget.question.text != null &&
widget.question.text!.isNotEmpty)
SizedBox(height: 8.h),
_buildTargetContent(context),
],
),
),
),
],
),
);
} }
Widget _buildTargetContent(BuildContext context) { Widget _buildTargetContent(BuildContext context) {

View file

@ -85,35 +85,38 @@ class PackCardItem extends StatelessWidget {
}, },
child: Hero( child: Hero(
tag: 'card_${card.id}', tag: 'card_${card.id}',
child: Container( child: Material(
key: ValueKey('$cardRootKeyPrefix${card.id}'), color: Colors.transparent,
alignment: Alignment.center, child: Container(
decoration: BoxDecoration( key: ValueKey('$cardRootKeyPrefix${card.id}'),
color: Theme.of(context).scaffoldBackgroundColor, alignment: Alignment.center,
border: Border.all(color: color.withOpacity(0.3), width: 1), decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: Theme.of(context).scaffoldBackgroundColor,
boxShadow: [ border: Border.all(color: color.withOpacity(0.3), width: 1),
BoxShadow( borderRadius: BorderRadius.circular(8),
color: Theme.of( boxShadow: [
context, BoxShadow(
).colorScheme.primary.withOpacity(0.05), color: Theme.of(
blurRadius: 2.0, context,
), ).colorScheme.primary.withOpacity(0.05),
], blurRadius: 2.0,
), ),
clipBehavior: Clip.antiAlias, ],
child: Stack( ),
fit: StackFit.expand, clipBehavior: Clip.antiAlias,
children: [ child: Stack(
hasImage fit: StackFit.expand,
? _buildCardWithImage(context) children: [
: _buildCardTextOnly(context), hasImage
Positioned( ? _buildCardWithImage(context)
top: 4, : _buildCardTextOnly(context),
right: 4, Positioned(
child: _buildFavoriteButton(context), top: 4,
), right: 4,
], child: _buildFavoriteButton(context),
),
],
),
), ),
), ),
), ),