Merge pull request #5328 from FinnStutzenstein/projectorSubtitles

Subtitles for projected elements in the projector detail view
This commit is contained in:
Emanuel Schütze 2020-04-24 12:12:22 +02:00 committed by GitHub
commit cb52347354
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 105 additions and 139 deletions

View File

@ -25,6 +25,11 @@ import { HttpService } from './http.service';
import { ProjectorDataService } from './projector-data.service'; import { ProjectorDataService } from './projector-data.service';
import { ViewModelStoreService } from './view-model-store.service'; import { ViewModelStoreService } from './view-model-store.service';
export interface ProjectorTitle {
title: string;
subtitle?: string;
}
/** /**
* This service cares about Projectables being projected and manage all projection-related * This service cares about Projectables being projected and manage all projection-related
* actions. * actions.
@ -325,7 +330,7 @@ export class ProjectorService {
/** /**
*/ */
public getSlideTitle(element: ProjectorElement): string { public getSlideTitle(element: ProjectorElement): ProjectorTitle {
if (this.slideManager.canSlideBeMappedToModel(element.name)) { if (this.slideManager.canSlideBeMappedToModel(element.name)) {
const idElement = this.slideManager.getIdentifialbeProjectorElement(element); const idElement = this.slideManager.getIdentifialbeProjectorElement(element);
const viewModel = this.getViewModelFromProjectorElement(idElement); const viewModel = this.getViewModelFromProjectorElement(idElement);
@ -338,7 +343,7 @@ export class ProjectorService {
return configuration.getSlideTitle(element, this.translate, this.viewModelStore); return configuration.getSlideTitle(element, this.translate, this.viewModelStore);
} }
return this.translate.instant(this.slideManager.getSlideVerboseName(element.name)); return { title: this.translate.instant(this.slideManager.getSlideVerboseName(element.name)) };
} }
/** /**

View File

@ -14,6 +14,7 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
import { ItemTitleInformation, ViewItem } from 'app/site/agenda/models/view-item'; import { ItemTitleInformation, ViewItem } from 'app/site/agenda/models/view-item';
import { ViewAssignment } from 'app/site/assignments/models/view-assignment'; import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
import { import {
AgendaListTitle,
BaseViewModelWithAgendaItem, BaseViewModelWithAgendaItem,
isBaseViewModelWithAgendaItem isBaseViewModelWithAgendaItem
} from 'app/site/base/base-view-model-with-agenda-item'; } from 'app/site/base/base-view-model-with-agenda-item';
@ -79,7 +80,7 @@ export class ItemRepositoryService extends BaseHasContentObjectRepository<
return this.translate.instant(plural ? 'Items' : 'Item'); return this.translate.instant(plural ? 'Items' : 'Item');
}; };
public getTitle = (titleInformation: ItemTitleInformation) => { private getAgendaTitle(titleInformation: ItemTitleInformation): AgendaListTitle {
if (titleInformation.contentObject) { if (titleInformation.contentObject) {
return titleInformation.contentObject.getAgendaListTitle(); return titleInformation.contentObject.getAgendaListTitle();
} else { } else {
@ -88,36 +89,14 @@ export class ItemRepositoryService extends BaseHasContentObjectRepository<
) as BaseIsAgendaItemContentObjectRepository<any, any, any>; ) as BaseIsAgendaItemContentObjectRepository<any, any, any>;
return repo.getAgendaListTitle(titleInformation.title_information); return repo.getAgendaListTitle(titleInformation.title_information);
} }
}
public getTitle = (titleInformation: ItemTitleInformation) => {
return this.getAgendaTitle(titleInformation).title;
}; };
/** public getSubtitle = (titleInformation: ItemTitleInformation) => {
* Overrides the base function, if implemented. return this.getAgendaTitle(titleInformation).subtitle;
*
* @returns An optional subtitle as `string`. Defaults to `null`.
*/
public getSubtitle = (viewItem: ViewItem) => {
if (viewItem.contentObject) {
return viewItem.contentObject.getAgendaSubtitle();
} else {
// The subtitle is not present in the title_information yet.
return null;
}
};
/**
* Overrides the base function.
*
* @returns The title without any prefix like item number.
*/
public getTitleWithoutItemNumber = (titleInformation: ItemTitleInformation) => {
if (titleInformation.contentObject) {
return titleInformation.contentObject.getAgendaListTitleWithoutItemNumber();
} else {
const repo = this.collectionStringMapperService.getRepository(
titleInformation.contentObjectData.collection
) as BaseIsAgendaItemContentObjectRepository<any, any, any>;
return repo.getAgendaListTitleWithoutItemNumber(titleInformation.title_information);
}
}; };
/** /**

View File

@ -3,6 +3,7 @@ import { ViewItem } from 'app/site/agenda/models/view-item';
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers'; import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model'; import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
import { import {
AgendaListTitle,
BaseViewModelWithAgendaItem, BaseViewModelWithAgendaItem,
TitleInformationWithAgendaItem TitleInformationWithAgendaItem
} from 'app/site/base/base-view-model-with-agenda-item'; } from 'app/site/base/base-view-model-with-agenda-item';
@ -52,14 +53,11 @@ export abstract class BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
}); });
} }
public getAgendaListTitle(titleInformation: T): string { public getAgendaListTitle(titleInformation: T): AgendaListTitle {
// Return the agenda title with the model's verbose name appended // Return the agenda title with the model's verbose name appended
const numberPrefix = titleInformation.agenda_item_number() ? `${titleInformation.agenda_item_number()} · ` : ''; const numberPrefix = titleInformation.agenda_item_number() ? `${titleInformation.agenda_item_number()} · ` : '';
return numberPrefix + this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')'; const title = numberPrefix + this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')';
} return { title };
public getAgendaSubtitle(viewModel: V): string | null {
return null;
} }
public getAgendaSlideTitle(titleInformation: T): string { public getAgendaSlideTitle(titleInformation: T): string {
@ -68,19 +66,8 @@ export abstract class BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
return numberPrefix + this.getTitle(titleInformation); return numberPrefix + this.getTitle(titleInformation);
} }
/**
* Function to get the list-title without the item-number.
*
* @param titleInformation The title-information for an object.
*
* @returns {string} The title without any prefix like item-number.
*/
public getAgendaListTitleWithoutItemNumber(titleInformation: T): string {
return this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')';
}
public getListOfSpeakersTitle = (titleInformation: T) => { public getListOfSpeakersTitle = (titleInformation: T) => {
return this.getAgendaListTitle(titleInformation); return this.getAgendaListTitle(titleInformation).title;
}; };
public getListOfSpeakersSlideTitle = (titleInformation: T) => { public getListOfSpeakersSlideTitle = (titleInformation: T) => {
@ -90,9 +77,7 @@ export abstract class BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
protected createViewModelWithTitles(model: M): V { protected createViewModelWithTitles(model: M): V {
const viewModel = super.createViewModelWithTitles(model); const viewModel = super.createViewModelWithTitles(model);
viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel); viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel);
viewModel.getAgendaListTitleWithoutItemNumber = () => this.getAgendaListTitleWithoutItemNumber(viewModel);
viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel); viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel);
viewModel.getAgendaSubtitle = () => this.getAgendaSubtitle(viewModel);
viewModel.getListOfSpeakersTitle = () => this.getListOfSpeakersTitle(viewModel); viewModel.getListOfSpeakersTitle = () => this.getListOfSpeakersTitle(viewModel);
viewModel.getListOfSpeakersSlideTitle = () => this.getListOfSpeakersSlideTitle(viewModel); viewModel.getListOfSpeakersSlideTitle = () => this.getListOfSpeakersSlideTitle(viewModel);
return viewModel; return viewModel;

View File

@ -2,6 +2,7 @@ import { TranslateService } from '@ngx-translate/core';
import { ViewItem } from 'app/site/agenda/models/view-item'; import { ViewItem } from 'app/site/agenda/models/view-item';
import { import {
AgendaListTitle,
BaseViewModelWithAgendaItem, BaseViewModelWithAgendaItem,
TitleInformationWithAgendaItem TitleInformationWithAgendaItem
} from 'app/site/base/base-view-model-with-agenda-item'; } from 'app/site/base/base-view-model-with-agenda-item';
@ -29,8 +30,7 @@ export interface IBaseIsAgendaItemContentObjectRepository<
M extends BaseModel, M extends BaseModel,
T extends TitleInformationWithAgendaItem T extends TitleInformationWithAgendaItem
> extends BaseRepository<V, M, T> { > extends BaseRepository<V, M, T> {
getAgendaListTitle: (titleInformation: T) => string; getAgendaListTitle: (titleInformation: T) => AgendaListTitle;
getAgendaListTitleWithoutItemNumber: (titleInformation: T) => string;
getAgendaSlideTitle: (titleInformation: T) => string; getAgendaSlideTitle: (titleInformation: T) => string;
} }
@ -77,31 +77,11 @@ export abstract class BaseIsAgendaItemContentObjectRepository<
* @returns the agenda title for the agenda item list. Should * @returns the agenda title for the agenda item list. Should
* be `<item number> · <title> (<type>)`. E.g. `7 · the is an election (Election)`. * be `<item number> · <title> (<type>)`. E.g. `7 · the is an election (Election)`.
*/ */
public getAgendaListTitle(titleInformation: T): string { public getAgendaListTitle(titleInformation: T): AgendaListTitle {
// Return the agenda title with the model's verbose name appended // Return the agenda title with the model's verbose name appended
const numberPrefix = titleInformation.agenda_item_number() ? `${titleInformation.agenda_item_number()} · ` : ''; const numberPrefix = titleInformation.agenda_item_number() ? `${titleInformation.agenda_item_number()} · ` : '';
return numberPrefix + this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')'; const title = numberPrefix + this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')';
} return { title };
/**
* Overrides the base function. Returns an optional subtitle.
*
* @param viewModel The model to get the subtitle from.
* @returns A string as subtitle. Defaults to `null`.
*/
public getAgendaSubtitle(viewModel: V): string | null {
return null;
}
/**
* Function to return the title without item-number, in example used for pdf-creation.
*
* @param titleInformation The title information.
*
* @returns {string} The title without any prefix like the item-number.
*/
public getAgendaListTitleWithoutItemNumber(titleInformation: T): string {
return this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')';
} }
/** /**
@ -117,9 +97,7 @@ export abstract class BaseIsAgendaItemContentObjectRepository<
protected createViewModelWithTitles(model: M): V { protected createViewModelWithTitles(model: M): V {
const viewModel = super.createViewModelWithTitles(model); const viewModel = super.createViewModelWithTitles(model);
viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel); viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel);
viewModel.getAgendaListTitleWithoutItemNumber = () => this.getAgendaListTitleWithoutItemNumber(viewModel);
viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel); viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel);
viewModel.getAgendaSubtitle = () => this.getAgendaSubtitle(viewModel);
return viewModel; return viewModel;
} }
} }

View File

@ -17,6 +17,7 @@ import { Motion } from 'app/shared/models/motions/motion';
import { Submitter } from 'app/shared/models/motions/submitter'; import { Submitter } from 'app/shared/models/motions/submitter';
import { ViewUnifiedChange, ViewUnifiedChangeType } from 'app/shared/models/motions/view-unified-change'; import { ViewUnifiedChange, ViewUnifiedChangeType } from 'app/shared/models/motions/view-unified-change';
import { PersonalNoteContent } from 'app/shared/models/users/personal-note'; import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
import { AgendaListTitle } from 'app/site/base/base-view-model-with-agenda-item';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { ViewCategory } from 'app/site/motions/models/view-category'; import { ViewCategory } from 'app/site/motions/models/view-category';
import { MotionTitleInformation, ViewMotion } from 'app/site/motions/models/view-motion'; import { MotionTitleInformation, ViewMotion } from 'app/site/motions/models/view-motion';
@ -269,46 +270,40 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo
public getAgendaListTitle = (titleInformation: MotionTitleInformation) => { public getAgendaListTitle = (titleInformation: MotionTitleInformation) => {
const numberPrefix = titleInformation.agenda_item_number() ? `${titleInformation.agenda_item_number()} · ` : ''; const numberPrefix = titleInformation.agenda_item_number() ? `${titleInformation.agenda_item_number()} · ` : '';
// Append the verbose name only, if not the special format 'Motion <identifier>' is used. // Append the verbose name only, if not the special format 'Motion <identifier>' is used.
let title;
if (titleInformation.identifier) { if (titleInformation.identifier) {
return `${numberPrefix}${this.translate.instant('Motion')} ${titleInformation.identifier} · ${ title = `${numberPrefix}${this.translate.instant('Motion')} ${titleInformation.identifier} · ${
titleInformation.title titleInformation.title
}`; }`;
} else { } else {
return `${numberPrefix}${titleInformation.title} (${this.getVerboseName()})`; title = `${numberPrefix}${titleInformation.title} (${this.getVerboseName()})`;
} }
}; const agendaTitle: AgendaListTitle = { title };
/** // Subtitle.
* @override The base function and returns the submitters as optional subtitle. // This is a bit hacky: If one has not motions.can_see, the titleinformation is nut sufficient for
*/ // submitters. So try-cast titleInformation to a ViewMotion and check, if submittersAsUsers is available
public getAgendaSubtitle = (motion: ViewMotion) => { const viewMotion: ViewMotion = titleInformation as ViewMotion;
if (motion.submittersAsUsers && motion.submittersAsUsers.length) { if (viewMotion.submittersAsUsers && viewMotion.submittersAsUsers.length) {
return `${this.translate.instant('by')} ${motion.submittersAsUsers.join(', ')}`; agendaTitle.subtitle = `${this.translate.instant('by')} ${viewMotion.submittersAsUsers.join(', ')}`;
} else {
return null;
}
};
/**
* @override The base function
*/
public getAgendaListTitleWithoutItemNumber = (titleInformation: MotionTitleInformation) => {
if (titleInformation.identifier) {
return this.translate.instant('Motion') + ' ' + titleInformation.identifier;
} else {
return titleInformation.title + `(${this.getVerboseName()})`;
} }
return agendaTitle;
}; };
public getVerboseName = (plural: boolean = false) => { public getVerboseName = (plural: boolean = false) => {
return this.translate.instant(plural ? 'Motions' : 'Motion'); return this.translate.instant(plural ? 'Motions' : 'Motion');
}; };
public getProjectorTitle = (viewMotion: ViewMotion) => {
const subtitle = viewMotion.item && viewMotion.item.comment ? viewMotion.item.comment : null;
return { title: this.getAgendaSlideTitle(viewMotion), subtitle };
};
protected createViewModelWithTitles(model: Motion): ViewMotion { protected createViewModelWithTitles(model: Motion): ViewMotion {
const viewModel = super.createViewModelWithTitles(model); const viewModel = super.createViewModelWithTitles(model);
viewModel.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewModel); viewModel.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewModel);
viewModel.getProjectorTitle = () => this.getAgendaSlideTitle(viewModel); viewModel.getProjectorTitle = () => this.getProjectorTitle(viewModel);
return viewModel; return viewModel;
} }

