Commit fa912dcb authored by Robert Knight's avatar Robert Knight

Transmit content info banner data from sidebar to main guest

When data for the content info banner has been received by the sidebar and added
to the store, send it to the main guest in a `showContentInfo` call. The guest
then calls the integration's `showContentInfo` method to actually display the
data to the user, in a location that is appropriate for the content type.
parent cc770347
......@@ -436,6 +436,12 @@ export class Guest {
annotations => annotations.forEach(annotation => this.anchor(annotation))
);
this._sidebarRPC.on(
'showContentInfo',
/** @param {ContentInfoConfig} info */
info => this._integration.showContentInfo?.(info)
);
// Connect to sidebar and send document info/URIs to it.
//
// RPC calls are deferred until a connection is made, so these steps can
......
......@@ -494,6 +494,26 @@ describe('Guest', () => {
});
});
});
describe('on "showContentInfo" event', () => {
const contentInfo = {
logo: {},
item: { title: 'Some article' },
links: {},
};
it('triggers display of content info in integration', () => {
createGuest();
emitSidebarEvent('showContentInfo', contentInfo);
assert.calledWith(fakeIntegration.showContentInfo, contentInfo);
});
it('does nothing if integration does not support content info display', () => {
createGuest();
fakeIntegration.showContentInfo = null;
emitSidebarEvent('showContentInfo', contentInfo);
});
});
});
describe('document events', () => {
......
......@@ -238,6 +238,15 @@ export class FrameSyncService {
onStoreAnnotationsChanged(annotations, frames, prevAnnotations),
shallowEqual
);
watch(
this._store.subscribe,
() => this._store.getContentInfo(),
contentInfo => {
const mainGuest = this._guestRPC.get(null);
mainGuest?.call('showContentInfo', contentInfo);
}
);
}
/**
......@@ -273,6 +282,16 @@ export class FrameSyncService {
frameIdentifier = info.frameIdentifier;
this._guestRPC.set(frameIdentifier, guestRPC);
// Show the content info banner, if this is the main guest frame and
// if we already have the data for it.
//
// We send this only after "documentInfoChanged" is received, because
// we don't know before then if this is the main guest or not.
const contentInfo = this._store.getContentInfo();
if (frameIdentifier === null && contentInfo) {
guestRPC.call('showContentInfo', contentInfo);
}
this._store.connectFrame({
id: info.frameIdentifier,
metadata: info.metadata,
......
......@@ -89,7 +89,12 @@ describe('FrameSyncService', () => {
};
fakeStore = fakeReduxStore(
{ annotations: [], frames: [], profile: { features: {} } },
{
annotations: [],
frames: [],
profile: { features: {} },
contentInfo: null,
},
{
allAnnotations() {
return this.getState().annotations;
......@@ -119,6 +124,14 @@ describe('FrameSyncService', () => {
return this.getState().profile;
},
getContentInfo() {
return this.getState().contentInfo;
},
setContentInfo(info) {
this.setState({ contentInfo: info });
},
findIDsForTags: sinon.stub(),
focusAnnotations: sinon.stub(),
isLoggedIn: sinon.stub().returns(false),
......@@ -591,6 +604,43 @@ describe('FrameSyncService', () => {
await connectGuest();
assert.calledWith(channel.call, 'setHighlightsVisible', false);
});
[
{
haveContentInfo: false,
isMainGuest: true,
},
{
haveContentInfo: true,
isMainGuest: false,
},
{
haveContentInfo: true,
isMainGuest: true,
},
].forEach(({ haveContentInfo, isMainGuest }) => {
it('sends content info to main guest if available', async () => {
let channel;
setupPortRPC = rpc => {
channel = rpc;
};
const contentInfo = { item: { title: 'Some article' } };
if (haveContentInfo) {
fakeStore.setContentInfo(contentInfo);
}
await connectGuest();
emitGuestEvent('documentInfoChanged', {
uri: 'https://publisher.org/article.pdf',
frameIdentifier: isMainGuest ? null : 'sub-frame',
});
assert.equal(
channel.call.calledWith('showContentInfo', contentInfo),
haveContentInfo && isMainGuest
);
});
});
});
context('when a guest frame is destroyed', () => {
......@@ -775,4 +825,34 @@ describe('FrameSyncService', () => {
assert.calledWith(guestRPC().call, 'featureFlagsUpdated', currentFlags());
});
});
context('when content info in store changes', () => {
const contentInfo = { item: { title: 'Some article' } };
it('sends content info to main guest', async () => {
await frameSync.connect();
await connectGuest();
emitGuestEvent('documentInfoChanged', {
uri: 'https://publisher.org/article.pdf',
frameIdentifier: null,
});
fakeStore.setContentInfo(contentInfo);
assert.calledWith(guestRPC().call, 'showContentInfo', contentInfo);
});
it("doesn't send content info to non-main guests", async () => {
await frameSync.connect();
await connectGuest();
emitGuestEvent('documentInfoChanged', {
uri: 'https://publisher.org/article.pdf',
frameIdentifier: 'sub-frame',
});
fakeStore.setContentInfo(contentInfo);
assert.isFalse(guestRPC().call.calledWith('showContentInfo'));
});
});
});
......@@ -132,7 +132,12 @@ export type SidebarToGuestEvent =
/**
* The sidebar relays to the guest(s) to set the annotation highlights on/off.
*/
| 'setHighlightsVisible';
| 'setHighlightsVisible'
/**
* Show a banner with information about the current content.
*/
| 'showContentInfo';
/**
* Events that the sidebar sends to the host
......
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