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
7ab3442a
Unverified
Commit
7ab3442a
authored
Feb 21, 2020
by
Lyza Gardner
Committed by
GitHub
Feb 21, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1813 from hypothesis/disable-unauthenticated-annotation
Disable unauthenticated annotating and highlighting
parents
b1f2a088
1854fb9b
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
225 additions
and
24 deletions
+225
-24
hypothesis-app.js
src/sidebar/components/hypothesis-app.js
+2
-0
login-prompt-panel.js
src/sidebar/components/login-prompt-panel.js
+45
-0
sidebar-content.js
src/sidebar/components/sidebar-content.js
+1
-0
sidebar-panel.js
src/sidebar/components/sidebar-panel.js
+12
-0
hypothesis-app-test.js
src/sidebar/components/test/hypothesis-app-test.js
+8
-0
login-prompt-panel-test.js
src/sidebar/components/test/login-prompt-panel-test.js
+64
-0
sidebar-panel-test.js
src/sidebar/components/test/sidebar-panel-test.js
+8
-0
index.js
src/sidebar/index.js
+3
-1
frame-sync.js
src/sidebar/services/frame-sync.js
+11
-1
frame-sync-test.js
src/sidebar/services/test/frame-sync-test.js
+58
-13
sidebar-content.html
src/sidebar/templates/sidebar-content.html
+3
-0
ui-constants.js
src/sidebar/ui-constants.js
+1
-0
panel.scss
src/styles/mixins/panel.scss
+9
-0
sidebar-content-error.scss
src/styles/sidebar/components/sidebar-content-error.scss
+0
-9
No files found.
src/sidebar/components/hypothesis-app.js
View file @
7ab3442a
...
...
@@ -103,6 +103,8 @@ function HypothesisAppController(
return
auth
.
login
()
.
then
(()
=>
{
// If the prompt-to-log-in sidebar panel is open, close it
store
.
closeSidebarPanel
(
uiConstants
.
PANEL_LOGIN_PROMPT
);
store
.
clearGroups
();
session
.
reload
();
})
...
...
src/sidebar/components/login-prompt-panel.js
0 → 100644
View file @
7ab3442a
import
{
createElement
}
from
'preact'
;
import
propTypes
from
'prop-types'
;
import
useStore
from
'../store/use-store'
;
import
uiConstants
from
'../ui-constants'
;
import
Button
from
'./button'
;
import
SidebarPanel
from
'./sidebar-panel'
;
/**
* A sidebar panel that prompts a user to log in (or sign up) to annotate.
*/
export
default
function
LoginPromptPanel
({
onLogin
,
onSignUp
})
{
const
isLoggedIn
=
useStore
(
store
=>
store
.
isLoggedIn
());
if
(
isLoggedIn
)
{
return
null
;
}
return
(
<
SidebarPanel
icon
=
"restricted"
title
=
"Login needed"
panelName
=
{
uiConstants
.
PANEL_LOGIN_PROMPT
}
>
<
p
>
Please
log
in
to
create
annotations
or
highlights
.
<
/p
>
<
div
className
=
"sidebar-panel__actions"
>
<
Button
buttonText
=
"Sign up"
className
=
"sidebar-panel__button"
onClick
=
{
onSignUp
}
/
>
<
Button
buttonText
=
"Log in"
className
=
"sidebar-panel__button"
onClick
=
{
onLogin
}
usePrimaryStyle
/>
<
/div
>
<
/SidebarPanel
>
);
}
LoginPromptPanel
.
propTypes
=
{
onLogin
:
propTypes
.
func
.
isRequired
,
onSignUp
:
propTypes
.
func
.
isRequired
,
};
src/sidebar/components/sidebar-content.js
View file @
7ab3442a
...
...
@@ -197,6 +197,7 @@ export default {
bindings
:
{
auth
:
'<'
,
onLogin
:
'&'
,
onSignUp
:
'&'
,
},
template
:
require
(
'../templates/sidebar-content.html'
),
};
src/sidebar/components/sidebar-panel.js
View file @
7ab3442a
...
...
@@ -7,6 +7,7 @@ import useStore from '../store/use-store';
import
Button
from
'./button'
;
import
Slider
from
'./slider'
;
import
SvgIcon
from
'./svg-icon'
;
/**
* Base component for a sidebar panel.
...
...
@@ -17,6 +18,7 @@ import Slider from './slider';
*/
export
default
function
SidebarPanel
({
children
,
icon
=
''
,
panelName
,
title
,
onActiveChanged
,
...
...
@@ -48,6 +50,11 @@ export default function SidebarPanel({
<
Slider
visible
=
{
panelIsActive
}
>
<
div
className
=
"sidebar-panel"
ref
=
{
panelElement
}
>
<
div
className
=
"sidebar-panel__header"
>
{
icon
&&
(
<
div
className
=
"sidebar-panel__header-icon"
>
<
SvgIcon
name
=
{
icon
}
title
=
{
title
}
/
>
<
/div
>
)}
<
div
className
=
"sidebar-panel__title u-stretch"
>
{
title
}
<
/div
>
<
div
>
<
Button
...
...
@@ -67,6 +74,11 @@ export default function SidebarPanel({
SidebarPanel
.
propTypes
=
{
children
:
propTypes
.
any
,
/**
* An optional icon name for display next to the panel's title
*/
icon
:
propTypes
.
string
,
/**
* A string identifying this panel. Only one `panelName` may be active at
* any time. Multiple panels with the same `panelName` would be "in sync",
...
...
src/sidebar/components/test/hypothesis-app-test.js
View file @
7ab3442a
...
...
@@ -75,6 +75,7 @@ describe('sidebar.components.hypothesis-app', function() {
},
}),
clearGroups
:
sinon
.
stub
(),
closeSidebarPanel
:
sinon
.
stub
(),
openSidebarPanel
:
sinon
.
stub
(),
// draft store
countDrafts
:
sandbox
.
stub
().
returns
(
0
),
...
...
@@ -335,6 +336,13 @@ describe('sidebar.components.hypothesis-app', function() {
});
});
it
(
'closes the login prompt panel'
,
()
=>
{
const
ctrl
=
createController
();
return
ctrl
.
login
().
then
(()
=>
{
assert
.
called
(
fakeStore
.
closeSidebarPanel
);
});
});
it
(
'reports an error if login fails'
,
()
=>
{
fakeAuth
.
login
.
returns
(
Promise
.
reject
(
new
Error
(
'Login failed'
)));
...
...
src/sidebar/components/test/login-prompt-panel-test.js
0 → 100644
View file @
7ab3442a
import
{
mount
}
from
'enzyme'
;
import
{
createElement
}
from
'preact'
;
import
LoginPromptPanel
from
'../login-prompt-panel'
;
import
{
$imports
}
from
'../login-prompt-panel'
;
import
{
checkAccessibility
}
from
'../../../test-util/accessibility'
;
import
mockImportedComponents
from
'../../../test-util/mock-imported-components'
;
describe
(
'LoginPromptPanel'
,
function
()
{
let
fakeOnLogin
;
let
fakeOnSignUp
;
let
fakeStore
;
function
createComponent
(
props
)
{
return
mount
(
<
LoginPromptPanel
onLogin
=
{
fakeOnLogin
}
onSignUp
=
{
fakeOnSignUp
}
{...
props
}
/
>
);
}
beforeEach
(()
=>
{
fakeStore
=
{
isLoggedIn
:
sinon
.
stub
().
returns
(
false
),
};
fakeOnLogin
=
sinon
.
stub
();
fakeOnSignUp
=
sinon
.
stub
();
$imports
.
$mock
(
mockImportedComponents
());
$imports
.
$mock
({
'../store/use-store'
:
callback
=>
callback
(
fakeStore
),
});
});
afterEach
(()
=>
{
$imports
.
$restore
();
});
it
(
'should render if user not logged in'
,
()
=>
{
fakeStore
.
isLoggedIn
.
returns
(
false
);
const
wrapper
=
createComponent
();
assert
.
isTrue
(
wrapper
.
find
(
'SidebarPanel'
).
exists
());
});
it
(
'should not render if user is logged in'
,
()
=>
{
fakeStore
.
isLoggedIn
.
returns
(
true
);
const
wrapper
=
createComponent
();
assert
.
isFalse
(
wrapper
.
find
(
'SidebarPanel'
).
exists
());
});
it
(
'should pass a11y checks'
,
checkAccessibility
({
content
:
()
=>
createComponent
(),
})
);
});
src/sidebar/components/test/sidebar-panel-test.js
View file @
7ab3442a
...
...
@@ -44,6 +44,14 @@ describe('SidebarPanel', () => {
assert
.
equal
(
titleEl
.
text
(),
'My Panel'
);
});
it
(
'renders an icon if provided'
,
()
=>
{
const
wrapper
=
createSidebarPanel
({
icon
:
'restricted'
});
const
icon
=
wrapper
.
find
(
'SvgIcon'
).
filter
({
name
:
'restricted'
});
assert
.
isTrue
(
icon
.
exists
());
});
it
(
'closes the panel when close button is clicked'
,
()
=>
{
const
wrapper
=
createSidebarPanel
({
panelName
:
'flibberty'
});
...
...
src/sidebar/index.js
View file @
7ab3442a
...
...
@@ -85,7 +85,7 @@ function configureRoutes($routeProvider) {
});
$routeProvider
.
otherwise
({
template
:
'<sidebar-content auth="vm.auth" on-login="vm.login()"></sidebar-content>'
,
'<sidebar-content auth="vm.auth" on-login="vm.login()"
on-sign-up="vm.signUp()"
></sidebar-content>'
,
reloadOnSearch
:
false
,
resolve
:
resolve
,
});
...
...
@@ -135,6 +135,7 @@ import AnnotationQuote from './components/annotation-quote';
import
FocusedModeHeader
from
'./components/focused-mode-header'
;
import
HelpPanel
from
'./components/help-panel'
;
import
LoggedOutMessage
from
'./components/logged-out-message'
;
import
LoginPromptPanel
from
'./components/login-prompt-panel'
;
import
ModerationBanner
from
'./components/moderation-banner'
;
import
SearchStatusBar
from
'./components/search-status-bar'
;
import
SelectionTabs
from
'./components/selection-tabs'
;
...
...
@@ -274,6 +275,7 @@ function startAngularApp(config) {
.
component
(
'annotationThread'
,
annotationThread
)
.
component
(
'annotationViewerContent'
,
annotationViewerContent
)
.
component
(
'helpPanel'
,
wrapComponent
(
HelpPanel
))
.
component
(
'loginPromptPanel'
,
wrapComponent
(
LoginPromptPanel
))
.
component
(
'loggedOutMessage'
,
wrapComponent
(
LoggedOutMessage
))
.
component
(
'moderationBanner'
,
wrapComponent
(
ModerationBanner
))
.
component
(
'searchStatusBar'
,
wrapComponent
(
SearchStatusBar
))
...
...
src/sidebar/services/frame-sync.js
View file @
7ab3442a
...
...
@@ -128,8 +128,18 @@ export default function FrameSync($rootScope, $window, store, bridge) {
function
setupSyncFromFrame
()
{
// A new annotation, note or highlight was created in the frame
bridge
.
on
(
'beforeCreateAnnotation'
,
function
(
event
)
{
inFrame
.
add
(
event
.
tag
);
const
annot
=
Object
.
assign
({},
event
.
msg
,
{
$tag
:
event
.
tag
});
// If user is not logged in, we can't really create a meaningful highlight
// or annotation. Instead, we need to open the sidebar, show an error,
// and delete the (unsaved) annotation so it gets un-selected in the
// target document
if
(
!
store
.
isLoggedIn
())
{
bridge
.
call
(
'showSidebar'
);
store
.
openSidebarPanel
(
uiConstants
.
PANEL_LOGIN_PROMPT
);
bridge
.
call
(
'deleteAnnotation'
,
formatAnnot
(
annot
));
return
;
}
inFrame
.
add
(
event
.
tag
);
$rootScope
.
$broadcast
(
events
.
BEFORE_ANNOTATION_CREATED
,
annot
);
});
...
...
src/sidebar/services/test/frame-sync-test.js
View file @
7ab3442a
...
...
@@ -66,6 +66,8 @@ describe('sidebar.frame-sync', function() {
findIDsForTags
:
sinon
.
stub
(),
focusAnnotations
:
sinon
.
stub
(),
frames
:
sinon
.
stub
().
returns
([
fixtures
.
framesListEntry
]),
isLoggedIn
:
sinon
.
stub
().
returns
(
false
),
openSidebarPanel
:
sinon
.
stub
(),
selectAnnotations
:
sinon
.
stub
(),
selectTab
:
sinon
.
stub
(),
toggleSelectedAnnotations
:
sinon
.
stub
(),
...
...
@@ -211,21 +213,64 @@ describe('sidebar.frame-sync', function() {
});
context
(
'when a new annotation is created in the frame'
,
function
()
{
it
(
'emits a BEFORE_ANNOTATION_CREATED event'
,
function
()
{
const
onCreated
=
sinon
.
stub
();
const
ann
=
{
target
:
[]
};
$rootScope
.
$on
(
events
.
BEFORE_ANNOTATION_CREATED
,
onCreated
);
context
(
'when an authenticated user is present'
,
()
=>
{
it
(
'emits a BEFORE_ANNOTATION_CREATED event'
,
function
()
{
fakeStore
.
isLoggedIn
.
returns
(
true
);
const
onCreated
=
sinon
.
stub
();
const
ann
=
{
target
:
[]
};
$rootScope
.
$on
(
events
.
BEFORE_ANNOTATION_CREATED
,
onCreated
);
fakeBridge
.
emit
(
'beforeCreateAnnotation'
,
{
tag
:
't1'
,
msg
:
ann
});
assert
.
calledWithMatch
(
onCreated
,
sinon
.
match
.
any
,
sinon
.
match
({
$tag
:
't1'
,
target
:
[],
})
);
});
});
fakeBridge
.
emit
(
'beforeCreateAnnotation'
,
{
tag
:
't1'
,
msg
:
ann
});
context
(
'when no authenticated user is present'
,
()
=>
{
beforeEach
(()
=>
{
fakeStore
.
isLoggedIn
.
returns
(
false
);
});
assert
.
calledWithMatch
(
onCreated
,
sinon
.
match
.
any
,
sinon
.
match
({
$tag
:
't1'
,
target
:
[],
})
);
it
(
'should not emit BEFORE_ANNOTATION_CREATED event'
,
()
=>
{
const
onCreated
=
sinon
.
stub
();
const
ann
=
{
target
:
[]
};
$rootScope
.
$on
(
events
.
BEFORE_ANNOTATION_CREATED
,
onCreated
);
fakeBridge
.
emit
(
'beforeCreateAnnotation'
,
{
tag
:
't1'
,
msg
:
ann
});
assert
.
notCalled
(
onCreated
);
});
it
(
'should open the sidebar'
,
()
=>
{
const
ann
=
{
target
:
[]
};
fakeBridge
.
emit
(
'beforeCreateAnnotation'
,
{
tag
:
't1'
,
msg
:
ann
});
assert
.
calledWith
(
fakeBridge
.
call
,
'showSidebar'
);
});
it
(
'should open the login prompt panel'
,
()
=>
{
const
ann
=
{
target
:
[]
};
fakeBridge
.
emit
(
'beforeCreateAnnotation'
,
{
tag
:
't1'
,
msg
:
ann
});
assert
.
calledWith
(
fakeStore
.
openSidebarPanel
,
uiConstants
.
PANEL_LOGIN_PROMPT
);
});
it
(
'should send a "deleteAnnotation" message to the frame'
,
()
=>
{
const
ann
=
{
target
:
[]
};
fakeBridge
.
emit
(
'beforeCreateAnnotation'
,
{
tag
:
't1'
,
msg
:
ann
});
assert
.
calledWith
(
fakeBridge
.
call
,
'deleteAnnotation'
);
});
});
});
...
...
src/sidebar/templates/sidebar-content.html
View file @
7ab3442a
...
...
@@ -2,6 +2,8 @@
ng-if=
"vm.showFocusedHeader()"
>
</focused-mode-header>
<login-prompt-panel
on-login=
"vm.onLogin()"
on-sign-up=
"vm.onSignUp()"
></login-prompt-panel>
<selection-tabs
ng-if=
"vm.showSelectedTabs()"
is-loading=
"vm.isLoading()"
>
...
...
@@ -27,6 +29,7 @@
>
</sidebar-content-error>
<thread-list
on-change-collapsed=
"vm.setCollapsed(id, collapsed)"
on-focus=
"vm.focus(annotation)"
...
...
src/sidebar/ui-constants.js
View file @
7ab3442a
...
...
@@ -4,6 +4,7 @@
export
default
{
PANEL_HELP
:
'help'
,
PANEL_LOGIN_PROMPT
:
'loginPrompt'
,
PANEL_SHARE_ANNOTATIONS
:
'shareGroupAnnotations'
,
TAB_ANNOTATIONS
:
'annotation'
,
TAB_NOTES
:
'note'
,
...
...
src/styles/mixins/panel.scss
View file @
7ab3442a
...
...
@@ -44,6 +44,15 @@
margin
:
1em
;
margin-top
:
0
;
}
&
__button
{
margin-left
:
1em
;
}
&
__actions
{
display
:
flex
;
justify-content
:
flex-end
;
}
}
/**
...
...
src/styles/sidebar/components/sidebar-content-error.scss
View file @
7ab3442a
...
...
@@ -5,13 +5,4 @@
@include
panel
.
panel
;
position
:
relative
;
margin-bottom
:
0
.75em
;
&
__button
{
margin-left
:
1em
;
}
&
__actions
{
display
:
flex
;
justify-content
:
flex-end
;
}
}
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