Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
2ae4096e
Unverified
Commit
2ae4096e
authored
Apr 20, 2019
by
Hannah Stepanek
Committed by
GitHub
Apr 20, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1064 from hypothesis/handle-illegal-group-link-states
Handle illegal group link states
parents
750b2017
8df71147
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
296 additions
and
53 deletions
+296
-53
search-status-bar.js
src/sidebar/components/search-status-bar.js
+2
-1
sidebar-content.js
src/sidebar/components/sidebar-content.js
+34
-10
search-status-bar-test.js
src/sidebar/components/test/search-status-bar-test.js
+2
-2
sidebar-content-test.js
src/sidebar/components/test/sidebar-content-test.js
+135
-8
thread-list-test.js
src/sidebar/components/test/thread-list-test.js
+7
-0
thread-list.js
src/sidebar/components/thread-list.js
+35
-22
groups.js
src/sidebar/services/groups.js
+23
-4
groups-test.js
src/sidebar/services/test/groups-test.js
+33
-0
search-status-bar.html
src/sidebar/templates/search-status-bar.html
+1
-1
sidebar-content.html
src/sidebar/templates/sidebar-content.html
+15
-2
virtual-thread-list.js
src/sidebar/virtual-thread-list.js
+9
-3
No files found.
src/sidebar/components/search-status-bar.js
View file @
2ae4096e
...
@@ -15,7 +15,8 @@ module.exports = {
...
@@ -15,7 +15,8 @@ module.exports = {
onClearSelection
:
'&'
,
onClearSelection
:
'&'
,
searchQuery
:
'<'
,
searchQuery
:
'<'
,
selectedTab
:
'<'
,
selectedTab
:
'<'
,
selectionCount
:
'<'
,
// Boolean indicating all annotations are visible (none are hidden).
areAllAnnotationsVisible
:
'<'
,
totalAnnotations
:
'<'
,
totalAnnotations
:
'<'
,
totalNotes
:
'<'
,
totalNotes
:
'<'
,
},
},
...
...
src/sidebar/components/sidebar-content.js
View file @
2ae4096e
...
@@ -49,6 +49,8 @@ function SidebarContentController(
...
@@ -49,6 +49,8 @@ function SidebarContentController(
streamFilter
streamFilter
)
{
)
{
const
self
=
this
;
const
self
=
this
;
this
.
directLinkedGroupFetchFailed
=
!!
settings
.
group
&&
settings
.
group
!==
store
.
focusedGroup
().
id
;
function
thread
()
{
function
thread
()
{
return
rootThread
.
thread
(
store
.
getState
());
return
rootThread
.
thread
(
store
.
getState
());
...
@@ -161,7 +163,7 @@ function SidebarContentController(
...
@@ -161,7 +163,7 @@ function SidebarContentController(
searchClient
.
get
({
uri
:
uris
,
group
:
group
});
searchClient
.
get
({
uri
:
uris
,
group
:
group
});
}
}
function
isLoading
()
{
this
.
isLoading
=
function
()
{
if
(
if
(
!
store
.
frames
().
some
(
function
(
frame
)
{
!
store
.
frames
().
some
(
function
(
frame
)
{
return
frame
.
uri
;
return
frame
.
uri
;
...
@@ -177,7 +179,7 @@ function SidebarContentController(
...
@@ -177,7 +179,7 @@ function SidebarContentController(
}
}
return
false
;
return
false
;
}
}
;
/**
/**
* Load annotations for all URLs associated with `frames`.
* Load annotations for all URLs associated with `frames`.
...
@@ -272,7 +274,7 @@ function SidebarContentController(
...
@@ -272,7 +274,7 @@ function SidebarContentController(
// of switching to the group containing a direct-linked annotation.
// of switching to the group containing a direct-linked annotation.
//
//
// In that case, we don't want to trigger reloading annotations again.
// In that case, we don't want to trigger reloading annotations again.
if
(
isLoading
())
{
if
(
this
.
isLoading
())
{
return
;
return
;
}
}
store
.
clearSelectedAnnotations
();
store
.
clearSelectedAnnotations
();
...
@@ -283,6 +285,17 @@ function SidebarContentController(
...
@@ -283,6 +285,17 @@ function SidebarContentController(
true
true
);
);
this
.
showSelectedTabs
=
function
()
{
if
(
this
.
selectedAnnotationUnavailable
()
||
this
.
selectedGroupUnavailable
()
||
this
.
search
.
query
()
)
{
return
false
;
}
return
true
;
};
this
.
setCollapsed
=
function
(
id
,
collapsed
)
{
this
.
setCollapsed
=
function
(
id
,
collapsed
)
{
store
.
setCollapsed
(
id
,
collapsed
);
store
.
setCollapsed
(
id
,
collapsed
);
};
};
...
@@ -297,17 +310,26 @@ function SidebarContentController(
...
@@ -297,17 +310,26 @@ function SidebarContentController(
this
.
focus
=
focusAnnotation
;
this
.
focus
=
focusAnnotation
;
this
.
scrollTo
=
scrollToAnnotation
;
this
.
scrollTo
=
scrollToAnnotation
;
this
.
selectedAnnotationCount
=
function
()
{
this
.
areAllAnnotationsVisible
=
function
()
{
if
(
this
.
directLinkedGroupFetchFailed
)
{
return
true
;
}
const
selection
=
store
.
getState
().
selectedAnnotationMap
;
const
selection
=
store
.
getState
().
selectedAnnotationMap
;
if
(
!
selection
)
{
if
(
!
selection
)
{
return
0
;
return
false
;
}
}
return
Object
.
keys
(
selection
).
length
;
return
Object
.
keys
(
selection
).
length
>
0
;
};
this
.
selectedGroupUnavailable
=
function
()
{
return
!
this
.
isLoading
()
&&
this
.
directLinkedGroupFetchFailed
;
};
};
this
.
selectedAnnotationUnavailable
=
function
()
{
this
.
selectedAnnotationUnavailable
=
function
()
{
const
selectedID
=
firstKey
(
store
.
getState
().
selectedAnnotationMap
);
const
selectedID
=
firstKey
(
store
.
getState
().
selectedAnnotationMap
);
return
!
isLoading
()
&&
!!
selectedID
&&
!
store
.
annotationExists
(
selectedID
);
return
(
!
this
.
isLoading
()
&&
!!
selectedID
&&
!
store
.
annotationExists
(
selectedID
)
);
};
};
this
.
shouldShowLoggedOutMessage
=
function
()
{
this
.
shouldShowLoggedOutMessage
=
function
()
{
...
@@ -332,11 +354,11 @@ function SidebarContentController(
...
@@ -332,11 +354,11 @@ function SidebarContentController(
// annotation. If there is an annotation selection and that
// annotation. If there is an annotation selection and that
// selection is available to the user, show the CTA.
// selection is available to the user, show the CTA.
const
selectedID
=
firstKey
(
store
.
getState
().
selectedAnnotationMap
);
const
selectedID
=
firstKey
(
store
.
getState
().
selectedAnnotationMap
);
return
!
isLoading
()
&&
!!
selectedID
&&
store
.
annotationExists
(
selectedID
);
return
(
!
this
.
isLoading
()
&&
!!
selectedID
&&
store
.
annotationExists
(
selectedID
)
);
};
};
this
.
isLoading
=
isLoading
;
const
visibleCount
=
memoize
(
function
(
thread
)
{
const
visibleCount
=
memoize
(
function
(
thread
)
{
return
thread
.
children
.
reduce
(
return
thread
.
children
.
reduce
(
function
(
count
,
child
)
{
function
(
count
,
child
)
{
...
@@ -365,6 +387,8 @@ function SidebarContentController(
...
@@ -365,6 +387,8 @@ function SidebarContentController(
store
.
clearSelectedAnnotations
();
store
.
clearSelectedAnnotations
();
store
.
selectTab
(
selectedTab
);
store
.
selectTab
(
selectedTab
);
// Clear direct-linked group fetch failed state.
this
.
directLinkedGroupFetchFailed
=
false
;
};
};
}
}
...
...
src/sidebar/components/test/search-status-bar-test.js
View file @
2ae4096e
...
@@ -30,7 +30,7 @@ describe('searchStatusBar', function() {
...
@@ -30,7 +30,7 @@ describe('searchStatusBar', function() {
const
msg
=
'Show all annotations'
;
const
msg
=
'Show all annotations'
;
const
msgCount
=
'(2)'
;
const
msgCount
=
'(2)'
;
const
elem
=
util
.
createDirective
(
document
,
'searchStatusBar'
,
{
const
elem
=
util
.
createDirective
(
document
,
'searchStatusBar'
,
{
selectionCount
:
1
,
areAllAnnotationsVisible
:
true
,
totalAnnotations
:
2
,
totalAnnotations
:
2
,
selectedTab
:
'annotation'
,
selectedTab
:
'annotation'
,
});
});
...
@@ -43,7 +43,7 @@ describe('searchStatusBar', function() {
...
@@ -43,7 +43,7 @@ describe('searchStatusBar', function() {
const
msg
=
'Show all notes'
;
const
msg
=
'Show all notes'
;
const
msgCount
=
'(3)'
;
const
msgCount
=
'(3)'
;
const
elem
=
util
.
createDirective
(
document
,
'searchStatusBar'
,
{
const
elem
=
util
.
createDirective
(
document
,
'searchStatusBar'
,
{
selectionCount
:
1
,
areAllAnnotationsVisible
:
true
,
totalNotes
:
3
,
totalNotes
:
3
,
selectedTab
:
'note'
,
selectedTab
:
'note'
,
});
});
...
...
src/sidebar/components/test/sidebar-content-test.js
View file @
2ae4096e
...
@@ -6,6 +6,7 @@ const EventEmitter = require('tiny-emitter');
...
@@ -6,6 +6,7 @@ const EventEmitter = require('tiny-emitter');
const
events
=
require
(
'../../events'
);
const
events
=
require
(
'../../events'
);
const
noCallThru
=
require
(
'../../../shared/test/util'
).
noCallThru
;
const
noCallThru
=
require
(
'../../../shared/test/util'
).
noCallThru
;
const
uiConstants
=
require
(
'../../ui-constants'
);
let
searchClients
;
let
searchClients
;
...
@@ -150,7 +151,7 @@ describe('sidebar.components.sidebar-content', function() {
...
@@ -150,7 +151,7 @@ describe('sidebar.components.sidebar-content', function() {
});
});
}
}
beforeEach
(
const
makeSidebarContentController
=
()
=>
{
angular
.
mock
.
inject
(
function
(
$componentController
,
_store_
,
_$rootScope_
)
{
angular
.
mock
.
inject
(
function
(
$componentController
,
_store_
,
_$rootScope_
)
{
$rootScope
=
_$rootScope_
;
$rootScope
=
_$rootScope_
;
$scope
=
$rootScope
.
$new
();
$scope
=
$rootScope
.
$new
();
...
@@ -163,13 +164,84 @@ describe('sidebar.components.sidebar-content', function() {
...
@@ -163,13 +164,84 @@ describe('sidebar.components.sidebar-content', function() {
auth
:
{
status
:
'unknown'
},
auth
:
{
status
:
'unknown'
},
}
}
);
);
})
});
);
};
beforeEach
(()
=>
{
makeSidebarContentController
();
});
afterEach
(
function
()
{
afterEach
(
function
()
{
return
sandbox
.
restore
();
return
sandbox
.
restore
();
});
});
describe
(
'clearSelection'
,
()
=>
{
it
(
'sets selectedTab to Annotations tab if selectedTab is null'
,
()
=>
{
store
.
selectTab
(
uiConstants
.
TAB_ORPHANS
);
$scope
.
$digest
();
ctrl
.
clearSelection
();
assert
.
equal
(
store
.
getState
().
selectedTab
,
uiConstants
.
TAB_ANNOTATIONS
);
});
it
(
'sets selectedTab to Annotations tab if selectedTab is set to orphans'
,
()
=>
{
store
.
selectTab
(
uiConstants
.
TAB_ORPHANS
);
$scope
.
$digest
();
ctrl
.
clearSelection
();
assert
.
equal
(
store
.
getState
().
selectedTab
,
uiConstants
.
TAB_ANNOTATIONS
);
});
it
(
'clears selected annotations'
,
()
=>
{
ctrl
.
clearSelection
();
assert
.
equal
(
store
.
getState
().
selectedAnnotationMap
,
null
);
assert
.
equal
(
store
.
getState
().
filterQuery
,
null
);
});
it
(
'clears the directLinkedGroupFetchFailed state'
,
()
=>
{
ctrl
.
directLinkedGroupFetchFailed
=
true
;
ctrl
.
clearSelection
();
assert
.
isFalse
(
ctrl
.
directLinkedGroupFetchFailed
);
});
});
describe
(
'showSelectedTabs'
,
()
=>
{
beforeEach
(()
=>
{
setFrames
([{
uri
:
'http://www.example.com'
}]);
ctrl
.
search
=
{
query
:
sinon
.
stub
().
returns
(
undefined
)
};
});
it
(
'returns false if there is a search query'
,
()
=>
{
ctrl
.
search
=
{
query
:
sinon
.
stub
().
returns
(
'tag:foo'
)
};
assert
.
isFalse
(
ctrl
.
showSelectedTabs
());
});
it
(
'returns false if selected group is unavailable'
,
()
=>
{
fakeSettings
.
group
=
'group-id'
;
store
.
loadGroups
([{
id
:
'default-id'
}]);
store
.
focusGroup
(
'default-id'
);
fakeGroups
.
focused
.
returns
({
id
:
'default-id'
});
$scope
.
$digest
();
// Re-construct the controller after the environment setup.
makeSidebarContentController
();
assert
.
isFalse
(
ctrl
.
showSelectedTabs
());
});
it
(
'returns false if selected annotation is unavailable'
,
()
=>
{
store
.
selectAnnotations
([
'missing'
]);
$scope
.
$digest
();
assert
.
isFalse
(
ctrl
.
showSelectedTabs
());
});
it
(
'returns true in all other cases'
,
()
=>
{
assert
.
isTrue
(
ctrl
.
showSelectedTabs
());
});
});
describe
(
'#loadAnnotations'
,
function
()
{
describe
(
'#loadAnnotations'
,
function
()
{
it
(
'unloads any existing annotations'
,
function
()
{
it
(
'unloads any existing annotations'
,
function
()
{
// When new clients connect, all existing annotations should be unloaded
// When new clients connect, all existing annotations should be unloaded
...
@@ -255,7 +327,62 @@ describe('sidebar.components.sidebar-content', function() {
...
@@ -255,7 +327,62 @@ describe('sidebar.components.sidebar-content', function() {
assert
.
isTrue
(
updateSpy
.
calledWith
(
frameUris
[
1
],
true
));
assert
.
isTrue
(
updateSpy
.
calledWith
(
frameUris
[
1
],
true
));
});
});
context
(
'when there is a selection'
,
function
()
{
context
(
'when there is a direct-linked group error'
,
()
=>
{
beforeEach
(()
=>
{
setFrames
([{
uri
:
'http://www.example.com'
}]);
fakeSettings
.
group
=
'group-id'
;
store
.
loadGroups
([{
id
:
'default-id'
}]);
store
.
focusGroup
(
'default-id'
);
fakeGroups
.
focused
.
returns
({
id
:
'default-id'
});
$scope
.
$digest
();
// Re-construct the controller after the environment setup.
makeSidebarContentController
();
});
it
(
'sets directLinkedGroupFetchFailed to true'
,
()
=>
{
assert
.
isTrue
(
ctrl
.
directLinkedGroupFetchFailed
);
});
it
(
'areAllAnnotationsVisible returns true since there is an error message'
,
()
=>
{
assert
.
isTrue
(
ctrl
.
areAllAnnotationsVisible
());
});
it
(
'selectedGroupUnavailable returns true'
,
()
=>
{
assert
.
isTrue
(
ctrl
.
selectedGroupUnavailable
());
});
});
context
(
'when there is a direct-linked group selection'
,
()
=>
{
beforeEach
(()
=>
{
setFrames
([{
uri
:
'http://www.example.com'
}]);
fakeSettings
.
group
=
'group-id'
;
store
.
loadGroups
([{
id
:
fakeSettings
.
group
}]);
store
.
focusGroup
(
fakeSettings
.
group
);
fakeGroups
.
focused
.
returns
({
id
:
fakeSettings
.
group
});
$scope
.
$digest
();
});
it
(
'sets directLinkedGroupFetchFailed to false'
,
()
=>
{
assert
.
isFalse
(
ctrl
.
directLinkedGroupFetchFailed
);
});
it
(
'areAllAnnotationsVisible returns false since group has no annotations'
,
()
=>
{
assert
.
isFalse
(
ctrl
.
areAllAnnotationsVisible
());
});
it
(
'selectedGroupUnavailable returns false'
,
()
=>
{
assert
.
isFalse
(
ctrl
.
selectedGroupUnavailable
());
});
it
(
'fetches annotations for the direct-linked group'
,
()
=>
{
assert
.
calledWith
(
searchClients
[
0
].
get
,
{
uri
:
[
'http://www.example.com'
],
group
:
'group-id'
,
});
});
});
context
(
'when there is a direct-linked annotation selection'
,
function
()
{
const
uri
=
'http://example.com'
;
const
uri
=
'http://example.com'
;
const
id
=
uri
+
'123'
;
const
id
=
uri
+
'123'
;
...
@@ -265,8 +392,8 @@ describe('sidebar.components.sidebar-content', function() {
...
@@ -265,8 +392,8 @@ describe('sidebar.components.sidebar-content', function() {
$scope
.
$digest
();
$scope
.
$digest
();
});
});
it
(
'
selectedAnnotationCount is > 0
'
,
function
()
{
it
(
'
areAllAnnotationsVisible is true
'
,
function
()
{
assert
.
equal
(
ctrl
.
selectedAnnotationCount
(),
1
);
assert
.
isTrue
(
ctrl
.
areAllAnnotationsVisible
()
);
});
});
it
(
"switches to the selected annotation's group"
,
function
()
{
it
(
"switches to the selected annotation's group"
,
function
()
{
...
@@ -295,8 +422,8 @@ describe('sidebar.components.sidebar-content', function() {
...
@@ -295,8 +422,8 @@ describe('sidebar.components.sidebar-content', function() {
$scope
.
$digest
();
$scope
.
$digest
();
});
});
it
(
'
selectedAnnotationCount is 0
'
,
function
()
{
it
(
'
areAllAnnotationsVisible is false
'
,
function
()
{
assert
.
equal
(
ctrl
.
selectedAnnotationCount
(),
0
);
assert
.
isFalse
(
ctrl
.
areAllAnnotationsVisible
()
);
});
});
it
(
'fetches annotations for the current group'
,
function
()
{
it
(
'fetches annotations for the current group'
,
function
()
{
...
...
src/sidebar/components/test/thread-list-test.js
View file @
2ae4096e
...
@@ -70,6 +70,13 @@ class FakeVirtualThreadList extends EventEmitter {
...
@@ -70,6 +70,13 @@ class FakeVirtualThreadList extends EventEmitter {
this
.
yOffsetOf
=
function
()
{
this
.
yOffsetOf
=
function
()
{
return
42
;
return
42
;
};
};
this
.
calculateVisibleThreads
=
()
=>
{
return
{
offscreenLowerHeight
:
10
,
offscreenUpperHeight
:
20
,
visibleThreads
:
thread
.
children
,
};
};
}
}
}
}
...
...
src/sidebar/components/thread-list.js
View file @
2ae4096e
...
@@ -82,7 +82,21 @@ function ThreadListController($element, $scope, settings, VirtualThreadList) {
...
@@ -82,7 +82,21 @@ function ThreadListController($element, $scope, settings, VirtualThreadList) {
this
.
thread
,
this
.
thread
,
options
options
);
);
visibleThreads
.
on
(
'changed'
,
function
(
state
)
{
// Calculate the visible threads.
onVisibleThreadsChanged
(
visibleThreads
.
calculateVisibleThreads
());
// Subscribe onVisibleThreadsChanged to the visibleThreads 'changed' event
// after calculating visible threads, to avoid an undesired second call to
// onVisibleThreadsChanged that occurs from the emission of the 'changed'
// event during the visibleThreads calculation.
visibleThreads
.
on
(
'changed'
,
onVisibleThreadsChanged
);
};
/**
* Update which threads are visible in the virtualThreadsList.
*
* @param {Object} state the new state of the virtualThreadsList
*/
function
onVisibleThreadsChanged
(
state
)
{
self
.
virtualThreadList
=
{
self
.
virtualThreadList
=
{
visibleThreads
:
state
.
visibleThreads
,
visibleThreads
:
state
.
visibleThreads
,
invisibleThreads
:
state
.
invisibleThreads
,
invisibleThreads
:
state
.
invisibleThreads
,
...
@@ -103,8 +117,7 @@ function ThreadListController($element, $scope, settings, VirtualThreadList) {
...
@@ -103,8 +117,7 @@ function ThreadListController($element, $scope, settings, VirtualThreadList) {
},
},
50
50
);
);
});
}
};
/**
/**
* Return the vertical scroll offset for the document in order to position the
* Return the vertical scroll offset for the document in order to position the
...
...
src/sidebar/services/groups.js
View file @
2ae4096e
...
@@ -68,6 +68,19 @@ function groups(
...
@@ -68,6 +68,19 @@ function groups(
directLinkedAnnotationId
,
directLinkedAnnotationId
,
directLinkedGroupId
directLinkedGroupId
)
{
)
{
// Filter the directLinkedGroup out if it is out of scope and scope is enforced.
if
(
directLinkedGroupId
)
{
const
directLinkedGroup
=
groups
.
find
(
g
=>
g
.
id
===
directLinkedGroupId
);
if
(
directLinkedGroup
&&
!
directLinkedGroup
.
isScopedToUri
&&
directLinkedGroup
.
scopes
.
enforced
)
{
groups
=
groups
.
filter
(
g
=>
g
.
id
!==
directLinkedGroupId
);
directLinkedGroupId
=
undefined
;
}
}
// If service groups are specified only return those.
// If service groups are specified only return those.
// If a service group doesn't exist in the list of groups don't return it.
// If a service group doesn't exist in the list of groups don't return it.
if
(
svc
&&
svc
.
groups
)
{
if
(
svc
&&
svc
.
groups
)
{
...
@@ -186,9 +199,15 @@ function groups(
...
@@ -186,9 +199,15 @@ function groups(
// particular group as well since it may not be in the results returned
// particular group as well since it may not be in the results returned
// by group.list or profile.groups.
// by group.list or profile.groups.
if
(
directLinkedGroup
)
{
if
(
directLinkedGroup
)
{
const
selectedGroupApi
=
api
.
group
.
read
({
const
selectedGroupApi
=
api
.
group
.
read
({
id
:
directLinkedGroup
,
id
:
directLinkedGroup
,
expand
:
params
.
expand
,
expand
:
params
.
expand
,
})
.
catch
(()
=>
{
// If the group does not exist or the user doesn't have permission,
// return undefined.
return
undefined
;
});
});
groupApiRequests
=
groupApiRequests
.
concat
(
selectedGroupApi
);
groupApiRequests
=
groupApiRequests
.
concat
(
selectedGroupApi
);
}
}
...
...
src/sidebar/services/test/groups-test.js
View file @
2ae4096e
...
@@ -181,6 +181,39 @@ describe('groups', function() {
...
@@ -181,6 +181,39 @@ describe('groups', function() {
});
});
describe
(
'#load'
,
function
()
{
describe
(
'#load'
,
function
()
{
it
(
'filters out direct-linked groups that are out of scope and scope enforced'
,
()
=>
{
const
svc
=
service
();
fakeLocalStorage
.
getItem
.
returns
(
dummyGroups
[
0
].
id
);
const
outOfScopeEnforcedGroup
=
{
id
:
'oos'
,
scopes
:
{
enforced
:
true
,
uri_patterns
:
[
'http://foo.com'
]
},
};
fakeSettings
.
group
=
outOfScopeEnforcedGroup
.
id
;
fakeApi
.
group
.
read
.
returns
(
Promise
.
resolve
(
outOfScopeEnforcedGroup
));
return
svc
.
load
().
then
(
groups
=>
{
// The focus group is not set to the direct-linked group.
assert
.
calledWith
(
fakeStore
.
focusGroup
,
dummyGroups
[
0
].
id
);
// The direct-linked group is not in the list of groups.
assert
.
isFalse
(
groups
.
some
(
g
=>
g
.
id
===
fakeSettings
.
group
));
});
});
it
(
'catches 404 error from api.group.read request'
,
()
=>
{
const
svc
=
service
();
fakeLocalStorage
.
getItem
.
returns
(
dummyGroups
[
0
].
id
);
fakeSettings
.
group
=
'does-not-exist'
;
fakeApi
.
group
.
read
.
returns
(
Promise
.
reject
(
"404 Not Found: Either the resource you requested doesn't exist,
\
or you are not currently authorized to see it."
)
);
return
svc
.
load
().
then
(()
=>
{
// The focus group is not set to the direct-linked group.
assert
.
calledWith
(
fakeStore
.
focusGroup
,
dummyGroups
[
0
].
id
);
});
});
it
(
'combines groups from both endpoints'
,
function
()
{
it
(
'combines groups from both endpoints'
,
function
()
{
const
svc
=
service
();
const
svc
=
service
();
...
...
src/sidebar/templates/search-status-bar.html
View file @
2ae4096e
...
@@ -11,7 +11,7 @@
...
@@ -11,7 +11,7 @@
'one': '1 search result',
'one': '1 search result',
'other': '{} search results'}"
></span>
'other': '{} search results'}"
></span>
</div>
</div>
<div
class=
"search-status-bar"
ng-if=
"!vm.filterActive && vm.
selectionCount > 0
"
>
<div
class=
"search-status-bar"
ng-if=
"!vm.filterActive && vm.
areAllAnnotationsVisible
"
>
<button
class=
"primary-action-btn primary-action-btn--short"
<button
class=
"primary-action-btn primary-action-btn--short"
ng-click=
"vm.onClearSelection()"
ng-click=
"vm.onClearSelection()"
title=
"Clear the selection and show all annotations"
>
title=
"Clear the selection and show all annotations"
>
...
...
src/sidebar/templates/sidebar-content.html
View file @
2ae4096e
<selection-tabs
<selection-tabs
ng-if=
"
!vm.search.query() && vm.selectedAnnotationCount() === 0
"
ng-if=
"
vm.showSelectedTabs()
"
is-waiting-to-anchor-annotations=
"vm.waitingToAnchorAnnotations"
is-waiting-to-anchor-annotations=
"vm.waitingToAnchorAnnotations"
is-loading=
"vm.isLoading"
is-loading=
"vm.isLoading"
selected-tab=
"vm.selectedTab"
selected-tab=
"vm.selectedTab"
...
@@ -14,13 +14,14 @@
...
@@ -14,13 +14,14 @@
filter-match-count=
"vm.visibleCount()"
filter-match-count=
"vm.visibleCount()"
on-clear-selection=
"vm.clearSelection()"
on-clear-selection=
"vm.clearSelection()"
search-query=
"vm.search.query()"
search-query=
"vm.search.query()"
selection-count=
"vm.selectedAnnotationCount
()"
are-all-annotations-visible=
"vm.areAllAnnotationsVisible
()"
total-count=
"vm.topLevelThreadCount()"
total-count=
"vm.topLevelThreadCount()"
selected-tab=
"vm.selectedTab"
selected-tab=
"vm.selectedTab"
total-annotations=
"vm.totalAnnotations"
total-annotations=
"vm.totalAnnotations"
total-notes=
"vm.totalNotes"
>
total-notes=
"vm.totalNotes"
>
</search-status-bar>
</search-status-bar>
<!-- Display error message if direct-linked annotation fetch failed. -->
<sidebar-content-error
<sidebar-content-error
class=
"sidebar-content-error"
class=
"sidebar-content-error"
logged-out-error-message=
"'This annotation is not available.'"
logged-out-error-message=
"'This annotation is not available.'"
...
@@ -31,6 +32,17 @@
...
@@ -31,6 +32,17 @@
>
>
</sidebar-content-error>
</sidebar-content-error>
<!-- Display error message if direct-linked group fetch failed. -->
<sidebar-content-error
class=
"sidebar-content-error"
logged-out-error-message=
"'This group is not available.'"
logged-in-error-message=
"'You either do not have permission to view this group, the group does not exist, or the group is not visible at this URL.'"
on-login-request=
"vm.onLogin()"
is-logged-in=
"vm.auth.status === 'logged-in'"
ng-if=
"vm.selectedGroupUnavailable()"
>
</sidebar-content-error>
<thread-list
<thread-list
on-change-collapsed=
"vm.setCollapsed(id, collapsed)"
on-change-collapsed=
"vm.setCollapsed(id, collapsed)"
on-clear-selection=
"vm.clearSelection()"
on-clear-selection=
"vm.clearSelection()"
...
@@ -38,6 +50,7 @@
...
@@ -38,6 +50,7 @@
on-force-visible=
"vm.forceVisible(thread)"
on-force-visible=
"vm.forceVisible(thread)"
on-select=
"vm.scrollTo(annotation)"
on-select=
"vm.scrollTo(annotation)"
show-document-info=
"false"
show-document-info=
"false"
ng-if=
"!vm.selectedGroupUnavailable()"
thread=
"vm.rootThread"
>
thread=
"vm.rootThread"
>
</thread-list>
</thread-list>
...
...
src/sidebar/virtual-thread-list.js
View file @
2ae4096e
...
@@ -49,7 +49,7 @@ class VirtualThreadList extends EventEmitter {
...
@@ -49,7 +49,7 @@ class VirtualThreadList extends EventEmitter {
this
.
scrollRoot
=
options
.
scrollRoot
||
document
.
body
;
this
.
scrollRoot
=
options
.
scrollRoot
||
document
.
body
;
const
debouncedUpdate
=
debounce
(
function
()
{
const
debouncedUpdate
=
debounce
(
function
()
{
self
.
_upd
ateVisibleThreads
();
self
.
calcul
ateVisibleThreads
();
$scope
.
$digest
();
$scope
.
$digest
();
},
20
);
},
20
);
this
.
scrollRoot
.
addEventListener
(
'scroll'
,
debouncedUpdate
);
this
.
scrollRoot
.
addEventListener
(
'scroll'
,
debouncedUpdate
);
...
@@ -83,7 +83,7 @@ class VirtualThreadList extends EventEmitter {
...
@@ -83,7 +83,7 @@ class VirtualThreadList extends EventEmitter {
return
;
return
;
}
}
this
.
_rootThread
=
thread
;
this
.
_rootThread
=
thread
;
this
.
_upd
ateVisibleThreads
();
this
.
calcul
ateVisibleThreads
();
}
}
/**
/**
...
@@ -131,7 +131,7 @@ class VirtualThreadList extends EventEmitter {
...
@@ -131,7 +131,7 @@ class VirtualThreadList extends EventEmitter {
*
*
* Emits a `changed` event with the recalculated set of visible threads.
* Emits a `changed` event with the recalculated set of visible threads.
*/
*/
_upd
ateVisibleThreads
()
{
calcul
ateVisibleThreads
()
{
// Space above the viewport in pixels which should be considered 'on-screen'
// Space above the viewport in pixels which should be considered 'on-screen'
// when calculating the set of visible threads
// when calculating the set of visible threads
const
MARGIN_ABOVE
=
800
;
const
MARGIN_ABOVE
=
800
;
...
@@ -205,6 +205,12 @@ class VirtualThreadList extends EventEmitter {
...
@@ -205,6 +205,12 @@ class VirtualThreadList extends EventEmitter {
visibleThreads
:
visibleThreads
,
visibleThreads
:
visibleThreads
,
invisibleThreads
:
invisibleThreads
,
invisibleThreads
:
invisibleThreads
,
});
});
return
{
offscreenLowerHeight
,
offscreenUpperHeight
,
visibleThreads
,
invisibleThreads
,
};
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment