...
 
Commits (16)
# ARSnova lite
# frag.jetzt
This is the new frontend for the Audience Response System [ARSnova](https://arsnova.eu) and is currently in beta.
The project aims at creating the best possible user experience with a web-based audience response app. You can head over to [ARSnova lite](https://beta.arsnova.eu) to see it in action.
Nomen est omen: The app's name says it all: it stands for both the app's main purpose and the web address https://frag.jetzt
## Documentation
......@@ -10,4 +8,4 @@ The project aims at creating the best possible user experience with a web-based
## Credits
frag.jetzt is powered by Technische Hochschule Mittelhessen - University of Applied Sciences.
frag.jetzt is powered by Technische Hochschule Mittelhessen | University of Applied Sciences.
......@@ -15,6 +15,14 @@
},
"logLevel": "debug"
},
"/api/bonustoken": {
"target": "http://localhost:8088",
"secure": false,
"pathRewrite": {
"^/api": ""
},
"logLevel": "debug"
},
"/api/settings": {
"target": "http://localhost:8088",
"secure": false,
......
......@@ -41,6 +41,7 @@ import { DemoVideoComponent } from './components/home/_dialogs/demo-video/demo-v
import { HomeCreatorPageComponent } from './components/home/home-creator-page/home-creator-page.component';
import { HomeParticipantPageComponent } from './components/home/home-participant-page/home-participant-page.component';
import { CommentSettingsService } from './services/http/comment-settings.service';
import { BonusTokenService } from './services/http/bonus-token.service';
import { ModeratorModule } from './components/moderator/moderator.module';
import { ImprintComponent } from './components/home/_dialogs/imprint/imprint.component';
import { DataProtectionComponent } from './components/home/_dialogs/data-protection/data-protection.component';
......@@ -150,6 +151,7 @@ export function initializeApp(appConfig: AppConfig) {
VoteService,
ModeratorService,
CommentSettingsService,
BonusTokenService,
WsConnectorService,
{
provide: MatDialogRef,
......
<div mat-dialog-content>
<h1>{{'room-page.bonus-token-header' | translate }}</h1>
<mat-divider></mat-divider>
<div fxLayout="row" *ngFor="let bonusToken of bonusTokens">
<h2>
{{bonusToken.token}}
</h2>
</div>
</div>
import { Component, OnInit } from '@angular/core';
import { BonusTokenService } from '../../../../services/http/bonus-token.service';
import { BonusToken } from '../../../../models/bonus-token';
@Component({
selector: 'app-bonus-token',
templateUrl: './bonus-token.component.html',
styleUrls: ['./bonus-token.component.scss']
})
export class BonusTokenComponent implements OnInit {
roomId: string;
bonusTokens: BonusToken[] = [];
constructor(
private bonusTokenService: BonusTokenService
) {
}
ngOnInit() {
this.bonusTokenService.getTokensByRoomId(this.roomId).subscribe( list => {
this.bonusTokens = list;
});
}
}
......@@ -23,6 +23,7 @@ import { ContentEditComponent } from './_dialogs/content-edit/content-edit.compo
import { ContentPresentationComponent } from './content-presentation/content-presentation.component';
import { CommentExportComponent } from './_dialogs/comment-export/comment-export.component';
import { ModeratorsComponent } from './_dialogs/moderators/moderators.component';
import { BonusTokenComponent } from './_dialogs/bonus-token/bonus-token.component';
import { CommentSettingsComponent } from './_dialogs/comment-settings/comment-settings.component';
import { ModeratorDeleteComponent } from './_dialogs/moderator-delete/moderator-delete.component';
import { DeleteCommentComponent } from './_dialogs/delete-comment/delete-comment.component';
......@@ -59,6 +60,7 @@ import { DeleteCommentsComponent } from './_dialogs/delete-comments/delete-comme
ContentPresentationComponent,
CommentExportComponent,
ModeratorsComponent,
BonusTokenComponent,
CommentSettingsComponent,
ModeratorDeleteComponent,
DeleteCommentsComponent,
......@@ -77,6 +79,7 @@ import { DeleteCommentsComponent } from './_dialogs/delete-comments/delete-comme
ContentEditComponent,
CommentExportComponent,
ModeratorsComponent,
BonusTokenComponent,
CommentSettingsComponent,
ModeratorDeleteComponent,
DeleteCommentsComponent,
......
......@@ -35,6 +35,10 @@
<mat-icon>gavel</mat-icon>
{{ 'room-page.moderators' | translate}}
</button>
<button mat-menu-item (click)="showBonusTokenDialog()" aria-labelledby= "person">
<mat-icon>grade</mat-icon>
{{ 'room-page.bonus-token' | translate}}
</button>
</mat-menu>
<button id="settings-menu"
mat-icon-button class="corner-icons" [matMenuTriggerFor]="settingsMenu" aria-labelledby="settings">
......
......@@ -14,6 +14,7 @@ import { TSMap } from 'typescript-map';
import { WsCommentServiceService } from '../../../services/websockets/ws-comment-service.service';
import { CommentService } from '../../../services/http/comment.service';
import { ModeratorsComponent } from '../_dialogs/moderators/moderators.component';
import { BonusTokenComponent } from '../_dialogs/bonus-token/bonus-token.component';
import { CommentSettingsComponent } from '../_dialogs/comment-settings/comment-settings.component';
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { EventService } from '../../../services/util/event.service';
......@@ -202,6 +203,13 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni
dialogRef.componentInstance.roomId = this.room.id;
}
showBonusTokenDialog(): void {
const dialogRef = this.dialog.open(BonusTokenComponent, {
width: '400px'
});
dialogRef.componentInstance.roomId = this.room.id;
}
copyShortId(): void {
const selBox = document.createElement('textarea');
selBox.style.position = 'fixed';
......
......@@ -3,23 +3,25 @@
<div class="header-container">
<h1 mat-dialog-title tabindex="0" id="cookie-header">{{ 'cookies.title' | translate }}</h1>
<button mat-icon-button aria-labelledby="info-label" class="info" (click)="openDataProtection()">
<mat-icon class="info-icon">security</mat-icon>
</button>
<mat-icon class="info-icon">security</mat-icon>
</button>
</div>
<mat-divider></mat-divider>
<mat-dialog-content tabindex="0">
<app-cookies-de *ngIf="currentLang=='de'"></app-cookies-de>
<app-cookies-en *ngIf="currentLang=='en'"></app-cookies-en>
</mat-dialog-content>
<app-dialog-action-buttons
buttonsLabelSection="cookies"
confirmButtonLabel="accept"
[confirmButtonType]=confirmButtonType
[cancelButtonClickAction]="buildDeclineActionCallback()"
[confirmButtonClickAction]="buildConfirmActionCallback()"
></app-dialog-action-buttons>
<app-dialog-action-buttons
buttonsLabelSection="cookies"
confirmButtonLabel="accept"
[confirmButtonType]=confirmButtonType
[cancelButtonClickAction]="buildDeclineActionCallback()"
[confirmButtonClickAction]="buildConfirmActionCallback()">
</app-dialog-action-buttons>
</mat-dialog-content>
<div class="visually-hidden">
<div id="info-label">{{'cookies.info-label' | translate }}</div>
......
<div fxLayout="column" fxLayoutAlign="center center" fxlayoutgap="20px" fxFill>
<app-room-join></app-room-join>
<button [disabled]="cookiesDisabled()" id="new_session-button" mat-fab class="fab-extended" (click)="createSession()" aria-labelledby="create-label">
<button [disabled]="cookiesDisabled()" id="new_session-button" mat-fab class="fab-extended" (click)="openCreateRoomDialog()" aria-labelledby="create-label">
<mat-icon class="add">add</mat-icon>
{{'home-page.create-session' | translate}}
</button>
......
......@@ -4,9 +4,6 @@ import { MatDialog } from '@angular/material';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { LanguageService } from '../../../services/util/language.service';
import { AuthenticationService } from '../../../services/http/authentication.service';
import { User } from '../../../models/user';
import { UserRole } from '../../../models/user-roles.enum';
@Component({
selector: 'app-new-landing',
......@@ -15,29 +12,15 @@ import { UserRole } from '../../../models/user-roles.enum';
})
export class NewLandingComponent implements OnInit {
user: User;
constructor(private router: Router,
public dialog: MatDialog,
private translateService: TranslateService,
protected langService: LanguageService,
public authenticationService: AuthenticationService) {
protected langService: LanguageService) {
langService.langEmitter.subscribe(lang => translateService.use(lang));
}
ngOnInit() {
this.translateService.use(localStorage.getItem('currentLang'));
this.authenticationService.watchUser.subscribe(newUser => this.user = newUser);
}
createSession() {
if (!this.user) {
this.authenticationService.guestLogin(UserRole.CREATOR).subscribe( () => {
this.openCreateRoomDialog();
});
} else {
this.openCreateRoomDialog();
}
}
openCreateRoomDialog(): void {
......
......@@ -56,7 +56,7 @@ export class CreateCommentComponent implements OnInit {
const comment = new Comment();
comment.roomId = localStorage.getItem(`roomId`);
comment.body = body;
comment.userId = this.user.id;
comment.creatorId = this.user.id;
comment.createdFromLecturer = this.user.role === 1;
this.dialogRef.close(comment);
}
......
......@@ -10,6 +10,7 @@ import { AuthenticationService } from '../../../../services/http/authentication.
import { TranslateService } from '@ngx-translate/core';
import { TSMap } from 'typescript-map';
import { EventService } from '../../../../services/util/event.service';
import { User } from '../../../../models/user';
@Component({
selector: 'app-room-create',
......@@ -21,6 +22,7 @@ export class RoomCreateComponent implements OnInit {
emptyInputs = false;
room: Room;
roomId: string;
user: User;
constructor(
private roomService: RoomService,
......@@ -29,7 +31,7 @@ export class RoomCreateComponent implements OnInit {
private notification: NotificationService,
public dialogRef: MatDialogRef<RoomCreateComponent>,
private translateService: TranslateService,
private authService: AuthenticationService,
private authenticationService: AuthenticationService,
public eventService: EventService,
@Inject(MAT_DIALOG_DATA) public data: any
) {
......@@ -37,12 +39,23 @@ export class RoomCreateComponent implements OnInit {
ngOnInit() {
this.translateService.use(localStorage.getItem('currentLang'));
this.authenticationService.watchUser.subscribe(newUser => this.user = newUser);
}
resetEmptyInputs(): void {
this.emptyInputs = false;
}
checkLogin(longRoomName: string) {
if (!this.user) {
this.authenticationService.guestLogin(UserRole.CREATOR).subscribe(() => {
this.addRoom(longRoomName);
});
} else {
this.addRoom(longRoomName);
}
}
addRoom(longRoomName: string) {
longRoomName = longRoomName.trim();
if (!longRoomName) {
......@@ -64,8 +77,8 @@ export class RoomCreateComponent implements OnInit {
this.translateService.get('home-page.created-1').subscribe(msg => { msg1 = msg; });
this.translateService.get('home-page.created-2').subscribe(msg => { msg2 = msg; });
this.notification.show(msg1 + longRoomName + msg2);
this.authService.setAccess(room.shortId, UserRole.CREATOR);
this.authService.assignRole(UserRole.CREATOR);
this.authenticationService.setAccess(room.shortId, UserRole.CREATOR);
this.authenticationService.assignRole(UserRole.CREATOR);
this.router.navigate([`/creator/room/${this.room.shortId}`]);
this.closeDialog();
});
......@@ -84,7 +97,7 @@ export class RoomCreateComponent implements OnInit {
* Returns a lambda which executes the dialog dedicated action on call.
*/
buildRoomCreateActionCallback(room: HTMLInputElement): () => void {
return () => this.addRoom(room.value);
return () => this.checkLogin(room.value);
}
......
<div mat-dialog-content>
<h1>{{'user-bonus-token-dialog.header' | translate }}</h1>
<mat-divider></mat-divider>
<div fxLayout="row" *ngFor="let bonusToken of bonusTokens">
<h2>
{{bonusToken.token}}
</h2>
</div>
</div>
import { Component, OnInit } from '@angular/core';
import { BonusTokenService } from '../../../../services/http/bonus-token.service';
import { BonusToken } from '../../../../models/bonus-token';
@Component({
selector: 'app-user-bonus-token',
templateUrl: './user-bonus-token.component.html',
styleUrls: ['./user-bonus-token.component.scss']
})
export class UserBonusTokenComponent implements OnInit {
userId: string;
bonusTokens: BonusToken[] = [];
constructor(
private bonusTokenService: BonusTokenService
) {
}
ngOnInit() {
this.bonusTokenService.getTokensByUserId(this.userId).subscribe( list => {
this.bonusTokens = list;
});
}
}
......@@ -35,13 +35,13 @@
<mat-icon class="searchBarIcon">filter_list</mat-icon>
</button>
<button id="pause-comments" mat-fab aria-labelledby="pause" class="actionButton"
<button id="pause-comments" mat-fab aria-labelledby="pause" class="freezeButton"
*ngIf="!searchBox.value && !search && !freeze" (click)="pauseCommentStream()"
matTooltip="{{ 'comment-list.pause-comments' | translate }}">
<mat-icon class="freezeIcon">pause</mat-icon>
</button>
<button id="play-comments" mat-fab aria-labelledby="play" class="actionButton"
<button id="play-comments" mat-fab aria-labelledby="play" class="freezeButton"
*ngIf="!searchBox.value && !search && freeze" (click)="playCommentStream()"
matTooltip="{{ 'comment-list.play-comments' | translate }}">
<mat-icon class="freezeIcon">play_arrow</mat-icon>
......
......@@ -71,14 +71,21 @@ app-comment {
background-color: var(--primary);
}
.freezeButton {
width: 40px;
height: 40px;
margin: 0 2% 0 5%;
background-color: var(--secondary);
}
.actionIcon {
transform: scale(1.5);
color: var(--surface)
color: var(--background)
}
.freezeIcon {
transform: scale(1.3);
color: var(--surface);
color: var(--background);
}
.close {
......
......@@ -194,6 +194,12 @@ export class CommentListComponent implements OnInit {
break;
case this.favorite:
this.comments[i].favorite = <boolean>value;
console.log(this.comments[i]);
if (this.user.id === this.comments[i].creatorId && <boolean>value) {
this.translateService.get('comment-list.comment-got-favorited').subscribe(ret => {
this.notificationService.show(ret);
});
}
break;
case 'score':
this.comments[i].score = <number>value;
......
......@@ -34,6 +34,10 @@
<span *ngIf="!user.isGuest">{{'header.my-sessions' | translate}}</span>
<span *ngIf="user.isGuest" svgIcon="meeting_room">{{'header.visited-sessions' | translate}}</span>
</button>
<button mat-menu-item *ngIf="user" (click)="openUserBonusTokenDialog()">
<mat-icon color="warn">grade</mat-icon>
<span>{{'header.user-bonus-token' | translate}}</span>
</button>
<button mat-menu-item *ngIf="user && !user.isGuest" (click)="openDeleteUserDialog()">
<mat-icon color="warn">delete</mat-icon>
<span>{{'header.delete-account' | translate}}</span>
......
......@@ -15,6 +15,7 @@ import { AppComponent } from '../../../app.component';
import { Rescale } from '../../../models/rescale';
import { KeyboardUtils } from '../../../utils/keyboard';
import { KeyboardKey } from '../../../utils/keyboard/keys';
import { UserBonusTokenComponent } from '../_dialogs/user-bonus-token/user-bonus-token.component';
@Component({
selector: 'app-header',
......@@ -151,6 +152,13 @@ export class HeaderComponent implements OnInit {
});
}
openUserBonusTokenDialog() {
const dialogRef = this.dialog.open(UserBonusTokenComponent, {
width: '600px'
});
dialogRef.componentInstance.userId = this.user.id;
}
cookiesDisabled(): boolean {
return localStorage.getItem('cookieAccepted') === 'false';
}
......
......@@ -19,6 +19,7 @@ import { ChartsModule } from 'ng2-charts';
import { StatisticComponent } from './statistic/statistic.component';
import { RoomJoinComponent } from './room-join/room-join.component';
import { RoomCreateComponent } from './_dialogs/room-create/room-create.component';
import { UserBonusTokenComponent } from './_dialogs/user-bonus-token/user-bonus-token.component';
import { LoginComponent } from './login/login.component';
import { StatisticHelpComponent } from './_dialogs/statistic-help/statistic-help.component';
import { CommentComponent } from './comment/comment.component';
......@@ -54,6 +55,7 @@ import { MatRippleModule } from '@angular/material';
ListStatisticComponent,
StatisticComponent,
RoomCreateComponent,
UserBonusTokenComponent,
LoginComponent,
StatisticHelpComponent,
CommentComponent,
......@@ -88,7 +90,8 @@ import { MatRippleModule } from '@angular/material';
StatisticHelpComponent,
CreateCommentComponent,
PresentCommentComponent,
DeleteAccountComponent
DeleteAccountComponent,
UserBonusTokenComponent
]
})
export class SharedModule {
......
export class BonusToken {
roomId: string;
userId: string;
token: string;
constructor(
roomId: string,
userId: string,
token: string
) {
this.roomId = roomId;
this.userId = userId;
this.token = token;
}
}
......@@ -3,7 +3,7 @@ import { CorrectWrong } from './correct-wrong.enum';
export class Comment {
id: string;
roomId: string;
userId: string;
creatorId: string;
revision: string;
body: string;
read: boolean;
......@@ -16,7 +16,7 @@ export class Comment {
ack: boolean;
constructor(roomId: string = '',
userId: string = '',
creatorId: string = '',
body: string = '',
read: boolean = false,
correct: CorrectWrong = CorrectWrong.NULL,
......@@ -28,7 +28,7 @@ export class Comment {
ack: boolean = true) {
this.id = '';
this.roomId = roomId;
this.userId = userId;
this.creatorId = creatorId;
this.revision = '';
this.body = body;
this.read = read;
......
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { BonusToken } from '../../models/bonus-token';
import { catchError, tap } from 'rxjs/operators';
import { BaseHttpService } from './base-http.service';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable()
export class BonusTokenService extends BaseHttpService {
private apiUrl = {
base: '/api',
bonustoken: '/bonustoken',
find: '/find'
};
constructor(private http: HttpClient) {
super();
}
getTokensByRoomId(roomId: string): Observable<BonusToken[]> {
const connectionUrl = `${this.apiUrl.base + this.apiUrl.bonustoken + this.apiUrl.find}`;
return this.http.post<BonusToken[]>(connectionUrl, {
properties: {
roomId: roomId
}
}).pipe(
tap(() => ''),
catchError(this.handleError<BonusToken[]>(`get bonus token by roomid = ${roomId}`))
);
}
getTokensByUserId(userId: string): Observable<BonusToken[]> {
const connectionUrl = `${this.apiUrl.base + this.apiUrl.bonustoken + this.apiUrl.find}`;
return this.http.post<BonusToken[]>(connectionUrl, {
properties: {
userId: userId
}
}).pipe(
tap(() => ''),
catchError(this.handleError<BonusToken[]>(`get bonus token by userId = ${userId}`))
);
}
}
......@@ -20,7 +20,7 @@ export class WsCommentServiceService {
constructor(private wsConnector: WsConnectorService) { }
add(comment: Comment): void {
const message = new CreateComment(comment.roomId, comment.userId, comment.body);
const message = new CreateComment(comment.roomId, comment.creatorId, comment.body);
this.wsConnector.send(`/queue/comment.command.create`, JSON.stringify(message));
}
......
<p>
Deine Fragen und Bewertungen sind anonym.
Ein »frag.jetzt«-Konto ist nur sinnvoll,
wenn du entweder deine Sitzungen für 180 Tage nach dem letzten Besuch speichern
Ein »frag.jetzt«-Konto ist nur erforderlich,
wenn du deine Sitzungen für 180 Tage nach dem letzten Besuch speichern
oder an einer Sitzung mit Bonusvergabe für interessante Fragen teilnehmen willst.
Du kannst dein Konto jederzeit löschen. Wir geben deine Mail-Adresse nicht weiter.
</p>
<p>
Mit Anklicken des Buttons »Akzeptieren« erklärst du dich mit der Verwendung Local-Storage-Technik sowie unseren Hinweisen zum Datenschutz einverstanden.
Mit Anklicken des Buttons »Akzeptieren« erklärst du dich mit der Verwendung der
<a href="https://de.wikipedia.org/wiki/Web_Storage" target="_blank">Local-Storage-Technik</a>
und unserer Daten&shy;schutz&shy;erklärung einverstanden.
Diese kannst du über das Datenschutz-Symbol einsehen.
</p>
<p>
Your questions and votes are anonymous.
A »frag.jetzt« account only makes sense,
if you either want to save your sessions for 180 days after the last visit
or want to participate in a session with bonuses for interesting questions.
A »frag.&shy;jetzt« account is only required,
if you want to save your sessions for 180 days after the last visit
or want to participate in a session with bonus awarding for interesting questions.
You can delete your account at any time. We will not share your email address with anyone.
</p>
<p>
By clicking the button »Accept« you agree to the use of a local storage as well as our privacy policy.
You can view these by clicking on the data protection symbol.
By clicking the button »Accept« you agree to the use of a
<a href="https://en.wikipedia.org/wiki/Web_storage" target="_blank">local storage</a>
and our privacy policy.
You can view our policy by clicking on the data protection symbol above.
</p>
......@@ -61,7 +61,7 @@
"a11y-text_textForVotes": "Bewertungen",
"a11y-text_wrong": "Diese Frage wurde verneint.",
"abort": "Abbrechen",
"ask-question-description": "Gib hier deine Frage ein!",
"ask-question-description": "Gib hier deine Frage ans Auditorium ein!",
"acknowledge": "Lässt die Frage zu und setzt sie zurück auf die öffentliche Liste",
"cancel": "Abbrechen",
"cancel-description": "Abbrechen",
......@@ -69,7 +69,7 @@
"comma": "Excel-Format",
"delete": "Frage löschen",
"delimiter": "Dateiformat",
"enter-comment": "Frage",
"enter-comment": "Deine Frage ans Auditorium…",
"enter-title": "Titel",
"error-both-fields": "Bitte fülle alle Felder aus.",
"error-comment": "Bitte gib eine Frage ein.",
......@@ -161,6 +161,8 @@
"a11y-threshold": "Schieberegler zum Einstellen des Schwellenwerts für sichtbare Fragen",
"abort": "Abbrechen",
"answer-statistics": "Statistiken",
"bonus-token": "Tokens für Bonuspunkte",
"bonus-token-header": "Tokens für Bonuspunkte",
"cancel": "Abbrechen",
"cancel-description": "Abbrechen",
"changes-successful": "Änderungen gespeichert.",
......
......@@ -63,14 +63,14 @@
"a11y-text_wrong": "Die Frage wurde verneint.",
"abort": "Cancel",
"acknowledge": "Let the question go",
"ask-question-description": "Enter your question here!",
"ask-question-description": "Enter your question to the audience here!",
"cancel": "Cancel",
"cancel-description": "Cancel",
"comma": "Excel format",
"comment": "Question {{ comment }} was asked at {{ time }} and has currently {{ votes }}. {{correct}} {{wrong}} {{bonus}} {{beamer}}",
"delete": "Delete question",
"delimiter": "Please choose a format!",
"enter-comment": "Question",
"enter-comment": "Your question to the audience…",
"enter-title": "Title",
"error-both-fields": "Please fill in all fields.",
"error-comment": "Please ask a question.",
......@@ -162,6 +162,8 @@
"a11y-threshold": "Slider for setting the threshold for visible questions",
"abort": "Abort",
"answer-statistics": "Statistics",
"bonus-token": "Tokens for bonus points",
"bonus-token-header": "Tokens for bonus points",
"cancel": "Cancel",
"cancel-description": "Cancel",
"changes-successful": "Successfully updated.",
......
......@@ -2,7 +2,7 @@
<div class="videoWrapper">
<iframe tabindex="0"
width="560" height="315"
src="https://youtu.be/qHRC6z9VtWs?autoplay=0" frameborder="0"
src="https://www.youtube.com/embed/qHRC6z9VtWs?autoplay=0" frameborder="0"
allowfullscreen
></iframe>
</div>
......@@ -61,7 +61,14 @@
»frag.jetzt« wird als kostenlose »Software as a Service« von der TransMIT GmbH betrieben, siehe Impressum.
</p>
<h2>Handy-Nutzung</h2>
<h2>Handy-Nutzung im Hörsaal</h2>
<p>
Die Nutzung des Smartphones während der Vorlesung bringt natürlich ein hohes Ablenkungsrisiko mit sich.
Wir haben aus unserer langjährigen Erfahrung mit Audience-Response-Systemen im Hörsaal gelernt
(siehe unser <a href="https://arsnova.thm.de/blog/" target="_blank">ARSnova-Projekt</a>),
wie man das Ablenkungsrisiko didaktisch angehen kann:
</p>
<ol>
<li>
......
......@@ -2,7 +2,7 @@
<div class="videoWrapper">
<iframe tabindex="0"
width="560" height="315"
src="https://youtu.be/qHRC6z9VtWs?autoplay=0" frameborder="0"
src="https://www.youtube.com/embed/qHRC6z9VtWs?autoplay=0" frameborder="0"
allowfullscreen
></iframe>
</div>
......@@ -63,7 +63,14 @@
»frag.jetzt« is operated as free »Software as a Service« by TransMIT GmbH, see imprint.
</p>
<h2>Mobile phone use</h2>
<h2>Mobile phone use in the lecture hall</h2>
<p>
The use of the smartphone during the lecture naturally entails a high risk of distraction.
We have learned from our many years of experience with audience response systems in lecture halls,
see our <a href="https://arsnova.thm.de/blog/en/homepage/" target="_blank">ARSnova Project</a>,
how to didactically approach the risk of distraction:
</p>
<ol>
<li>
......
......@@ -9,7 +9,7 @@
<iframe tabindex="0"
width="560" height="349"
[ngClass]="{'mobileFrame': deviceType === 'mobile', 'desktopFrame': deviceType === 'desktop'}"
src="https://www.youtube.com/embed/RODwmMxLKa0?autoplay=0" frameborder="0"
src="https://www.youtube.com/embed/qHRC6z9VtWs?autoplay=0" frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
></iframe>
</div>
......
......@@ -59,6 +59,7 @@
"my-sessions": "Meine Sitzungen",
"really-delete-account": "Willst du dein Konto mit allen Sitzungen unwiderruflich löschen?",
"sure": "Bist du sicher?",
"user-bonus-token": "Zu deinen Bonus Tokens",
"visited-sessions": "Besuchte Sitzungen"
},
"help": {
......@@ -122,7 +123,7 @@
"restart-account-activation-button": "Erneut senden",
"restart-account-activation-correct": "Der Aktivierungsschlüssel wurde erneut gesendet.",
"restart-account-activation-tooltip": "Sendet den Aktivierungsschlüssel erneut an die angegebene Adresse",
"welcome": "Online fragen & Fragen bewerten"
"welcome": "Fragen stellen | Fragen bewerten"
},
"overlay": {
"cancel": "Doch akzeptieren?",
......@@ -220,5 +221,8 @@
"activation-key-input-description": "Gib hier den Aktivierungs-Schlüssel ein, den du per E-Mail zugesendet bekommen hast.",
"cancel": "Abbrechen",
"cancel-description": "Abbrechen"
},
"user-bonus-token": {
"header": "Tokens für Bonuspunkte"
}
}
......@@ -60,6 +60,7 @@
"my-sessions": "My sessions",
"really-delete-account": "Do you really want to irrevocably delete your account with the associated sessions?",
"sure": "Are you sure?",
"user-bonus-token": "See your bonus tokens",
"visited-sessions": "Visited sessions"
},
"help": {
......@@ -124,7 +125,7 @@
"restart-account-activation-button": "Send again",
"restart-account-activation-correct": "Activation key sent again",
"restart-account-activation-tooltip": "Sends the activation key again",
"welcome": "Ask online & vote questions"
"welcome": "ask questions | vote questions"
},
"overlay": {
"cancel": "Accept after all?",
......@@ -222,5 +223,8 @@
"activation-key-input-description": "Enter the activation key that you received by e-mail.",
"cancel": "Cancel",
"cancel-description": "Cancel"
},
"user-bonus-token-dialog": {
"header": "Tokens for bonus points"
}
}
<div class="max-height">
<p>
»frag.jetzt« wird vom Land Hessen im Rahmen des HMWK-Verbundprojekts <a href="https://www.digll-hessen.de/de" target="_blank">»Digital gestütztes Lehren und Lernen in Hessen«</a> (digLL) gefördert.<br>
»frag.jetzt« wird vom Land Hessen im Rahmen des <abbr title="Hessisches Ministerium für Wissenschaft und Kunst">HMWK</abbr>-Verbundprojekts <a href="https://www.digll-hessen.de/de" target="_blank">»Digital gestütztes Lehren und Lernen in Hessen«</a> (digLL) gefördert.<br>
<br>
<img src="https://arsnova.thm.de/blog/wp-content/uploads/2015/07/HMWK-Logo-300x136.png" alt="HMWK-Logo" width="200px"><br>
<br>
......
<div class="max-height">
<p>
"frag.jetzt" is funded by the State of the Federal State of Hessen as part of the HMWK joint project <a
href="https://www.digll-hessen.de/de" target="_blank">"Digitally supported teaching and learning in the State of Hessen"</a> (digLL).<br>
»frag.jetzt« is funded by the Federal State of Hessen as part of the <abbr title="Hessisches Ministerium für Wissenschaft und Kunst">HMWK</abbr> joint project <a
href="https://www.digll-hessen.de/de" target="_blank">»Digitally supported teaching and learning in the State of Hessen«</a> (digLL).<br>
<br>
<img src="https://arsnova.thm.de/blog/wp-content/uploads/2015/07/HMWK-Logo-300x136.png" alt="HMWK-Logo" width="200px"><br>
<br>
......@@ -10,7 +10,7 @@
</p>
<p>
<a href="https://arsnova.thm.de/blog/wp-content/uploads/2019/09/Projektantrag-DigLL@THM-frag_jetzt.pdf" target="_blank">Project outline for "frag.jetzt"</a>
<a href="https://arsnova.thm.de/blog/wp-content/uploads/2019/09/Projektantrag-DigLL@THM-frag_jetzt.pdf" target="_blank">Project outline for »frag.jetzt«</a>
</p>
<p>
......
......@@ -28,6 +28,7 @@
"add-comment": "Stell deine Frage!",
"pause-comments": "Pausiere den Fragen-Stream",
"play-comments": "Starte den Fragen-Stream",
"comment-got-favorited": "Eine Frage von dir wurde favorisiert!",
"comment-stream-stopped": "Der Fragen-Stream wurde gestoppt.",
"comment-stream-started": "Der Fragen-Stream wurde gestartet.",
"comment-sent": "Die Frage wurde veröffentlicht.",
......@@ -64,11 +65,11 @@
"a11y-text_textForVotes": "Bewertungen",
"a11y-text_wrong": "Diese Frage wurde verneint.",
"abort": "Abbrechen",
"ask-question-description": "Gib hier deine Frage ein!",
"ask-question-description": "Gib hier deine Frage an den Prof ein!",
"cancel": "Abbrechen",
"cancel-description": "Abbrechen",
"comment": "Die Frage {{ comment }} wurde um {{ time }} Uhr gestellt und hat derzeitig {{ votes }}. {{correct}} {{wrong}} {{bonus}} {{beamer}}",
"enter-comment": "Deine Frage",
"enter-comment": "Deine Frage an den Prof…",
"enter-title": "Titel",
"error-both-fields": "Bitte fülle alle Felder aus.",
"error-comment": "Bitte gib deine Frage ein.",
......
......@@ -28,6 +28,7 @@
"add-comment": "Ask a question!",
"pause-comments": "Pause the question stream",
"play-comments": "Start the question stream",
"comment-got-favorited": "A question of yours got favorited!",
"comment-stream-stopped": "Question stream has been stopped.",
"comment-stream-started": "Question stream has been started.",
"comment-sent": "The question has been published.",
......@@ -64,11 +65,11 @@
"a11y-text_textForVotes": ",Votes",
"a11y-text_wrong": "This question was marked as not correct.",
"abort": "Cancel",
"ask-question-description": "Enter your question here!",
"ask-question-description": "Enter your question to the professor here!",
"cancel": "Cancel",
"cancel-description": "Cancel",
"comment": "Question {{ comment }} was asked at {{ time }} and has currently {{ votes }}. {{correct}} {{wrong}} {{bonus}} {{beamer}}",
"enter-comment": "Your question",
"enter-comment": "Your question to the professor…",
"enter-title": "Title",
"error-both-fields": "Please fill in all fields.",
"error-comment": "Please enter a question.",
......