-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial release; the Recommender widget was build by Tim, with some m… (
#2072) * Initial release; the Recommender widget was build by Tim, with some modications by me * When user logs out, save the '' username - and switch to search help tab
- Loading branch information
1 parent
4182455
commit 60df2e1
Showing
15 changed files
with
788 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
define([], function() { | ||
const actions = { | ||
GET_RECOMMENDATIONS: 'GET_RECOMMENDATIONS', | ||
GET_DOCS: 'GET_DOCS', | ||
SET_DOCS: 'SET_DOCS', | ||
SET_QUERY: 'SET_QUERY', | ||
UPDATE_SEARCH_BAR: 'UPDATE_SEARCH_BAR', | ||
GET_FULL_LIST: 'GET_FULL_LIST', | ||
EMIT_ANALYTICS: 'EMIT_ANALYTICS', | ||
SET_TAB: 'SET_TAB', | ||
SET_ORACLE_TARGET: 'SET_ORACLE_TARGET', | ||
SET_QUERY_PARAMS: 'SET_QUERY_PARAMS', | ||
UPDATE_USERNAME: 'UPDATE_USERNAME' | ||
}; | ||
|
||
const actionCreators = { | ||
getRecommendations: () => ({ | ||
type: actions.GET_RECOMMENDATIONS, | ||
}), | ||
getDocs: (query) => ({ | ||
type: 'API_REQUEST', | ||
scope: actions.GET_DOCS, | ||
options: { | ||
type: 'GET', | ||
target: 'search/query', | ||
query, | ||
}, | ||
}), | ||
setDocs: (docs) => ({ | ||
type: actions.SET_DOCS, | ||
payload: docs, | ||
}), | ||
setQuery: (query) => ({ | ||
type: actions.SET_QUERY, | ||
payload: query, | ||
}), | ||
setQueryParams: (payload) => ({ | ||
type: actions.SET_QUERY_PARAMS, | ||
payload, | ||
}), | ||
updateSearchBar: (text) => ({ | ||
type: actions.UPDATE_SEARCH_BAR, | ||
payload: text, | ||
}), | ||
updateUserName: (text) => ({ | ||
type: actions.UPDATE_USERNAME, | ||
payload: text, | ||
}), | ||
getFullList: () => ({ | ||
type: actions.GET_FULL_LIST, | ||
}), | ||
emitAnalytics: (payload) => ({ | ||
type: actions.EMIT_ANALYTICS, | ||
payload, | ||
}), | ||
setTab: (tab) => ({ | ||
type: actions.SET_TAB, | ||
payload: tab, | ||
}), | ||
setOracleTarget: (target) => ({ | ||
type: actions.SET_ORACLE_TARGET, | ||
payload: target, | ||
}), | ||
}; | ||
|
||
return { ...actions, ...actionCreators }; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
define([ | ||
'react', | ||
'react-bootstrap', | ||
'react-prop-types', | ||
'react-redux', | ||
'../actions', | ||
'es6!./RecommendedList.jsx', | ||
'es6!./SearchExamples.jsx', | ||
], function( | ||
React, | ||
{ Nav, NavItem }, | ||
PropTypes, | ||
{ useDispatch, useSelector }, | ||
{ setTab, emitAnalytics }, | ||
RecommendedList, | ||
SearchExamples | ||
) { | ||
const selector = (state) => ({ | ||
tab: state.tab, | ||
}); | ||
|
||
const App = () => { | ||
const dispatch = useDispatch(); | ||
const { tab } = useSelector(selector); | ||
const onSelected = (key) => { | ||
dispatch(setTab(key)); | ||
dispatch( | ||
emitAnalytics([ | ||
'send', | ||
'event', | ||
'interaction.main-page', | ||
key === 1 ? 'recommender' : 'help', | ||
]) | ||
); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Nav | ||
bsStyle="tabs" | ||
justified | ||
activeKey={tab} | ||
onSelect={(key) => onSelected(key)} | ||
> | ||
<NavItem eventKey={1} href="javascript:void(0);"> | ||
Recommendations | ||
</NavItem> | ||
<NavItem eventKey={2} href="javascript:void(0);"> | ||
Search examples | ||
</NavItem> | ||
</Nav> | ||
<div style={{ minHeight: 200, padding: '1rem 0' }}> | ||
{tab === 1 ? <RecommendedList /> : <SearchExamples />} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
return App; | ||
}); |
200 changes: 200 additions & 0 deletions
200
src/js/react/Recommender/components/RecommendedList.jsx.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
define([ | ||
'react', | ||
'react-prop-types', | ||
'react-redux', | ||
'react-bootstrap', | ||
'../actions', | ||
], function( | ||
React, | ||
PropTypes, | ||
{ useSelector, useDispatch }, | ||
{ Button }, | ||
{ getRecommendations, getFullList, emitAnalytics } | ||
) { | ||
const Paper = ({ title, bibcode, author, totalAuthors, onClick }) => { | ||
const el = React.useRef(null); | ||
React.useEffect(() => { | ||
if (el.current) { | ||
el.current.addEventListener('click', onClick); | ||
} | ||
return () => { | ||
if (el.current) { | ||
el.current.removeEventListener('click', onClick); | ||
} | ||
}; | ||
}, []); | ||
|
||
return ( | ||
<li style={{ marginTop: '1rem' }}> | ||
<a href={`#/abs/${bibcode}/abstract`} ref={el}> | ||
{title} | ||
</a> | ||
<ul className="list-inline"> | ||
{author.map((entry, i) => ( | ||
<li key={entry}>{`${entry}${i < 2 ? ';' : ''}`}</li> | ||
))} | ||
{totalAuthors > 3 && <li>...</li>} | ||
</ul> | ||
</li> | ||
); | ||
}; | ||
Paper.defaultProps = { | ||
title: '', | ||
bibcode: '', | ||
author: [], | ||
totalAuthors: 0, | ||
onClick: () => {}, | ||
}; | ||
|
||
Paper.propTypes = { | ||
title: PropTypes.string, | ||
bibcode: PropTypes.string, | ||
author: PropTypes.arrayOf(PropTypes.string), | ||
totalAuthors: PropTypes.number, | ||
onClick: PropTypes.func, | ||
}; | ||
|
||
const Message = ({ children }) => ( | ||
<div | ||
style={{ | ||
display: 'flex', | ||
justifyContent: 'center', | ||
padding: '2rem 0', | ||
}} | ||
> | ||
{children} | ||
</div> | ||
); | ||
Message.propTypes = { | ||
children: PropTypes.element.isRequired, | ||
}; | ||
|
||
var executed = 0; | ||
var userName = null; | ||
const selector = (state) => { | ||
// reset if it is a different user | ||
if (state.userName !== userName) { | ||
executed = 0; | ||
userName = state.userName; | ||
} | ||
|
||
return { | ||
getRecommendationsRequest: state.requests.GET_RECOMMENDATIONS, | ||
getDocsRequest: state.requests.GET_DOCS, | ||
docs: state.docs, | ||
queryParams: state.queryParams, | ||
executed: executed | ||
}; | ||
}; | ||
|
||
const RecommendedList = () => { | ||
const dispatch = useDispatch(); | ||
const onGetMore = () => { | ||
dispatch(getFullList()); | ||
}; | ||
const { | ||
getRecommendationsRequest, | ||
getDocsRequest, | ||
docs, | ||
queryParams, | ||
userName | ||
} = useSelector(selector); | ||
|
||
React.useEffect(() => { | ||
if ((executed + 12*60*60*1000) < Date.now()) { | ||
// the hook gets called too many times even with [docs] in the args to useEffect | ||
// (and oracle returns 404 when nothing is found; which is IMHO wrong) but we can't | ||
// rely on status.failure for that reason | ||
executed = Date.now(); | ||
dispatch(getRecommendations()); | ||
} | ||
else { | ||
if (executed && getDocsRequest && getDocsRequest.status && docs.length === 0) { | ||
// we are rendered (send the signal everytime -- even if it was sent already) | ||
dispatch( | ||
emitAnalytics([ | ||
'send', | ||
'event', | ||
'interaction.recommendation', // category | ||
'no-useful-recommendations', // action | ||
'', // label, | ||
0, // value | ||
]) | ||
); | ||
} | ||
} | ||
}); | ||
|
||
|
||
const onPaperSelect = ({ bibcode }, index) => { | ||
dispatch( | ||
emitAnalytics([ | ||
'send', | ||
'event', | ||
'interaction.recommendation', // category | ||
queryParams.function, // action | ||
bibcode, // label, | ||
index, // value | ||
]) | ||
); | ||
}; | ||
|
||
if ( | ||
getRecommendationsRequest.status === 'pending' || | ||
getDocsRequest.status === 'pending' | ||
) { | ||
return ( | ||
<Message> | ||
<span> | ||
<i className="fa fa-spinner fa-spin" aria-hidden="true" />{' '} | ||
Loading... | ||
</span> | ||
</Message> | ||
); | ||
} | ||
|
||
if ( | ||
getRecommendationsRequest.status === 'failure' || | ||
getDocsRequest.status === 'failure' | ||
) { | ||
return ( | ||
<Message> | ||
<span> | ||
<i | ||
className="fa fa-exclamation-triangle text-danger" | ||
aria-hidden="true" | ||
/>{' '} | ||
{getRecommendationsRequest.error || getDocsRequest.error} | ||
</span> | ||
</Message> | ||
); | ||
} | ||
|
||
if (docs.length === 0) { | ||
return ( | ||
<Message> | ||
Sorry, we don't have any recommendations for you just yet! ADS provides users recommendations based on their reading history, and we suggest that you create an ADS account to take advantage of this feature. If you already have an account, then be sure you are logged in while searching and reading papers. In due time we will be able to provide you with suggestions based on your inferred interests. | ||
</Message> | ||
); | ||
} | ||
|
||
return ( | ||
<div> | ||
<ul className="list-unstyled"> | ||
{docs.map(({ title, bibcode, author, totalAuthors }, index) => ( | ||
<Paper | ||
key={bibcode} | ||
title={title} | ||
bibcode={bibcode} | ||
author={author} | ||
totalAuthors={totalAuthors} | ||
onClick={() => onPaperSelect(docs[index], index)} | ||
/> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
}; | ||
|
||
return RecommendedList; | ||
}); |
Oops, something went wrong.