Commit df07f8e7 authored by Robert Knight's avatar Robert Knight

Use book or chapter URL in VS integration depending on "book_as_single_document" flag

Add the plumbing needed to make the VitalSource integration return either the
book or chapter URL depending on the value of the "book_as_single_document"
flag. Additionally handle changes in this flag after the integration is created
by notifying the guest about the URI change.

When the flag is enabled, the "book URL" is currently a placeholder that will
in future be replaced by a real value fetched from the `<mosaic-book>`
element in the container frame.
parent 499fa138
......@@ -11,7 +11,7 @@ import { warnOnce } from '../shared/warn-once';
*
* @type {string[]}
*/
const annotatorFlags = ['html_side_by_side'];
const annotatorFlags = ['book_as_single_document', 'html_side_by_side'];
/**
* An observable container of feature flags.
......
......@@ -25,7 +25,9 @@ export function createIntegration(annotator) {
const vsFrameRole = vitalSourceFrameRole();
if (vsFrameRole === 'content') {
return new VitalSourceContentIntegration();
return new VitalSourceContentIntegration(document.body, {
features: annotator.features,
});
}
return new HTMLIntegration({ features: annotator.features });
......
import { delay, waitFor } from '../../../test-util/wait';
import { FeatureFlags } from '../../features';
import {
VitalSourceInjector,
VitalSourceContentIntegration,
......@@ -54,12 +55,14 @@ class FakeVitalSourceViewer {
}
describe('annotator/integrations/vitalsource', () => {
let featureFlags;
let fakeViewer;
let FakeHTMLIntegration;
let fakeHTMLIntegration;
let fakeInjectClient;
beforeEach(() => {
featureFlags = new FeatureFlags();
fakeViewer = new FakeVitalSourceViewer();
fakeHTMLIntegration = {
......@@ -192,7 +195,9 @@ describe('annotator/integrations/vitalsource', () => {
let integrations;
function createIntegration() {
const integration = new VitalSourceContentIntegration();
const integration = new VitalSourceContentIntegration(document.body, {
features: featureFlags,
});
integrations.push(integration);
return integration;
}
......@@ -279,7 +284,8 @@ describe('annotator/integrations/vitalsource', () => {
history.back();
});
it('returns book URL excluding query string', async () => {
context('when "book_as_single_document" flag is off', () => {
it('returns book chapter URL excluding query string', async () => {
const integration = createIntegration();
const uri = await integration.uri();
const parsedURL = new URL(uri);
......@@ -292,6 +298,32 @@ describe('annotator/integrations/vitalsource', () => {
});
});
context('when "book_as_single_document" flag is on', () => {
it('returns book reader URL', async () => {
featureFlags.update({ book_as_single_document: true });
const integration = createIntegration();
const uri = await integration.uri();
assert.equal(
uri,
'https://bookshelf.vitalsource.com/reader/books/1234'
);
});
});
it('updates when "book_as_single_document" flag changes', async () => {
const uriChanged = sinon.stub();
const integration = createIntegration();
integration.on('uriChanged', uriChanged);
const uri1 = await integration.uri();
featureFlags.update({ book_as_single_document: true });
assert.calledOnce(uriChanged);
const uri2 = await integration.uri();
assert.notEqual(uri1, uri2);
});
});
context('in PDF documents', () => {
let FakeImageTextLayer;
let fakeImageTextLayer;
......
......@@ -8,7 +8,12 @@ import { preserveScrollPosition } from './html-side-by-side';
import { ImageTextLayer } from './image-text-layer';
import { injectClient } from '../hypothesis-injector';
import type { Anchor, Integration, SidebarLayout } from '../../types/annotator';
import type {
Anchor,
FeatureFlags as IFeatureFlags,
Integration,
SidebarLayout,
} from '../../types/annotator';
import type { Selector } from '../../types/api';
import type { InjectConfig } from '../hypothesis-injector';
......@@ -201,21 +206,36 @@ export class VitalSourceContentIntegration
extends TinyEmitter
implements Integration
{
private _features: IFeatureFlags;
private _htmlIntegration: HTMLIntegration;
private _listeners: ListenerCollection;
private _textLayer?: ImageTextLayer;
constructor(container: HTMLElement = document.body) {
constructor(
container: HTMLElement = document.body,
options: { features: IFeatureFlags }
) {
super();
const features = new FeatureFlags();
this._features = options.features;
// If the book_as_single_document flag changed, this will change the
// document URI returned by this integration.
this._features.on('flagsChanged', () => {
this.emit('uriChanged');
});
const htmlFeatures = new FeatureFlags();
// Forcibly enable the side-by-side feature for VS books. This feature is
// only behind a flag for regular web pages, which are typically more
// complex and varied than EPUB books.
features.update({ html_side_by_side: true });
htmlFeatures.update({ html_side_by_side: true });
this._htmlIntegration = new HTMLIntegration({ container, features });
this._htmlIntegration = new HTMLIntegration({
container,
features: htmlFeatures,
});
this._listeners = new ListenerCollection();
......@@ -339,6 +359,12 @@ export class VitalSourceContentIntegration
}
async uri() {
if (this._bookIsSingleDocument()) {
// Dummy book ID that will in future be replaced with a real value
// retrieved via the `<mosaic-book>` element's API.
const bookId = '1234';
return `https://bookshelf.vitalsource.com/reader/books/${bookId}`;
} else {
// An example of a typical URL for the chapter content in the Bookshelf reader is:
//
// https://jigsaw.vitalsource.com/books/9781848317703/epub/OPS/xhtml/chapter_001.html#cfi=/6/10%5B;vnd.vst.idref=chap001%5D!/4
......@@ -355,8 +381,17 @@ export class VitalSourceContentIntegration
uri.search = '';
return uri.toString();
}
}
async scrollToAnchor(anchor: Anchor) {
return this._htmlIntegration.scrollToAnchor(anchor);
}
/**
* Return true if the feature flag to treat books as one document is enabled,
* as opposed to treating each chapter/segment/page as a separate document.
*/
_bookIsSingleDocument(): boolean {
return this._features.flagEnabled('book_as_single_document');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment