src/app/models/scenario-ose.ts
constructor(accountService: AccountService, globalService: GlobalService, oseJourneyService: OseJourneyService, page: any, cd: ChangeDetectorRef, ttsService: any)
|
| runMathiaSpeech |
runMathiaSpeech(content: any[], globalBubble: boolean)
|
|
Runs a tutorial tour to present to the user the different inputMethods of the activity (2 versions: with microphone plugged or not)
Returns:
any
|
| runMathiaSpeechCore |
runMathiaSpeechCore(tutorial: Tutorial)
|
|
Returns:
void
|
| waitResolveFromOutside |
waitResolveFromOutside()
|
|
creates a Promise to be awaited and resolved from outside if not in skipSequence mode
Returns:
any
|
| launchFeedbackWithEnd |
launchFeedbackWithEnd(feedbackHtml: string, globalBubble: boolean)
|
|
Read of feedback at the end of the activity
Returns:
void
|
| readSpeechSequenceFromFicheContent |
readSpeechSequenceFromFicheContent(scenarios: TTSContent[])
|
|
convert TTSContent array from fiche & build / read a speechSequence for runMathiaSpeech with punctuation
Returns:
void
|
| splitTextPunctationToScenarioPhraseArray |
splitTextPunctationToScenarioPhraseArray(text: string)
|
|
splits given text depending on punctuation and returns a SpeechSequence array
Returns:
void
|
| Protected oseJourneyService |
oseJourneyService: |
| previousTitleRead |
previousTitleRead: |
| Public resolveScenarioCallbackFromOutside |
resolveScenarioCallbackFromOutside: |
| Public resolveTimeoutCancelable |
resolveTimeoutCancelable: |
| ttsService |
ttsService: |
import { PlayTTSService } from "src/app/services/play-tts.service";
import { TTSContent, ContentTagNames } from "./fiche";
import { ChangeDetectorRef, QueryList } from "@angular/core";
import { AccountService } from "../services/account.service";
import { GlobalService } from "../services/global.service";
import { ScenarioPhrase } from "./scenario-phrase";
import { Tutorial } from "./tutorial";
import { FicheComponent } from "../components/fiche/fiche.component";
import { OseExerciceType } from "./ose2-journey";
import { OseJourneyService } from "../services/ose-journeys.service";
import { Scenario } from "./scenario";
import { ReplaySubject } from "rxjs";
export class ScenarioOse extends Scenario {
protected oseJourneyService: OseJourneyService;
public resolveTimeoutCancelable: (value: unknown) => void;
public resolveScenarioCallbackFromOutside: () => void;
previousTitleRead: string;
constructor(
accountService: AccountService,
globalService: GlobalService,
oseJourneyService: OseJourneyService,
page: any,
cd: ChangeDetectorRef,
public ttsService: PlayTTSService
) {
super(accountService, globalService, page, cd, ttsService);
this.oseJourneyService = oseJourneyService;
}
/**
* Runs a tutorial tour to present to the user the different inputMethods of the activity (2 versions: with microphone plugged or not)
* @ignore
*/
runMathiaSpeech(content: any[], globalBubble?: boolean): Promise<void> {
const p = new Promise<void>((resolve, reject) => {
console.log("runMathiaSpeech entered");
this.globalService.mathiaSpeechRunning = true;
let lastElement;
const contentClone = content.slice(0);
contentClone.reverse().forEach(element => {
const current = new Tutorial();
current.phrase = element.phrase;
if (element.phraseTTS) {
current.phraseTTS = element.phraseTTS;
}
if (element.onlySpeech) {
current.onlySpeech = element.onlySpeech;
}
if (element.randomSpeechMode) {
current.randomSpeechMode = element.randomSpeechMode;
}
if (element.textBubble) {
current.textBubble = element.textBubble;
}
if (element.buttons) {
current.buttons = element.buttons;
}
if (element.disableSkip) {
current.disableSkip = element.disableSkip;
}
if (element.buttonText) {
current.buttonText = element.buttonText;
}
if (element.shootingStar) {
current.shootingStar = element.shootingStar;
}
if (element.normalStar) {
current.normalStar = element.normalStar;
}
if (element.moon) {
current.moon = element.moon;
}
if (element.keepBubble) {
current.keepBubble = element.keepBubble;
}
//array read in reverse so the first tutorial is the end and need to resolve
if (!lastElement) {
current.callback = async () => {
if (element.callback) {
if (typeof element.callback === "function" && !this.lockCallback) {
try {
await element.callback();
} catch (e) {
console.error("catch callback", e);
}
}
}
this.globalService.mathiaSpeechRunning = false;
this.skipSequence = false;
resolve();
};
} else {
current.callback = async () => {
if (element.callback) {
if (typeof element.callback === "function" && !this.lockCallback) {
try {
await element.callback();
} catch (e) {
console.error("catch callback", e);
}
}
}
};
}
current.next = lastElement;
lastElement = current;
});
if (this.lockCallback) {
// console.error("rejected by lockCallback");
reject();
} else {
this.runMathiaSpeechCore(lastElement);
}
});
this.currentRunMathiaSpeechPromise.push({promise: p, scenario: content});
return p;
}
/**
* @ignore
*/
runMathiaSpeechCore(tutorial: Tutorial): void {
if (tutorial) {
if (Array.isArray(tutorial.phrase) && tutorial.phrase.length > 1) {
if (tutorial.randomSpeechMode) {
// select random index from tutorial.phrase array
const randomIndex = Math.floor(Math.random() * tutorial.phrase.length);
// console.log("random index = " + randomIndex);oe
tutorial.phrase = tutorial.phrase[randomIndex];
if (tutorial.phraseTTS) {
tutorial.phraseTTS = tutorial.phraseTTS[randomIndex];
}
} else {
// increment index of tutorial.phrase array
this.mscPhraseArrayIndex++;
if (this.mscPhraseArrayIndex >= tutorial.phrase.length) {
this.mscPhraseArrayIndex = 0;
}
tutorial.phrase = tutorial.phrase[this.mscPhraseArrayIndex];
if (tutorial.phraseTTS) {
tutorial.phraseTTS = tutorial.phraseTTS[this.mscPhraseArrayIndex];
}
}
} else if (Array.isArray(tutorial.phrase) && tutorial.phrase.length === 1) {
tutorial.phrase = tutorial.phrase[0];
if (tutorial.phraseTTS) {
tutorial.phraseTTS = tutorial.phraseTTS[0];
}
}
this.page?.eventMessage?.next(tutorial.phrase);
this.lastPhrase = tutorial.phrase;
if (!tutorial.onlySpeech && this.page?.displayTTSBubble) {
this.page.displayTTSBubble = true;
}
if (!this.skipSequence && this.cd) {
this.detectChanges();
}
if (!this.skipSequence && !this.lockCallback) {
if (this.page?.injectTextToBubble) {
this.page.injectTextToBubble(tutorial.phrase);
}
this.ttsService.playTTSEventProtected(tutorial, async () => {
tutorial.callback().then(() => {
this.page.mathiaSpeechButtonText = null;
if (!this.page.menuOpen && !this.globalService.appIdle) {
this.detectChanges();
return this.runMathiaSpeechCore(tutorial.next);
} else {
this.detectChanges();
this.tutoCallbackSave = tutorial.next;
}
});
});
} else if (this.skipSequence === true && !this.lockCallback) {
tutorial.callback().then(async () => {
this.page.mathiaSpeechButtonText = null;
if (!this.page.menuOpen && !this.globalService.appIdle) {
return this.runMathiaSpeechCore(tutorial.next);
} else {
this.detectChanges();
this.tutoCallbackSave = tutorial.next;
}
});
}
}
}
/**
* creates a Promise to be awaited and resolved from outside if not in skipSequence mode
* used in createSpeechSequenceFromFicheFeedback()
* @ignore
*/
async waitResolveFromOutside(): Promise<void> {
return new Promise(async resolve => {
console.error("waitResolveFromOutside enter ");
if (!this.skipSequence && !this.page.ficheSkipped) {
this.resolveScenarioCallbackFromOutside = resolve;
} else {
this.resolveScenarioCallbackFromOutside = null;
resolve();
}
});
}
/**
* Read of feedback at the end of the activity
*/
async launchFeedbackWithEnd(feedbackHtml: string, globalBubble = true) {
const speechSequence = new Array();
if (feedbackHtml && feedbackHtml != "") {
await this.readHTML(feedbackHtml, globalBubble);
} else {
if (this.globalService.onActivityPage) {
switch (this.globalService.activityRunningName) {
case OseExerciceType.fiche:
break;
case OseExerciceType.memory:
speechSequence.push(new ScenarioPhrase($localize`Bravo tu as trouvé toutes les paires`));
break;
case OseExerciceType.quizz:
break;
case OseExerciceType.coloriage:
speechSequence.push(new ScenarioPhrase($localize`Bravo tu as terminé le coloriage`));
break;
case OseExerciceType.puzzle:
speechSequence.push(new ScenarioPhrase($localize`Bravo tu as bien reconstruit l’image`));
break;
case OseExerciceType.spotDifference:
speechSequence.push(new ScenarioPhrase($localize`Bravo tu as réussi à trouver toutes les erreurs présentes dans l’image`));
break;
}
}
}
if (this.globalService.onActivityPage && this.oseJourneyService.currentJourney) {
speechSequence.push(new ScenarioPhrase($localize`Appuie sur le bouton en bas à droite pour passer à la prochaine activité.`));
}
if (speechSequence && speechSequence.length > 0) {
await this.runMathiaSpeech(speechSequence, globalBubble);
}
}
/**
* convert TTSContent array from fiche & build / read a speechSequence for runMathiaSpeech
* autoplays video
* splits <p> with punctuation
*/
async readSpeechSequenceFromFicheContent(scenarios: TTSContent[]) {
// console.error(JSON.stringify(scenarios));
await this.globalService.speechBubbleComponent.displayGlobalBubble(false);
let vimeoPresent = false;
const speechSequence = new Array();
const videoPlayerScenario = new Array()
scenarios?.forEach((scenario: TTSContent) => {
if (scenario) {
if (scenario.tag === ContentTagNames.H1) {
speechSequence.push(new ScenarioPhrase([scenario.text]));
}
if (scenario.tag === ContentTagNames.H2) {
if (this.previousTitleRead !== scenario.text) {
speechSequence.push(new ScenarioPhrase([scenario.text]));
this.previousTitleRead = scenario.text;
}
}
if (scenario.tag === ContentTagNames.H3) {
speechSequence.push(new ScenarioPhrase([scenario.text]));
}
if (scenario.tag === ContentTagNames.P) {
const scenarioPhraseArray = this.splitTextPunctationToScenarioPhraseArray(scenario.text);
scenarioPhraseArray.forEach(scenarioPhrase => {
speechSequence.push(scenarioPhrase);
});
}
if (scenario.tag === ContentTagNames.AUDIO) { }
if (scenario.tag === ContentTagNames.VIDEO_VIMEO) {
vimeoPresent = true;
videoPlayerScenario.push(
new ScenarioPhrase([" "], async () => {
if (!this.skipSequence) {
await this.page.ficheComponent.vimeoFirstVideoLoaded;
if (
(this.page.ficheComponent as FicheComponent).vimeoPlayers &&
(this.page.ficheComponent as FicheComponent).vimeoPlayers.length > 0
) {
(this.page.ficheComponent as FicheComponent).vimeoPlayers[0].play();
setTimeout(() => {
if (this.page.ficheComponent) {
(this.page.ficheComponent as FicheComponent).animateNextButton = true;
}
}, 3000);
}
}
})
);
if (this.globalService.muteSounds) {
speechSequence.push(...videoPlayerScenario);
}
}
if (scenario.tag === ContentTagNames.VIDEO_WP) {
}
if (scenario.tag === ContentTagNames.IMG) {
}
}
});
if (!vimeoPresent) {
speechSequence.push(
new ScenarioPhrase([" "], async () => {
if (this.page.ficheComponent) {
(this.page.ficheComponent as FicheComponent).animateNextButton = true;
}
})
);
} else if (!this.globalService.muteSounds) {
speechSequence.push(...videoPlayerScenario)
}
if(this.globalService.muteSounds){
if (this.page.ficheComponent) {
(this.page.ficheComponent as FicheComponent).animateNextButton = true;
}
}
await this.runMathiaSpeech(speechSequence);
}
/**
* splits given text depending on punctuation and returns a SpeechSequence array
*/
splitTextPunctationToScenarioPhraseArray(text: string) {
const speechSequence = [];
if (text.match(/(\.|\?|\!)/g)) {
// Split feedback with punctuation:
const feedbackSequence = text.split(/(\.|\?|\!)/g);
// move punctuation index content to the end of the previous index:
feedbackSequence.forEach((phrase, index) => {
if (phrase.match(/(\.|\?|\!)/g)) {
feedbackSequence[index - 1] = feedbackSequence[index - 1] + phrase;
feedbackSequence.splice(index, 1);
}
});
feedbackSequence.forEach((phrase, index) => {
if (phrase.length === 0 || phrase.trim().length === 0) {
// remove empty or blank indexes:
feedbackSequence.splice(index, 1);
} else if (/^\s/.test(phrase)) {
// remove empty space at the beginning of phrases:
phrase = phrase.substring(1);
feedbackSequence.splice(index, 1, phrase);
}
});
for (const phrase of feedbackSequence) {
speechSequence.push(new ScenarioPhrase(phrase));
}
} else {
speechSequence.push(new ScenarioPhrase(text));
}
return speechSequence;
}
}