Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display placeholder canvas message for all empty manifests #277

Merged
merged 5 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/components/MediaPlayer/MediaPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
playlist
} =
manifestState;
const { player } = playerState;
const { player, currentTime } = playerState;

React.useEffect(() => {
if (manifest) {
Expand Down Expand Up @@ -106,7 +106,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
sources,
tracks,
});
updatePlayerSrcDetails(canvas.duration, sources, isMultiSource);
updatePlayerSrcDetails(canvas.duration, sources, canvasId, isMultiSource);
setIsMultiSource(isMultiSource);

setCIndex(canvasId);
Expand All @@ -131,10 +131,11 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
* Update contexts based on the items in the canvas(es) in manifest
* @param {Number} duration canvas duration
* @param {Array} sources array of sources passed into player
* @param {Number} cIndex latest canvas index
* @param {Boolean} isMultiSource flag indicating whether there are
* multiple items in the canvas
*/
const updatePlayerSrcDetails = (duration, sources, isMultiSource) => {
const updatePlayerSrcDetails = (duration, sources, cIndex, isMultiSource) => {
let timeFragment = {};
if (isMultiSource) {
playerDispatch({
Expand All @@ -147,7 +148,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
playerDispatch({
type: 'updatePlayer'
});
const itemMessage = inaccessibleItemMessage(manifest, canvasIndex);
const itemMessage = inaccessibleItemMessage(manifest, cIndex);
setPlayerConfig({
...playerConfig,
error: itemMessage
Expand Down Expand Up @@ -228,11 +229,13 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
duration: canvasDuration,
srcIndex,
targets,
currentTime: currentTime || 0,
nextItemClicked,
},
videoJSCurrentTime: {
srcIndex,
targets,
currentTime: currentTime || 0,
},
// make the volume slider horizontal for audio
volumePanel: { inline: isVideo ? false : true },
Expand Down Expand Up @@ -350,7 +353,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
};
}

if (playlist.isPlaylist && canvasIsEmpty) {
if (canvasIsEmpty) {
return (
<div
data-testid="inaccessible-item"
Expand All @@ -359,8 +362,8 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
role="presentation"
>
<div className="ramp--no-media-message">
<div className="message-display">
<p>{playerConfig.error}</p>
<div className="message-display" data-testid="inaccessible-message"
dangerouslySetInnerHTML={{ __html: playerConfig.error }}>
</div>
<VideoJSPlayer
isVideo={true}
Expand Down
12 changes: 9 additions & 3 deletions src/components/MediaPlayer/MediaPlayer.scss
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
@import '../../styles/vars';

.ramp--no-media-message {
aspect-ratio: 16/9;
background-color: black;
width: 60%;
background-color: $primaryDarkest;
width: 80%;
margin: auto;

.message-display {
transform: translate(40%, 50%);
transform: translate(60%, 80%);
position: absolute;
margin: 0;
color: white;
width: 25%;

a {
color: $primaryGreen;
}
}

#iiif-media-player {
Expand Down
22 changes: 21 additions & 1 deletion src/components/MediaPlayer/MediaPlayer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { withManifestAndPlayerProvider } from '../../services/testing-helpers';
import MediaPlayer from './MediaPlayer';
import audioManifest from '@TestData/transcript-canvas';
import videoManifest from '@TestData/lunchroom-manners';
import emptyCanvasManifest from '@TestData/transcript-annotation';
import playlistManifest from '@TestData/playlist';

let manifestState = {
Expand Down Expand Up @@ -82,7 +83,7 @@ describe('MediaPlayer component', () => {
});
});

describe('with a manifest', () => {
describe('with a non-playlist manifest', () => {
describe('with a single canvas', () => {
test('does not render previous/next section buttons', () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
Expand Down Expand Up @@ -116,6 +117,25 @@ describe('MediaPlayer component', () => {
expect(screen.queryByTestId('videojs-previous-button')).toBeInTheDocument();
});
});

test('renders a message with HTML from placeholderCanvas for empty canvas', () => {
// Stub loading HTMLMediaElement for jsdom
window.HTMLMediaElement.prototype.load = () => { };

const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: {
manifest: emptyCanvasManifest,
canvasIndex: 1,
playlist: { isPlaylist: false }
},
initialPlayerState: {},
});
render(<PlayerWithManifest />);
expect(screen.queryByTestId('inaccessible-item')).toBeInTheDocument();
expect(screen.getByTestId('inaccessible-message').textContent)
.toEqual('You do not have permission to playback this item. \nPlease ' +
'contact support to report this error: admin-list@example.com.\n');
});
});

describe('with a playlist manifest', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/MediaPlayer/VideoJS/VideoJSPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ function VideoJSPlayer({
* in the player's time rail.
* */
const handleTimeUpdate = () => {
if (player !== null && isReadyRef.current) {
if (player !== null && isReadyRef.current && !isClicked) {
const activeSegment = getActiveSegment(player.currentTime());
if (activeSegment && activeIdRef.current != activeSegment['id']) {
// Set the active segment id in component's state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,23 @@ function CurrentTimeDisplay({ player, options }) {

const [currTime, setCurrTime] = React.useState(player.currentTime());

let initTimeRef = React.useRef(options.currentTime);
const setInitTime = (t) => {
initTimeRef.current = t;
};

player.on('timeupdate', () => {
if (player.isDisposed()) return;
let time = player.currentTime();

let time;
// Update time from the given initial time if it is not zero
if (initTimeRef.current > 0 && player.currentTime() == 0) {
time = initTimeRef.current;
} else {
time = player.currentTime();
}
if (targets.length > 1) time += targets[srcIndex].altStart;
setCurrTime(time);
setInitTime(0);
});

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class VideoJSProgress extends vjsComponent {

this.player = player;
this.options = options;
this.currentTime = options.currentTime;
this.state = { startTime: null, endTime: null };
this.times = options.targets[options.srcIndex];

Expand Down Expand Up @@ -154,6 +155,7 @@ class VideoJSProgress extends vjsComponent {
handleOnChange={this.handleOnChange}
player={this.player}
handleTimeUpdate={this.handleTimeUpdate}
initCurrentTime={this.options.currentTime}
times={this.times}
options={this.options}
/>,
Expand All @@ -171,8 +173,8 @@ class VideoJSProgress extends vjsComponent {
* @param {obj.options} - options passed when initilizing the component
* @returns
*/
function ProgressBar({ player, handleTimeUpdate, times, options }) {
const [progress, _setProgress] = React.useState(0);
function ProgressBar({ player, handleTimeUpdate, initCurrentTime, times, options }) {
const [progress, _setProgress] = React.useState(initCurrentTime);
const [currentTime, setCurrentTime] = React.useState(player.currentTime());
const timeToolRef = React.useRef();
const leftBlockRef = React.useRef();
Expand All @@ -184,6 +186,10 @@ function ProgressBar({ player, handleTimeUpdate, times, options }) {

const isMultiSourced = options.targets.length > 1 ? true : false;

let initTimeRef = React.useRef(initCurrentTime);
const setInitTime = (t) => {
initTimeRef.current = t;
};
let progressRef = React.useRef(progress);
const setProgress = (p) => {
progressRef.current = p;
Expand Down Expand Up @@ -233,9 +239,18 @@ function ProgressBar({ player, handleTimeUpdate, times, options }) {

player.on('timeupdate', () => {
if (player.isDisposed()) return;
const curTime = player.currentTime();
let curTime;
// Initially update progress from the prop passed from Ramp,
// this accounts for structured navigation when switching canvases
if ((initTimeRef.current > 0 && player.currentTime() == 0)) {
curTime = initTimeRef.current;
player.currentTime(initTimeRef.current);
} else {
curTime = player.currentTime();
}
setProgress(curTime);
handleTimeUpdate(curTime);
setInitTime(0);
});

/**
Expand Down
15 changes: 8 additions & 7 deletions src/components/StructuredNavigation/StructuredNavigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const StructuredNavigation = () => {
useManifestState();

let structureItemsRef = React.useRef();
let canvasIsEmptyRef = React.useRef(canvasIsEmpty);

React.useEffect(() => {
// Update currentTime and canvasIndex in state if a
Expand All @@ -52,7 +53,7 @@ const StructuredNavigation = () => {
}
}, [manifest]);

// Set currentNavItem when current Canvas is an inaccessible item
// Set currentNavItem when current Canvas is an inaccessible/empty item
React.useEffect(() => {
if (canvasIsEmpty && playlist.isPlaylist) {
manifestDispatch({
Expand Down Expand Up @@ -96,14 +97,11 @@ const StructuredNavigation = () => {
canvasIndex: currentCanvasIndex,
type: 'switchCanvas',
});
canvasIsEmptyRef.current = structureItemsRef.current[currentCanvasIndex].isEmpty;
}
}

if (canvasIsEmpty) {
// Reset isClicked in state for
// inaccessible items (empty canvases)
playerDispatch({ type: 'resetClick' });
} else if (player) {
if (player && !canvasIsEmptyRef.current) {
player.currentTime(timeFragmentStart);
playerDispatch({
startTime: timeFragment.start,
Expand All @@ -118,7 +116,10 @@ const StructuredNavigation = () => {
// Setting userActive to true shows timerail breifly, helps
// to visualize the structure in player while playing
if (isPlaying) player.userActive(true);
player.currentTime(timeFragmentStart);
} else if (canvasIsEmptyRef.current) {
// Reset isClicked in state for
// inaccessible items (empty canvases)
playerDispatch({ type: 'resetClick' });
}
}
}, [isClicked, player]);
Expand Down
4 changes: 2 additions & 2 deletions src/services/iiif-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,11 @@ export function inaccessibleItemMessage(manifest, canvasIndex) {
const item = items[0].getBody()[0];
itemMessage = item.getLabel().getValue()
? getLabelValue(item.getLabel().getValue())
: 'No associated media source(s) in the Canvas';
: 'This item cannot be played.';
}
return itemMessage;
} else {
return null;
return 'This item cannot be played.';
}
}

Expand Down
11 changes: 5 additions & 6 deletions src/services/iiif-parser.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import manifest from '@TestData/transcript-annotation';
import playlistManifest from '@TestData/playlist';
import volleyballManifest from '@TestData/volleyball-for-boys';
import lunchroomManifest from '@TestData/lunchroom-manners';
import manifestWoStructure from '@TestData/transcript-canvas';
Expand Down Expand Up @@ -122,7 +121,7 @@ describe('iiif-parser', () => {
expect(sources[0].selected).toBeTruthy();
});

if ('sets default source when not multisourced', () => {
it('sets default source when not multisourced', () => {
const { sources } = iiifParser.getMediaInfo({
manifest: singleSrcManifest,
canvasIndex: 0
Expand Down Expand Up @@ -354,17 +353,17 @@ describe('iiif-parser', () => {
describe('inaccessibleItemMessage()', () => {
it('returns text under placeholderCanvas', () => {
const itemMessage = iiifParser.inaccessibleItemMessage(manifest, 1);
expect(itemMessage).toEqual('You do not have permission to playback this item.');
expect(itemMessage).toEqual('You do not have permission to playback this item. \nPlease contact support to report this error: <a href="mailto:admin-list@example.com">admin-list@example.com</a>.\n');
});

it('returns hard coded text when placeholderCanvas has no text', () => {
const itemMessage = iiifParser.inaccessibleItemMessage(lunchroomManifest, 0);
expect(itemMessage).toEqual('No associated media source(s) in the Canvas');
expect(itemMessage).toEqual('This item cannot be played.');
});

if ('returns null when no placeholderCanvas is in the Canvas', () => {
it('returns null when no placeholderCanvas is in the Canvas', () => {
const itemMessage = iiifParser.inaccessibleItemMessage(singleSrcManifest, 0);
expect(itemMessage).toBeNull();
expect(itemMessage).toEqual('This item cannot be played.');
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/test_data/transcript-annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export default {
id: null,
type: "Text",
format: "text/plain",
label: { en: ['You do not have permission to playback this item.'] }
label: { en: ['You do not have permission to playback this item. \nPlease contact support to report this error: <a href="mailto:admin-list@example.com">admin-list@example.com</a>.\n'] }
},
target: 'https://example.com/sample/transcript-annotation/canvas/2/placeholder'
}
Expand Down