Repository: chanind/hanzi-writer
Pull request #293 (2023-02-08 17:47:58)

fix: respect highlightCompleteColor when finishing quiz
This PR uses the `highCompleteColor` when highlighting the character after finishing a quiz rather than ignoring the param and just using `highlightColor`. Thank you @GnitLores for finding this bug!

related to #292. 


Patch:
diff --git a/src/Mutation.ts b/src/Mutation.ts
index fe45f995..6c230670 100644
--- a/src/Mutation.ts
+++ b/src/Mutation.ts
@@ -21,8 +21,6 @@ export interface GenericMutation<
   cancel(renderState: TRenderStateClass): void;
 }
 
-export type MutationChain = GenericMutation<any>[];
-
 class Delay implements GenericMutation {
   scope: string;
   _runningPromise: Promise<void> | undefined;
diff --git a/src/Quiz.ts b/src/Quiz.ts
index 1e373ba9..cf7de4ff 100644
--- a/src/Quiz.ts
+++ b/src/Quiz.ts
@@ -8,7 +8,7 @@ import * as characterActions from './characterActions';
 import Character from './models/Character';
 import { ParsedHanziWriterOptions, Point, StrokeData } from './typings/types';
 import RenderState from './RenderState';
-import { MutationChain } from './Mutation';
+import { GenericMutation } from './Mutation';
 
 const getDrawnPath = (userStroke: UserStroke) => ({
   pathString: geometry.getPathString(userStroke.externalPoints),
@@ -184,6 +184,8 @@ export default class Quiz {
       onComplete,
       highlightOnComplete,
       strokeFadeDuration,
+      highlightCompleteColor,
+      highlightColor,
       strokeHighlightDuration,
     } = this._options;
 
@@ -191,7 +193,7 @@ export default class Quiz {
       ...this._getStrokeData({ isCorrect: true, meta }),
     });
 
-    let animation: MutationChain = characterActions.showStroke(
+    let animation: GenericMutation[] = characterActions.showStroke(
       'main',
       this._currentStrokeIndex,
       strokeFadeDuration,
@@ -212,6 +214,7 @@ export default class Quiz {
         animation = animation.concat(
           quizActions.highlightCompleteChar(
             this._character,
+            colorStringToVals(highlightCompleteColor || highlightColor),
             (strokeHighlightDuration || 0) * 2,
           ),
         );
diff --git a/src/characterActions.ts b/src/characterActions.ts
index aec9f51d..43dc8f8f 100644
--- a/src/characterActions.ts
+++ b/src/characterActions.ts
@@ -1,7 +1,7 @@
 import Stroke from './models/Stroke';
 import { ColorObject, RecursivePartial } from './typings/types';
 import Character from './models/Character';
-import Mutation, { MutationChain } from './Mutation';
+import Mutation, { GenericMutation } from './Mutation';
 import { objRepeat } from './utils';
 import { CharacterName, CharacterRenderState, RenderStateObject } from './RenderState';
 
@@ -9,7 +9,7 @@ export const showStrokes = (
   charName: CharacterName,
   character: Character,
   duration: number,
-): MutationChain => {
+): GenericMutation[] => {
   return [
     new Mutation(
       `character.${charName}.strokes`,
@@ -26,7 +26,7 @@ export const showCharacter = (
   charName: CharacterName,
   character: Character,
   duration: number,
-): MutationChain => {
+): GenericMutation[] => {
   return [
     new Mutation(
       `character.${charName}`,
@@ -43,7 +43,7 @@ export const hideCharacter = (
   charName: CharacterName,
   character: Character,
   duration?: number,
-): MutationChain => {
+): GenericMutation[] => {
   return [
     new Mutation(`character.${charName}.opacity`, 0, { duration, force: true }),
     ...showStrokes(charName, character, 0),
@@ -62,11 +62,11 @@ export const highlightStroke = (
   stroke: Stroke,
   color: ColorObject | null,
   speed: number,
-): MutationChain => {
+): GenericMutation[] => {
   const strokeNum = stroke.strokeNum;
   const duration = (stroke.getLength() + 600) / (3 * speed);
   return [
-    new Mutation('character.highlight.strokeColor', color),
+    new Mutation('options.highlightColor', color),
     new Mutation('character.highlight', {
       opacity: 1,
       strokes: {
@@ -92,7 +92,7 @@ export const animateStroke = (
   charName: CharacterName,
   stroke: Stroke,
   speed: number,
-): MutationChain => {
+): GenericMutation[] => {
   const strokeNum = stroke.strokeNum;
   const duration = (stroke.getLength() + 600) / (3 * speed);
   return [
@@ -116,7 +116,7 @@ export const animateSingleStroke = (
   character: Character,
   strokeNum: number,
   speed: number,
-): MutationChain => {
+): GenericMutation[] => {
   const mutationStateFunc = (state: RenderStateObject) => {
     const curCharState = state.character[charName];
     const mutationState: RecursivePartial<CharacterRenderState> = {
@@ -141,7 +141,7 @@ export const showStroke = (
   charName: CharacterName,
   strokeNum: number,
   duration: number,
-): MutationChain => {
+): GenericMutation[] => {
   return [
     new Mutation(
       `character.${charName}.strokes.${strokeNum}`,
@@ -160,8 +160,8 @@ export const animateCharacter = (
   fadeDuration: number,
   speed: number,
   delayBetweenStrokes: number,
-): MutationChain => {
-  let mutations: MutationChain = hideCharacter(charName, character, fadeDuration);
+): GenericMutation[] => {
+  let mutations: GenericMutation[] = hideCharacter(charName, character, fadeDuration);
   mutations = mutations.concat(showStrokes(charName, character, 0));
   mutations.push(
     new Mutation(
@@ -187,7 +187,7 @@ export const animateCharacterLoop = (
   speed: number,
   delayBetweenStrokes: number,
   delayBetweenLoops: number,
-): MutationChain => {
+): GenericMutation[] => {
   const mutations = animateCharacter(
     charName,
     character,
diff --git a/src/quizActions.ts b/src/quizActions.ts
index b53bf65c..c88097c2 100644
--- a/src/quizActions.ts
+++ b/src/quizActions.ts
@@ -1,14 +1,14 @@
-import Mutation, { MutationChain } from './Mutation';
+import Mutation, { GenericMutation } from './Mutation';
 import * as characterActions from './characterActions';
 import { objRepeat, objRepeatCb } from './utils';
 import Character from './models/Character';
-import { Point } from './typings/types';
+import { ColorObject, Point } from './typings/types';
 
 export const startQuiz = (
   character: Character,
   fadeDuration: number,
   startStrokeNum: number,
-): MutationChain => {
+): GenericMutation[] => {
   return [
     ...characterActions.hideCharacter('main', character, fadeDuration),
     new Mutation(
@@ -32,7 +32,7 @@ export const startQuiz = (
   ];
 };
 
-export const startUserStroke = (id: string | number, point: Point): MutationChain => {
+export const startUserStroke = (id: string | number, point: Point): GenericMutation[] => {
   return [
     new Mutation('quiz.activeUserStrokeId', id, { force: true }),
     new Mutation(
@@ -49,14 +49,14 @@ export const startUserStroke = (id: string | number, point: Point): MutationChai
 export const updateUserStroke = (
   userStrokeId: string | number,
   points: Point[],
-): MutationChain => {
+): GenericMutation[] => {
   return [new Mutation(`userStrokes.${userStrokeId}.points`, points, { force: true })];
 };
 
 export const removeUserStroke = (
   userStrokeId: string | number,
   duration: number,
-): MutationChain => {
+): GenericMutation[] => {
   return [
     new Mutation(`userStrokes.${userStrokeId}.opacity`, 0, { duration }),
     new Mutation(`userStrokes.${userStrokeId}`, null, { force: true }),
@@ -65,9 +65,11 @@ export const removeUserStroke = (
 
 export const highlightCompleteChar = (
   character: Character,
+  color: ColorObject | null,
   duration: number,
-): MutationChain => {
+): GenericMutation[] => {
   return [
+    new Mutation('options.highlightColor', color),
     ...characterActions.hideCharacter('highlight', character),
     ...characterActions.showCharacter('highlight', character, duration / 2),
     ...characterActions.hideCharacter('highlight', character, duration / 2),


Test patch:
diff --git a/demo/test.js b/demo/test.js
index 7416245a..62c757a1 100644
--- a/demo/test.js
+++ b/demo/test.js
@@ -17,6 +17,7 @@ function updateCharacter() {
     height: 400,
     renderer: 'svg',
     radicalColor: '#166E16',
+    highlightCompleteColor: '#FF0000',
     onCorrectStroke: printStrokePoints,
     onMistake: printStrokePoints,
     showCharacter: false,
diff --git a/src/__tests__/Quiz-test.ts b/src/__tests__/Quiz-test.ts
index 531dd570..6565b69f 100644
--- a/src/__tests__/Quiz-test.ts
+++ b/src/__tests__/Quiz-test.ts
@@ -9,6 +9,7 @@ import Positioner from '../Positioner';
 import { resolvePromises } from '../testUtils';
 import strokeMatches from '../strokeMatches';
 import { Point } from '../typings/types';
+import { colorStringToVals } from '../utils';
 
 (Positioner as any).mockImplementation(() => ({
   convertExternalPoint: (point: Point) => ({ x: point.x + 5, y: point.y + 5 }),
@@ -807,6 +808,7 @@ describe('Quiz', () => {
       clock.tick(1000);
       await resolvePromises();
 
+      expect(renderState.state.options.highlightColor).toEqual(colorStringToVals('#0F0'));
       expect(renderState.state.character.highlight.opacity).toBe(1);
       clock.tick(1000);
       await resolvePromises();