Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Tabu
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
9
Issues
9
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Container Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dennis Willers
Tabu
Commits
1b4ef211
Commit
1b4ef211
authored
Feb 13, 2021
by
Dennis Willers
🏀
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement new History Feature
parent
c797d4ab
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
663 additions
and
61 deletions
+663
-61
app.component.ts
src/app/app.component.ts
+0
-2
app.module.ts
src/app/app.module.ts
+7
-1
TabuMiddlewareService.ts
src/app/dao/TabuMiddlewareService.ts
+12
-0
socketDataService.ts
src/app/dao/socketDataService.ts
+12
-1
game.component.html
src/app/game/game.component.html
+16
-3
game.component.scss
src/app/game/game.component.scss
+5
-0
game.component.ts
src/app/game/game.component.ts
+25
-12
guesser.component.html
src/app/guesser/guesser.component.html
+13
-1
guesser.component.ts
src/app/guesser/guesser.component.ts
+16
-4
gameStatus.ts
src/app/interface/gameStatus.ts
+3
-0
overview.component.html
src/app/overview/overview.component.html
+39
-1
overview.component.scss
src/app/overview/overview.component.scss
+8
-0
overview.component.ts
src/app/overview/overview.component.ts
+82
-36
round-history.component.html
src/app/round-history/round-history.component.html
+133
-0
round-history.component.scss
src/app/round-history/round-history.component.scss
+48
-0
round-history.component.spec.ts
src/app/round-history/round-history.component.spec.ts
+25
-0
round-history.component.ts
src/app/round-history/round-history.component.ts
+215
-0
rules.component.html
src/app/rules/rules.component.html
+4
-0
No files found.
src/app/app.component.ts
View file @
1b4ef211
...
...
@@ -13,10 +13,8 @@ export class AppComponent implements OnInit {
ngOnInit
():
void
{
this
.
isMobileResolution
=
window
.
innerWidth
<
768
;
if
(
this
.
isMobileResolution
)
{
console
.
log
(
'
MobileView
'
);
this
.
view
=
'
mobileView
'
;
}
else
{
console
.
log
(
'
DesktopView
'
);
this
.
view
=
'
desktopView
'
;
}
}
...
...
src/app/app.module.ts
View file @
1b4ef211
...
...
@@ -27,6 +27,9 @@ import {IsAllowedToPlay} from './dao/isAllowedToPlay';
import
{
SocketDataService
}
from
'
./dao/socketDataService
'
;
import
{
ImpressumComponent
}
from
'
./impressum/impressum.component
'
;
import
{
RulesComponent
}
from
'
./rules/rules.component
'
;
import
{
RoundHistoryComponent
}
from
'
./round-history/round-history.component
'
;
import
{
MatTableModule
}
from
'
@angular/material/table
'
;
import
{
MatTreeModule
}
from
'
@angular/material/tree
'
;
@
NgModule
({
declarations
:
[
...
...
@@ -40,6 +43,7 @@ import { RulesComponent } from './rules/rules.component';
GuesserComponent
,
ImpressumComponent
,
RulesComponent
,
RoundHistoryComponent
,
],
imports
:
[
HttpClientModule
,
...
...
@@ -56,7 +60,9 @@ import { RulesComponent } from './rules/rules.component';
FormsModule
,
MatSlideToggleModule
,
MatIconModule
,
MatTooltipModule
MatTooltipModule
,
MatTableModule
,
MatTreeModule
],
providers
:
[
IsAllowedToPlay
,
...
...
src/app/dao/TabuMiddlewareService.ts
View file @
1b4ef211
...
...
@@ -66,7 +66,19 @@ export class TabuMiddlewareService {
return
this
.
request
(
'
POST
'
,
`
${
environment
.
tabuMiddlewareURL
}
endRound`
,
req
);
}
skipCard
(
req
:
any
):
Promise
<
any
>
{
return
this
.
request
(
'
POST
'
,
`
${
environment
.
tabuMiddlewareURL
}
skipCard`
,
req
);
}
getCard
(
req
:
any
):
Promise
<
any
>
{
return
this
.
request
(
'
POST
'
,
`
${
environment
.
tabuMiddlewareURL
}
getCard`
,
req
);
}
getSessionHistory
(
req
:
any
):
Promise
<
any
>
{
return
this
.
request
(
'
POST
'
,
`
${
environment
.
tabuMiddlewareURL
}
getSessionHistory`
,
req
);
}
historyChangePoint
(
req
:
any
):
Promise
<
any
>
{
return
this
.
request
(
'
POST
'
,
`
${
environment
.
tabuMiddlewareURL
}
historyChangePoint`
,
req
);
}
}
src/app/dao/socketDataService.ts
View file @
1b4ef211
...
...
@@ -12,7 +12,6 @@ export class SocketDataService {
}
connect
(
sessionName
:
string
):
void
{
console
.
log
(
'
NEW CONNECTION
'
);
this
.
sessionName
=
sessionName
;
if
(
!
this
.
isConnected
)
{
this
.
socket
=
io
(
environment
.
tabuServerURL
);
...
...
@@ -86,4 +85,16 @@ export class SocketDataService {
});
});
}
getHistoryChangedInfo
(
sessionName
:
string
):
Observable
<
any
>
{
if
(
!
this
.
isConnected
)
{
this
.
connect
(
sessionName
);
}
this
.
sessionName
=
sessionName
;
return
new
Observable
(
observer
=>
{
this
.
socket
.
on
(
this
.
sessionName
+
'
:historyChanged
'
,
(
res
:
any
)
=>
{
observer
.
next
(
res
);
});
});
}
}
src/app/game/game.component.html
View file @
1b4ef211
...
...
@@ -44,9 +44,9 @@
</h2>
</mat-card>
<br>
<button
*ngIf=
"isExplainer"
mat-raised-button
color=
"primary"
(click)=
"rightAnswer()"
>
Richtig
✔
</button>
<button
*ngIf=
"isExplainer"
mat-raised-button
color=
"accent"
style=
"margin: 40px"
(click)=
"new
Card()"
>
Überspringen
»
</button>
<button
mat-raised-button
color=
"warn"
[disabled]=
"PressTaboohDisabled"
(click)=
"wrongAnswer()"
>
Tabooh
✘
</button>
<button
*ngIf=
"isExplainer"
class=
"marginButton"
mat-raised-button
color=
"primary"
(click)=
"rightAnswer()"
>
Richtig
✔
</button>
<button
*ngIf=
"isExplainer"
class=
"marginButton"
mat-raised-button
color=
"accent"
(click)=
"skip
Card()"
>
Überspringen
»
</button>
<button
mat-raised-button
c
lass=
"marginButton"
c
olor=
"warn"
[disabled]=
"PressTaboohDisabled"
(click)=
"wrongAnswer()"
>
Tabooh
✘
</button>
<br>
<br>
<mat-card
class=
"transparent"
>
...
...
@@ -65,3 +65,16 @@
</mat-card-content>
</mat-card>
</div>
<br>
<br>
<br>
<app-round-history
[round]=
"gameStatus.round"
[canEdit]=
"false"
[history]=
"history"
[sessionName]=
"sessionName"
>
</app-round-history>
<br>
<br>
<br>
src/app/game/game.component.scss
View file @
1b4ef211
...
...
@@ -51,3 +51,8 @@ body {
margin-right
:
auto
;
}
.marginButton
{
margin-right
:
10px
;
margin-left
:
10px
;
}
src/app/game/game.component.ts
View file @
1b4ef211
...
...
@@ -41,13 +41,17 @@ export class GameComponent implements OnInit, OnDestroy {
gameStatus
:
GameStatus
=
{
sessionID
:
-
1
,
round
:
0
,
red
:
0
,
blue
:
0
,
redTurn
:
true
,
activeExplainer
:
1
,
activeCard
:
1
activeCard
:
1
,
isTabooh
:
1
,
lastPlay
:
'
01/01/9999
'
};
team
=
''
;
history
:
any
=
[];
color
=
'
primary
'
;
value
=
50
;
...
...
@@ -78,10 +82,8 @@ export class GameComponent implements OnInit, OnDestroy {
this
.
router
.
navigate
([
this
.
sessionName
+
'
/
'
+
this
.
team
]);
}
else
{
this
.
isExplainer
=
true
;
//this.service.getS2C({spielname: this.sessionName});
}
}
console
.
log
(
'
FILL GAME
'
);
this
.
fillGamestatus
();
});
this
.
startTimer
();
...
...
@@ -95,7 +97,6 @@ export class GameComponent implements OnInit, OnDestroy {
this
.
subNewCard
=
this
.
socketDataService
.
getNewCard
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
))
.
subscribe
(
data
=>
{
console
.
log
(
'
GAME SOCKET CARD:
'
,
data
);
this
.
service
.
getCard
({
cardID
:
data
}).
then
(
value
=>
{
this
.
fillNewCard
(
value
);
this
.
fillGamestatus
();
...
...
@@ -104,9 +105,7 @@ export class GameComponent implements OnInit, OnDestroy {
this
.
subEndRound
=
this
.
socketDataService
.
getNewRound
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
))
.
subscribe
(
data
=>
{
console
.
log
(
'
GAME SOCKET END ROUND:
'
,
data
);
if
(
!
JSON
.
parse
(
data
))
{
console
.
log
(
'
GAME END:
'
,
data
);
this
.
unsubscribeAll
();
this
.
router
.
navigate
([
this
.
sessionName
+
'
/
'
+
this
.
team
]);
}
...
...
@@ -114,7 +113,6 @@ export class GameComponent implements OnInit, OnDestroy {
this
.
subCanPressTabooh
=
this
.
socketDataService
.
getCanPressTabooh
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
))
.
subscribe
(
data
=>
{
console
.
log
(
'
Status Tabooh can pressed:
'
,
data
);
this
.
PressTaboohDisabled
=
!
JSON
.
parse
(
data
);
});
}
...
...
@@ -130,7 +128,6 @@ export class GameComponent implements OnInit, OnDestroy {
this
.
timePassed
=
this
.
timePassed
+=
1
;
this
.
timeLeft
=
(
this
.
TIME_LIMIT
-
this
.
timePassed
);
if
(
this
.
timeLeft
===
0
)
{
console
.
log
(
'
Times up!
'
);
//this.onTimesUp();
}
},
593
);
...
...
@@ -189,6 +186,15 @@ export class GameComponent implements OnInit, OnDestroy {
}
}
skipCard
():
void
{
this
.
service
.
skipCard
({
spielname
:
this
.
sessionName
,
cardID
:
this
.
cardInfo
.
cardID
}).
then
(
value
=>
{
const
status
=
JSON
.
parse
(
value
.
status
);
if
(
status
)
{
this
.
newCard
();
}
}).
catch
(
reason
=>
console
.
log
(
reason
));
}
newCard
():
void
{
this
.
service
.
getS2C
({
spielname
:
this
.
sessionName
}).
then
(
value
=>
{
const
status
=
JSON
.
parse
(
value
.
status
);
...
...
@@ -206,24 +212,22 @@ export class GameComponent implements OnInit, OnDestroy {
this
.
cardInfo
.
tabu3
=
value
.
tabu3
;
this
.
cardInfo
.
tabu4
=
value
.
tabu4
;
this
.
cardInfo
.
tabu5
=
value
.
tabu5
;
this
.
getHistory
();
}
fillGamestatus
():
void
{
this
.
service
.
getGamestatus
({
spielname
:
this
.
sessionName
}).
then
(
value
=>
{
const
status
=
JSON
.
parse
(
value
.
status
);
if
(
status
)
{
console
.
log
(
value
);
this
.
gameStatus
.
sessionID
=
JSON
.
parse
(
value
.
sessionID
);
this
.
gameStatus
.
round
=
JSON
.
parse
(
value
.
round
);
this
.
gameStatus
.
red
=
JSON
.
parse
(
value
.
red
);
this
.
gameStatus
.
blue
=
JSON
.
parse
(
value
.
blue
);
this
.
gameStatus
.
redTurn
=
JSON
.
parse
(
value
.
redTurn
);
this
.
gameStatus
.
activeExplainer
=
JSON
.
parse
(
value
.
activeExplainer
);
this
.
gameStatus
.
activeCard
=
JSON
.
parse
(
value
.
activeCard
);
console
.
log
(
'
SOLUTION:
'
,
this
.
cardInfo
.
cardID
);
if
(
this
.
cardInfo
.
cardID
===
-
1
)
{
console
.
log
(
'
I am in
'
);
this
.
service
.
getCard
({
cardID
:
value
.
activeCard
}).
then
(
value1
=>
{
console
.
log
(
'
FIRST CARDS:
'
,
value1
);
this
.
fillNewCard
(
value1
);
});
}
...
...
@@ -231,6 +235,15 @@ export class GameComponent implements OnInit, OnDestroy {
});
}
getHistory
():
void
{
this
.
service
.
getSessionHistory
({
spielname
:
this
.
sessionName
}).
then
(
value
=>
{
const
status
=
JSON
.
parse
(
value
.
status
);
if
(
status
)
{
this
.
history
=
value
.
history
;
}
});
}
unsubscribeAll
():
void
{
this
.
subNewCard
.
unsubscribe
();
this
.
subEndRound
.
unsubscribe
();
...
...
src/app/guesser/guesser.component.html
View file @
1b4ef211
...
...
@@ -23,5 +23,17 @@
</mat-progress-spinner>
<mat-card-content
class =
"timer"
>
{{timeRemaining}}
</mat-card-content>
</mat-card>
<br>
<br>
<br>
<app-round-history
[round]=
"gameStatus.round"
[canEdit]=
"false"
[history]=
"history"
[sessionName]=
"sessionName"
>
</app-round-history>
<br>
<br>
<br>
src/app/guesser/guesser.component.ts
View file @
1b4ef211
...
...
@@ -16,6 +16,7 @@ export class GuesserComponent implements OnInit, OnDestroy {
private
ngUnsubscribe
=
new
Subject
();
subNewCard
:
any
;
subEndRound
:
any
;
history
:
any
=
[];
sessionName
=
''
;
TIME_LIMIT
=
100
;
timePassed
=
0
;
...
...
@@ -24,11 +25,14 @@ export class GuesserComponent implements OnInit, OnDestroy {
timeLeft
=
this
.
TIME_LIMIT
;
gameStatus
:
GameStatus
=
{
sessionID
:
-
1
,
round
:
0
,
red
:
0
,
blue
:
0
,
redTurn
:
true
,
activeExplainer
:
1
,
activeCard
:
1
activeCard
:
1
,
isTabooh
:
1
,
lastPlay
:
'
01/01/9999
'
};
team
=
''
;
...
...
@@ -59,13 +63,11 @@ export class GuesserComponent implements OnInit, OnDestroy {
this
.
subNewCard
=
this
.
socketDataService
.
getNewCard
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
))
.
subscribe
(
data
=>
{
console
.
log
(
'
GUESSER SOCKET CARD:
'
,
data
);
this
.
fillGamestatus
();
});
this
.
subEndRound
=
this
.
socketDataService
.
getEndRound
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
))
.
subscribe
(
data
=>
{
console
.
log
(
'
GUESSER SOCKET END ROUND:
'
,
data
);
if
(
JSON
.
parse
(
data
))
{
this
.
unsubscribeAll
();
this
.
router
.
navigate
([
this
.
sessionName
+
'
/
'
+
this
.
team
]);
...
...
@@ -84,7 +86,6 @@ export class GuesserComponent implements OnInit, OnDestroy {
this
.
timePassed
=
this
.
timePassed
+=
1
;
this
.
timeLeft
=
(
this
.
TIME_LIMIT
-
this
.
timePassed
);
if
(
this
.
timeLeft
===
0
)
{
console
.
log
(
'
Times up!
'
);
//this.onTimesUp();
}
},
593
);
...
...
@@ -100,11 +101,22 @@ export class GuesserComponent implements OnInit, OnDestroy {
const
status
=
JSON
.
parse
(
value
.
status
);
if
(
status
)
{
this
.
gameStatus
.
sessionID
=
JSON
.
parse
(
value
.
sessionID
);
this
.
gameStatus
.
round
=
JSON
.
parse
(
value
.
round
);
this
.
gameStatus
.
red
=
JSON
.
parse
(
value
.
red
);
this
.
gameStatus
.
blue
=
JSON
.
parse
(
value
.
blue
);
this
.
gameStatus
.
redTurn
=
JSON
.
parse
(
value
.
redTurn
);
this
.
gameStatus
.
activeExplainer
=
JSON
.
parse
(
value
.
activeExplainer
);
this
.
gameStatus
.
activeCard
=
JSON
.
parse
(
value
.
activeCard
);
this
.
getHistory
();
}
});
}
getHistory
():
void
{
this
.
service
.
getSessionHistory
({
spielname
:
this
.
sessionName
}).
then
(
value
=>
{
const
status
=
JSON
.
parse
(
value
.
status
);
if
(
status
)
{
this
.
history
=
value
.
history
;
}
});
}
...
...
src/app/interface/gameStatus.ts
View file @
1b4ef211
export
interface
GameStatus
{
sessionID
:
number
;
round
:
number
;
red
:
number
;
blue
:
number
;
redTurn
:
boolean
;
activeExplainer
:
number
;
activeCard
:
number
;
isTabooh
:
number
;
lastPlay
:
string
;
}
src/app/overview/overview.component.html
View file @
1b4ef211
<mat-card
*ngIf=
"scrolled === 1"
class=
"fixed-content transparent"
[ngStyle]=
"{'width.px': matCard.offsetWidth}"
>
<mat-card-title>
Spielstand
</mat-card-title>
<mat-card-content>
<br>
<table>
<tr>
<th>
Team rot
</th>
<th>
Team blau
</th>
</tr>
<tr>
<th
class =
"redfont"
>
{{gameStatus.red}}
</th>
<th
class =
"bluefont"
>
{{gameStatus.blue}}
</th>
</tr>
</table>
</mat-card-content>
</mat-card>
<mat-card
class=
"transparent"
>
<div
class=
"FloatLeftAndCenterElement"
>
<div
class=
"newStartButtonLeft"
>
...
...
@@ -19,7 +36,7 @@
<br>
<br>
<mat-card
class=
"transparent"
>
<mat-card-title>
Spielstand
</mat-card-title>
<mat-card-title
#matCard
>
Spielstand
</mat-card-title>
<mat-card-content>
<br>
<table>
...
...
@@ -45,3 +62,24 @@
<button
[disabled]=
"buttonNextRoundDisabled || gameStatus.activeExplainer == 1"
mat-raised-button
color=
"primary"
(click)=
"nextRound()"
>
Ich erkläre und los!
</button>
</mat-card-actions>
</mat-card>
<br>
<br>
<br>
<mat-card
class=
"transparent"
*ngIf=
"history.length > 0"
>
<mat-card-title>
Spielverlauf
</mat-card-title>
<mat-card-subtitle>
Stimmt das Ergebnis nicht?
<br>
Dann überprüfe den Spielverlauf und passe die Antworten gegebenenfalls an
</mat-card-subtitle>
</mat-card>
<a
*ngFor=
"let r of getArrayRounds(); let i = index;"
>
<br>
<app-round-history
[round]=
"gameStatus.round - i"
[canEdit]=
"!isActiveRound"
[history]=
"history"
[sessionName]=
"sessionName"
(gameStatus)=
"fillGameStatus($event)"
(canUpdate)=
"updateChangeHistory($event)"
>
</app-round-history>
</a>
<br>
<br>
<br>
src/app/overview/overview.component.scss
View file @
1b4ef211
...
...
@@ -50,3 +50,11 @@ th{
.rightPlace
{
margin-right
:
5px
;
}
.fixed-content
{
position
:
fixed
;
top
:
2em
;
z-index
:
100
;
left
:
50%
;
transform
:
translateX
(
-50%
);
}
src/app/overview/overview.component.ts
View file @
1b4ef211
import
{
Component
,
OnDestroy
,
OnInit
}
from
'
@angular/core
'
;
import
{
Component
,
HostListener
,
OnDestroy
,
OnInit
}
from
'
@angular/core
'
;
import
{
ActivatedRoute
,
Router
}
from
'
@angular/router
'
;
import
{
TabuMiddlewareService
}
from
'
../dao/TabuMiddlewareService
'
;
import
{
GameStatus
}
from
'
../interface/gameStatus
'
;
...
...
@@ -19,6 +19,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
subNewCard
:
any
;
subNewRound
:
any
;
subEndRound
:
any
;
subHistoryChanged
:
any
;
wantToBeExplainer
=
false
;
isActiveRound
=
false
;
red
=
'
choose
'
;
...
...
@@ -31,12 +32,20 @@ export class OverviewComponent implements OnInit, OnDestroy {
membership
=
'
Wähle dein Team
'
;
gameStatus
:
GameStatus
=
{
sessionID
:
-
1
,
round
:
0
,
red
:
0
,
blue
:
0
,
redTurn
:
true
,
activeExplainer
:
1
,
activeCard
:
1
activeCard
:
1
,
isTabooh
:
1
,
lastPlay
:
'
01/01/9999
'
};
history
:
any
=
[];
canUpdateHistory
=
true
;
scrolled
=
0
;
scrollLimit
=
290
;
constructor
(
private
router
:
Router
,
private
activatedRoute
:
ActivatedRoute
,
private
service
:
TabuMiddlewareService
,
...
...
@@ -52,7 +61,6 @@ export class OverviewComponent implements OnInit, OnDestroy {
if
(
!
JSON
.
parse
(
value
.
status
))
{
this
.
router
.
navigate
([
'
error
'
]);
}
else
{
console
.
log
(
'
Team:
'
,
this
.
team
);
if
(
this
.
team
===
'
red
'
){
this
.
membership
=
'
Du gehörst zu Team rot!
'
;
this
.
buttonNewGameDisabled
=
false
;
...
...
@@ -72,7 +80,6 @@ export class OverviewComponent implements OnInit, OnDestroy {
this
.
router
.
navigate
([
'
error
'
]);
return
;
}
console
.
log
(
"
GET SOCKET DATA
"
);
this
.
getGameStatus
();
}
});
...
...
@@ -84,41 +91,45 @@ export class OverviewComponent implements OnInit, OnDestroy {
}
getSocketData
():
void
{
this
.
subHistoryChanged
=
this
.
socketDataService
.
getHistoryChangedInfo
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
)).
subscribe
(
data
=>
{
console
.
log
(
'
WANT UPDATE:
'
,
this
.
canUpdateHistory
);
if
(
this
.
canUpdateHistory
)
{
console
.
log
(
'
UPDATE HISTORY
'
);
this
.
getGameStatus
();
}
});
this
.
subNewGame
=
this
.
socketDataService
.
getNewGame
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
)).
subscribe
(
data
=>
{
console
.
log
(
'
OVERVIEW SOCKET GAME:
'
,
data
);
this
.
getGameStatus
();
});
this
.
subNewCard
=
this
.
socketDataService
.
getNewCard
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
)).
subscribe
(
data
=>
{
console
.
log
(
'
OVERVIEW SOCKET CARD:
'
,
data
);
console
.
log
(
'
UPDATE CARD
'
);
this
.
getGameStatus
();
});
this
.
subEndRound
=
this
.
socketDataService
.
getEndRound
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
)).
subscribe
(
data
=>
{
console
.
log
(
'
OVERVIEW SOCKET END ROUND:
'
,
data
);
if
(
JSON
.
parse
(
data
))
{
this
.
getGameStatus
();
}
});
this
.
subNewRound
=
this
.
socketDataService
.
getNewRound
(
this
.
sessionName
)
.
pipe
(
takeUntil
(
this
.
ngUnsubscribe
)).
subscribe
(
data
=>
{
console
.
log
(
'
OVERVIEW SOCKET NEW ROUND:
'
,
data
);
if
(
JSON
.
parse
(
data
))
{
console
.
log
(
'
OVERVIEW:
'
,
data
);
if
(((
this
.
team
===
'
red
'
&&
this
.
nextTeam
===
'
Rot
'
)
||
(
this
.
team
===
'
blue
'
&&
this
.
nextTeam
===
'
Blau
'
))
&&
!
this
.
wantToBeExplainer
)
{
console
.
log
(
'
OVERVIEW GO GUESSER:
'
,
data
);
this
.
isAllowedToPlay
.
isAllowed
=
true
;
this
.
isAllowedToPlay
.
role
=
'
guesser
'
;
this
.
unsubscribeAll
();
this
.
router
.
navigate
([
this
.
sessionName
+
'
/
'
+
this
.
team
+
'
/guesser
'
]);
}
else
if
((
this
.
team
===
'
red
'
||
this
.
team
===
'
blue
'
)
&&
!
this
.
wantToBeExplainer
)
{
console
.
log
(
'
OVERVIEW GO WATCHDOG:
'
,
data
);
this
.
isAllowedToPlay
.
isAllowed
=
true
;
this
.
isAllowedToPlay
.
role
=
'
watchdog
'
;
this
.
unsubscribeAll
();
this
.
router
.
navigate
([
this
.
sessionName
+
'
/
'
+
this
.
team
+
'
/watchdog
'
]);
}
else
{
this
.
getGameStatus
();
}
}
});
...
...
@@ -136,14 +147,14 @@ export class OverviewComponent implements OnInit, OnDestroy {
}
nextRound
():
void
{
this
.
service
.
getS2C
({
spielname
:
this
.
sessionName
}).
then
(
value
=>
{
console
.
log
(
'
START NEXT ROUND
'
,
value
);
this
.
service
.
newRound
({
spielname
:
this
.
sessionName
});
this
.
isAllowedToPlay
.
isAllowed
=
true
;
this
.
isAllowedToPlay
.
role
=
'
explainer
'
;
this
.
unsubscribeAll
(
);
this
.
router
.
navigate
([
this
.
router
.
url
+
'
/explainer
'
,
])
;
this
.
wantToBeExplainer
=
true
;
this
.
unsubscribeAll
();
this
.
service
.
newRound
({
spielname
:
this
.
sessionName
}).
then
(
valueNotUsed
=>
{
this
.
service
.
getS2C
({
spielname
:
this
.
sessionName
}).
then
(
value
=>
{
this
.
isAllowedToPlay
.
isAllowed
=
true
;
this
.
isAllowedToPlay
.
role
=
'
explainer
'
;
this
.
router
.
navigate
([
this
.
sessionName
+
'
/
'
+
this
.
team
+
'
/explainer
'
,
]
);
this
.
wantToBeExplainer
=
true
;
})
;
});
}
...
...
@@ -155,26 +166,60 @@ export class OverviewComponent implements OnInit, OnDestroy {
getGameStatus
():
void
{
this
.
service
.
getGamestatus
({
spielname
:
this
.
sessionName
}).
then
(
value
=>
{
this
.
fillGameStatus
(
value
);
this
.
getHistory
();
}).
catch
(
reason
=>
console
.
log
(
reason
));
}
fillGameStatus
(
value
:
any
):
void
{
const
status
=
JSON
.
parse
(
value
.
status
);
if
(
status
)
{
this
.
gameStatus
.
sessionID
=
JSON
.
parse
(
value
.
sessionID
);
this
.
gameStatus
.
round
=
JSON
.
parse
(
value
.
round
);
this
.
gameStatus
.
red
=
JSON
.
parse
(
value
.
red
);
this
.
gameStatus
.
blue
=
JSON
.
parse
(
value
.
blue
);
this
.
gameStatus
.
redTurn
=
JSON
.
parse
(
value
.
redTurn
);
this
.
gameStatus
.
activeExplainer
=
JSON
.
parse
(
value
.
activeExplainer
);
this
.
gameStatus
.
activeCard
=
JSON
.
parse
(
value
.
activeCard
);
if
(
this
.
gameStatus
.
redTurn
){
this
.
nextTeam
=
'
Rot
'
;
this
.
buttonNextRoundDisabled
=
this
.
team
!==
'
red
'
;
}
else
{
this
.
nextTeam
=
'
Blau
'
;
this
.
buttonNextRoundDisabled
=
this
.
team
!==
'
blue
'
;
}
this
.
isActiveRound
=
this
.
gameStatus
.
activeExplainer
===
1
;
}
}
updateChangeHistory
(
value
:
boolean
):
void
{
console
.
log
(
'
UPDATE:
'
,
value
);
this
.
canUpdateHistory
=
value
;
}
getArrayRounds
():
any
[]
{
return
Array
(
this
.
gameStatus
.
round
);
}
getHistory
():
void
{
this
.
service
.
getSessionHistory
({
spielname
:
this
.
sessionName
}).
then
(
value
=>
{
const
status
=
JSON
.
parse
(
value
.
status
);
if
(
status
)
{
this
.
gameStatus
.
sessionID
=
JSON
.
parse
(
value
.
sessionID
);
this
.
gameStatus
.
red
=
JSON
.
parse
(
value
.
red
);
this
.
gameStatus
.
blue
=
JSON
.
parse
(
value
.
blue
);
this
.
gameStatus
.
redTurn
=
JSON
.
parse
(
value
.
redTurn
);
this
.
gameStatus
.
activeExplainer
=
JSON
.
parse
(
value
.
activeExplainer
);
this
.
gameStatus
.
activeCard
=
JSON
.
parse
(
value
.
activeCard
);
if
(
this
.
gameStatus
.
redTurn
){
this
.
nextTeam
=
'
Rot
'
;
this
.
buttonNextRoundDisabled
=
this
.
team
!==
'
red
'
;
}
else
{
this
.
nextTeam
=
'
Blau
'
;
this
.
buttonNextRoundDisabled
=
this
.
team
!==
'
blue
'
;
}
this
.
isActiveRound
=
this
.
gameStatus
.
activeExplainer
===
1
;
this
.
history
=
value
.
history
;
}
console
.
log
(
this
.
gameStatus
);
}).
catch
(
reason
=>
console
.
log
(
reason
));
});
}
@
HostListener
(
'
window:scroll
'
,
[
'
$event
'
])
onWindowScroll
(
$event
:
any
):
void
{
const
numb
=
window
.
scrollY
;
if
(
numb
>=
this
.
scrollLimit
){
this
.
scrolled
=
1
;
}
else
{
this
.
scrolled
=
0
;
}
}
unsubscribeAll
():
void
{
...
...
@@ -182,6 +227,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
this
.
subNewRound
.
unsubscribe
();
this
.
subEndRound
.
unsubscribe
();
this
.
subNewGame
.
unsubscribe
();
this
.
subHistoryChanged
.
unsubscribe
();
this
.
ngUnsubscribe
.
next
();
this
.
ngUnsubscribe
.
complete
();
...
...
src/app/round-history/round-history.component.html
0 → 100644
View file @
1b4ef211
<mat-card
*ngIf=
"round !== 0"
class=
"transparent {{redTurn}}"
>
<mat-card-title>
Runde {{round}}
</mat-card-title>
</mat-card>
<div
class=
"example-container mat-elevation-z8"
>
<mat-table
#table
[dataSource]=
"dataSource"
>
<!-- Answer Column -->
<ng-container
matColumnDef=
"answer"
>
<mat-cell
*matCellDef=
"let element"
>
<button
class=
"volleBreite"
*ngIf=
"element.answer === answer.zeitLaueft"
mat-raised-button
>
Zeit läuft
⏱
</button>
<button
class=
"volleBreite"
*ngIf=
"element.answer === answer.zeitAbgelaufen"
mat-raised-button
>
Zeit abgelaufen
⏱
</button>
<button
class=
"volleBreite"
*ngIf=
"element.answer === answer.richtig"
mat-raised-button
color=
"primary"
>
Richtig
✔
</button>
<button
class=
"volleBreite"
*ngIf=
"element.answer === answer.uebersprungen"
mat-raised-button
color=
"accent"
>
Überspringen
»
</button>
<button
class=
"volleBreite"
*ngIf=
"element.answer === answer.tabooh"
mat-raised-button
color=
"warn"
>
Tabooh
✘
</button>
<button
class=
"volleBreite"
*ngIf=
"element.answer === answer.KeineEingabe"
mat-raised-button
>
Keine Eingabe
✘
</button>
</mat-cell>
</ng-container>
<!-- Solution Column -->
<ng-container
matColumnDef=
"solution"
>
<mat-cell
*matCellDef=
"let element"
>
<b
*ngIf=
"element.answer !== answer.zeitLaueft"
>
{{element.solution}}
</b></mat-cell>
</ng-container>
<!-- Tabu1 Column -->
<ng-container
matColumnDef=
"tabu1"
>
<mat-cell
*matCellDef=
"let element"
>
<a
*ngIf=
"element.answer !== answer.zeitLaueft"
>
{{element.tabu1}}
</a></mat-cell>
</ng-container>
<!-- Tabu2 Column -->
<ng-container
matColumnDef=
"tabu2"
>
<mat-cell
*matCellDef=
"let element"
>
<a
*ngIf=
"element.answer !== answer.zeitLaueft"
>
{{element.tabu2}}
</a></mat-cell>
</ng-container>
<!-- Tabu3 Column -->
<ng-container
matColumnDef=
"tabu3"
>
<mat-cell
*matCellDef=
"let element"
>
<a
*ngIf=
"element.answer !== answer.zeitLaueft"
>
{{element.tabu3}}
</a></mat-cell>
</ng-container>
<!-- Tabu4 Column -->
<ng-container
matColumnDef=
"tabu4"
>
<mat-cell
*matCellDef=
"let element"
>
<a
*ngIf=
"element.answer !== answer.zeitLaueft"
>
{{element.tabu4}}
</a></mat-cell>
</ng-container>
<!-- Tabu5 Column -->
<ng-container
matColumnDef=
"tabu5"
>
<mat-cell
*matCellDef=
"let element"
>
<a
*ngIf=
"element.answer !== answer.zeitLaueft"
>
{{element.tabu5}}
</a></mat-cell>
</ng-container>
<!-- Expanded Content Column - The detail row is made up of this one column -->
<ng-container
matColumnDef=
"expandedDetail"
class=
"volleBreite"
>
<mat-cell
*matCellDef=
"let detail"
class=
"expandStyle"
>
<!-- Richtig -->
<button
mat-stroked-button
*ngIf=
"detail.element.answer !== answer.richtig"
color=
"primary"
class=
"expandStyle"
(click)=
"updateAnswer(detail.element, answer.richtig)"
>
Richtig
✔
</button>
<button
mat-raised-button
*ngIf=
"detail.element.answer === answer.richtig"
color=
"primary"
class=
"expandStyle"
>
Richtig
✔
</button>
<!-- Zeit abgelaufen -->
<button
mat-stroked-button
*ngIf=
"detail.element.answer !== answer.zeitAbgelaufen
&& detail.element.s2cID === lastS2CID"
class=
"expandStyle"
(click)=
"updateAnswer(detail.element, answer.zeitAbgelaufen)"
>
Zeit abgelaufen
⏱
</button>
<button
mat-raised-button
*ngIf=
"detail.element.answer === answer.zeitAbgelaufen
&& detail.element.s2cID === lastS2CID"
class=
"expandStyle"
>
Zeit abgelaufen
⏱
</button>
<!-- Uebersprungen -->
<button
mat-stroked-button
*ngIf=
"detail.element.answer !== answer.uebersprungen
&& detail.element.s2cID !== lastS2CID"
color=
"accent"
class=
"expandStyle"
(click)=
"updateAnswer(detail.element, answer.uebersprungen)"
>
Überspringen
»
</button>
<button
mat-raised-button
*ngIf=
"detail.element.answer === answer.uebersprungen
&& detail.element.s2cID !== lastS2CID"
color=
"accent"
class=
"expandStyle"
>
Überspringen
»
</button>
<!-- Tabooh -->
<button
mat-stroked-button
*ngIf=
"detail.element.answer !== answer.tabooh"
color=
"warn"
class=
"expandStyle"
(click)=
"updateAnswer(detail.element, answer.tabooh)"
>
Tabooh
✘
</button>
<button
mat-raised-button
*ngIf=
"detail.element.answer === answer.tabooh"
color=
"warn"
class=
"expandStyle"
>
Tabooh
✘
</button>
</mat-cell>
</ng-container>
<mat-row
*matRowDef=
"let row; columns: displayedColumns;"
matRipple
class=
"{{elementRowStyle}}"
[class.expanded]=
"expandedElement == row"
(click)=
"expandedElement = row"
>
</mat-row>
<mat-row
*matRowDef=
"let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
[@
detailExpand]=
"canEdit && row.element == expandedElement ? 'expanded' : 'collapsed'"
style=
"overflow: hidden"
>
</mat-row>
</mat-table>
</div>
src/app/round-history/round-history.component.scss
0 → 100644
View file @
1b4ef211
.blue
{
background-color
:
rgba
(
215
,
212
,
255
,
1
);
}
.red
{
background-color
:
rgba
(
255
,
212
,
212
,
1
);
}
.volleBreite
{
width
:
100%
;
margin-right
:
2em
;
font-size
:
small
;
}
.mat-column-answer
{
flex
:
0
0
11em
;
}
.expandStyle
{
display
:flex
;
justify-content
:center
;
margin-right
:
2em
;
}
.example-container
{
display
:
flex
;
flex-direction
:
column
;
}
.element-row
{
position
:
relative
;
}
.element-row
:not
(
.expanded
)
{
cursor
:
pointer
;
}
.element-row
:not
(
.expanded
)
:hover
{
background
:
#f5f5f5
;
}
.element-row.expanded
{
border-bottom-color
:
transparent
;
}
.element-row-noEdit
{
position
:
relative
;
}
src/app/round-history/round-history.component.spec.ts
0 → 100644
View file @
1b4ef211
import
{
ComponentFixture
,
TestBed
}
from
'
@angular/core/testing
'
;
import
{
RoundHistoryComponent
}
from
'
./round-history.component
'
;
describe
(
'
RoundHistoryComponent
'
,
()
=>
{
let
component
:
RoundHistoryComponent
;
let
fixture
:
ComponentFixture
<
RoundHistoryComponent
>
;
beforeEach
(
async
()
=>
{
await
TestBed
.
configureTestingModule
({
declarations
:
[
RoundHistoryComponent
]
})
.
compileComponents
();
});
beforeEach
(()
=>
{
fixture
=
TestBed
.
createComponent
(
RoundHistoryComponent
);
component
=
fixture
.
componentInstance
;
fixture
.
detectChanges
();
});
it
(
'
should create
'
,
()
=>
{
expect
(
component
).
toBeTruthy
();
});
});
src/app/round-history/round-history.component.ts
0 → 100644
View file @
1b4ef211
import
{
Component
,
EventEmitter
,
Input
,
OnChanges
,
OnInit
,
Output
,
SimpleChanges
}
from
'
@angular/core
'
;
import
{
animate
,
state
,
style
,
transition
,
trigger
}
from
'
@angular/animations
'
;
import
{
DataSource
}
from
'
@angular/cdk/table
'
;
import
{
Observable
,
of
}
from
'
rxjs
'
;
import
{
TabuMiddlewareService
}
from
'
../dao/TabuMiddlewareService
'
;
export
interface
CardResultHistory
{
answer
:
Answer
;
solution
:
string
;
redTurn
:
number
;
tabu1
:
string
;
tabu2
:
string
;
tabu3
:
string
;
tabu4
:
string
;
tabu5
:
string
;
s2cID
:
number
;
}
export
enum
Answer
{
zeitLaueft
,
zeitAbgelaufen
,
richtig
,
uebersprungen
,
tabooh
,
KeineEingabe
}
@
Component
({
selector
:
'
app-round-history
'
,
templateUrl
:
'
./round-history.component.html
'
,
styleUrls
:
[
'
./round-history.component.scss
'
],
animations
:
[
trigger
(
'
detailExpand
'
,
[
state
(
'
collapsed
'
,
style
({
height
:
'
0px
'
,
minHeight
:
'
0
'
,
visibility
:
'
hidden
'
})),
state
(
'
expanded
'
,
style
({
height
:
'
*
'
,
visibility
:
'
visible
'
})),
transition
(
'
expanded <=> collapsed
'
,
animate
(
'
225ms cubic-bezier(0.4, 0.0, 0.2, 1)
'
)),
]),
],
})
export
class
RoundHistoryComponent
extends
DataSource
<
any
>
implements
OnInit
,
OnChanges
{
@
Input
()
round
:
number
|
undefined
;
@
Input
()
history
:
any
|
undefined
;
@
Input
()
sessionName
:
string
|
undefined
;
@
Input
()
canEdit
:
boolean
|
undefined
;
@
Output
()
gameStatus
=
new
EventEmitter
();
@
Output
()
canUpdate
=
new
EventEmitter
();
redTurn
=
''
;
lastS2CID
=
0
;
elementRowStyle
=
'
element-row
'
;
roundHistory
:
CardResultHistory
[]
=
[];
displayedColumns
=
[
'
answer
'
,
'
solution
'
,
'
tabu1
'
,
'
tabu2
'
,
'
tabu3
'
,
'
tabu4
'
,
'
tabu5
'
];
// displayedColumns = ['position', 'name', 'weight'];
dataSource
=
this
.
connect
();
answer
=
Answer
;
expandedElement
:
any
;
isExpansionDetailRow
=
(
i
:
number
,
row
:
object
)
=>
row
.
hasOwnProperty
(
'
detailRow
'
);
constructor
(
private
service
:
TabuMiddlewareService
)
{
super
();
}
ngOnInit
():
void
{
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
this
.
round
===
6
)
{
console
.
log
(
changes
);
}
if
(
changes
.
hasOwnProperty
(
'
round
'
))
{
this
.
round
=
changes
.
round
.
currentValue
;
}
if
(
changes
.
hasOwnProperty
(
'
history
'
))
{
this
.
history
=
changes
.
history
.
currentValue
;
this
.
getRoundDataFromHistory
();
}
if
(
changes
.
hasOwnProperty
(
'
sessionName
'
))
{
this
.
sessionName
=
changes
.
sessionName
.
currentValue
;
}
if
(
changes
.
hasOwnProperty
(
'
canEdit
'
))
{
this
.
canEdit
=
changes
.
canEdit
.
currentValue
;
if
(
this
.
canEdit
)
{
this
.
elementRowStyle
=
'
element-row
'
;
}
else
{
this
.
elementRowStyle
=
'
element-row-noEdit
'
;
}
}
}
getRoundDataFromHistory
():
void
{
this
.
roundHistory
=
[];
let
redTurn
=
null
;
for
(
const
h
of
this
.
history
)
{
if
(
h
.
Round
===
this
.
round
)
{
let
cardResultName
=
Answer
.
KeineEingabe
;
const
answer
=
JSON
.
parse
(
h
.
CardResultID
);
if
(
answer
===
0
)
{
cardResultName
=
Answer
.
zeitLaueft
;
}
else
if
(
answer
===
1
)
{
cardResultName
=
Answer
.
zeitAbgelaufen
;
}
else
if
(
answer
===
2
)
{
cardResultName
=
Answer
.
richtig
;
}
else
if
(
answer
===
3
)
{
cardResultName
=
Answer
.
uebersprungen
;
}
else
if
(
answer
===
4
)
{
cardResultName
=
Answer
.
tabooh
;
}
const
newCardResultHistory
:
CardResultHistory
=
{
s2cID
:
JSON
.
parse
(
h
.
S2CID
),
solution
:
h
.
Solution
,
redTurn
:
h
.
RedTurn
,
answer
:
cardResultName
,
tabu1
:
h
.
Tabu1
,
tabu2
:
h
.
Tabu2
,
tabu3
:
h
.
Tabu3
,
tabu4
:
h
.
Tabu4
,
tabu5
:
h
.
Tabu5
};
this
.
roundHistory
.
push
(
newCardResultHistory
);
// First Found
if
(
redTurn
===
null
)
{
this
.
lastS2CID
=
JSON
.
parse
(
h
.
S2CID
);
redTurn
=
JSON
.
parse
(
h
.
RedTurn
);
if
(
redTurn
===
1
)
{
this
.
redTurn
=
'
red
'
;
}
else
if
(
redTurn
===
0
)
{
this
.
redTurn
=
'
blue
'
;
}
}
}
}
this
.
disconnect
();
this
.
dataSource
=
this
.
connect
();
/*if (this.getExpansionDetailObjekt !== null) {
this.isExpansionDetailRow(this.getExpansionDetailNumber, this.getExpansionDetailObjekt);
}*/
}
updateAnswer
(
element
:
CardResultHistory
,
answer
:
Answer
):
void
{
const
s2cCard
=
this
.
roundHistory
.
findIndex
(
x
=>
x
.
s2cID
===
element
.
s2cID
);
const
oldAnswer
=
this
.
roundHistory
[
s2cCard
].
answer
;
this
.
roundHistory
[
s2cCard
].
answer
=
answer
;
let
cardResultID
=
0
;
let
point
=
0
;
// Richtig Button selected
if
(
answer
===
Answer
.
richtig
)
{
cardResultID
=
2
;
if
(
oldAnswer
===
Answer
.
tabooh
)
{
point
=
2
;
}
else
if
(
oldAnswer
===
Answer
.
uebersprungen
||
oldAnswer
===
Answer
.
zeitAbgelaufen
)
{
point
=
1
;
}
}
// Ueberspringen or Zeit abgelaufen Button selected
if
(
answer
===
Answer
.
uebersprungen
||
answer
===
Answer
.
zeitAbgelaufen
)
{
if
(
answer
===
Answer
.
zeitAbgelaufen
)
{
cardResultID
=
1
;
}
if
(
answer
===
Answer
.
uebersprungen
)
{
cardResultID
=
3
;
}
if
(
oldAnswer
===
Answer
.
tabooh
)
{
point
=
1
;
}
else
if
(
oldAnswer
===
Answer
.
richtig
)
{
point
=
-
1
;
}
}
// Tabooh Button selected
if
(
answer
===
Answer
.
tabooh
)
{
cardResultID
=
4
;
if
(
oldAnswer
===
Answer
.
richtig
)
{
point
=
-
2
;
}
else
if
(
oldAnswer
===
Answer
.
zeitAbgelaufen
||
oldAnswer
===
Answer
.
uebersprungen
)
{
point
=
-
1
;
}
}
const
timestamp
=
Date
.
now
();
this
.
canUpdate
.
emit
(
false
);
this
.
service
.
historyChangePoint
(
{
spielname
:
this
.
sessionName
,
redTurn
:
element
.
redTurn
,
point
,
s2cID
:
element
.
s2cID
,
cardResultID
,
time
:
timestamp
}
).
then
(
value
=>
{
const
status
=
JSON
.
parse
(
value
.
status
);
if
(
status
)
{
this
.
canUpdate
.
emit
(
true
);
this
.
gameStatus
.
emit
(
value
);
}
}).
catch
(
reason
=>
console
.
log
(
reason
)
);
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect
():
Observable
<
CardResultHistory
[]
>
{
const
rows
:
any
=
[];
this
.
roundHistory
.
forEach
(
element
=>
rows
.
push
(
element
,
{
detailRow
:
true
,
element
}));
return
of
(
rows
);
}
disconnect
():
void
{
}
}
src/app/rules/rules.component.html
View file @
1b4ef211
...
...
@@ -113,6 +113,10 @@
<li>
<b>
Nach
</b>
einer
<b>
Spielrunde
</b>
ist das
<b>
andere Team
</b>
an der
<b>
Reihe
</b>
.
</li>
<li>
Bei einer
<b><a
class=
"redfont"
>
falschen Eingabe
</a></b>
habt ihr die
<b>
Möglichkeit
</b>
,
<br>
<b>
nach
</b>
einer
<b>
Runde
</b>
die
<b>
Antworten
</b>
im dargestellten
<b>
Spielverlauf azupassen
</b>
.
</li>
</ul>
</mat-card-content>
</mat-card>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment