Commit 5796eedb authored by Konstantin Schulz's avatar Konstantin Schulz
Browse files

new feature: users can now share any single exercise using a deep link

parent ae01a261
{
"name": "mc_frontend",
"version": "1.3.3",
"version": "1.3.5",
"author": "Ionic Framework",
"homepage": "https://ionicframework.com/",
"scripts": {
......
......@@ -62,6 +62,7 @@ export class CorpusService {
lemma: new PhenomenonMapContent({translationObject: null}),
partOfSpeech: new PhenomenonMapContent({translationObject: PartOfSpeechTranslation})
});
public shareLinkCopiedString: string;
constructor(public translate: TranslateService,
public http: HttpClient,
......@@ -273,6 +274,7 @@ export class CorpusService {
});
this.translate.get('INVALID_TEXT_RANGE').subscribe(value => this.invalidTextRangeString = value);
this.translate.get('ERROR_CITATIONS_UNAVAILABLE').subscribe(value => this.citationsUnavailableString = value);
this.translate.get('LINK_COPIED').subscribe(value => this.shareLinkCopiedString = value);
}
initPhenomenonMap() {
......
......@@ -112,7 +112,7 @@ export class ExerciseListPage implements OnInit {
}
showExercise(exercise: ExerciseMC) {
const url = HelperService.config['backendBaseUrl'] + HelperService.config['backendApiExercisePath'];
const url: string = HelperService.config['backendBaseUrl'] + HelperService.config['backendApiExercisePath'];
const params: HttpParams = new HttpParams().set('eid', exercise.eid);
HelperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((ar: AnnisResponse) => {
HelperService.mostRecentSetup.annisResponse = ar;
......
......@@ -16,6 +16,7 @@ window.onresize = () => {
})
export class ExerciseService {
public excludeOOV = false;
public exerciseTypePath: string;
public fillBlanksString = 'fill_blanks';
public h5pIframeString = '#h5p-iframe-1';
public kwicGraphs: string;
......
/* tslint:disable:no-string-literal */
import {Component, OnInit} from '@angular/core';
import {HelperService} from '../helper.service';
import {NavController} from '@ionic/angular';
import {NavController, ToastController} from '@ionic/angular';
import {ActivatedRoute} from '@angular/router';
import {TranslateService} from '@ngx-translate/core';
import {ExerciseService} from 'src/app/exercise.service';
import {HttpClient, HttpParams} from '@angular/common/http';
import {AnnisResponse} from 'src/app/models/annisResponse';
import {ExerciseType, MoodleExerciseType} from 'src/app/models/enum';
import {CorpusService} from 'src/app/corpus.service';
@Component({
selector: 'app-exercise',
......@@ -18,16 +22,46 @@ export class ExercisePage implements OnInit {
constructor(public navCtrl: NavController,
public activatedRoute: ActivatedRoute,
public translateService: TranslateService,
public exerciseService: ExerciseService) {
public exerciseService: ExerciseService,
public http: HttpClient,
public toastCtrl: ToastController,
public helperService: HelperService,
public corpusService: CorpusService) {
this.corpusService.checkAnnisResponse().then(() => {
this.loadExercise();
});
}
loadExercise(): void {
this.activatedRoute.queryParams.subscribe((params: object) => {
const exerciseType: string = params['type'];
const exerciseTypePath: string = exerciseType === this.exerciseService.vocListString ?
this.exerciseService.fillBlanksString : exerciseType;
const file: string = params['file'];
const lang: string = this.translateService.currentLang;
window.localStorage.setItem(HelperService.config['localStorageKeyH5P'],
HelperService.baseUrl + '/assets/h5p/' + exerciseType + '/content/' + file + '_' + lang + '.json');
this.exerciseService.initH5P(exerciseTypePath);
if (params['eid']) {
let url: string = HelperService.config['backendBaseUrl'] + HelperService.config['backendApiExercisePath'];
const httpParams: HttpParams = new HttpParams().set('eid', params['eid']);
HelperService.makeGetRequest(this.http, this.toastCtrl, url, httpParams).then((ar: AnnisResponse) => {
HelperService.mostRecentSetup.annisResponse = ar;
this.helperService.saveMostRecentSetup().then();
this.corpusService.annisResponse = ar;
const met: MoodleExerciseType = MoodleExerciseType[ar.exercise_type];
this.corpusService.exercise.type = ExerciseType[met.toString()];
// this will be called via GET request from the h5p standalone javascript library
url = `${HelperService.config['backendBaseUrl']}${HelperService.config['backendApiH5pPath']}` +
`?eid=${this.corpusService.annisResponse.exercise_id}&lang=${this.translateService.currentLang}`;
window.localStorage.setItem(HelperService.config['localStorageKeyH5P'], url);
const exerciseTypePath: string = this.corpusService.exercise.type === ExerciseType.markWords ?
'mark_words' : 'drag_text';
this.exerciseService.initH5P(exerciseTypePath);
}, () => {
});
} else {
const exerciseType: string = params['type'];
const exerciseTypePath: string = exerciseType === this.exerciseService.vocListString ?
this.exerciseService.fillBlanksString : exerciseType;
const file: string = params['file'];
const lang: string = this.translateService.currentLang;
window.localStorage.setItem(HelperService.config['localStorageKeyH5P'],
HelperService.baseUrl + '/assets/h5p/' + exerciseType + '/content/' + file + '_' + lang + '.json');
this.exerciseService.initH5P(exerciseTypePath);
}
});
}
......
......@@ -8,6 +8,7 @@ import {TextComplexity} from 'src/app/models/textComplexity';
export class AnnisResponse {
public directed: boolean;
public exercise_id: string;
public exercise_type: string;
public frequency_analysis: FrequencyItem[];
public links: LinkMC[];
public multigraph: boolean;
......
......@@ -23,6 +23,24 @@
(click)="HelperService.goToTextRangePage(navCtrl)">{{ "CHANGE_TEXT_RANGE" | translate}}</ion-button>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<button class="button-icon-only" (click)="toggleShareLink()">
<ion-icon name="share"></ion-icon>
<ion-label>{{ "SHARE" | translate}}</ion-label>
</button>
</ion-col>
</ion-row>
<ion-row *ngIf="shareLink">
<ion-col size="11">
<ion-textarea id="shareLink" (ionFocus)="selectLink()" [(ngModel)]="shareLink"></ion-textarea>
</ion-col>
<ion-col size="1">
<button class="button-copy-link" (click)="copyLink()">
<ion-icon name="copy"></ion-icon>
</button>
</ion-col>
</ion-row>
<ion-row *ngIf="HelperService.isVocabularyCheck">
<ion-col>
<label>
......@@ -35,7 +53,7 @@
<ion-row>
<ion-col>
<!-- TODO: enable solution shuffling for H5P ? -->
<div *ngIf="corpusService.exercise.type !== ExerciseType.kwic" class="h5p-container"></div>
<div class="h5p-container"></div>
</ion-col>
</ion-row>
<ion-row>
......
div {
display: inline;
}
h4 {
font-size: 1.2em;
}
......@@ -7,14 +11,31 @@ h5 {
display: inline-block;
}
div {
display: inline;
}
.first {
max-width: 250px;
ion-textarea {
border-radius: 10px;
border: solid;
border-color: lightgrey !important;
}
ol {
text-align: left;
}
.button-copy-link {
-ms-transform: scale(1.5); /* IE */
-moz-transform: scale(1.5); /* FF */
-webkit-transform: scale(1.5); /* Safari and Chrome */
-o-transform: scale(1.5); /* Opera */
margin-top: 25px;
}
.button-icon-only {
-ms-transform: scale(1.5); /* IE */
-moz-transform: scale(1.5); /* FF */
-webkit-transform: scale(1.5); /* Safari and Chrome */
-o-transform: scale(1.5); /* Opera */
}
.first {
max-width: 250px;
}
......@@ -2,7 +2,7 @@
import {AnnisResponse} from 'src/app/models/annisResponse';
import {ExerciseType, FileType} from 'src/app/models/enum';
import {HelperService} from 'src/app/helper.service';
import {NavController} from '@ionic/angular';
import {NavController, ToastController} from '@ionic/angular';
import {ExerciseService} from 'src/app/exercise.service';
import {CorpusService} from 'src/app/corpus.service';
import {Component, OnDestroy, OnInit} from '@angular/core';
......@@ -25,9 +25,11 @@ export class PreviewPage implements OnDestroy, OnInit {
public FileType = FileType;
public currentSolutions: Solution[];
public maxGapLength = 0;
public shareLink: string;
public showInstructions = false;
public solutionIndicesString: string;
public solutionNodeIdSet: Set<string> = new Set<string>();
public textareaSelector = '#shareLink textarea';
public urlBase: string;
constructor(public navCtrl: NavController,
......@@ -35,7 +37,8 @@ export class PreviewPage implements OnDestroy, OnInit {
public exerciseService: ExerciseService,
public translateService: TranslateService,
public corpusService: CorpusService,
public helperService: HelperService) {
public helperService: HelperService,
public toastCtrl: ToastController) {
this.currentSolutions = [];
if (!HelperService.isVocabularyCheck) {
this.exerciseService.excludeOOV = false;
......@@ -49,17 +52,28 @@ export class PreviewPage implements OnDestroy, OnInit {
});
}
initH5P() {
if (this.corpusService.exercise.type !== ExerciseType.kwic) {
const solutionIndicesString: string = this.exerciseService.excludeOOV ? '&solution_indices=' +
JSON.stringify(this.currentSolutions.map(x => this.corpusService.annisResponse.solutions.indexOf(x))) : '';
// this will be called via GET request from the h5p standalone javascript library
const url: string = HelperService.config['backendBaseUrl'] + HelperService.config['backendApiH5pPath'] + '?' + 'eid='
+ this.corpusService.annisResponse.exercise_id + '&lang=' + this.translateService.currentLang + solutionIndicesString;
window.localStorage.setItem(HelperService.config['localStorageKeyH5P'], url);
const exerciseTypePath: string = this.corpusService.exercise.type === ExerciseType.markWords ? 'mark_words' : 'drag_text';
this.exerciseService.initH5P(exerciseTypePath);
}
async copyLink(): Promise<void> {
const ta: HTMLTextAreaElement = document.querySelector(this.textareaSelector);
ta.select();
document.execCommand('copy');
ta.setSelectionRange(0, 0);
const toast = await this.toastCtrl.create({
message: this.corpusService.shareLinkCopiedString,
duration: 3000,
position: 'middle'
});
toast.present().then();
}
initH5P(): void {
const solutionIndicesString: string = this.exerciseService.excludeOOV ? '&solution_indices=' +
JSON.stringify(this.currentSolutions.map(x => this.corpusService.annisResponse.solutions.indexOf(x))) : '';
// this will be called via GET request from the h5p standalone javascript library
const url: string = `${HelperService.config['backendBaseUrl'] + HelperService.config['backendApiH5pPath']}` +
`?eid=${this.corpusService.annisResponse.exercise_id}&lang=${this.translateService.currentLang + solutionIndicesString}`;
window.localStorage.setItem(HelperService.config['localStorageKeyH5P'], url);
const exerciseTypePath: string = this.corpusService.exercise.type === ExerciseType.markWords ? 'mark_words' : 'drag_text';
this.exerciseService.initH5P(exerciseTypePath);
this.updateFileUrl();
}
......@@ -85,7 +99,7 @@ export class PreviewPage implements OnDestroy, OnInit {
});
}
processAnnisResponse(ar: AnnisResponse) {
processAnnisResponse(ar: AnnisResponse): void {
this.corpusService.annisResponse.solutions = ar.solutions;
this.processSolutions(ar.solutions);
this.corpusService.annisResponse.uri = ar.uri;
......@@ -94,7 +108,7 @@ export class PreviewPage implements OnDestroy, OnInit {
this.corpusService.annisResponse.links = isUrn ? this.corpusService.annisResponse.links : ar.links;
}
processSolutions(solutions: Solution[]) {
processSolutions(solutions: Solution[]): void {
const isCloze: boolean = this.corpusService.exercise.type === ExerciseType.cloze;
if (this.exerciseService.excludeOOV) {
const nodeIdSet: Set<string> = new Set(this.corpusService.annisResponse.nodes.filter(
......@@ -115,7 +129,12 @@ export class PreviewPage implements OnDestroy, OnInit {
this.currentSolutions = newSolutions;
}
sendData(result: TestResultMC) {
selectLink(): void {
const ta: HTMLTextAreaElement = document.querySelector(this.textareaSelector);
ta.select();
}
sendData(result: TestResultMC): void {
const fileUrl: string = HelperService.config['backendBaseUrl'] + HelperService.config['backendApiFilePath'];
HelperService.currentError = null;
HelperService.isLoading = true;
......@@ -130,13 +149,22 @@ export class PreviewPage implements OnDestroy, OnInit {
});
}
switchOOV() {
switchOOV(): void {
this.currentSolutions = [];
this.processSolutions(this.corpusService.annisResponse.solutions);
this.initH5P();
}
updateFileUrl() {
toggleShareLink(): void {
if (this.shareLink) {
this.shareLink = '';
} else {
this.shareLink = `${HelperService.baseUrl}/${HelperService.config['frontendExercisePage']}?eid=` +
this.corpusService.annisResponse.exercise_id;
}
}
updateFileUrl(): void {
const fileId: string = this.corpusService.annisResponse.exercise_id;
const fileTypeBase = '&type=';
this.urlBase = HelperService.config['backendBaseUrl'] + HelperService.config['backendApiFilePath'] + '?id=' + fileId + fileTypeBase;
......
......@@ -15,6 +15,7 @@
"bambergCoreVocabularyUrl": "https://www.ccbuchner.de/reihe-0-0/adeo-53/",
"callidusProjectUrl": "https://www.projekte.hu-berlin.de/de/callidus",
"developerMailTo": "mailto:sulzkons@hu-berlin.de",
"frontendExercisePage": "exercise",
"intervalCorporaUpdate": 1209600000,
"localStorageKeyCorpora": "mc/corpora",
"localStorageKeyH5P": "mc/h5p",
......
......@@ -104,6 +104,7 @@
"INVALID_TEXT_RANGE": "Ungültige Textauswahl",
"KWIC": "Beispielkontexte",
"LANGUAGE_CHANGE": "Sprache wählen",
"LINK_COPIED": "Link kopiert",
"MACHINA_CALLIDA": "Machina Callida",
"MACHINA_CALLIDA_BACKEND": "Machina Callida Backend",
"MACHINA_CALLIDA_FRONTEND": "Machina Callida Frontend",
......@@ -136,6 +137,7 @@
"QUERY_VALUE_EMPTY": "Keine Suchanfrage ausgewählt",
"RESULT": "Ergebnis",
"SEARCH": "Durchsuchen...",
"SHARE": "Teilen",
"SHOW_TEXT": "Text anzeigen",
"SOFTWARE_DEPENDENCIES": "Software-Abhängigkeiten",
"SOLUTIONS_SHUFFLE": "Mischen",
......
......@@ -104,6 +104,7 @@
"INVALID_TEXT_RANGE": "Invalid text range",
"KWIC": "Keyword in context",
"LANGUAGE_CHANGE": "Choose language",
"LINK_COPIED": "Link copied",
"MACHINA_CALLIDA": "Machina Callida",
"MACHINA_CALLIDA_BACKEND": "Machina Callida Backend",
"MACHINA_CALLIDA_FRONTEND": "Machina Callida Frontend",
......@@ -136,6 +137,7 @@
"QUERY_VALUE_EMPTY": "Query value is empty",
"RESULT": "Result",
"SEARCH": "Search...",
"SHARE": "Share",
"SHOW_TEXT": "Show text",
"SOFTWARE_DEPENDENCIES": "Software Dependencies",
"SOLUTIONS_SHUFFLE": "Shuffle",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment