diff --git a/client/src/app/core/core-services/projector.service.ts b/client/src/app/core/core-services/projector.service.ts index e7dd36ac1..1fd3935a6 100644 --- a/client/src/app/core/core-services/projector.service.ts +++ b/client/src/app/core/core-services/projector.service.ts @@ -18,6 +18,7 @@ import { SlideManager } from 'app/slides/services/slide-manager.service'; import { BaseModel } from 'app/shared/models/base/base-model'; import { ViewModelStoreService } from './view-model-store.service'; import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model'; +import { TranslateService } from '@ngx-translate/core'; /** * This service cares about Projectables being projected and manage all projection-related @@ -39,7 +40,8 @@ export class ProjectorService { private DS: DataStoreService, private http: HttpService, private slideManager: SlideManager, - private viewModelStore: ViewModelStoreService + private viewModelStore: ViewModelStoreService, + private translate: TranslateService ) {} /** @@ -273,6 +275,24 @@ export class ProjectorService { return viewModel; } + /** + */ + public getSlideTitle(element: ProjectorElement): string { + if (this.slideManager.canSlideBeMappedToModel(element.name)) { + const idElement = this.slideManager.getIdentifialbeProjectorElement(element); + const viewModel = this.getViewModelFromProjectorElement(idElement); + if (viewModel) { + return viewModel.getProjectorTitle(); + } + } + const configuration = this.slideManager.getSlideConfiguration(element.name); + if (configuration.getSlideTitle) { + return configuration.getSlideTitle(element, this.translate, this.viewModelStore); + } + + return this.translate.instant(this.slideManager.getSlideVerboseName(element.name)); + } + /** * Projects the next slide in the queue. Moves all currently projected * non-stable slides to the history. diff --git a/client/src/app/core/repositories/agenda/item-repository.service.ts b/client/src/app/core/repositories/agenda/item-repository.service.ts index bd419e1a9..716cd4ba4 100644 --- a/client/src/app/core/repositories/agenda/item-repository.service.ts +++ b/client/src/app/core/repositories/agenda/item-repository.service.ts @@ -20,6 +20,7 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s import { BaseViewModel } from 'app/site/base/base-view-model'; import { ViewUser } from 'app/site/users/models/view-user'; import { TranslateService } from '@ngx-translate/core'; +import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; /** * Repository service for users @@ -77,6 +78,28 @@ export class ItemRepositoryService extends BaseRepository { viewItem.getVerboseName = (plural: boolean = false) => { return this.translate.instant(plural ? 'Items' : 'Item'); }; + viewItem.getTitle = () => { + if (viewItem.contentObject) { + return viewItem.contentObject.getAgendaTitle(); + } else { + const repo = this.collectionStringMapperService.getRepository( + viewItem.item.content_object.collection + ) as BaseAgendaContentObjectRepository; + return repo.getAgendaTitle(viewItem); + } + }; + viewItem.getListTitle = () => { + const numberPrefix = viewItem.itemNumber ? `${viewItem.itemNumber} · ` : ''; + + if (viewItem.contentObject) { + return numberPrefix + viewItem.contentObject.getAgendaTitleWithType(); + } else { + const repo = this.collectionStringMapperService.getRepository( + viewItem.item.content_object.collection + ) as BaseAgendaContentObjectRepository; + return numberPrefix + repo.getAgendaTitleWithType(viewItem); + } + }; return viewItem; } diff --git a/client/src/app/core/repositories/agenda/topic-repository.service.ts b/client/src/app/core/repositories/agenda/topic-repository.service.ts index bb9eabc9a..47490b254 100644 --- a/client/src/app/core/repositories/agenda/topic-repository.service.ts +++ b/client/src/app/core/repositories/agenda/topic-repository.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + import { Topic } from 'app/shared/models/topics/topic'; -import { BaseRepository } from 'app/core/repositories/base-repository'; import { Mediafile } from 'app/shared/models/mediafiles/mediafile'; import { Item } from 'app/shared/models/agenda/item'; import { DataStoreService } from 'app/core/core-services/data-store.service'; @@ -13,7 +14,7 @@ import { CreateTopic } from 'app/site/agenda/models/create-topic'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; import { ViewItem } from 'app/site/agenda/models/view-item'; -import { TranslateService } from '@ngx-translate/core'; +import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; /** * Repository for topics @@ -21,7 +22,7 @@ import { TranslateService } from '@ngx-translate/core'; @Injectable({ providedIn: 'root' }) -export class TopicRepositoryService extends BaseRepository { +export class TopicRepositoryService extends BaseAgendaContentObjectRepository { /** * Constructor calls the parent constructor * @@ -39,6 +40,19 @@ export class TopicRepositoryService extends BaseRepository { super(DS, mapperService, viewModelStoreService, Topic, [Mediafile, Item]); } + public getAgendaTitle = (topic: Partial | Partial) => { + return topic.title; + }; + + public getAgendaTitleWithType = (topic: Partial | Partial) => { + // Do not append ' (Topic)' to the title. + return topic.title; + }; + + public getVerboseName = (plural: boolean = false) => { + return this.translate.instant(plural ? 'Topics' : 'Topic'); + }; + /** * Creates a new viewModel out of the given model * @@ -49,9 +63,9 @@ export class TopicRepositoryService extends BaseRepository { const attachments = this.viewModelStoreService.getMany(ViewMediafile, topic.attachments_id); const item = this.viewModelStoreService.get(ViewItem, topic.agenda_item_id); const viewTopic = new ViewTopic(topic, attachments, item); - viewTopic.getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Topics' : 'Topic'); - }; + viewTopic.getVerboseName = this.getVerboseName; + viewTopic.getAgendaTitle = () => this.getAgendaTitle(viewTopic); + viewTopic.getAgendaTitleWithType = () => this.getAgendaTitle(viewTopic); return viewTopic; } diff --git a/client/src/app/core/repositories/assignments/assignment-repository.service.ts b/client/src/app/core/repositories/assignments/assignment-repository.service.ts index b2dc482ca..9a6ac6a89 100644 --- a/client/src/app/core/repositories/assignments/assignment-repository.service.ts +++ b/client/src/app/core/repositories/assignments/assignment-repository.service.ts @@ -1,10 +1,12 @@ import { Injectable } from '@angular/core'; + +import { TranslateService } from '@ngx-translate/core'; + import { ViewAssignment } from 'app/site/assignments/models/view-assignment'; import { Assignment } from 'app/shared/models/assignments/assignment'; import { User } from 'app/shared/models/users/user'; import { Tag } from 'app/shared/models/core/tag'; import { Item } from 'app/shared/models/agenda/item'; -import { BaseRepository } from '../base-repository'; import { DataStoreService } from '../../core-services/data-store.service'; import { Identifiable } from 'app/shared/models/base/identifiable'; import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; @@ -12,7 +14,7 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s import { ViewItem } from 'app/site/agenda/models/view-item'; import { ViewUser } from 'app/site/users/models/view-user'; import { ViewTag } from 'app/site/tags/models/view-tag'; -import { TranslateService } from '@ngx-translate/core'; +import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; /** * Repository Service for Assignments. @@ -22,7 +24,7 @@ import { TranslateService } from '@ngx-translate/core'; @Injectable({ providedIn: 'root' }) -export class AssignmentRepositoryService extends BaseRepository { +export class AssignmentRepositoryService extends BaseAgendaContentObjectRepository { /** * Constructor for the Assignment Repository. * @@ -38,15 +40,27 @@ export class AssignmentRepositoryService extends BaseRepository | Partial) => { + return assignment.title; + }; + + public getAgendaTitleWithType = (assignment: Partial | Partial) => { + return assignment.title + ' (' + this.getVerboseName() + ')'; + }; + + public getVerboseName = (plural: boolean = false) => { + return this.translate.instant(plural ? 'Elections' : 'Election'); + }; + public createViewModel(assignment: Assignment): ViewAssignment { const relatedUser = this.viewModelStoreService.getMany(ViewUser, assignment.candidates_id); const agendaItem = this.viewModelStoreService.get(ViewItem, assignment.agenda_item_id); const tags = this.viewModelStoreService.getMany(ViewTag, assignment.tags_id); const viewAssignment = new ViewAssignment(assignment, relatedUser, agendaItem, tags); - viewAssignment.getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Elections' : 'Election'); - }; + viewAssignment.getVerboseName = this.getVerboseName; + viewAssignment.getAgendaTitle = () => this.getAgendaTitle(viewAssignment); + viewAssignment.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewAssignment); return viewAssignment; } diff --git a/client/src/app/core/repositories/base-agenda-content-object-repository.ts b/client/src/app/core/repositories/base-agenda-content-object-repository.ts new file mode 100644 index 000000000..67c84ff83 --- /dev/null +++ b/client/src/app/core/repositories/base-agenda-content-object-repository.ts @@ -0,0 +1,37 @@ +import { BaseViewModel } from '../../site/base/base-view-model'; +import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model'; +import { CollectionStringMapperService } from '../core-services/collectionStringMapper.service'; +import { DataStoreService } from '../core-services/data-store.service'; +import { ViewModelStoreService } from '../core-services/view-model-store.service'; +import { BaseRepository } from './base-repository'; + +export function isBaseAgendaContentObjectRepository(obj: any): obj is BaseAgendaContentObjectRepository { + const repo = obj as BaseAgendaContentObjectRepository; + return ( + !!obj && + repo.getVerboseName !== undefined && + repo.getAgendaTitle !== undefined && + repo.getAgendaTitleWithType !== undefined + ); +} + +export abstract class BaseAgendaContentObjectRepository< + V extends BaseViewModel, + M extends BaseModel +> extends BaseRepository { + public abstract getAgendaTitle: (model: Partial | Partial) => string; + public abstract getAgendaTitleWithType: (model: Partial | Partial) => string; + public abstract getVerboseName: (plural?: boolean) => string; + + /** + */ + public constructor( + DS: DataStoreService, + collectionStringMapperService: CollectionStringMapperService, + viewModelStoreService: ViewModelStoreService, + baseModelCtor: ModelConstructor, + depsModelCtors?: ModelConstructor[] + ) { + super(DS, collectionStringMapperService, viewModelStoreService, baseModelCtor, depsModelCtors); + } +} diff --git a/client/src/app/core/repositories/motions/change-recommendation-repository.service.ts b/client/src/app/core/repositories/motions/change-recommendation-repository.service.ts index 0585377a3..fa80ad058 100644 --- a/client/src/app/core/repositories/motions/change-recommendation-repository.service.ts +++ b/client/src/app/core/repositories/motions/change-recommendation-repository.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { User } from 'app/shared/models/users/user'; @@ -14,7 +15,6 @@ import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-cha import { Identifiable } from 'app/shared/models/base/identifiable'; import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; -import { TranslateService } from '@ngx-translate/core'; /** * Repository Services for change recommendations diff --git a/client/src/app/core/repositories/motions/motion-block-repository.service.ts b/client/src/app/core/repositories/motions/motion-block-repository.service.ts index 82975e168..f4704dbe9 100644 --- a/client/src/app/core/repositories/motions/motion-block-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-block-repository.service.ts @@ -2,8 +2,8 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; -import { BaseRepository } from 'app/core/repositories/base-repository'; import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataStoreService } from 'app/core/core-services/data-store.service'; @@ -17,7 +17,7 @@ import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { Item } from 'app/shared/models/agenda/item'; import { ViewItem } from 'app/site/agenda/models/view-item'; -import { TranslateService } from '@ngx-translate/core'; +import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; /** * Repository service for motion blocks @@ -25,7 +25,7 @@ import { TranslateService } from '@ngx-translate/core'; @Injectable({ providedIn: 'root' }) -export class MotionBlockRepositoryService extends BaseRepository { +export class MotionBlockRepositoryService extends BaseAgendaContentObjectRepository { /** * Constructor for the motion block repository * @@ -47,6 +47,18 @@ export class MotionBlockRepositoryService extends BaseRepository | Partial) => { + return motionBlock.title; + }; + + public getAgendaTitleWithType = (motionBlock: Partial | Partial) => { + return motionBlock.title + ' (' + this.getVerboseName() + ')'; + }; + + public getVerboseName = (plural: boolean = false) => { + return this.translate.instant(plural ? 'Motion blocks' : 'Motion block'); + }; + /** * Converts a given motion block into a ViewModel * @@ -56,9 +68,9 @@ export class MotionBlockRepositoryService extends BaseRepository { - return this.translate.instant(plural ? 'Motion blocks' : 'Motion block'); - }; + viewMotionBlock.getVerboseName = this.getVerboseName; + viewMotionBlock.getAgendaTitle = () => this.getAgendaTitle(viewMotionBlock); + viewMotionBlock.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewMotionBlock); return viewMotionBlock; } diff --git a/client/src/app/core/repositories/motions/motion-repository.service.ts b/client/src/app/core/repositories/motions/motion-repository.service.ts index 04c6ec434..eed5c50e1 100644 --- a/client/src/app/core/repositories/motions/motion-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-repository.service.ts @@ -4,7 +4,6 @@ import { TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { tap, map } from 'rxjs/operators'; -import { BaseRepository } from '../base-repository'; import { Category } from 'app/shared/models/motions/category'; import { ChangeRecoMode, ViewMotion } from 'app/site/motions/models/view-motion'; import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; @@ -40,6 +39,7 @@ import { ViewItem } from 'app/site/agenda/models/view-item'; import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; import { ViewTag } from 'app/site/tags/models/view-tag'; +import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; /** * Repository Services for motions (and potentially categories) @@ -54,7 +54,7 @@ import { ViewTag } from 'app/site/tags/models/view-tag'; @Injectable({ providedIn: 'root' }) -export class MotionRepositoryService extends BaseRepository { +export class MotionRepositoryService extends BaseAgendaContentObjectRepository { /** * Creates a MotionRepository * @@ -92,6 +92,28 @@ export class MotionRepositoryService extends BaseRepository ]); } + public getAgendaTitle = (motion: Partial | Partial) => { + // if the identifier is set, the title will be 'Motion '. + if (motion.identifier) { + return this.translate.instant('Motion') + ' ' + motion.identifier; + } else { + return motion.title; + } + }; + + public getAgendaTitleWithType = (motion: Partial | Partial) => { + // Append the verbose name only, if not the special format 'Motion ' is used. + if (motion.identifier) { + return this.translate.instant('Motion') + ' ' + motion.identifier; + } else { + return motion.title + ' (' + this.getVerboseName() + ')'; + } + }; + + public getVerboseName = (plural: boolean = false) => { + return this.translate.instant(plural ? 'Motions' : 'Motion'); + }; + /** * Converts a motion to a ViewMotion and adds it to the store. * @@ -127,26 +149,10 @@ export class MotionRepositoryService extends BaseRepository tags, parent ); - viewMotion.getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Motions' : 'Motion'); - }; - viewMotion.getAgendaTitle = () => { - // if the identifier is set, the title will be 'Motion '. - if (viewMotion.identifier) { - return this.translate.instant('Motion') + ' ' + viewMotion.identifier; - } else { - return viewMotion.getTitle(); - } - }; + viewMotion.getVerboseName = this.getVerboseName; + viewMotion.getAgendaTitle = () => this.getAgendaTitle(viewMotion); viewMotion.getProjectorTitle = viewMotion.getAgendaTitle; - viewMotion.getAgendaTitleWithType = () => { - // Append the verbose name only, if not the special format 'Motion ' is used. - if (viewMotion.identifier) { - return this.translate.instant('Motion') + ' ' + viewMotion.identifier; - } else { - return viewMotion.getTitle() + ' (' + viewMotion.getVerboseName() + ')'; - } - }; + viewMotion.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewMotion); return viewMotion; } diff --git a/client/src/app/shared/components/projection-dialog/projection-dialog.component.html b/client/src/app/shared/components/projection-dialog/projection-dialog.component.html index afbfbe1d4..5c68acc25 100644 --- a/client/src/app/shared/components/projection-dialog/projection-dialog.component.html +++ b/client/src/app/shared/components/projection-dialog/projection-dialog.component.html @@ -1,5 +1,5 @@

- Project {{ projectorElementBuildDescriptor.getTitle() }}? + Project {{ projectorElementBuildDescriptor.getDialogTitle() }}?

- videocam - + + + + + diff --git a/client/src/app/shared/components/projector-button/projector-button.component.scss b/client/src/app/shared/components/projector-button/projector-button.component.scss index 5d253055f..8cb69302f 100644 --- a/client/src/app/shared/components/projector-button/projector-button.component.scss +++ b/client/src/app/shared/components/projector-button/projector-button.component.scss @@ -1,4 +1,18 @@ -.projectorbutton-inactive { +.projector-inactive { background-color: white !important; - color: grey !important; + + mat-icon { + color: grey !important; + } +} + +/** TODO: Take this from the accent color. Make the hovering visible */ + +.projector-active { + background-color: #03a9f4; + color: white !important; +} + +button.mat-menu-item.projector-active:hover { + background-color: #03a9f4; } diff --git a/client/src/app/shared/components/projector-button/projector-button.component.ts b/client/src/app/shared/components/projector-button/projector-button.component.ts index dad433ed8..57c6047a8 100644 --- a/client/src/app/shared/components/projector-button/projector-button.component.ts +++ b/client/src/app/shared/components/projector-button/projector-button.component.ts @@ -39,6 +39,12 @@ export class ProjectorButtonComponent implements OnInit { } } + @Input() + public text: string | null; + + @Input() + public menuItem = false; + /** * The constructor */ diff --git a/client/src/app/shared/models/agenda/item.ts b/client/src/app/shared/models/agenda/item.ts index a28c9c04b..5c2e2b647 100644 --- a/client/src/app/shared/models/agenda/item.ts +++ b/client/src/app/shared/models/agenda/item.ts @@ -29,8 +29,7 @@ export class Item extends BaseModel { public id: number; public item_number: string; - public title: string; - public title_with_type: string; + public title_information: object; public comment: string; public closed: boolean; public type: number; diff --git a/client/src/app/site/agenda/components/speaker-list/speaker-list.component.html b/client/src/app/site/agenda/components/speaker-list/speaker-list.component.html index 6f1edbe31..1fd70e6a3 100644 --- a/client/src/app/site/agenda/components/speaker-list/speaker-list.component.html +++ b/client/src/app/site/agenda/components/speaker-list/speaker-list.component.html @@ -6,7 +6,7 @@ Current list of speakers
- @@ -142,7 +142,7 @@ drag_indicator
- {{ i+1 }}. {{ getElementDescription(element) }} + {{ i+1 }}. {{ getSlideTitle(element) }}
diff --git a/client/src/app/site/projector/components/projector-detail/projector-detail.component.ts b/client/src/app/site/projector/components/projector-detail/projector-detail.component.ts index f3bf25e16..c5d3e1b70 100644 --- a/client/src/app/site/projector/components/projector-detail/projector-detail.component.ts +++ b/client/src/app/site/projector/components/projector-detail/projector-detail.component.ts @@ -116,16 +116,8 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni this.projectorService.projectPreviewSlide(this.projector.projector, elementIndex).then(null, this.raiseError); } - public getElementDescription(element: ProjectorElement): string { - if (this.slideManager.canSlideBeMappedToModel(element.name)) { - const idElement = this.slideManager.getIdentifialbeProjectorElement(element); - const viewModel = this.projectorService.getViewModelFromProjectorElement(idElement); - if (viewModel) { - return viewModel.getProjectorTitle(); - } - } - - return this.slideManager.getSlideVerboseName(element.name); + public getSlideTitle(element: ProjectorElement): string { + return this.projectorService.getSlideTitle(element); } public isProjected(obj: Projectable): boolean { diff --git a/client/src/app/site/projector/models/view-countdown.ts b/client/src/app/site/projector/models/view-countdown.ts index 73bb3e7c2..d35e32675 100644 --- a/client/src/app/site/projector/models/view-countdown.ts +++ b/client/src/app/site/projector/models/view-countdown.ts @@ -64,7 +64,7 @@ export class ViewCountdown extends BaseProjectableViewModel { } ], projectionDefaultName: 'countdowns', - getTitle: () => this.getTitle() + getDialogTitle: () => this.getTitle() }; } } diff --git a/client/src/app/site/projector/models/view-projector-message.ts b/client/src/app/site/projector/models/view-projector-message.ts index 5e0866e2c..04df9785a 100644 --- a/client/src/app/site/projector/models/view-projector-message.ts +++ b/client/src/app/site/projector/models/view-projector-message.ts @@ -47,7 +47,7 @@ export class ViewProjectorMessage extends BaseProjectableViewModel { }), slideOptions: [], projectionDefaultName: 'messages', - getTitle: () => this.getTitle() + getDialogTitle: () => this.getTitle() }; } diff --git a/client/src/app/site/users/models/view-user.ts b/client/src/app/site/users/models/view-user.ts index 7915cb6e4..6912bbea2 100644 --- a/client/src/app/site/users/models/view-user.ts +++ b/client/src/app/site/users/models/view-user.ts @@ -195,7 +195,7 @@ export class ViewUser extends BaseProjectableViewModel implements Searchable { }), slideOptions: [], projectionDefaultName: 'users', - getTitle: () => this.getTitle() + getDialogTitle: () => this.getTitle() }; } diff --git a/client/src/app/slides/agenda/base/agenda-current-list-of-speakers-slide-data.ts b/client/src/app/slides/agenda/base/agenda-current-list-of-speakers-slide-data.ts deleted file mode 100644 index b6caa2101..000000000 --- a/client/src/app/slides/agenda/base/agenda-current-list-of-speakers-slide-data.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface AgendaCurrentListOfSpeakersSlideData { - error: string; -} diff --git a/client/src/app/slides/agenda/base/current-list-of-speakers-slide-data.ts b/client/src/app/slides/agenda/base/current-list-of-speakers-slide-data.ts new file mode 100644 index 000000000..d3a113f1b --- /dev/null +++ b/client/src/app/slides/agenda/base/current-list-of-speakers-slide-data.ts @@ -0,0 +1,3 @@ +export interface CurrentListOfSpeakersSlideData { + error: string; +} diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.spec.ts b/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.spec.ts deleted file mode 100644 index f8b0ddcaa..000000000 --- a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { AgendaCurrentListOfSpeakersOverlaySlideComponent } from './agenda-current-list-of-speakers-overlay-slide.component'; -import { E2EImportsModule } from '../../../../e2e-imports.module'; - -describe('AgendaCurrentListOfSpeakersOverlaySlideComponent', () => { - let component: AgendaCurrentListOfSpeakersOverlaySlideComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [E2EImportsModule], - declarations: [AgendaCurrentListOfSpeakersOverlaySlideComponent] - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(AgendaCurrentListOfSpeakersOverlaySlideComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.ts b/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.ts deleted file mode 100644 index 8a467b9a2..000000000 --- a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component } from '@angular/core'; - -import { BaseSlideComponent } from 'app/slides/base-slide-component'; -import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-list-of-speakers-slide-data'; - -@Component({ - selector: 'os-agenda-current-list-of-speakers-overlay-slide', - templateUrl: './agenda-current-list-of-speakers-overlay-slide.component.html', - styleUrls: ['./agenda-current-list-of-speakers-overlay-slide.component.scss'] -}) -export class AgendaCurrentListOfSpeakersOverlaySlideComponent extends BaseSlideComponent< - AgendaCurrentListOfSpeakersSlideData -> { - public constructor() { - super(); - } -} diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.module.spec.ts b/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.module.spec.ts deleted file mode 100644 index 6a55a7cdd..000000000 --- a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.module.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { AgendaCurrentListOfSpeakersOverlaySlideModule } from './agenda-current-list-of-speakers-overlay-slide.module'; - -describe('AgendaCurrentListOfSpeakersOverlaySlideModule', () => { - let agendaCurrentListOfSpeakersOverlaySlideModule: AgendaCurrentListOfSpeakersOverlaySlideModule; - - beforeEach(() => { - agendaCurrentListOfSpeakersOverlaySlideModule = new AgendaCurrentListOfSpeakersOverlaySlideModule(); - }); - - it('should create an instance', () => { - expect(agendaCurrentListOfSpeakersOverlaySlideModule).toBeTruthy(); - }); -}); diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.module.ts b/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.module.ts deleted file mode 100644 index a6111807e..000000000 --- a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { makeSlideModule } from 'app/slides/base-slide-module'; -import { AgendaCurrentListOfSpeakersOverlaySlideComponent } from './agenda-current-list-of-speakers-overlay-slide.component'; - -@NgModule(makeSlideModule(AgendaCurrentListOfSpeakersOverlaySlideComponent)) -export class AgendaCurrentListOfSpeakersOverlaySlideModule {} diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.html b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.html similarity index 100% rename from client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.html rename to client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.html diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.scss b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.scss similarity index 100% rename from client/src/app/slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.component.scss rename to client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.scss diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.spec.ts b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.spec.ts new file mode 100644 index 000000000..4461b8f5a --- /dev/null +++ b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.spec.ts @@ -0,0 +1,26 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CurrentListOfSpeakersOverlaySlideComponent } from './current-list-of-speakers-overlay-slide.component'; +import { E2EImportsModule } from '../../../../e2e-imports.module'; + +describe('CurrentListOfSpeakersOverlaySlideComponent', () => { + let component: CurrentListOfSpeakersOverlaySlideComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + declarations: [CurrentListOfSpeakersOverlaySlideComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CurrentListOfSpeakersOverlaySlideComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.ts b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.ts new file mode 100644 index 000000000..d8ab6f79a --- /dev/null +++ b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; + +import { BaseSlideComponent } from 'app/slides/base-slide-component'; +import { CurrentListOfSpeakersSlideData } from '../base/current-list-of-speakers-slide-data'; + +@Component({ + selector: 'os-current-list-of-speakers-overlay-slide', + templateUrl: './current-list-of-speakers-overlay-slide.component.html', + styleUrls: ['./current-list-of-speakers-overlay-slide.component.scss'] +}) +export class CurrentListOfSpeakersOverlaySlideComponent extends BaseSlideComponent { + public constructor() { + super(); + } +} diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.module.spec.ts b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.module.spec.ts new file mode 100644 index 000000000..7b235d22f --- /dev/null +++ b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.module.spec.ts @@ -0,0 +1,13 @@ +import { CurrentListOfSpeakersOverlaySlideModule } from './current-list-of-speakers-overlay-slide.module'; + +describe('CurrentListOfSpeakersOverlaySlideModule', () => { + let currentListOfSpeakersOverlaySlideModule: CurrentListOfSpeakersOverlaySlideModule; + + beforeEach(() => { + currentListOfSpeakersOverlaySlideModule = new CurrentListOfSpeakersOverlaySlideModule(); + }); + + it('should create an instance', () => { + expect(currentListOfSpeakersOverlaySlideModule).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.module.ts b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.module.ts new file mode 100644 index 000000000..e93f2dd29 --- /dev/null +++ b/client/src/app/slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; + +import { makeSlideModule } from 'app/slides/base-slide-module'; +import { CurrentListOfSpeakersOverlaySlideComponent } from './current-list-of-speakers-overlay-slide.component'; + +@NgModule(makeSlideModule(CurrentListOfSpeakersOverlaySlideComponent)) +export class CurrentListOfSpeakersOverlaySlideModule {} diff --git a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.ts b/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.ts deleted file mode 100644 index 436248765..000000000 --- a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { BaseSlideComponent } from 'app/slides/base-slide-component'; -import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-list-of-speakers-slide-data'; - -@Component({ - selector: 'os-agenda-current-list-of-speakers-slide', - templateUrl: './agenda-current-list-of-speakers-slide.component.html', - styleUrls: ['./agenda-current-list-of-speakers-slide.component.scss'] -}) -export class AgendaCurrentListOfSpeakersSlideComponent extends BaseSlideComponent - implements OnInit { - public constructor() { - super(); - } - - public ngOnInit(): void {} -} diff --git a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.module.spec.ts b/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.module.spec.ts deleted file mode 100644 index 87e5349c5..000000000 --- a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.module.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { AgendaCurrentListOfSpeakersSlideModule } from './agenda-current-list-of-speakers-slide.module'; - -describe('AgendaCurrentListOfSpeakersModule', () => { - let agendaCurrentListOfSpeakersSlideModule: AgendaCurrentListOfSpeakersSlideModule; - - beforeEach(() => { - agendaCurrentListOfSpeakersSlideModule = new AgendaCurrentListOfSpeakersSlideModule(); - }); - - it('should create an instance', () => { - expect(agendaCurrentListOfSpeakersSlideModule).toBeTruthy(); - }); -}); diff --git a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.module.ts b/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.module.ts deleted file mode 100644 index fbb3075db..000000000 --- a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { makeSlideModule } from 'app/slides/base-slide-module'; -import { AgendaCurrentListOfSpeakersSlideComponent } from './agenda-current-list-of-speakers-slide.component'; - -@NgModule(makeSlideModule(AgendaCurrentListOfSpeakersSlideComponent)) -export class AgendaCurrentListOfSpeakersSlideModule {} diff --git a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.html b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.html similarity index 100% rename from client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.html rename to client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.html diff --git a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.scss b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.scss similarity index 100% rename from client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.scss rename to client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.scss diff --git a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.spec.ts b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.spec.ts similarity index 52% rename from client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.spec.ts rename to client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.spec.ts index 97f34678a..9294f79f8 100644 --- a/client/src/app/slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.component.spec.ts +++ b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.spec.ts @@ -1,21 +1,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { AgendaCurrentListOfSpeakersSlideComponent } from './agenda-current-list-of-speakers-slide.component'; +import { CurrentListOfSpeakersSlideComponent } from './current-list-of-speakers-slide.component'; import { E2EImportsModule } from '../../../../e2e-imports.module'; -describe('CoreCountdownSlideComponent', () => { - let component: AgendaCurrentListOfSpeakersSlideComponent; - let fixture: ComponentFixture; +describe('CurrentListOfSpeakersSlideComponent', () => { + let component: CurrentListOfSpeakersSlideComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [E2EImportsModule], - declarations: [AgendaCurrentListOfSpeakersSlideComponent] + declarations: [CurrentListOfSpeakersSlideComponent] }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(AgendaCurrentListOfSpeakersSlideComponent); + fixture = TestBed.createComponent(CurrentListOfSpeakersSlideComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.ts b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.ts new file mode 100644 index 000000000..ed84c50e7 --- /dev/null +++ b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.component.ts @@ -0,0 +1,18 @@ +import { Component, OnInit } from '@angular/core'; + +import { BaseSlideComponent } from 'app/slides/base-slide-component'; +import { CurrentListOfSpeakersSlideData } from '../base/current-list-of-speakers-slide-data'; + +@Component({ + selector: 'os-current-list-of-speakers-slide', + templateUrl: './current-list-of-speakers-slide.component.html', + styleUrls: ['./current-list-of-speakers-slide.component.scss'] +}) +export class CurrentListOfSpeakersSlideComponent extends BaseSlideComponent + implements OnInit { + public constructor() { + super(); + } + + public ngOnInit(): void {} +} diff --git a/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.module.spec.ts b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.module.spec.ts new file mode 100644 index 000000000..68ee60fff --- /dev/null +++ b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.module.spec.ts @@ -0,0 +1,13 @@ +import { CurrentListOfSpeakersSlideModule } from './current-list-of-speakers-slide.module'; + +describe('CurrentListOfSpeakersSlideModule', () => { + let currentListOfSpeakersSlideModule: CurrentListOfSpeakersSlideModule; + + beforeEach(() => { + currentListOfSpeakersSlideModule = new CurrentListOfSpeakersSlideModule(); + }); + + it('should create an instance', () => { + expect(currentListOfSpeakersSlideModule).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.module.ts b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.module.ts new file mode 100644 index 000000000..30ef00d3d --- /dev/null +++ b/client/src/app/slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; + +import { makeSlideModule } from 'app/slides/base-slide-module'; +import { CurrentListOfSpeakersSlideComponent } from './current-list-of-speakers-slide.component'; + +@NgModule(makeSlideModule(CurrentListOfSpeakersSlideComponent)) +export class CurrentListOfSpeakersSlideModule {} diff --git a/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide-data.ts b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide-data.ts new file mode 100644 index 000000000..aaa07fbd6 --- /dev/null +++ b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide-data.ts @@ -0,0 +1,13 @@ +interface SlideSpeaker { + user: string; + marked: boolean; +} + +export interface ListOfSpeakersSlideData { + waiting: SlideSpeaker[]; + current: SlideSpeaker; + finished: SlideSpeaker[]; + title_information: object; + content_object_collection: string; + item_number: string; +} diff --git a/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.html b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.html new file mode 100644 index 000000000..ced0e6349 --- /dev/null +++ b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.html @@ -0,0 +1,22 @@ +
+

List of speakers

+

{{ getTitle() }}

+ +
+ Finished: +
+ {{ speaker.user }} +
+
+ +
+ Current: {{ data.data.current.user }} +
+ +
+ Waiting: +
+ {{ speaker.user }} {{ speaker.marked ? 'marked' : '' }} +
+
+
diff --git a/client/src/app/slides/agenda/topic/topics-topic-slide.component.scss b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.scss similarity index 100% rename from client/src/app/slides/agenda/topic/topics-topic-slide.component.scss rename to client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.scss diff --git a/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.spec.ts b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.spec.ts new file mode 100644 index 000000000..1cae3241e --- /dev/null +++ b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.spec.ts @@ -0,0 +1,26 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListOfSpeakersSlideComponent } from './list-of-speakers-slide.component'; +import { E2EImportsModule } from '../../../../e2e-imports.module'; + +describe('ListOfSpeakersSlideComponent', () => { + let component: ListOfSpeakersSlideComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + declarations: [ListOfSpeakersSlideComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ListOfSpeakersSlideComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.ts b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.ts new file mode 100644 index 000000000..c68fc102f --- /dev/null +++ b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.component.ts @@ -0,0 +1,28 @@ +import { Component } from '@angular/core'; + +import { BaseSlideComponent } from 'app/slides/base-slide-component'; +import { ListOfSpeakersSlideData } from './list-of-speakers-slide-data'; +import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { isBaseAgendaContentObjectRepository } from 'app/core/repositories/base-agenda-content-object-repository'; + +@Component({ + selector: 'os-list-of-speakers-slide', + templateUrl: './list-of-speakers-slide.component.html', + styleUrls: ['./list-of-speakers-slide.component.scss'] +}) +export class ListOfSpeakersSlideComponent extends BaseSlideComponent { + public constructor(private collectionStringMapperService: CollectionStringMapperService) { + super(); + } + + public getTitle(): string { + const numberPrefix = this.data.data.item_number ? `${this.data.data.item_number} · ` : ''; + const repo = this.collectionStringMapperService.getRepository(this.data.data.content_object_collection); + + if (isBaseAgendaContentObjectRepository(repo)) { + return numberPrefix + repo.getAgendaTitle(this.data.data.title_information); + } else { + throw new Error('The content object has no agenda based repository!'); + } + } +} diff --git a/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.module.spec.ts b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.module.spec.ts new file mode 100644 index 000000000..2655a8db0 --- /dev/null +++ b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.module.spec.ts @@ -0,0 +1,13 @@ +import { ListOfSpeakersSlideModule } from './list-of-speakers-slide.module'; + +describe('ListOfSpeakersSlideModule', () => { + let listOfSpeakersSlideModule: ListOfSpeakersSlideModule; + + beforeEach(() => { + listOfSpeakersSlideModule = new ListOfSpeakersSlideModule(); + }); + + it('should create an instance', () => { + expect(listOfSpeakersSlideModule).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.module.ts b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.module.ts new file mode 100644 index 000000000..3b9ce14d0 --- /dev/null +++ b/client/src/app/slides/agenda/list-of-speakers/list-of-speakers-slide.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; + +import { makeSlideModule } from 'app/slides/base-slide-module'; +import { ListOfSpeakersSlideComponent } from './list-of-speakers-slide.component'; + +@NgModule(makeSlideModule(ListOfSpeakersSlideComponent)) +export class ListOfSpeakersSlideModule {} diff --git a/client/src/app/slides/agenda/topic/topic-slide-data.ts b/client/src/app/slides/agenda/topic/topic-slide-data.ts new file mode 100644 index 000000000..8e5873ccc --- /dev/null +++ b/client/src/app/slides/agenda/topic/topic-slide-data.ts @@ -0,0 +1,4 @@ +export interface TopicSlideData { + title: string; + text: string; +} diff --git a/client/src/app/slides/agenda/topic/topics-topic-slide.component.html b/client/src/app/slides/agenda/topic/topic-slide.component.html similarity index 100% rename from client/src/app/slides/agenda/topic/topics-topic-slide.component.html rename to client/src/app/slides/agenda/topic/topic-slide.component.html diff --git a/client/src/app/slides/agenda/topic/topic-slide.component.scss b/client/src/app/slides/agenda/topic/topic-slide.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/slides/agenda/topic/topics-topic-slide.component.spec.ts b/client/src/app/slides/agenda/topic/topic-slide.component.spec.ts similarity index 57% rename from client/src/app/slides/agenda/topic/topics-topic-slide.component.spec.ts rename to client/src/app/slides/agenda/topic/topic-slide.component.spec.ts index 2edf21d5e..4cec18a57 100644 --- a/client/src/app/slides/agenda/topic/topics-topic-slide.component.spec.ts +++ b/client/src/app/slides/agenda/topic/topic-slide.component.spec.ts @@ -1,21 +1,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TopicsTopicSlideComponent } from './topics-topic-slide.component'; +import { TopicSlideComponent } from './topic-slide.component'; import { E2EImportsModule } from 'e2e-imports.module'; -describe('TopicsTopicSlideComponent', () => { - let component: TopicsTopicSlideComponent; - let fixture: ComponentFixture; +describe('TopicSlideComponent', () => { + let component: TopicSlideComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [E2EImportsModule], - declarations: [TopicsTopicSlideComponent] + declarations: [TopicSlideComponent] }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(TopicsTopicSlideComponent); + fixture = TestBed.createComponent(TopicSlideComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/client/src/app/slides/agenda/topic/topic-slide.component.ts b/client/src/app/slides/agenda/topic/topic-slide.component.ts new file mode 100644 index 000000000..04b36c8a5 --- /dev/null +++ b/client/src/app/slides/agenda/topic/topic-slide.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { BaseSlideComponent } from 'app/slides/base-slide-component'; +import { TopicSlideData } from './topic-slide-data'; + +@Component({ + selector: 'os-topic-slide', + templateUrl: './topic-slide.component.html', + styleUrls: ['./topic-slide.component.scss'] +}) +export class TopicSlideComponent extends BaseSlideComponent { + public constructor() { + super(); + } +} diff --git a/client/src/app/slides/agenda/topic/topic-slide.module.spec.ts b/client/src/app/slides/agenda/topic/topic-slide.module.spec.ts new file mode 100644 index 000000000..988328bb8 --- /dev/null +++ b/client/src/app/slides/agenda/topic/topic-slide.module.spec.ts @@ -0,0 +1,13 @@ +import { TopicSlideModule } from './topic-slide.module'; + +describe('TopicSlideModule', () => { + let topicsTopicSlideModule: TopicSlideModule; + + beforeEach(() => { + topicsTopicSlideModule = new TopicSlideModule(); + }); + + it('should create an instance', () => { + expect(topicsTopicSlideModule).toBeTruthy(); + }); +}); diff --git a/client/src/app/slides/agenda/topic/topic-slide.module.ts b/client/src/app/slides/agenda/topic/topic-slide.module.ts new file mode 100644 index 000000000..7830c11e7 --- /dev/null +++ b/client/src/app/slides/agenda/topic/topic-slide.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; + +import { makeSlideModule } from 'app/slides/base-slide-module'; +import { TopicSlideComponent } from './topic-slide.component'; + +@NgModule(makeSlideModule(TopicSlideComponent)) +export class TopicSlideModule {} diff --git a/client/src/app/slides/agenda/topic/topics-topic-slide-data.ts b/client/src/app/slides/agenda/topic/topics-topic-slide-data.ts deleted file mode 100644 index dfa23048b..000000000 --- a/client/src/app/slides/agenda/topic/topics-topic-slide-data.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface TopicsTopicSlideData { - title: string; - text: string; -} diff --git a/client/src/app/slides/agenda/topic/topics-topic-slide.component.ts b/client/src/app/slides/agenda/topic/topics-topic-slide.component.ts deleted file mode 100644 index 8267de3b7..000000000 --- a/client/src/app/slides/agenda/topic/topics-topic-slide.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Component } from '@angular/core'; -import { BaseSlideComponent } from 'app/slides/base-slide-component'; -import { TopicsTopicSlideData } from './topics-topic-slide-data'; - -@Component({ - selector: 'os-topic-slide', - templateUrl: './topics-topic-slide.component.html', - styleUrls: ['./topics-topic-slide.component.scss'] -}) -export class TopicsTopicSlideComponent extends BaseSlideComponent { - public constructor() { - super(); - } -} diff --git a/client/src/app/slides/agenda/topic/topics-topic-slide.module.spec.ts b/client/src/app/slides/agenda/topic/topics-topic-slide.module.spec.ts deleted file mode 100644 index 6661ad8f1..000000000 --- a/client/src/app/slides/agenda/topic/topics-topic-slide.module.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { TopicsTopicSlideModule } from './topics-topic-slide.module'; - -describe('TopicsTopicSlideModule', () => { - let topicsTopicSlideModule: TopicsTopicSlideModule; - - beforeEach(() => { - topicsTopicSlideModule = new TopicsTopicSlideModule(); - }); - - it('should create an instance', () => { - expect(topicsTopicSlideModule).toBeTruthy(); - }); -}); diff --git a/client/src/app/slides/agenda/topic/topics-topic-slide.module.ts b/client/src/app/slides/agenda/topic/topics-topic-slide.module.ts deleted file mode 100644 index 1e30afea4..000000000 --- a/client/src/app/slides/agenda/topic/topics-topic-slide.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { makeSlideModule } from 'app/slides/base-slide-module'; -import { TopicsTopicSlideComponent } from './topics-topic-slide.component'; - -@NgModule(makeSlideModule(TopicsTopicSlideComponent)) -export class TopicsTopicSlideModule {} diff --git a/client/src/app/slides/all-slide-configurations.ts b/client/src/app/slides/all-slide-configurations.ts index 9f4dac2aa..ea31ee9ea 100644 --- a/client/src/app/slides/all-slide-configurations.ts +++ b/client/src/app/slides/all-slide-configurations.ts @@ -1,4 +1,8 @@ +import { TranslateService } from '@ngx-translate/core'; + import { SlideDynamicConfiguration, Slide } from './slide-manifest'; +import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; +import { ProjectorElement } from 'app/shared/models/core/projector'; export const allSlidesDynamicConfiguration: (SlideDynamicConfiguration & Slide)[] = [ { @@ -31,6 +35,23 @@ export const allSlidesDynamicConfiguration: (SlideDynamicConfiguration & Slide)[ scaleable: false, scrollable: false }, + { + slide: 'agenda/list-of-speakers', + scaleable: true, + scrollable: true, + getSlideTitle: ( + element: ProjectorElement, + translate: TranslateService, + viewModelStore: ViewModelStoreService + ) => { + const item = viewModelStore.get('agenda/item', element.id); + if (item) { + const title = translate.instant('List of speakers for'); + return title + ' ' + item.getTitle(); + } + return translate.instant('List of speakers'); + } + }, { slide: 'agenda/current-list-of-speakers', scaleable: true, diff --git a/client/src/app/slides/all-slides.ts b/client/src/app/slides/all-slides.ts index 069a88e79..a4a699d6e 100644 --- a/client/src/app/slides/all-slides.ts +++ b/client/src/app/slides/all-slides.ts @@ -11,7 +11,7 @@ export const allSlides: SlideManifest[] = [ { slide: 'topics/topic', path: 'topics/topic', - loadChildren: './slides/agenda/topic/topics-topic-slide.module#TopicsTopicSlideModule', + loadChildren: './slides/agenda/topic/topic-slide.module#TopicSlideModule', verboseName: 'Topic', elementIdentifiers: ['name', 'id'], canBeMappedToModel: true @@ -60,7 +60,7 @@ export const allSlides: SlideManifest[] = [ slide: 'agenda/current-list-of-speakers', path: 'agenda/current-list-of-speakers', loadChildren: - './slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.module#AgendaCurrentListOfSpeakersSlideModule', + './slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.module#CurrentListOfSpeakersSlideModule', verboseName: 'Current list of speakers', elementIdentifiers: ['name', 'id'], canBeMappedToModel: false @@ -69,11 +69,19 @@ export const allSlides: SlideManifest[] = [ slide: 'agenda/current-list-of-speakers-overlay', path: 'agenda/current-list-of-speakers-overlay', loadChildren: - './slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.module#AgendaCurrentListOfSpeakersOverlaySlideModule', + './slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.module#CurrentListOfSpeakersOverlaySlideModule', verboseName: 'Current list of speakers overlay', elementIdentifiers: ['name', 'id'], canBeMappedToModel: false }, + { + slide: 'agenda/list-of-speakers', + path: 'agenda/list-of-speakers', + loadChildren: './slides/agenda/list-of-speakers/list-of-speakers-slide.module#ListOfSpeakersSlideModule', + verboseName: 'List of speakers', + elementIdentifiers: ['name', 'id'], + canBeMappedToModel: false + }, { slide: 'assignments/assignment', path: 'assignments/assignment', diff --git a/client/src/app/slides/motions/motion/motions-motion-slide.component.ts b/client/src/app/slides/motions/motion/motions-motion-slide.component.ts index 6dbd5fc3f..6f089f6b2 100644 --- a/client/src/app/slides/motions/motion/motions-motion-slide.component.ts +++ b/client/src/app/slides/motions/motion/motions-motion-slide.component.ts @@ -1,8 +1,9 @@ import { Component, Input } from '@angular/core'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; + import { BaseSlideComponent } from 'app/slides/base-slide-component'; import { MotionsMotionSlideData, MotionsMotionSlideDataAmendment } from './motions-motion-slide-data'; import { ChangeRecoMode, LineNumberingMode } from '../../../site/motions/models/view-motion'; -import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DiffLinesInParagraph, DiffService, LineRange } from '../../../core/ui-services/diff.service'; import { LinenumberingService } from '../../../core/ui-services/linenumbering.service'; import { ViewUnifiedChange } from '../../../shared/models/motions/view-unified-change'; diff --git a/client/src/app/slides/slide-manifest.ts b/client/src/app/slides/slide-manifest.ts index 9db2c5f42..b09577293 100644 --- a/client/src/app/slides/slide-manifest.ts +++ b/client/src/app/slides/slide-manifest.ts @@ -1,5 +1,7 @@ import { InjectionToken } from '@angular/core'; import { IdentifiableProjectorElement, ProjectorElement } from 'app/shared/models/core/projector'; +import { TranslateService } from '@ngx-translate/core'; +import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; type BooleanOrFunction = boolean | ((element: ProjectorElement) => boolean); @@ -20,6 +22,12 @@ export interface SlideDynamicConfiguration { * Should this slide be scaleable? */ scaleable: BooleanOrFunction; + + getSlideTitle?: ( + element: ProjectorElement, + translate: TranslateService, + viewModelStore: ViewModelStoreService + ) => string; } /** diff --git a/openslides/agenda/access_permissions.py b/openslides/agenda/access_permissions.py index 040ca3540..7ffa8f1c3 100644 --- a/openslides/agenda/access_permissions.py +++ b/openslides/agenda/access_permissions.py @@ -57,7 +57,13 @@ class ItemAccessPermissions(BaseAccessPermissions): # so that list of speakers is provided regardless. Hidden items can only be seen by managers. # We know that full_data has at least one entry which can be used to parse the keys. blocked_keys_internal_hidden_case = set(full_data[0].keys()) - set( - ("id", "title", "speakers", "speaker_list_closed", "content_object") + ( + "id", + "title_information", + "speakers", + "speaker_list_closed", + "content_object", + ) ) # In non internal case managers see everything and non managers see diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index dac6006e2..04d173491 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -283,32 +283,16 @@ class Item(RESTModelMixin, models.Model): ) unique_together = ("content_type", "object_id") - def __str__(self): - return self.title - @property - def title(self): + def title_information(self): """ - Return get_agenda_title() from the content_object. + Return get_agenda_title_information() from the content_object. """ try: - return self.content_object.get_agenda_title() + return self.content_object.get_agenda_title_information() except AttributeError: raise NotImplementedError( - "You have to provide a get_agenda_title " - "method on your related model." - ) - - @property - def title_with_type(self): - """ - Return get_agenda_title_with_type() from the content_object. - """ - try: - return self.content_object.get_agenda_title_with_type() - except AttributeError: - raise NotImplementedError( - "You have to provide a get_agenda_title_with_type " + "You have to provide a get_agenda_title_information " "method on your related model." ) diff --git a/openslides/agenda/projector.py b/openslides/agenda/projector.py index 731d76f0a..07095e38b 100644 --- a/openslides/agenda/projector.py +++ b/openslides/agenda/projector.py @@ -1,9 +1,11 @@ from collections import defaultdict from typing import Any, Dict, List, Tuple +from ..users.projector import get_user_name from ..utils.projector import ( AllData, ProjectorElementException, + get_config, register_projector_slide, ) @@ -75,19 +77,56 @@ def list_of_speakers_slide( Returns all usernames, that are on the list of speaker of a slide. """ - item_id = element.get("id") or 0 # item_id 0 means current_list_of_speakers + item_id = element.get("id") - # TODO: handle item_id == 0 + if item_id is None: + raise ProjectorElementException("id is required for list of speakers slide") try: item = all_data["agenda/item"][item_id] except KeyError: raise ProjectorElementException(f"Item {item_id} does not exist") - user_ids = [] + # Partition speaker objects to waiting, current and finished + speakers_waiting = [] + speakers_finished = [] + current_speaker = None for speaker in item["speakers"]: - user_ids.append(speaker["user"]) - return {"user_ids": user_ids} + user = get_user_name(all_data, speaker["user_id"]) + formatted_speaker = { + "user": user, + "marked": speaker["marked"], + "weight": speaker["weight"], + "end_time": speaker["end_time"], + } + + if speaker["begin_time"] is None and speaker["end_time"] is None: + speakers_waiting.append(formatted_speaker) + elif speaker["begin_time"] is not None and speaker["end_time"] is None: + current_speaker = formatted_speaker + else: + speakers_finished.append(formatted_speaker) + + # sort speakers + speakers_waiting = sorted(speakers_waiting, key=lambda s: s["weight"]) + speakers_finished = sorted(speakers_finished, key=lambda s: s["end_time"]) + + number_of_last_speakers = get_config(all_data, "agenda_show_last_speakers") + if number_of_last_speakers == 0: + speakers_finished = [] + else: + speakers_finished = speakers_finished[ + -number_of_last_speakers: + ] # Take the last speakers + + return { + "waiting": speakers_waiting, + "current": current_speaker, + "finished": speakers_finished, + "content_object_collection": item["content_object"]["collection"], + "title_information": item["title_information"], + "item_number": item["item_number"], + } def current_list_of_speakers_slide( diff --git a/openslides/agenda/serializers.py b/openslides/agenda/serializers.py index 7ebe047a4..3f12ae52e 100644 --- a/openslides/agenda/serializers.py +++ b/openslides/agenda/serializers.py @@ -1,4 +1,4 @@ -from openslides.utils.rest_api import ModelSerializer, RelatedField +from openslides.utils.rest_api import JSONField, ModelSerializer, RelatedField from .models import Item, Speaker @@ -42,13 +42,14 @@ class ItemSerializer(ModelSerializer): content_object = RelatedItemRelatedField(read_only=True) speakers = SpeakerSerializer(many=True, read_only=True) + title_information = JSONField(read_only=True) + class Meta: model = Item fields = ( "id", "item_number", - "title", - "title_with_type", + "title_information", "comment", "closed", "type", diff --git a/openslides/agenda/signals.py b/openslides/agenda/signals.py index 823bb6419..e18c4475d 100644 --- a/openslides/agenda/signals.py +++ b/openslides/agenda/signals.py @@ -16,7 +16,7 @@ def listen_to_related_object_post_save(sender, instance, created, **kwargs): Do not run caching and autoupdate if the instance has a key skip_autoupdate in the agenda_item_update_information container. """ - if hasattr(instance, "get_agenda_title"): + if hasattr(instance, "get_agenda_title_information"): if created: attrs = {} for attr in ("type", "parent_id", "comment", "duration", "weight"): diff --git a/openslides/assignments/models.py b/openslides/assignments/models.py index f418a4393..fefe77a9a 100644 --- a/openslides/assignments/models.py +++ b/openslides/assignments/models.py @@ -318,18 +318,8 @@ class Assignment(RESTModelMixin, models.Model): """ agenda_item_update_information: Dict[str, Any] = {} - def get_agenda_title(self): - """ - Returns the title for the agenda. - """ - return str(self) - - def get_agenda_title_with_type(self): - """ - Return a title for the agenda with the appended assignment verbose name. - Note: It has to be the same return value like in JavaScript. - """ - return f"{self.get_agenda_title()} (self._meta.verbose_name)" + def get_agenda_title_information(self): + return {"title": self.title} @property def agenda_item(self): diff --git a/openslides/motions/models.py b/openslides/motions/models.py index 8cab6f541..fad64bacf 100644 --- a/openslides/motions/models.py +++ b/openslides/motions/models.py @@ -520,32 +520,8 @@ class Motion(RESTModelMixin, models.Model): """ agenda_item_update_information: Dict[str, Any] = {} - def get_agenda_title(self): - """ - Return the title string for the agenda. - - If the identifier is given, the title consists of the motion verbose name - and the identifier. - Note: It has to be the same return value like in JavaScript. - """ - if self.identifier: - title = f"{self._meta.verbose_name} {self.identifier}" - else: - title = self.title - return title - - def get_agenda_title_with_type(self): - """ - Return a title for the agenda with the type or the modified title if the - identifier is set.. - - Note: It has to be the same return value like in JavaScript. - """ - if self.identifier: - title = f"{self._meta.verbose_name} {self.identifier}" - else: - title = f"{self.title} ({self._meta.verbose_name})" - return title + def get_agenda_title_information(self): + return {"title": self.title, "identifier": self.identifier} @property def agenda_item(self): @@ -908,11 +884,8 @@ class MotionBlock(RESTModelMixin, models.Model): """ return self.agenda_item.pk - def get_agenda_title(self): - return self.title - - def get_agenda_title_with_type(self): - return f"{self.get_agenda_title()} ({self._meta.verbose_name})" + def get_agenda_title_information(self): + return {"title": self.title} class MotionLog(RESTModelMixin, models.Model): diff --git a/openslides/topics/models.py b/openslides/topics/models.py index 84b04b443..2c40a32ac 100644 --- a/openslides/topics/models.py +++ b/openslides/topics/models.py @@ -67,14 +67,5 @@ class Topic(RESTModelMixin, models.Model): """ return self.agenda_item.pk - def get_agenda_title(self): - """ - Returns the title for the agenda. - """ - return self.title - - def get_agenda_title_with_type(self): - """ - Returns the agenda title. Topicy should not get a type postfix. - """ - return self.get_agenda_title() + def get_agenda_title_information(self): + return {"title": self.title} diff --git a/openslides/users/projector.py b/openslides/users/projector.py index ca0d9ee3b..e1f55b7bf 100644 --- a/openslides/users/projector.py +++ b/openslides/users/projector.py @@ -25,19 +25,18 @@ def user_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]: if user_id is None: raise ProjectorElementException("id is required for user slide") - try: - user = all_data["users/user"][user_id] - except KeyError: - raise ProjectorElementException(f"user with id {user_id} does not exist") - - return {"user": get_user_name(all_data, user["id"])} + return {"user": get_user_name(all_data, user_id)} def get_user_name(all_data: AllData, user_id: int) -> str: """ Returns the short name for an user_id. """ - user = all_data["users/user"][user_id] + try: + user = all_data["users/user"][user_id] + except KeyError: + raise ProjectorElementException(f"user with id {user_id} does not exist") + name_parts: List[str] = [] for name_part in ("title", "first_name", "last_name"): if user[name_part]: diff --git a/tests/integration/agenda/test_viewset.py b/tests/integration/agenda/test_viewset.py index d747f063f..ee44730a6 100644 --- a/tests/integration/agenda/test_viewset.py +++ b/tests/integration/agenda/test_viewset.py @@ -77,7 +77,13 @@ class RetrieveItem(TestCase): self.assertEqual( sorted(response.data.keys()), sorted( - ("id", "title", "speakers", "speaker_list_closed", "content_object") + ( + "id", + "title_information", + "speakers", + "speaker_list_closed", + "content_object", + ) ), ) forbidden_keys = ( diff --git a/tests/unit/agenda/test_models.py b/tests/unit/agenda/test_models.py index 3d0f9e572..b2859e69f 100644 --- a/tests/unit/agenda/test_models.py +++ b/tests/unit/agenda/test_models.py @@ -8,15 +8,17 @@ class TestItemTitle(TestCase): @patch("openslides.agenda.models.Item.content_object") def test_title_from_content_object(self, content_object): item = Item() - content_object.get_agenda_title.return_value = "related_title" + content_object.get_agenda_title_information.return_value = { + "attr": "related_title" + } - self.assertEqual(item.title, "related_title") + self.assertEqual(item.title_information, {"attr": "related_title"}) @patch("openslides.agenda.models.Item.content_object") def test_title_invalid_related(self, content_object): item = Item() - content_object.get_agenda_title.return_value = "related_title" - del content_object.get_agenda_title + content_object.get_agenda_title_information.return_value = "related_title" + del content_object.get_agenda_title_information with self.assertRaises(NotImplementedError): - item.title + item.title_information