Commit d4b30506 authored by Konstantin Schulz's avatar Konstantin Schulz
Browse files

fixed layout for mobile usage

parent 0efc170e
......@@ -3,7 +3,7 @@
<name>mcClient</name>
<description>An awesome Ionic/Cordova app.</description>
<author email="hi@ionicframework" href="http://ionicframework.com/">Ionic Framework Team</author>
<content src="index.html" />
<content original-src="index.html" src="http://192.168.1.9:8100" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
......@@ -77,6 +77,8 @@
</platform>
<plugin name="cordova-plugin-whitelist" spec="1.3.3" />
<plugin name="cordova-plugin-device" spec="2.0.2" />
<plugin name="cordova-plugin-ionic-webview" spec="1.1.19" />
<plugin name="cordova-plugin-ionic-keyboard" spec="2.0.5" />
<plugin name="cordova-plugin-ionic-webview" spec="^2.2.5" />
<allow-navigation href="http://192.168.1.9:8100" sessionid="5af1af03" />
<engine name="android" spec="7.0.0" />
</widget>
This diff is collapsed.
{
"name": "mcClient",
"version": "0.3.0",
"version": "0.3.1",
"author": "Ionic Framework",
"homepage": "http://ionicframework.com/",
"private": true,
......@@ -21,18 +21,20 @@
"@angular/http": "^5.2.11",
"@angular/platform-browser": "^5.2.11",
"@angular/platform-browser-dynamic": "^5.2.11",
"@ionic-native/core": "^4.17.0",
"@ionic-native/status-bar": "^4.17.0",
"@ionic-native/core": "^5.0.0-beta.21",
"@ionic-native/ionic-webview": "^5.0.0-beta.21",
"@ionic-native/status-bar": "^5.0.0-beta.21",
"@ionic/storage": "^2.2.0",
"@ngx-translate/core": "^9.1.1",
"@ngx-translate/http-loader": "^2.0.1",
"@types/xml2js": "^0.4.3",
"ajv": "^5.0.0",
"cordova-android": "7.0.0",
"cordova-plugin-device": "^2.0.2",
"cordova-plugin-ionic-keyboard": "^2.1.3",
"cordova-plugin-ionic-webview": "^2.2.3",
"cordova-plugin-ionic-webview": "^2.2.5",
"cordova-plugin-whitelist": "^1.3.3",
"ionic": "^4.3.1",
"ionic": "^4.5.0",
"ionic-angular": "3.9.2",
"ionicons": "^4.4.7",
"rxjs": "^5.5.11",
......@@ -49,9 +51,11 @@
"plugins": {
"cordova-plugin-whitelist": {},
"cordova-plugin-device": {},
"cordova-plugin-ionic-webview": {},
"cordova-plugin-ionic-keyboard": {}
"cordova-plugin-ionic-keyboard": {},
"cordova-plugin-ionic-webview": {}
},
"platforms": []
"platforms": [
"android"
]
}
}
}
\ No newline at end of file
import {Component} from '@angular/core';
import {Config, Platform} from 'ionic-angular';
import {StatusBar} from '@ionic-native/status-bar';
import {StatusBar} from '@ionic-native/status-bar/ngx';
import {HomePage} from '../pages/home/home';
import {TranslateService} from "@ngx-translate/core";
......@@ -12,12 +12,12 @@ import {HelperProvider} from "../providers/helper/helper";
export class MCclient {
rootPage: any = HomePage;
constructor(platform: Platform, statusBar: StatusBar, private translate: TranslateService, private config: Config,
constructor(platform: Platform, public statusBar: StatusBar, private translate: TranslateService, private config: Config,
public helperProvider: HelperProvider) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
this.statusBar.styleDefault();
});
this.initTranslate();
this.helperProvider.initConfig();
......@@ -32,6 +32,8 @@ export class MCclient {
} else {
this.translate.use(this.translate.getDefaultLang()); // Set your language here
}
// TODO: DELETE THIS
this.translate.use('de');
this.translate.get(['BACK_BUTTON_TEXT']).subscribe(values => {
this.config.set('ios', 'backButtonText', values.BACK_BUTTON_TEXT);
......
import {BrowserModule} from '@angular/platform-browser';
import {ErrorHandler, NgModule} from '@angular/core';
import {IonicApp, IonicErrorHandler, IonicModule} from 'ionic-angular';
import {StatusBar} from '@ionic-native/status-bar';
import {StatusBar} from '@ionic-native/status-bar/ngx';
import {MCclient} from './app.component';
import {CorpusProvider} from '../providers/corpus/corpus';
......@@ -19,6 +19,8 @@ import {HelperProvider} from '../providers/helper/helper';
import {FeedbackPageModule} from "../pages/feedback/feedback.module";
import {VocabularyCheckPageModule} from "../pages/vocabulary-check/vocabulary-check.module";
import {VocabularyProvider} from '../providers/vocabulary/vocabulary';
import {ExerciseParametersPageModule} from "../pages/exercise-parameters/exercise-parameters.module";
import {RankingPageModule} from "../pages/ranking/ranking.module";
// The translate loader needs to know where to load i18n files
// in Ionic's static asset pipeline.
......@@ -52,10 +54,12 @@ class CustomErrorHandler extends IonicErrorHandler {
}),
AuthorDetailPageModule,
AuthorPageModule,
TextRangePageModule,
ExerciseParametersPageModule,
FeedbackPageModule,
PreviewPageModule,
RankingPageModule,
ShowTextPageModule,
TextRangePageModule,
VocabularyCheckPageModule
],
bootstrap: [IonicApp],
......
{
"AUTHORS": "Autoren",
"AUTHOR_SEARCH": "Autor suchen...",
"AUTHOR_SELECT": "Autor auswählen",
"CALLIDUS_PROJECT": "CALLIDUS-Projekt",
"CANCEL": "Abbrechen",
"CASE_ABLATIVE": "Ablativ",
"CASE_ACCUSATIVE": "Akkusativ",
"CASE_DATIVE": "Dativ",
......@@ -45,7 +45,7 @@
"DEPENDENT_WORD": "Abhängiges Wort",
"EMAIL": "E-Mail",
"END": "Ende",
"EXERCISE_DOWNLOAD_NEXT_STEPS": "Nächste Schritte:",
"EXERCISE_DOWNLOAD_NEXT_STEPS": "Nächste Schritte (XML)",
"EXERCISE_FEEDBACK": "Feedback",
"EXERCISE_FEEDBACK_CORRECT": "korrekt",
"EXERCISE_FEEDBACK_CORRECT_DEFAULT": "Das ist korrekt. Gut gemacht!",
......@@ -56,6 +56,8 @@
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT": "teilweise korrekt",
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "Das ist teilweise korrekt.",
"EXERCISE_GENERATE": "Aufgabe erstellen",
"EXERCISE_PARAMETERS": "Übungsparameter",
"EXERCISE_SET_PARAMETERS": "Übungsparameter festlegen",
"EXERCISE_TYPE": "Aufgabentyp",
"EXERCISE_TYPE_CLOZE": "Lückentext",
"EXERCISE_TYPE_MATCHING": "Zuordnung",
......
{
"AUTHORS": "Authors",
"AUTHOR_SEARCH": "Search author...",
"AUTHOR_SELECT": "Select author",
"CALLIDUS_PROJECT": "CALLIDUS Project",
"CANCEL": "Cancel",
"CASE_ABLATIVE": "Ablative",
"CASE_ACCUSATIVE": "Accusative",
"CASE_DATIVE": "Dative",
......@@ -45,7 +45,7 @@
"DEPENDENT_WORD": "Base word",
"EMAIL": "E-Mail",
"END": "End",
"EXERCISE_DOWNLOAD_NEXT_STEPS": "Next steps:",
"EXERCISE_DOWNLOAD_NEXT_STEPS": "Next steps (XML)",
"EXERCISE_FEEDBACK": "Feedback",
"EXERCISE_FEEDBACK_CORRECT": "correct",
"EXERCISE_FEEDBACK_CORRECT_DEFAULT": "That's correct. Well done!",
......@@ -56,6 +56,8 @@
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT": "partially correct",
"EXERCISE_FEEDBACK_PARTIALLY_CORRECT_DEFAULT": "That is partially correct.",
"EXERCISE_GENERATE": "Create exercise",
"EXERCISE_PARAMETERS": "Exercise parameters",
"EXERCISE_SET_PARAMETERS": "Set exercise parameters",
"EXERCISE_TYPE": "Exercise type",
"EXERCISE_TYPE_CLOZE": "Cloze",
"EXERCISE_TYPE_MATCHING": "Matching",
......
......@@ -9,10 +9,10 @@
<ion-navbar>
<ion-title>{{corpusProvider.currentAuthor.name}}</ion-title>
<ion-buttons end>
<button ion-button icon-only color="primary" (click)="navCtrl.push(HomePage)">
<button ion-button icon-only color="primary" (click)="HelperProvider.goToHomePage(navCtrl)">
<ion-icon name="home"></ion-icon>
</button>
<button ion-button icon-only color="primary" (click)="navCtrl.push(FeedbackPage)">
<button ion-button icon-only color="primary" (click)="HelperProvider.goToFeedbackPage(navCtrl)">
<ion-icon name="help-circle"></ion-icon>
</button>
</ion-buttons>
......@@ -25,7 +25,7 @@
<ion-list>
<ion-item-sliding *ngFor="let corpus of corpusProvider.currentAuthor.corpora">
<button ion-item (click)="showPossibleReferences(corpus)">
<p>{{corpus.title}}</p>
<h1>{{corpus.title}}</h1>
</button>
</ion-item-sliding>
</ion-list>
......
......@@ -5,8 +5,7 @@ import {CorpusMC} from "../../models/corpusMC";
import {TranslateService} from "@ngx-translate/core";
import {HttpClient} from "@angular/common/http";
import {TextRangePage} from "../text-range/text-range";
import {FeedbackPage} from "../feedback/feedback";
import {HomePage} from "../home/home";
import {HelperProvider} from "../../providers/helper/helper";
/**
* Generated class for the AuthorDetailPage page.
......@@ -21,9 +20,7 @@ import {HomePage} from "../home/home";
templateUrl: 'author-detail.html',
})
export class AuthorDetailPage {
FeedbackPage = FeedbackPage;
HomePage = HomePage;
HelperProvider = HelperProvider;
constructor(public navCtrl: NavController, public navParams: NavParams,
public corpusProvider: CorpusProvider,
......
......@@ -4,10 +4,10 @@
{{ 'AUTHOR_SELECT' | translate }}
</ion-title>
<ion-buttons end>
<button ion-button icon-only color="primary" (click)="navCtrl.push(HomePage)">
<button ion-button icon-only color="primary" (click)="HelperProvider.goToHomePage(navCtrl)">
<ion-icon name="home"></ion-icon>
</button>
<button ion-button icon-only color="primary" (click)="navCtrl.push(FeedbackPage)">
<button ion-button icon-only color="primary" (click)="HelperProvider.goToFeedbackPage(navCtrl)">
<ion-icon name="help-circle"></ion-icon>
</button>
</ion-buttons>
......@@ -15,14 +15,12 @@
</ion-header>
<ion-content padding>
<ion-searchbar (ionInput)="getAuthors($event)" placeholder="{{ 'AUTHOR_SEARCH' | translate }}"></ion-searchbar>
<ion-searchbar (ionInput)="getAuthors($event.target.value)" (ionCancel)="getAuthors('')"
placeholder="{{ 'AUTHOR_SEARCH' | translate }}"></ion-searchbar>
<ion-list *ngIf="corpusProvider.availableAuthors.length > 0; else loading">
<ion-title>
{{ 'AUTHORS' | translate }}
</ion-title>
<ion-item-sliding *ngFor="let author of authorsDisplayed">
<button ion-item (click)="showCorpora(author)">
<p>{{author.name}}</p>
<h1>{{author.name}}</h1>
</button>
</ion-item-sliding>
</ion-list>
......
page-author {
ion-list {
max-height: 80%;
overflow-y: scroll;
}
.searchbar-input {
font-size: 2em !important;
}
}
......@@ -5,8 +5,7 @@ import {TranslateService} from "@ngx-translate/core";
import {Author} from "../../models/author";
import {CorpusProvider} from "../../providers/corpus/corpus";
import {AuthorDetailPage} from "../author-detail/author-detail";
import {FeedbackPage} from "../feedback/feedback";
import {HomePage} from "../home/home";
import {HelperProvider} from "../../providers/helper/helper";
/**
* Generated class for the AuthorPage page.
......@@ -22,8 +21,7 @@ import {HomePage} from "../home/home";
})
export class AuthorPage {
public authorsDisplayed: Author[];
FeedbackPage = FeedbackPage;
HomePage = HomePage;
HelperProvider = HelperProvider;
constructor(public navCtrl: NavController,
public translate: TranslateService,
......@@ -34,14 +32,10 @@ export class AuthorPage {
this.corpusProvider.isTextRangeCorrect = false;
}
getAuthors(event) { // Type: InputEvent
let value: string = event.target.value;
if (value === "") {
getAuthors(value: string) {
if (!value) {
this.authorsDisplayed = this.corpusProvider.availableAuthors;
}
else if (!value || !value.trim()) {
this.authorsDisplayed = [];
}
else {
this.authorsDisplayed = this.corpusProvider.availableAuthors.filter((author: Author) => {
return AuthorPage.filterAuthor(author, value)
......
<!--
Generated template for the ExerciseParametersPage page.
See http://ionicframework.com/docs/components/#navigation for more info on
Ionic pages and navigation.
-->
<ion-header>
<ion-navbar>
<ion-title>{{ 'EXERCISE_PARAMETERS' | translate }}</ion-title>
<ion-buttons end>
<button ion-button icon-only color="primary" (click)="HelperProvider.goToHomePage(navCtrl)">
<ion-icon name="home"></ion-icon>
</button>
<button ion-button icon-only color="primary" (click)="HelperProvider.goToFeedbackPage(navCtrl)">
<ion-icon name="help-circle"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-grid>
<ion-row>
<ion-col>
<!-- We don't use ion-select because the font-size for ion-option cannot be changed -->
<label>
<div class="label">
{{ 'EXERCISE_TYPE' | translate }}
</div>
<select margin-left [(ngModel)]="exerciseProvider.exercise.type" name="exerciseType"
(change)="adjustTranslations()">
<option *ngFor="let key of ObjectKeys(ExerciseType)" [value]=ExerciseType[key]>
{{ ExerciseTypeTranslation[key] | translate }}
</option>
</select>
</label>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-label>
{{ 'INSTRUCTIONS' | translate }}
</ion-label>
<ion-input [(ngModel)]="exerciseProvider.exercise.instructionsTranslation"
name="instructionsTranslation"></ion-input>
</ion-col>
</ion-row>
<ion-row *ngFor="let query of exerciseProvider.exercise.queryItems; let i = index">
<ion-col>
<ion-grid [class.matching]="exerciseProvider.exercise.type === ExerciseType.matching">
<ion-row *ngIf="exerciseProvider.exercise.type === ExerciseType.matching">
<ion-col><h3>{{ (i === 0 ? 'HEAD_WORD' : 'DEPENDENT_WORD') | translate }}</h3>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<label>
<div class="label">
{{ 'QUERY_PHENOMENON' | translate }}
</div>
<select margin-left [(ngModel)]="query.phenomenon" name="queryPhenomenon"
(change)="exerciseProvider.adjustQueryValue(query)">
<option *ngFor="let phenomenon of ObjectKeys(Phenomenon)" [value]=phenomenon>
{{ PhenomenonTranslation[phenomenon] | translate }}
</option>
</select>
</label>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<label>
<div class="label">
{{ 'QUERY_VALUE' | translate }}
</div>
<select *ngIf="exerciseProvider.exercise.type === ExerciseType.cloze; else matching"
margin-left [(ngModel)]="query.values" name="queryValue" multiple>
<option *ngFor="let key of getSortedQueryValues(query)" [value]=key>
{{ exerciseProvider.phenomenonMap[query.phenomenon][0][key] + " (" +
exerciseProvider.phenomenonMap[query.phenomenon][2][key] + ")" }}
</option>
</select>
</label>
<ng-template #matching>
<select margin-left [(ngModel)]="query.values[0]" name="queryValue">
<option *ngFor="let key of getSortedQueryValues(query)" [value]=key>
{{ exerciseProvider.phenomenonMap[query.phenomenon][0][key] + " (" +
exerciseProvider.phenomenonMap[query.phenomenon][2][key] + ")" }}
</option>
</select>
</ng-template>
</ion-col>
</ion-row>
</ion-grid>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<button *ngIf="showFeedback; else dropright" icon-only color="primary"
(click)="showFeedback = !showFeedback">
<ion-icon name="arrow-dropdown"></ion-icon>
</button>
<ng-template #dropright>
<button icon-only color="primary" (click)="showFeedback = !showFeedback">
<ion-icon name="arrow-dropright"></ion-icon>
</button>
</ng-template>
<h3>{{ 'EXERCISE_FEEDBACK' | translate }}</h3>
</ion-col>
</ion-row>
<ion-row *ngIf="showFeedback">
<ion-col>
<ion-label>{{ 'EXERCISE_FEEDBACK_GENERAL' | translate }}</ion-label>
<ion-textarea [(ngModel)]="exerciseProvider.exercise.feedback.general"
name="feedbackGeneral"></ion-textarea>
</ion-col>
</ion-row>
<ion-row *ngIf="showFeedback">
<ion-col>
<ion-label>{{ 'EXERCISE_FEEDBACK_CORRECT' | translate }}</ion-label>
<ion-textarea [(ngModel)]="exerciseProvider.exercise.feedback.correct"
name="feedbackCorrect"></ion-textarea>
</ion-col>
</ion-row>
<ion-row *ngIf="showFeedback">
<ion-col>
<ion-label>{{ 'EXERCISE_FEEDBACK_PARTIALLY_CORRECT' | translate }}</ion-label>
<ion-textarea [(ngModel)]="exerciseProvider.exercise.feedback.partiallyCorrect"
name="feedbackPartiallyCorrect"></ion-textarea>
</ion-col>
</ion-row>
<ion-row *ngIf="showFeedback">
<ion-col>
<ion-label>{{ 'EXERCISE_FEEDBACK_INCORRECT' | translate }}</ion-label>
<ion-textarea [(ngModel)]="exerciseProvider.exercise.feedback.incorrect"
name="feedbackIncorrect"></ion-textarea>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<button ion-button large (click)="generateExercise()">{{ 'PREVIEW' | translate }}</button>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
import {NgModule} from '@angular/core';
import {IonicPageModule} from 'ionic-angular';
import {ExerciseParametersPage} from './exercise-parameters';
import {TranslateModule} from "@ngx-translate/core";
@NgModule({
declarations: [
ExerciseParametersPage,
],
imports: [
IonicPageModule.forChild(ExerciseParametersPage),
TranslateModule.forChild()
],
})
export class ExerciseParametersPageModule {
}
page-exercise-parameters {
ion-content {
font-size: 1.5em;
}
.label {
font-weight: bold;
}
h3 {
display: inline-block;
}
.matching {
border: 2px solid lightgray;
}
}
import {Component} from '@angular/core';
import {IonicPage, NavController, NavParams, ToastController} from 'ionic-angular';
import {ExerciseProvider} from "../../providers/exercise/exercise";
import {
ExerciseType,
ExerciseTypeTranslation,
InstructionsTranslation,
Phenomenon,
PhenomenonTranslation
} from "../../models/enum";
import {PreviewPage} from "../preview/preview";
import {HelperProvider} from "../../providers/helper/helper";
import {TranslateService} from "@ngx-translate/core";
import {CorpusProvider} from "../../providers/corpus/corpus";
import {QueryMC} from "../../models/queryMC";
import {HomePage} from "../home/home";
import {FeedbackPage} from "../feedback/feedback";
/**
* Generated class for the ExerciseParametersPage page.
*
* See https://ionicframework.com/docs/components/#navigation for more info on
* Ionic pages and navigation.
*/
@IonicPage()
@Component({
selector: 'page-exercise-parameters',
templateUrl: 'exercise-parameters.html',
})
export class ExerciseParametersPage {
ObjectKeys = Object.keys;
public ExerciseType = ExerciseType;
Phenomenon = Phenomenon;
textTooLongString: string;
emptyQueryValueString: string;
ExerciseTypeTranslation = ExerciseTypeTranslation;
PhenomenonTranslation = PhenomenonTranslation;
showFeedback: boolean = false;
HelperProvider = HelperProvider;
constructor(public navCtrl: NavController, public navParams: NavParams,
public exerciseProvider: ExerciseProvider,
public toastCtrl: ToastController,
public translateService: TranslateService,
public corpusProvider: CorpusProvider) {
this.translateService.get("TEXT_TOO_LONG").subscribe(value => this.textTooLongString = value + HelperProvider.config["maxTextLength"]);
this.translateService.get("QUERY_VALUE_EMPTY").subscribe(value => this.emptyQueryValueString = value);
}
adjustTranslations() {
this.translateService.get(ExerciseTypeTranslation[this.exerciseProvider.exercise.type]).subscribe(value => this.exerciseProvider.exercise.typeTranslation = value);
this.translateService.get(InstructionsTranslation[this.exerciseProvider.exercise.type]).subscribe(value => this.exerciseProvider.exercise.instructionsTranslation = value);
if (this.exerciseProvider.exercise.type === ExerciseType.matching) {
this.exerciseProvider.exercise.queryItems = [new QueryMC({
phenomenon: Phenomenon.partOfSpeech,
values: [Object.keys(this.exerciseProvider.phenomenonMap[Phenomenon.partOfSpeech][2])[0]],
}), new QueryMC({
phenomenon: Phenomenon.partOfSpeech,
values: [Object.keys(this.exerciseProvider.phenomenonMap[Phenomenon.partOfSpeech][2])[0]],
})];
}
else if (this.exerciseProvider.exercise.queryItems.length > 1) {
this.exerciseProvider.exercise.queryItems.splice(1, 1);
}
}
generateExercise() {
if (0 < HelperProvider.config["maxTextLength"] && HelperProvider.config["maxTextLength"] < this.corpusProvider.currentText.length) {
let toast = this.toastCtrl.create({
message: this.textTooLongString,
duration: 3000,
position: 'top'
});
toast.present().then();
}
else if (this.exerciseProvider.exercise.queryItems[0].phenomenon === Phenomenon.lemma && !this.exerciseProvider.exercise.queryItems[0].values) {
let toast = this.toastCtrl.create({
message: this.emptyQueryValueString,
duration: 3000,
position: 'top'
});
toast.present().then();
}
else {
this.exerciseProvider.annisResponse.solutions = null;
// TODO: DISABLE HELPERPROVIDER ISVOCABULARYCHECK
this.navCtrl.push(PreviewPage).then();
}
}
getSortedQueryValues(query: QueryMC) {
let targetObject: object = this.exerciseProvider.phenomenonMap[query.phenomenon][0];
return Object.keys(this.exerciseProvider.phenomenonMap[query.phenomenon][2]).sort((a, b) => {
return a === b ? 0 : (targetObject[a] < targetObject[b] ? -1 : 1);
});
}
}