Skip to content

Commit 381908f

Browse files
authored
Merge pull request #23 from Singularity-Game/development
Development
2 parents cbcfaaf + b354577 commit 381908f

File tree

146 files changed

+3299
-161
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

146 files changed

+3299
-161
lines changed

apps/singularity-api/project.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@
5959
"serve": {
6060
"executor": "@nx/js:node",
6161
"options": {
62-
"buildTarget": "singularity-api:build"
62+
"buildTarget": "singularity-api:build",
63+
"runtimeArgs": ["--watch"]
6364
}
6465
},
6566
"lint": {

apps/singularity-api/src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { classes } from '@automapper/classes';
1111
import { UserManagementModule } from './user-management/user-management.module';
1212
import { ServeStaticModule } from '@nestjs/serve-static';
1313
import { PartyModule } from './party/party.module';
14+
import { ScheduleModule } from '@nestjs/schedule';
1415

1516
@Module({
1617
imports: [
@@ -50,6 +51,7 @@ import { PartyModule } from './party/party.module';
5051
AutomapperModule.forRoot({
5152
strategyInitializer: classes()
5253
}),
54+
ScheduleModule.forRoot(),
5355
SongModule,
5456
UserManagementModule,
5557
PartyModule
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
3+
import { createMap, forMember, mapFrom, Mapper, MappingProfile } from '@automapper/core';
4+
import { PartyParticipantDto } from '@singularity/api-interfaces';
5+
import { PartyParticipant } from '../models/party-participant';
6+
7+
@Injectable()
8+
export class PartyParticipantProfile extends AutomapperProfile {
9+
constructor(@InjectMapper() mapper: Mapper) {
10+
super(mapper);
11+
}
12+
13+
get profile(): MappingProfile {
14+
return (mapper) => {
15+
createMap(mapper, PartyParticipant, PartyParticipantDto, forMember(dest => dest.id, mapFrom(src => src.id)));
16+
createMap(mapper, PartyParticipantDto, PartyParticipant, forMember(dest => dest.id, mapFrom(src => src.id)));
17+
};
18+
}
19+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
3+
import { createMap, forMember, mapFrom, Mapper, MappingProfile } from '@automapper/core';
4+
import { PartyQueueItem } from '../models/party-queue-item';
5+
import { PartyParticipantDto, PartyQueueItemDto } from '@singularity/api-interfaces';
6+
import { PartyParticipant } from '../models/party-participant';
7+
8+
@Injectable()
9+
export class PartyQueueItemProfile extends AutomapperProfile {
10+
constructor(@InjectMapper() mapper: Mapper) {
11+
super(mapper);
12+
}
13+
14+
get profile(): MappingProfile {
15+
return (mapper) => {
16+
createMap(mapper, PartyQueueItem, PartyQueueItemDto,
17+
forMember(dest => dest.participants, mapFrom(src => mapper.mapArray(src.participants, PartyParticipant, PartyParticipantDto))),
18+
forMember(dest => dest.id, mapFrom(src => src.id)));
19+
createMap(mapper, PartyQueueItemDto, PartyQueueItem,
20+
forMember(dest => dest.participants, mapFrom(src => mapper.mapArray(src.participants, PartyParticipantDto, PartyParticipant))),
21+
forMember(dest => dest.id, mapFrom(src => src.id)));
22+
};
23+
}
24+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
3+
import { createMap, forMember, ignore, mapFrom, Mapper, MappingProfile } from '@automapper/core';
4+
import { Party } from '../models/party';
5+
import { PartyDto } from '@singularity/api-interfaces';
6+
7+
@Injectable()
8+
export class PartyProfile extends AutomapperProfile {
9+
constructor(@InjectMapper() mapper: Mapper) {
10+
super(mapper);
11+
}
12+
13+
get profile(): MappingProfile {
14+
return (mapper) => {
15+
createMap(mapper, Party, PartyDto, forMember(dest => dest.id, mapFrom(src => src.id)), forMember(dest => dest.creator, mapFrom(src => src.creator.username)));
16+
createMap(mapper, PartyDto, Party, forMember(dest => dest.id, mapFrom(src => src.id)), forMember(dest => dest.creator, ignore()));
17+
};
18+
}
19+
}
Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
import { UUID } from '@singularity/api-interfaces';
2+
import { AutoMap } from '@automapper/classes';
23

34
export class PartyParticipant {
5+
@AutoMap()
46
public readonly id: UUID;
7+
8+
@AutoMap()
59
public readonly name: string;
10+
11+
@AutoMap()
612
public readonly pictureBase64: string;
7-
public totalPoints: number;
8-
public totalPlays: number;
13+
14+
@AutoMap()
15+
public totalPoints = 0;
16+
17+
@AutoMap()
18+
public totalPlays = 0;
19+
20+
constructor(name: string, pictureBase64: string) {
21+
this.id = crypto.randomUUID();
22+
this.name = name;
23+
this.pictureBase64 = pictureBase64;
24+
}
925
}
Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
11
import { PartyParticipant } from './party-participant';
22
import { Song } from '../../song/models/song.entity';
3+
import { UUID } from '@singularity/api-interfaces';
4+
import { AutoMap } from '@automapper/classes';
35

46
export class PartyQueueItem {
7+
@AutoMap()
8+
public readonly id: UUID;
9+
10+
@AutoMap()
11+
public readonly song: Song;
12+
13+
@AutoMap()
514
public participants: PartyParticipant[];
6-
public song: Song;
15+
16+
constructor(song: Song, participants: PartyParticipant[]) {
17+
this.id = crypto.randomUUID();
18+
this.song = song;
19+
this.participants = participants;
20+
}
21+
22+
@AutoMap()
23+
public get ready(): boolean {
24+
return this.participants.length > 1;
25+
}
726
}
Lines changed: 129 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,142 @@
11
import { User } from '../../user-management/models/user.entity';
2-
import { UUID } from '@singularity/api-interfaces';
2+
import { AutoMap } from '@automapper/classes';
3+
import { Song } from '../../song/models/song.entity';
4+
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
35
import { PartyParticipant } from './party-participant';
6+
import { PartyQueueItem } from './party-queue-item';
7+
import { UUID } from '@singularity/api-interfaces';
48

59
export class Party {
6-
public readonly id: UUID;
10+
@AutoMap()
11+
public readonly id: string;
12+
13+
@AutoMap()
714
public readonly name: string;
15+
16+
@AutoMap()
817
public readonly creator: User;
9-
public participants: PartyParticipant[];
10-
public lastInteraction: Date;
18+
19+
private lastInteraction: Date;
20+
private participants: PartyParticipant[] = [];
21+
22+
private currentSongSubject = new ReplaySubject<Song>(1);
23+
private queueSubject = new BehaviorSubject<PartyQueueItem[]>([])
1124

1225
constructor(name: string, user: User) {
13-
this.id = crypto.randomUUID();
26+
this.id = Math.random().toString(36).slice(2, 7);
1427
this.lastInteraction = new Date();
15-
this.participants = [];
1628
this.name = name;
1729
this.creator = user;
1830
}
31+
32+
public join(participant: PartyParticipant): void {
33+
this.lastInteraction = new Date();
34+
35+
this.participants.push(participant);
36+
}
37+
38+
public getParticipantById(participantId: string): PartyParticipant {
39+
this.lastInteraction = new Date();
40+
41+
return this.participants.find((participant: PartyParticipant) => participant.id === participantId);
42+
}
43+
44+
public setCurrentSong(song: Song): void {
45+
this.lastInteraction = new Date();
46+
47+
this.currentSongSubject.next(song);
48+
}
49+
50+
public queueSong(queueItem: PartyQueueItem): void {
51+
this.lastInteraction = new Date();
52+
53+
const queue = this.queueSubject.getValue();
54+
queue.push(queueItem);
55+
this.queueSubject.next(queue);
56+
}
57+
58+
public popSong(queueItemId: string): void {
59+
this.lastInteraction = new Date();
60+
61+
const queue = this.queueSubject.getValue();
62+
const index = queue.findIndex((queueItem) => queueItem.id === queueItemId);
63+
64+
if(index === -1) {
65+
throw new Error('There is no queueItem with the given id')
66+
}
67+
68+
queue.splice(index, 1);
69+
this.queueSubject.next(queue);
70+
}
71+
72+
public joinQueuedSong(queueId: UUID, participant: PartyParticipant): PartyQueueItem {
73+
this.lastInteraction = new Date();
74+
75+
const queue = this.queueSubject.getValue();
76+
const queueItem = queue.find((item: PartyQueueItem) => item.id === queueId);
77+
78+
if(!queueItem) {
79+
throw Error('There is no QueueItem with the given Id');
80+
}
81+
82+
if(queueItem.participants.length >= 2) {
83+
throw Error('Max Participants for this QueueItem reached');
84+
}
85+
86+
queueItem.participants.push(participant);
87+
88+
// Move Item to the back of the queue when it is ready.
89+
if (queueItem.participants.length === 2) {
90+
queue.splice(queue.indexOf(queueItem), 1);
91+
queue.push(queueItem);
92+
}
93+
94+
this.queueSubject.next(queue);
95+
96+
return queueItem;
97+
}
98+
99+
public leaveQueuedSong(queueId: UUID, participant: PartyParticipant): PartyQueueItem {
100+
this.lastInteraction = new Date();
101+
102+
const queue = this.queueSubject.getValue();
103+
const queueItem = queue.find((item: PartyQueueItem) => item.id === queueId);
104+
105+
if(!queueItem) {
106+
throw Error('There is no QueueItem with the given Id');
107+
}
108+
109+
const index = queueItem.participants.findIndex((queueParticipant: PartyParticipant) => queueParticipant.id === participant.id);
110+
111+
if(index === -1) {
112+
throw Error('The given Participant is not a Participant of this queueItem');
113+
}
114+
115+
queueItem.participants.splice(index, 1);
116+
117+
// Remove the queueItem if there are no participants left
118+
if(queueItem.participants.length === 0) {
119+
queue.splice(queue.indexOf(queueItem));
120+
}
121+
122+
this.queueSubject.next(queue);
123+
124+
return queueItem;
125+
}
126+
127+
public getCurrentSong$(): Observable<Song> {
128+
this.lastInteraction = new Date();
129+
130+
return this.currentSongSubject.asObservable();
131+
}
132+
133+
public getQueue$(): Observable<PartyQueueItem[]> {
134+
this.lastInteraction = new Date();
135+
136+
return this.queueSubject.asObservable();
137+
}
138+
139+
public getLastInteraction(): Date {
140+
return this.lastInteraction;
141+
}
19142
}

0 commit comments

Comments
 (0)