labo-components
Version:
242 lines (211 loc) • 7.96 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import debounce from 'debounce';
import IDUtil from '../../util/IDUtil'; // for generating unique CSS classnames for this component
import SessionStorageHandler from '../../util/SessionStorageHandler';
import { ResourceViewerContext } from './ResourceViewerContext';
import ASR from './contentAnnotationsColumn/ASR';
import Segments from './contentAnnotationsColumn/Segments';
import Info from '../shared/Info';
import _Strings from './_Strings';
// Types
const TYPE_ASR = 'ASR';
const TYPE_SEGMENT = 'Segments';
const TYPE_SUBTITLES = 'Subtitles';
// Views
export const VIEW_LIST = 'List';
export const VIEW_WORDCLOUD = 'Word cloud';
const VIEWS = [VIEW_LIST, VIEW_WORDCLOUD];
// Other
const SESSION_SEARCH_TERM = 'bg__content-annotations-search-term';
export default class ContentAnnotationsColumn extends React.Component {
static contextType = ResourceViewerContext;
constructor(props) {
super(props);
// get last searchterm from sessionStorage
const searchTerm = SessionStorageHandler.get(SESSION_SEARCH_TERM, '');
this.state = {
type: '',
view: VIEWS[0],
searchTerm
};
this.searchInputRef = React.createRef();
this.debounceOnSearch = debounce(this.changeSearch, 200);
}
componentDidMount() {
this.checkCurrentType();
// Fill search term with query.term, if there is no user defined searchTerm set in
if (
!this.state.searchTerm &&
this.context.query &&
this.context.query.term
) {
this.setState({ searchTerm: this.context.query.term });
}
}
componentDidUpdate() {
this.checkCurrentType();
}
// check if the current type is available on the resource/mediaobject
// if not: change it
checkCurrentType() {
const types = this.getAvailableTypes();
if (types.length > 0 && !types.includes(this.state.type)) {
this.setState({ type: types.length > 0 ? types[0] : '' });
}
}
getActiveContent(type, view, searchTerm) {
const activeTranscript = this.context.getActiveTranscript(type, true);//fetch them by title
switch (type) {
case TYPE_SEGMENT:
return (
<Segments
view={view}
searchTerm={searchTerm}
activeMediaObject={this.context.activeMediaObject}
mediaEvents={this.context.mediaEvents}
/>
);
case TYPE_SUBTITLES:
return (
<div>
TODO: implement subtitles. Use ASR.jsx as example.
</div>
);
default: //otherwise return the selected transcript type
return activeTranscript ? (
<ASR
view={view}
searchTerm={searchTerm}
resource={this.context.resource}
activeMediaObject={this.context.activeMediaObject}
mediaEvents={this.context.mediaEvents}
transcript={activeTranscript} //could be different types of transcripts (ASR, Voting, Agenda Points...)
/>
) : null;
}
}
changeType = e => {
this.setState({ type: e.target.value });
};
changeView = e => {
this.setState({ view: e.target.value });
};
changeSearch = () => {
// use the ref so we use this as debounced function
this.setSearchTerm(this.searchInputRef.current.value);
};
clearSearch = () => {
// manually empty the input value as it is only assigned with attr:defaultValue
this.searchInputRef.current.value = '';
this.setSearchTerm('');
};
setSearchTerm = searchTerm => {
// update state
this.setState({ searchTerm });
// store searchTerm to session
SessionStorageHandler.set(SESSION_SEARCH_TERM, searchTerm);
};
// Return types available in current context
//FIXME this should not be done here, but in the collectionConfig!!
getAvailableTypes = () => {
const types = [];
// Segments
if (
this.context.activeMediaObject &&
this.context.activeMediaObject.segments
) {
types.push(TYPE_SEGMENT);
}
// Subtitles
// TODO: which field?
if (false) {
types.push(TYPE_SUBTITLES);
}
//loop through the transcripts of the resource and add them as types
const transcripts = this.context.getActiveTranscripts();
if(transcripts) {
transcripts.forEach(transcript => {
types.push(transcript.title);
});
}
return types;
};
render() {
const activeContent = this.getActiveContent(this.state.type, this.state.view, this.state.searchTerm);
// Types
const types = this.getAvailableTypes();
// No types available? Render a notification
if (types.length === 0) {
return (
<div
className={IDUtil.cssClassName(
'content-annotations-column'
)}
>
<div className="error-message">
No content annotations available
<Info
className="primary"
id={'info_content_annotations'}
text={
'There are no content annotations available on the selected resource or the active media object'
}
/>
</div>
</div>
);
}
const typeSelector = (
<select value={this.state.type} onChange={this.changeType}>
{types.map(type => (
<option value={type} key={type}>
{type}
</option>
))}
</select>
);
// Views
const viewSelector = (
<select value={this.state.view} onChange={this.changeView}>
{VIEWS.map(type => (
<option value={type} key={type}>
{type}
</option>
))}
</select>
);
return (
<div className={IDUtil.cssClassName('content-annotations-column')}>
<div className="type-selector">
<strong>Type:</strong>
{typeSelector}
<Info
className="primary"
id={'info_content_annotations'}
text={_Strings.CONTENT_ANNOTATIONS_TYPE_HELP}
/>
</div>
<div className="filter">
<strong>View:</strong>
{viewSelector}
<input
placeholder="Search..."
defaultValue={this.state.searchTerm}
onChange={this.debounceOnSearch}
onKeyUp={this.debounceOnSearch} // required to register empty field? :S
ref={this.searchInputRef}
/>
{this.state.searchTerm && (
<div
className="clear"
title="Clear search"
onClick={this.clearSearch}
/>
)}
</div>
{activeContent}
</div>
);
}
}