From ae2ed56302a78bc1f07d4bbe0d003e099c79111a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr> Date: Fri, 10 Sep 2021 16:22:26 +0200 Subject: [PATCH] Fix freeze on end game animation, clean some code, minor improvements --- android/gradle.properties | 4 +- .../metadata/android/en-US/changelogs/46.txt | 1 + .../metadata/android/fr-FR/changelogs/46.txt | 1 + lib/entities/cell.dart | 163 ++++++++++-------- lib/layout/board.dart | 18 +- lib/layout/game.dart | 54 +++--- lib/layout/parameters.dart | 8 +- lib/provider/data.dart | 51 +++--- lib/screens/home.dart | 51 +++++- lib/utils/board_animate.dart | 27 ++- lib/utils/board_utils.dart | 7 +- lib/utils/game_utils.dart | 12 +- 12 files changed, 246 insertions(+), 151 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/46.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/46.txt diff --git a/android/gradle.properties b/android/gradle.properties index d6af5de..7d3ad5f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.0.45 -app.versionCode=45 +app.versionName=0.0.46 +app.versionCode=46 diff --git a/fastlane/metadata/android/en-US/changelogs/46.txt b/fastlane/metadata/android/en-US/changelogs/46.txt new file mode 100644 index 0000000..ba9b39d --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/46.txt @@ -0,0 +1 @@ +Fix freeze on end game animation, clean some code, minor improvements diff --git a/fastlane/metadata/android/fr-FR/changelogs/46.txt b/fastlane/metadata/android/fr-FR/changelogs/46.txt new file mode 100644 index 0000000..a12f663 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/46.txt @@ -0,0 +1 @@ +Correction du blocage sur l'animation de fin de partie, nettoyage de code, améliorations mineures diff --git a/lib/entities/cell.dart b/lib/entities/cell.dart index 78b448f..8a664e5 100644 --- a/lib/entities/cell.dart +++ b/lib/entities/cell.dart @@ -1,12 +1,14 @@ import 'package:flutter/material.dart'; import '../provider/data.dart'; +import '../utils/board_animate.dart'; import '../utils/board_utils.dart'; class Cell { int value; bool isFixed; int conflictsCount = 0; + bool isAnimated = false; Cell( @@ -14,21 +16,16 @@ class Cell { @required this.isFixed, ); - static Color cellBorderDarkColor = Colors.black; - static Color cellBorderLightColor = Colors.grey; - static Color cellBorderSelectedColor = Colors.red; - - Container widget(Data myProvider, Border borders, int row, int col) { - String imageAsset = 'assets/skins/empty.png'; - if (this.value > 0) { - int cellValue = myProvider.getTranslatedValueForDisplay(this.value); - imageAsset = 'assets/skins/' + myProvider.skin + '_' + cellValue.toString() + '.png'; - } + /* + * Build widget for board cell, with interactions + */ + Container widget(Data myProvider, int row, int col) { + String imageAsset = this.getImageAssetName(myProvider); return Container( decoration: BoxDecoration( color: this.getBackgroundColor(myProvider), - border: borders, + border: this.getCellBorders(myProvider, row, col), ), child: GestureDetector( child: AnimatedSwitcher( @@ -39,7 +36,7 @@ class Cell { child: Image( image: AssetImage(imageAsset), fit: BoxFit.fill, - key: ValueKey<int>(this.value), + key: ValueKey<int>(imageAsset.hashCode), ), ), onTap: () { @@ -55,69 +52,26 @@ class Cell { ); } - Color getBackgroundColor(Data myProvider) { - Color editableCellColor = Colors.grey[100]; - Color editableCellColorConflict = Colors.pink[100]; - Color fixedCellColor = Colors.grey[300]; - Color fixedCellColorConflict = Colors.pink[200]; - Color editableSelectedValueColor = Colors.green[100]; - Color fixedSelectedValueColor = Colors.green[300]; - - Color backgroundColor = editableCellColor; - - if (this.isFixed) { - backgroundColor = fixedCellColor; - } - - if (myProvider.showConflicts && (this.conflictsCount != 0)) { - if (this.isFixed) { - backgroundColor = fixedCellColorConflict; - } else { - backgroundColor = editableCellColorConflict; - } - } - - if (myProvider.showConflicts && (this.value == myProvider.currentCellValue)) { - if (this.isFixed) { - backgroundColor = fixedSelectedValueColor; - } else { - backgroundColor = editableSelectedValueColor; - } - } - - if (this.isAnimated) { - if (this.isFixed) { - backgroundColor = Colors.green[300]; - } else { - backgroundColor = Colors.green[200]; - } - } - - return backgroundColor; - } - + /* + * Build widget for select/update value cell, with interactions + */ Container widgetUpdateValue(Data myProvider) { if (this.value < 0) { return Container(); } - String imageAsset = 'assets/skins/empty.png'; - if (this.value > 0) { - int cellValue = myProvider.getTranslatedValueForDisplay(this.value); - imageAsset = 'assets/skins/' + myProvider.skin + '_' + cellValue.toString() + '.png'; - } - + String imageAsset = this.getImageAssetName(myProvider); Color backgroundColor = Colors.grey[200]; - List cells = myProvider.cells; - int blockSizeHorizontal = myProvider.blockSizeHorizontal; - int blockSizeVertical = myProvider.blockSizeVertical; - if ( myProvider.showConflicts && myProvider.currentCellCol != null && myProvider.currentCellRow != null ) { + List cells = myProvider.cells; + int blockSizeHorizontal = myProvider.blockSizeHorizontal; + int blockSizeVertical = myProvider.blockSizeVertical; + if (!BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical, myProvider.currentCellCol, myProvider.currentCellRow, this.value)) { backgroundColor = Colors.pink[100]; } @@ -141,30 +95,102 @@ class Cell { myProvider.updateCellValue(myProvider.currentCellCol, myProvider.currentCellRow, this.value); } myProvider.selectCell(null, null); - BoardUtils.computeConflictsInBoard(myProvider); + if (BoardUtils.checkBoardIsSolved(myProvider)) { + BoardAnimate.startAnimation(myProvider, 'win'); + } }, ) ); } - static Border getCellBorders(Data myProvider, int row, int col) { + /* + * Compute image asset name, from skin and cell value/state + */ + String getImageAssetName(Data myProvider) { + String imageAsset = 'assets/skins/empty.png'; + if (this.value > 0) { + int cellValue = myProvider.getTranslatedValueForDisplay(this.value); + imageAsset = 'assets/skins/' + myProvider.skin + '_' + cellValue.toString() + '.png'; + } + + return imageAsset; + } + + // Compute cell background color, from cell state + Color getBackgroundColor(Data myProvider) { + Color editableCellColor = Colors.grey[100]; + Color editableCellColorConflict = Colors.pink[100]; + Color fixedCellColor = Colors.grey[300]; + Color fixedCellColorConflict = Colors.pink[200]; + Color editableSelectedValueColor = Colors.green[100]; + Color fixedSelectedValueColor = Colors.green[300]; + Color editableAnimated = Colors.green[200]; + Color fixedAnimated = Colors.green[300]; + + Color backgroundColor = editableCellColor; + + if (this.isFixed) { + backgroundColor = fixedCellColor; + } + + if (myProvider.showConflicts && (this.conflictsCount != 0)) { + if (this.isFixed) { + backgroundColor = fixedCellColorConflict; + } else { + backgroundColor = editableCellColorConflict; + } + } + + if (myProvider.showConflicts && (this.value == myProvider.currentCellValue)) { + if (this.isFixed) { + backgroundColor = fixedSelectedValueColor; + } else { + backgroundColor = editableSelectedValueColor; + } + } + + if (this.isAnimated) { + if (this.isFixed) { + backgroundColor = fixedAnimated; + } else { + backgroundColor = editableAnimated; + } + } + + return backgroundColor; + } + + // Compute cell borders, from board size and cell state + Border getCellBorders(Data myProvider, int row, int col) { int blockSizeHorizontal = myProvider.blockSizeHorizontal; int blockSizeVertical = myProvider.blockSizeVertical; + Color cellBorderDarkColor = Colors.black; + Color cellBorderLightColor = Colors.grey; + Color cellBorderSelectedColor = Colors.red; + + Color cellBorderColor = cellBorderSelectedColor; double cellBorderWidth = 4; - if (blockSizeVertical * blockSizeHorizontal > 8) { + // Reduce cell border width on big boards + int boardSize = blockSizeVertical * blockSizeHorizontal; + if (boardSize > 8) { cellBorderWidth = 2; - if (blockSizeVertical * blockSizeHorizontal > 10) { + if (boardSize > 10) { cellBorderWidth = 1; } } + if (!myProvider.gameIsRunning) { + cellBorderColor = Colors.green[700]; + } + Border borders = Border.all( - color: cellBorderSelectedColor, + color: cellBorderColor, width: cellBorderWidth, ); + // Update cell borders if not currently selected cell if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) { borders = Border( top: BorderSide(width: cellBorderWidth, color: ((row % blockSizeVertical) == 0) ? cellBorderDarkColor : cellBorderLightColor), @@ -176,5 +202,4 @@ class Cell { return borders; } - } diff --git a/lib/layout/board.dart b/lib/layout/board.dart index d68a621..9871246 100644 --- a/lib/layout/board.dart +++ b/lib/layout/board.dart @@ -1,27 +1,28 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; -import '../entities/cell.dart'; import '../provider/data.dart'; -import '../utils/board_utils.dart'; class Board { static Container buildGameBoard(Data myProvider) { + Color borderColor = Colors.black; + return Container( margin: EdgeInsets.all(2), padding: EdgeInsets.all(2), decoration: BoxDecoration( - color: Colors.black, + color: borderColor, borderRadius: BorderRadius.circular(2), border: Border.all( - color: Colors.black, + color: borderColor, width: 2, ), ), - - child: buildGameTileset(myProvider), + child: Column( + children: [ + buildGameTileset(myProvider), + ], + ), ); } @@ -38,7 +39,6 @@ class Board { Column(children: [ cells[row][col].widget( myProvider, - Cell.getCellBorders(myProvider, row, col), row, col ) diff --git a/lib/layout/game.dart b/lib/layout/game.dart index 67c8177..7ebbc93 100644 --- a/lib/layout/game.dart +++ b/lib/layout/game.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import '../entities/cell.dart'; import '../layout/board.dart'; import '../provider/data.dart'; -import '../utils/board_animate.dart'; import '../utils/board_utils.dart'; import '../utils/game_utils.dart'; @@ -13,24 +12,22 @@ class Game { static Container buildGameWidget(Data myProvider) { bool gameIsFinished = BoardUtils.checkBoardIsSolved(myProvider); - if (gameIsFinished) { - BoardAnimate.startAnimation(myProvider, 'win'); - } return Container( child: Column( mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Board.buildGameBoard(myProvider), SizedBox(height: 2), - gameIsFinished ? Game.buildWinMessage(myProvider) : Game.buildSelectCellValueBar(myProvider), + gameIsFinished + ? Game.buildEndGameMessage(myProvider) + : Game.buildSelectCellValueBar(myProvider), ], ), ); } - static Container buildSelectCellValueBar(Data myProvider) { List cells = myProvider.cells; @@ -74,15 +71,24 @@ class Game { ); } - - static Container buildWinMessage(Data myProvider) { - Column decorationImage = Column( - children: [ - Image( - image: AssetImage('assets/icons/game_win.png'), - fit: BoxFit.fill + static FlatButton buildRestartGameButton(Data myProvider) { + return FlatButton( + child: Container( + child: Image( + image: AssetImage('assets/icons/button_back.png'), + fit: BoxFit.fill, ), - ] + ), + onPressed: () => GameUtils.resetGame(myProvider), + ); + } + + static Container buildEndGameMessage(Data myProvider) { + Image decorationImage = Image( + image: AssetImage( + 'assets/icons/game_win.png' + ), + fit: BoxFit.fill ); return Container( @@ -94,21 +100,9 @@ class Game { children: [ TableRow( children: [ - decorationImage, - Column( - children: [ - FlatButton( - child: Container( - child: Image( - image: AssetImage('assets/icons/button_back.png'), - fit: BoxFit.fill - ), - ), - onPressed: () => GameUtils.resetGame(myProvider), - ), - ] - ), - decorationImage, + Column(children: [ decorationImage ]), + Column(children: [ myProvider.animationInProgress ? decorationImage : buildRestartGameButton(myProvider) ]), + Column(children: [ decorationImage ]), ], ), ] diff --git a/lib/layout/parameters.dart b/lib/layout/parameters.dart index f5bd84d..e63d3c7 100644 --- a/lib/layout/parameters.dart +++ b/lib/layout/parameters.dart @@ -26,7 +26,6 @@ class Parameters { ); } - static Container buildStartGameButton(Data myProvider) { Column decorationImage = Column( children: [ @@ -53,7 +52,7 @@ class Parameters { child: Container( child: Image( image: AssetImage('assets/icons/button_start.png'), - fit: BoxFit.fill + fit: BoxFit.fill, ), ), onPressed: () => GameUtils.startGame(myProvider), @@ -68,7 +67,6 @@ class Parameters { ); } - static Table buildParameterSelector(Data myProvider, String parameterCode) { List availableValues = myProvider.getParameterAvailableValues(parameterCode); @@ -89,7 +87,6 @@ class Parameters { ); } - static FlatButton _buildParameterButton(Data myProvider, String parameterCode, String parameterValue) { String currentValue = myProvider.getParameterValue(parameterCode).toString(); @@ -109,12 +106,11 @@ class Parameters { ), child: Image( image: AssetImage(imageAsset), - fit: BoxFit.fill + fit: BoxFit.fill, ), ), onPressed: () => myProvider.setParameterValue(parameterCode, parameterValue), ); } - } diff --git a/lib/provider/data.dart b/lib/provider/data.dart index 073682d..b3ca4b0 100644 --- a/lib/provider/data.dart +++ b/lib/provider/data.dart @@ -20,23 +20,24 @@ class Data extends ChangeNotifier { String _sizeDefault = '3x3'; String _skin = null; String _skinDefault = 'default'; - bool _showConflicts = false; // Game data - bool _stateRunning = false; + bool _assetsPreloaded = false; + bool _gameIsRunning = false; + bool _animationInProgress = false; int _blockSizeVertical = null; int _blockSizeHorizontal = null; List _cells = []; List _cellsSolved = []; + List _shuffledCellValues = []; int _currentCellCol = null; int _currentCellRow = null; int _currentCellValue = null; + bool _showConflicts = false; int _givenTipsCount = 0; - bool _animationInProgress = false; - List _shuffledCellValues = []; String get level => _level; - set updateLevel(String level) { + void updateLevel(String level) { _level = level; notifyListeners(); } @@ -44,7 +45,7 @@ class Data extends ChangeNotifier { String get size => _size; int get blockSizeVertical => _blockSizeVertical; int get blockSizeHorizontal => _blockSizeHorizontal; - set updateSize(String size) { + void updateSize(String size) { _size = size; _blockSizeHorizontal = int.parse(_size.split('x')[0]); _blockSizeVertical = int.parse(_size.split('x')[1]); @@ -52,17 +53,11 @@ class Data extends ChangeNotifier { } String get skin => _skin; - set updateSkin(String skin) { + void updateSkin(String skin) { _skin = skin; notifyListeners(); } - bool get showConflicts => _showConflicts; - set updateShowConflicts(bool showConflicts) { - _showConflicts = showConflicts; - notifyListeners(); - } - getParameterValue(String parameterCode) { switch(parameterCode) { case 'difficulty': { return _level; } @@ -87,11 +82,11 @@ class Data extends ChangeNotifier { setParameterValue(String parameterCode, String parameterValue) async { switch(parameterCode) { - case 'difficulty': { updateLevel = parameterValue; } + case 'difficulty': { updateLevel(parameterValue); } break; - case 'size': { updateSize = parameterValue; } + case 'size': { updateSize(parameterValue); } break; - case 'skin': { updateSkin = parameterValue; } + case 'skin': { updateSkin(parameterValue); } break; } final prefs = await SharedPreferences.getInstance(); @@ -105,14 +100,25 @@ class Data extends ChangeNotifier { setParameterValue('skin', prefs.getString('skin') ?? _skinDefault); } + bool get gameIsRunning => _gameIsRunning; + void updateGameIsRunning(bool gameIsRunning) { + _gameIsRunning = gameIsRunning; + notifyListeners(); + } + + bool get assetsPreloaded => _assetsPreloaded; + void updateAssetsPreloaded(bool assetsPreloaded) { + _assetsPreloaded = assetsPreloaded; + } + List get cells => _cells; - set updateCells(List cells) { + void updateCells(List cells) { _cells = cells; notifyListeners(); } List get cellsSolved => _cellsSolved; - set updateCellsSolved(List cells) { + void updateCellsSolved(List cells) { _cellsSolved = cells; } @@ -178,11 +184,14 @@ class Data extends ChangeNotifier { notifyListeners(); } - bool get stateRunning => _stateRunning; - set updateStateRunning(bool stateRunning) { - _stateRunning = stateRunning; + bool get showConflicts => _showConflicts; + void updateShowConflicts(bool showConflicts) { + _showConflicts = showConflicts; notifyListeners(); } + void toggleShowConflicts() { + updateShowConflicts(!showConflicts); + } bool get animationInProgress => _animationInProgress; void updateAnimationInProgress(bool animationInProgress) { diff --git a/lib/screens/home.dart b/lib/screens/home.dart index e797369..8b05ae8 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -23,13 +23,58 @@ class _HomeState extends State<Home> { myProvider.initParametersValues(); } + List getImagesAssets(Data myProvider) { + List assets = []; + + List gameImages = [ + 'button_back', + 'button_help', + 'button_show_conflicts', + 'button_start', + 'game_win', + ]; + myProvider.availableDifficultyLevels.forEach( + (difficulty) => gameImages.add('difficulty_' + difficulty) + ); + myProvider.availableSizes.forEach( + (size) => gameImages.add('size_' + size) + ); + + gameImages.forEach( + (image) => assets.add('assets/icons/' + image + '.png') + ); + + List skinImages = []; + for (int value = 1; value <= 16; value++) { + skinImages.add(value.toString()); + } + + myProvider.availableSkins.forEach( + (skin) => skinImages.forEach( + (image) => assets.add('assets/skins/' + skin + '_' + image + '.png') + ) + ); + + assets.add('assets/skins/empty.png'); + + return assets; + } + @override Widget build(BuildContext context) { Data myProvider = Provider.of<Data>(context); + if (!myProvider.assetsPreloaded) { + List assets = getImagesAssets(myProvider); + assets.forEach( + (asset) => precacheImage(AssetImage(asset), context) + ); + myProvider.updateAssetsPreloaded(true); + } + List<Widget> menuActions = []; - if (myProvider.stateRunning) { + if (myProvider.gameIsRunning) { menuActions = [ FlatButton( child: Container( @@ -79,7 +124,7 @@ class _HomeState extends State<Home> { ), ), onPressed: () { - myProvider.updateShowConflicts = !myProvider.showConflicts; + myProvider.toggleShowConflicts(); }, ), FlatButton( @@ -108,7 +153,7 @@ class _HomeState extends State<Home> { ), body: SafeArea( child: Center( - child: myProvider.stateRunning + child: myProvider.gameIsRunning ? Game.buildGameWidget(myProvider) : Parameters.buildParametersSelector(myProvider) ), diff --git a/lib/utils/board_animate.dart b/lib/utils/board_animate.dart index e328a0e..cd07690 100644 --- a/lib/utils/board_animate.dart +++ b/lib/utils/board_animate.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:math'; import '../entities/cell.dart'; import '../provider/data.dart'; @@ -40,7 +39,29 @@ class BoardAnimate { for (var row = 0; row < boardSideLength; row++) { List<bool> patternRow = []; for (var col = 0; col < boardSideLength; col++) { - patternRow.add(row > (patternIndex - 3)); + patternRow.add(row > (patternIndex - 4)); + } + pattern.add(patternRow); + } + patterns.add(pattern); + } + + return patterns; + } + + // Default multi-purpose animation: sliding stripes, from top left to right bottom + static List createDefaultAnimationPatterns(Data myProvider) { + List<List> patterns = []; + + int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical; + int patternsCount = boardSideLength; + + for (var patternIndex = 0; patternIndex < patternsCount; patternIndex++) { + List<List> pattern = []; + for (var row = 0; row < boardSideLength; row++) { + List<bool> patternRow = []; + for (var col = 0; col < boardSideLength; col++) { + patternRow.add(((patternIndex + row + col) % 4 == 0)); } pattern.add(patternRow); } @@ -60,6 +81,8 @@ class BoardAnimate { case 'win': patterns = createWinGameAnimationPatterns(myProvider); break; + default: + patterns = createDefaultAnimationPatterns(myProvider); } int _patternIndex = patterns.length; diff --git a/lib/utils/board_utils.dart b/lib/utils/board_utils.dart index bc5f4f0..2f75359 100644 --- a/lib/utils/board_utils.dart +++ b/lib/utils/board_utils.dart @@ -1,8 +1,8 @@ import 'dart:math'; import '../entities/cell.dart'; -import '../utils/random_pick_grid.dart'; import '../provider/data.dart'; +import '../utils/random_pick_grid.dart'; class BoardUtils { @@ -23,7 +23,6 @@ class BoardUtils { print(''); } - static Future<void> pickGrid(Data myProvider) async { String grid; RandomPickGrid randomPickGrid; @@ -41,8 +40,8 @@ class BoardUtils { if (grid.length == pow(blockSizeHorizontal * blockSizeVertical, 2)) { print('Picked grid from template: ' + grid); bool isSymetric = (blockSizeHorizontal == blockSizeVertical); - myProvider.updateCells = BoardUtils.createBoardFromTemplate(grid, isSymetric); - myProvider.updateCellsSolved = BoardUtils.getSolvedGrid(myProvider); + myProvider.updateCells(BoardUtils.createBoardFromTemplate(grid, isSymetric)); + myProvider.updateCellsSolved(BoardUtils.getSolvedGrid(myProvider)); myProvider.selectCell(null, null); printGrid(myProvider.cells, myProvider.cellsSolved); diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart index 51758ba..abca12e 100644 --- a/lib/utils/game_utils.dart +++ b/lib/utils/game_utils.dart @@ -6,15 +6,15 @@ import '../utils/game_utils.dart'; class GameUtils { static Future<void> resetGame(Data myProvider) async { - myProvider.updateStateRunning = false; + myProvider.updateGameIsRunning(false); } static Future<void> startGame(Data myProvider) async { - myProvider.updateSize = myProvider.size; - myProvider.updateStateRunning = true; + myProvider.updateSize(myProvider.size); + myProvider.updateGameIsRunning(true); myProvider.resetGivenTipsCount(); myProvider.shuffleCellValues(); - myProvider.updateCells = BoardUtils.createEmptyBoard(myProvider.blockSizeHorizontal * myProvider.blockSizeVertical); + myProvider.updateCells(BoardUtils.createEmptyBoard(myProvider.blockSizeHorizontal * myProvider.blockSizeVertical)); BoardUtils.pickGrid(myProvider); BoardAnimate.startAnimation(myProvider, 'start'); } @@ -86,7 +86,9 @@ class GameUtils { myProvider.updateCellValue(myProvider.currentCellCol, myProvider.currentCellRow, allowedValuesCount == 1 ? eligibleValue : 0); myProvider.selectCell(null, null); - BoardUtils.computeConflictsInBoard(myProvider); + if (BoardUtils.checkBoardIsSolved(myProvider)) { + BoardAnimate.startAnimation(myProvider, 'win'); + } } } -- GitLab