View File

@ -61,7 +61,7 @@ export class TopicRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCon
public getAgendaListTitle = (titleInformation: TopicTitleInformation) => { public getAgendaListTitle = (titleInformation: TopicTitleInformation) => {
// Do not append ' (Topic)' to the title. // Do not append ' (Topic)' to the title.
return this.getTitle(titleInformation); return { title: this.getTitle(titleInformation) };
}; };
public getAgendaSlideTitle = (titleInformation: TopicTitleInformation) => { public getAgendaSlideTitle = (titleInformation: TopicTitleInformation) => {
@ -69,15 +69,6 @@ export class TopicRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCon
return this.getTitle(titleInformation); return this.getTitle(titleInformation);
}; };
/**
* @override The base function.
*
* @returns The plain title.
*/
public getAgendaListTitleWithoutItemNumber = (titleInformation: TopicTitleInformation) => {
return titleInformation.title;
};
public getVerboseName = (plural: boolean = false) => { public getVerboseName = (plural: boolean = false) => {
return this.translate.instant(plural ? 'Topics' : 'Topic'); return this.translate.instant(plural ? 'Topics' : 'Topic');
}; };

View File

@ -1,3 +1,4 @@
import { ProjectorTitle } from 'app/core/core-services/projector.service';
import { ListOfSpeakers, ListOfSpeakersWithoutNestedModels } from 'app/shared/models/agenda/list-of-speakers'; import { ListOfSpeakers, ListOfSpeakersWithoutNestedModels } from 'app/shared/models/agenda/list-of-speakers';
import { ContentObject } from 'app/shared/models/base/content-object'; import { ContentObject } from 'app/shared/models/base/content-object';
import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object'; import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object';
@ -34,8 +35,8 @@ export class ViewListOfSpeakers extends BaseViewModelWithContentObject<ListOfSpe
return `/agenda/speakers/${this.id}`; return `/agenda/speakers/${this.id}`;
} }
public getProjectorTitle(): string { public getProjectorTitle(): ProjectorTitle {
return this.getTitle(); return { title: this.getTitle() };
} }
public getSlide(): ProjectorElementBuildDeskriptor { public getSlide(): ProjectorElementBuildDeskriptor {

View File

@ -87,7 +87,7 @@ export class AgendaPdfService {
text: nodeItem.item.item_number text: nodeItem.item.item_number
}, },
{ {
text: nodeItem.item.contentObject.getAgendaListTitleWithoutItemNumber() text: nodeItem.item.contentObject.getListTitle()
} }
] ]
}; };

View File

@ -1,3 +1,4 @@
import { ProjectorTitle } from 'app/core/core-services/projector.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { BaseModel } from 'app/shared/models/base/base-model'; import { BaseModel } from 'app/shared/models/base/base-model';
import { BaseViewModel } from './base-view-model'; import { BaseViewModel } from './base-view-model';
@ -13,7 +14,7 @@ export abstract class BaseProjectableViewModel<M extends BaseModel = any> extend
/** /**
* @returns the projector title used for managing projector elements. * @returns the projector title used for managing projector elements.
*/ */
public getProjectorTitle = () => { public getProjectorTitle(): ProjectorTitle {
return this.getTitle(); return { title: this.getTitle() };
}; }
} }

View File

@ -1,3 +1,4 @@
import { ProjectorTitle } from 'app/core/core-services/projector.service';
import { SearchRepresentation } from 'app/core/ui-services/search.service'; import { SearchRepresentation } from 'app/core/ui-services/search.service';
import { BaseModelWithAgendaItem } from 'app/shared/models/base/base-model-with-agenda-item'; import { BaseModelWithAgendaItem } from 'app/shared/models/base/base-model-with-agenda-item';
import { DetailNavigable, isDetailNavigable } from 'app/shared/models/base/detail-navigable'; import { DetailNavigable, isDetailNavigable } from 'app/shared/models/base/detail-navigable';
@ -5,6 +6,11 @@ import { BaseProjectableViewModel } from './base-projectable-view-model';
import { TitleInformation } from './base-view-model'; import { TitleInformation } from './base-view-model';
import { isSearchable, Searchable } from './searchable'; import { isSearchable, Searchable } from './searchable';
export interface AgendaListTitle {
title: string;
subtitle?: string;
}
export function isBaseViewModelWithAgendaItem(obj: any): obj is BaseViewModelWithAgendaItem { export function isBaseViewModelWithAgendaItem(obj: any): obj is BaseViewModelWithAgendaItem {
const model = <BaseViewModelWithAgendaItem>obj; const model = <BaseViewModelWithAgendaItem>obj;
return ( return (
@ -13,7 +19,6 @@ export function isBaseViewModelWithAgendaItem(obj: any): obj is BaseViewModelWit
isSearchable(model) && isSearchable(model) &&
model.getAgendaSlideTitle !== undefined && model.getAgendaSlideTitle !== undefined &&
model.getAgendaListTitle !== undefined && model.getAgendaListTitle !== undefined &&
model.getAgendaSubtitle !== undefined &&
model.getCSVExportText !== undefined && model.getCSVExportText !== undefined &&
model.item !== undefined && model.item !== undefined &&
model.getModel !== undefined && model.getModel !== undefined &&
@ -42,12 +47,7 @@ export interface BaseViewModelWithAgendaItem<M extends BaseModelWithAgendaItem =
/** /**
* @return the agenda title with the verbose name of the content object * @return the agenda title with the verbose name of the content object
*/ */
getAgendaListTitle: () => string; getAgendaListTitle: () => AgendaListTitle;
/**
* @return the agenda title with the verbose name of the content object
*/
getAgendaListTitleWithoutItemNumber: () => string;
} }
/** /**
@ -62,6 +62,15 @@ export abstract class BaseViewModelWithAgendaItem<
return this.item && this.item.item_number ? this.item.item_number : null; return this.item && this.item.item_number ? this.item.item_number : null;
} }
/**
* @returns the projector title used for managing projector elements.
* Appends the agneda item comment as the subtitle, if this model has an agenda item
*/
public getProjectorTitle(): ProjectorTitle {
const subtitle = this.item.comment || null;
return { title: this.getTitle(), subtitle };
}
/** /**
* @returns the (optional) descriptive text to be exported in the CSV. * @returns the (optional) descriptive text to be exported in the CSV.
* May be overridden by inheriting classes * May be overridden by inheriting classes
@ -70,15 +79,6 @@ export abstract class BaseViewModelWithAgendaItem<
return ''; return '';
} }
/**
* @override The base-method from `IBaseViewModelWithAgendaItem`.
*
* @returns Defaults to `null`.
*/
public getAgendaSubtitle(): string | null {
return null;
}
public abstract getDetailStateURL(): string; public abstract getDetailStateURL(): string;
/** /**

View File

@ -1,3 +1,4 @@
import { ProjectorTitle } from 'app/core/core-services/projector.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { IdentifiableProjectorElement, ProjectorElementOptions } from 'app/shared/models/core/projector'; import { IdentifiableProjectorElement, ProjectorElementOptions } from 'app/shared/models/core/projector';
import { Displayable } from 'app/site/base/displayable'; import { Displayable } from 'app/site/base/displayable';
@ -32,7 +33,7 @@ export function isProjectable(obj: any): obj is Projectable {
* Interface for every model, that should be projectable. * Interface for every model, that should be projectable.
*/ */
export interface Projectable extends Displayable { export interface Projectable extends Displayable {
getProjectorTitle: () => string; getProjectorTitle: () => ProjectorTitle;
getSlide(configSerice?: ConfigService): ProjectorElementBuildDeskriptor; getSlide(configSerice?: ConfigService): ProjectorElementBuildDeskriptor;
} }

View File

@ -169,8 +169,18 @@
<mat-icon>videocam</mat-icon> <mat-icon>videocam</mat-icon>
</button> </button>
<!-- Slide title and subtitle -->
<span class="ellipsis-overflow current-element-text"> <span class="ellipsis-overflow current-element-text">
{{ getSlideTitle(element) }} {{ getSlideTitle(element) }}
<os-icon-container
class="subtitle-nocolor"
size="small"
icon="comment"
[noWrap]="true"
*ngIf="getSlideSubtitle(element)"
>
{{ getSlideSubtitle(element) }}
</os-icon-container>
</span> </span>
<button type="button" mat-icon-button (click)="unprojectCurrent(element)"> <button type="button" mat-icon-button (click)="unprojectCurrent(element)">
@ -207,8 +217,18 @@
<mat-icon>videocam</mat-icon> <mat-icon>videocam</mat-icon>
</button> </button>
</div> </div>
<!-- Slide title ans subtitle -->
<div class="name"> <div class="name">
<span>{{ getSlideTitle(element) }}</span> <span class="ellipsis-overflow">{{ getSlideTitle(element) }}</span>
<os-icon-container
class="subtitle"
size="small"
icon="comment"
[noWrap]="true"
*ngIf="getSlideSubtitle(element)"
>
{{ getSlideSubtitle(element) }}
</os-icon-container>
</div> </div>
<div class="button-right" *ngIf="editQueue"> <div class="button-right" *ngIf="editQueue">
<div> <div>
@ -335,6 +355,7 @@
<ol> <ol>
<li *ngFor="let elements of projector.elements_history"> <li *ngFor="let elements of projector.elements_history">
{{ getSlideTitle(elements[0]) }} {{ getSlideTitle(elements[0]) }}
{{ getSlideSubtitle(elements[0]) }}
</li> </li>
</ol> </ol>
</mat-expansion-panel> </mat-expansion-panel>

View File

@ -208,7 +208,11 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
} }
public getSlideTitle(element: ProjectorElement): string { public getSlideTitle(element: ProjectorElement): string {
return this.projectorService.getSlideTitle(element); return this.projectorService.getSlideTitle(element).title;
}
public getSlideSubtitle(element: ProjectorElement): string | null {
return this.projectorService.getSlideTitle(element).subtitle;
} }
public isProjected(obj: Projectable): boolean { public isProjected(obj: Projectable): boolean {

View File

@ -3,6 +3,7 @@ import { LoadChildrenCallback } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ProjectorTitle } from 'app/core/core-services/projector.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { IdentifiableProjectorElement, ProjectorElement } from 'app/shared/models/core/projector'; import { IdentifiableProjectorElement, ProjectorElement } from 'app/shared/models/core/projector';
@ -30,7 +31,7 @@ export interface SlideDynamicConfiguration {
element: ProjectorElement, element: ProjectorElement,
translate: TranslateService, translate: TranslateService,
viewModelStore: ViewModelStoreService viewModelStore: ViewModelStoreService
) => string; ) => ProjectorTitle;
} }
/** /**

View File

@ -93,12 +93,16 @@
color: mat-color($foreground, secondary-text); color: mat-color($foreground, secondary-text);
} }
.subtitle { .subtitle-nocolor {
color: mat-color($foreground, secondary-text);
font-size: 12px; font-size: 12px;
font-weight: 400; font-weight: 400;
} }
.subtitle {
@extend .subtitle-nocolor;
color: mat-color($foreground, secondary-text);
}
.user-subtitle { .user-subtitle {
color: mat-color($foreground, secondary-text); color: mat-color($foreground, secondary-text);
} }