Commit 28ac128e authored by Nick Stenning's avatar Nick Stenning

Only make one search query per frame on page load

Previously, when loading Hypothesis into a page, the client would make
one search request for each and every URI found on the page, including
those found by the document metadata scanners (in the Annotator
"Document" and "PDF" plugins.)

This is wasteful of network bandwidth, and moreover should not be
necessary in most cases due to the ability to expand to equivalent URIs
on the server side.

The component responsible for coordinating communication across page
frames is the "crossframe" service. This services maintains a registry
of page frames (previously called "providers") and their associated
metadata. This commit reduces the metadata stored to simply the page
URL, or in the case of PDF documents, the fingerprint URN.

It's worth noting that this *does* change behaviour in one important
way: if a page contains document equivalence data that we've never seen
before, and one of the alternate URIs has already been annotated, then
this change will mean we do not load annotations until that page itself
has been annotated (and thus the document equivalence data updated). I
am assuming that this is an extreme edge case.
parent 0b2a6109
# Uses a channel between the sidebar and the attached providers to ensure
# Uses a channel between the sidebar and the attached frames to ensure
# the interface remains in sync.
module.exports = class AnnotationUISync
###*
......
# Instantiates all objects used for cross frame discovery and communication.
module.exports = class CrossFrame
providers: null
this.inject = [
'$rootScope', '$document', '$window', 'store', 'annotationUI'
'Discovery', 'bridge',
......@@ -12,7 +10,7 @@ module.exports = class CrossFrame
Discovery, bridge,
AnnotationSync, AnnotationUISync
) ->
@providers = []
@frames = []
createDiscovery = ->
options =
......@@ -46,20 +44,17 @@ module.exports = class CrossFrame
createAnnotationUISync = (annotationSync) ->
new AnnotationUISync($rootScope, $window, bridge, annotationSync, annotationUI)
addProvider = (channel) =>
provider = {channel: channel, entities: []}
addFrame = (channel) =>
channel.call
method: 'getDocumentInfo'
success: (info) =>
$rootScope.$apply =>
provider.entities = (link.href for link in info.metadata.link)
@providers.push(provider)
@frames.push({channel: channel, uri: info.uri})
this.connect = ->
discovery = createDiscovery()
bridge.onConnect(addProvider)
bridge.onConnect(addFrame)
annotationSync = createAnnotationSync()
annotationUISync = createAnnotationUISync(annotationSync)
......
......@@ -15,13 +15,11 @@ module.exports = ['crossframe', (crossframe) ->
if visible
scope.$evalAsync(-> elem.find('#via').focus().select())
scope.$watchCollection (-> crossframe.providers), ->
if crossframe.providers?.length
# XXX: Consider multiple providers in the future
p = crossframe.providers[0]
if p.entities?.length
e = p.entities[0]
scope.viaPageLink = 'https://via.hypothes.is/' + e
scope.$watchCollection (-> crossframe.frames), (frames) ->
if not frames.length
return
# XXX: Consider sharing multiple frames in the future?
scope.viaPageLink = 'https://via.hypothes.is/' + frames[0].uri
restrict: 'A'
templateUrl: 'share_dialog.html'
......
......@@ -15,7 +15,7 @@ describe 'share-dialog', ->
beforeEach module('h.templates')
beforeEach module ($provide) ->
fakeCrossFrame = {providers: []}
fakeCrossFrame = {frames: []}
$provide.value 'crossframe', fakeCrossFrame
return
......@@ -26,7 +26,7 @@ describe 'share-dialog', ->
it 'generates new via link', ->
$element = $compile('<div share-dialog="">')($scope)
fakeCrossFrame.providers.push {entities: ['http://example.com']}
fakeCrossFrame.frames.push({uri: 'http://example.com'})
$scope.$digest()
assert.equal($scope.viaPageLink,
'https://via.hypothes.is/http://example.com')
......@@ -64,8 +64,8 @@ describe 'CrossFrame', ->
'source', 'origin', 'token')
it 'queries discovered frames for metadata', ->
info = {metadata: link: [{href: 'http://example.com'}]}
channel = {call: sandbox.stub().yieldsTo('success', info)}
uri = 'http://example.com'
channel = {call: sandbox.stub().yieldsTo('success', {uri: uri})}
fakeBridge.onConnect.yields(channel)
crossframe.connect()
assert.calledWith(channel.call, {
......@@ -73,13 +73,13 @@ describe 'CrossFrame', ->
success: sinon.match.func
})
it 'updates the providers array', ->
info = {metadata: link: [{href: 'http://example.com'}]}
channel = {call: sandbox.stub().yieldsTo('success', info)}
it 'updates the frames array', ->
uri = 'http://example.com'
channel = {call: sandbox.stub().yieldsTo('success', {uri: uri})}
fakeBridge.onConnect.yields(channel)
crossframe.connect()
assert.deepEqual(crossframe.providers, [
{channel: channel, entities: ['http://example.com']}
assert.deepEqual(crossframe.frames, [
{channel: channel, uri: uri}
])
......
......@@ -31,7 +31,7 @@ describe 'WidgetController', ->
clearSelectedAnnotations: sandbox.spy()
}
fakeAuth = {user: null}
fakeCrossFrame = {providers: []}
fakeCrossFrame = {frames: []}
fakeStore = {
SearchResource:
......@@ -73,9 +73,9 @@ describe 'WidgetController', ->
sandbox.restore()
describe 'loadAnnotations', ->
it 'loads all annotation for a provider', ->
it 'loads all annotations for a frame', ->
viewer.chunkSize = 20
fakeCrossFrame.providers.push {entities: ['http://example.com']}
fakeCrossFrame.frames.push({uri: 'http://example.com'})
$scope.$digest()
loadSpy = fakeAnnotationMapper.loadAnnotations
assert.callCount(loadSpy, 5)
......
......@@ -33,20 +33,17 @@ module.exports = class WidgetController
annotationMapper.loadAnnotations(results.rows)
loadAnnotations = ->
query = {}
for p in crossframe.providers
for e in p.entities when e not in loaded
loaded.push e
q = angular.extend(uri: e, query)
_loadAnnotationsFrom q, 0
loadAnnotations = (frames) ->
for f in frames
if f.uri in loaded
continue
loaded.push(f.uri)
_loadAnnotationsFrom({uri: f.uri}, 0)
streamFilter.resetFilter().addClause('/uri', 'one_of', loaded)
streamer.send({filter: streamFilter.getFilter()})
$scope.$watchCollection (-> crossframe.providers), loadAnnotations
$scope.$watchCollection (-> crossframe.frames), loadAnnotations
$scope.focus = (annotation) ->
if angular.isObject annotation
......
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