From edcd8e57a4f511c192e7bda5c985e1aced98e752 Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 3 Sep 2015 14:15:00 +0200 Subject: [PATCH] notification app notification refactor in onion - split by piece/edition --- js/actions/notification_actions.js | 34 +++++ .../ascribe_detail/edition_container.js | 1 - js/components/header.js | 33 ++++- js/components/header_notification.js | 129 +++++++++++++----- js/components/piece_list.js | 3 +- js/constants/api_urls.js | 3 +- js/fetchers/notification_fetcher.js | 17 +++ js/stores/notification_store.js | 26 ++++ sass/ascribe_notification_list.scss | 28 +++- 9 files changed, 231 insertions(+), 43 deletions(-) create mode 100644 js/actions/notification_actions.js create mode 100644 js/fetchers/notification_fetcher.js create mode 100644 js/stores/notification_store.js diff --git a/js/actions/notification_actions.js b/js/actions/notification_actions.js new file mode 100644 index 00000000..06fce61f --- /dev/null +++ b/js/actions/notification_actions.js @@ -0,0 +1,34 @@ +'use strict'; + +import alt from '../alt'; + +import NotificationFetcher from '../fetchers/notification_fetcher'; + +class NotificationActions { + constructor() { + this.generateActions( + 'updatePieceListNotifications', + 'updateEditionListNotifications' + ); + } + + fetchPieceListNotifications() { + NotificationFetcher + .fetchPieceListNotifications() + .then((res) => { + this.actions.updatePieceListNotifications(res); + }) + .catch((err) => console.logGlobal(err)); + } + + fetchEditionListNotifications() { + NotificationFetcher + .fetchEditionListNotifications() + .then((res) => { + this.actions.updateEditionListNotifications(res); + }) + .catch((err) => console.logGlobal(err)); + } +} + +export default alt.createActions(NotificationActions); diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index 78b1e477..a19f67b9 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -61,7 +61,6 @@ let EditionContainer = React.createClass({ }, render() { - console.log(this.state); if('title' in this.state.edition) { return ( + + onSelected: function(e) { + // doesn't need to have functionality (necessarily) ... just wired up + } + Internally, a call to DropdownButton.setDropDownState(false) is made which will hide the dropdown menu. + So, you should be able to call that directly on the DropdownButton instance as well if needed. + + NOW, THAT DIDN'T WORK - the onSelect routine isnt triggered in all cases + Hence, we do this manually + */ + this.refs.dropdownbutton.setDropdownState(false); + }, + render() { let account; let signup; @@ -103,9 +128,15 @@ let Header = React.createClass({ if (this.state.currentUser.username){ account = ( - {getLangText('Account Settings')} + + {getLangText('Account Settings')} + {getLangText('Log out')} diff --git a/js/components/header_notification.js b/js/components/header_notification.js index 9222c0c4..73d68a5d 100644 --- a/js/components/header_notification.js +++ b/js/components/header_notification.js @@ -8,7 +8,8 @@ import MenuItem from 'react-bootstrap/lib/MenuItem'; import Nav from 'react-bootstrap/lib/Nav'; -import PieceListStore from '../stores/piece_list_store'; +import NotificationActions from '../actions/notification_actions'; +import NotificationStore from '../stores/notification_store'; import { mergeOptions } from '../utils/general_utils'; import { getLangText } from '../utils/lang_utils'; @@ -20,23 +21,25 @@ let HeaderNotifications = React.createClass({ getInitialState() { return mergeOptions( - PieceListStore.getState() + NotificationStore.getState() ); }, componentDidMount() { - PieceListStore.listen(this.onChange); + NotificationStore.listen(this.onChange); + NotificationActions.fetchPieceListNotifications(); + NotificationActions.fetchEditionListNotifications(); }, componentWillUnmount() { - PieceListStore.unlisten(this.onChange); + NotificationStore.unlisten(this.onChange); }, onChange(state) { this.setState(state); }, - onSelected(event) { + onMenuItemClick(event) { /* This is a hack to make the dropdown close after clicking on an item The function just need to be defined @@ -54,32 +57,87 @@ let HeaderNotifications = React.createClass({ } Internally, a call to DropdownButton.setDropDownState(false) is made which will hide the dropdown menu. So, you should be able to call that directly on the DropdownButton instance as well if needed. + + NOW, THAT DIDN'T WORK - the onSelect routine isnt triggered in all cases + Hence, we do this manually */ + this.refs.dropdownbutton.setDropdownState(false); + }, + + getPieceNotifications(){ + if (this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) { + return ( +
+
+ Artworks ({this.state.pieceListNotifications.length}) +
+ {this.state.pieceListNotifications.map((pieceNotification, i) => { + return ( + + + + ); + } + )} +
+ ); + } + return null; + }, + + getEditionNotifications(){ + if (this.state.editionListNotifications && this.state.editionListNotifications.length > 0) { + return ( +
+
+ Editions ({this.state.editionListNotifications.length}) +
+ {this.state.editionListNotifications.map((editionNotification, i) => { + return ( + + + + ); + } + )} +
+ ); + } + return null; }, render() { - if (this.state.requestActions && this.state.requestActions.length > 0) { + if ((this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) || + (this.state.editionListNotifications && this.state.editionListNotifications.length > 0)){ + let numNotifications = 0; + if (this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) { + numNotifications += this.state.pieceListNotifications.length; + } + if (this.state.editionListNotifications && this.state.editionListNotifications.length > 0) { + numNotifications += this.state.editionListNotifications.length; + } return ( ); @@ -90,33 +148,42 @@ let HeaderNotifications = React.createClass({ let NotificationListItem = React.createClass({ propTypes: { - pieceOrEdition: React.PropTypes.object + notification: React.PropTypes.array, + pieceOrEdition: React.PropTypes.object, + onClick: React.PropTypes.func + }, + + isPiece() { + return !(this.props.pieceOrEdition && this.props.pieceOrEdition.parent); }, getLinkData() { - if(this.props.pieceOrEdition && this.props.pieceOrEdition.parent) { - return { - to: 'edition', - params: { - editionId: this.props.pieceOrEdition.bitcoin_id - } - }; - } else { + if (this.isPiece()) { return { to: 'piece', params: { pieceId: this.props.pieceOrEdition.id } }; + } else { + return { + to: 'edition', + params: { + editionId: this.props.pieceOrEdition.bitcoin_id + } + }; } }, + onClick(event){ + this.props.onClick(event); + }, render() { if (this.props.pieceOrEdition) { return ( - +
@@ -127,11 +194,7 @@ let NotificationListItem = React.createClass({

{this.props.pieceOrEdition.title}

by {this.props.pieceOrEdition.artist_name}
- { - this.props.pieceOrEdition.request_action.map((requestAction) => { - return 'Pending ' + requestAction.action + ' request'; - }) - } + {'Pending ' + this.props.notification[0].action + ' request'}
diff --git a/js/components/piece_list.js b/js/components/piece_list.js index d841f7af..71304a63 100644 --- a/js/components/piece_list.js +++ b/js/components/piece_list.js @@ -65,8 +65,7 @@ let PieceList = React.createClass({ let orderBy = this.props.orderBy ? this.props.orderBy : this.state.orderBy; if (this.state.pieceList.length === 0 || this.state.page !== page){ PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, - orderBy, this.state.orderAsc, this.state.filterBy) - .then(() => PieceListActions.fetchPieceRequestActions()); + orderBy, this.state.orderAsc, this.state.filterBy); } }, diff --git a/js/constants/api_urls.js b/js/constants/api_urls.js index 6e43b8ef..f72eabdd 100644 --- a/js/constants/api_urls.js +++ b/js/constants/api_urls.js @@ -27,6 +27,8 @@ let ApiUrls = { 'note_private_piece': AppConstants.apiEndpoint + 'note/private/pieces/', 'note_public_edition': AppConstants.apiEndpoint + 'note/public/editions/', 'note_public_piece': AppConstants.apiEndpoint + 'note/public/pieces/', + 'notification_piecelist': AppConstants.apiEndpoint + 'notifications/pieces/', + 'notification_editionlist': AppConstants.apiEndpoint + 'notifications/editions/', 'ownership_contract_agreements': AppConstants.apiEndpoint + 'ownership/contract_agreements/', 'ownership_consigns': AppConstants.apiEndpoint + 'ownership/consigns/', 'ownership_consigns_confirm': AppConstants.apiEndpoint + 'ownership/consigns/confirm/', @@ -52,7 +54,6 @@ let ApiUrls = { 'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/', 'piece_first_edition_id': AppConstants.apiEndpoint + 'pieces/${piece_id}/edition_index/', 'pieces_list': AppConstants.apiEndpoint + 'pieces/', - 'pieces_list_request_actions': AppConstants.apiEndpoint + 'pieces/request_actions/', 'piece_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/pieces/${piece_id}/', 'user': AppConstants.apiEndpoint + 'users/', 'users_login': AppConstants.apiEndpoint + 'users/login/', diff --git a/js/fetchers/notification_fetcher.js b/js/fetchers/notification_fetcher.js new file mode 100644 index 00000000..c6f9be54 --- /dev/null +++ b/js/fetchers/notification_fetcher.js @@ -0,0 +1,17 @@ +'use strict'; + +import requests from '../utils/requests'; + + +let NotificationFetcher = { + + fetchPieceListNotifications() { + return requests.get('notification_piecelist'); + }, + + fetchEditionListNotifications() { + return requests.get('notification_editionlist'); + } +}; + +export default NotificationFetcher; diff --git a/js/stores/notification_store.js b/js/stores/notification_store.js new file mode 100644 index 00000000..c5401190 --- /dev/null +++ b/js/stores/notification_store.js @@ -0,0 +1,26 @@ +'use strict'; + +import React from 'react'; +import alt from '../alt'; + +import NotificationActions from '../actions/notification_actions'; + + +class NotificationStore { + constructor() { + this.pieceListNotifications = {}; + this.editionListNotifications = {}; + this.bindActions(NotificationActions); + } + + onUpdatePieceListNotifications(res) { + this.pieceListNotifications = res.notifications; + } + + onUpdateEditionListNotifications(res) { + this.editionListNotifications = res.notifications; + } + +} + +export default alt.createStore(NotificationStore, 'NotificationStore'); diff --git a/sass/ascribe_notification_list.scss b/sass/ascribe_notification_list.scss index bd3c0b20..c1d2ef65 100644 --- a/sass/ascribe_notification_list.scss +++ b/sass/ascribe_notification_list.scss @@ -2,13 +2,27 @@ $break-small: 764px; $break-medium: 991px; $break-medium: 1200px; -.notification-wrapper { +.notification-header,.notification-wrapper { width: 350px; - height:8em; - padding: 0.3em; - border-bottom: 1px solid #cccccc; - margin: -3px -20px; +} +.notification-header { + border-bottom: 1px solid #cccccc; + border-top: 1px solid #cccccc; + padding: 0.3em 1em; + background-color: #eeeeee; +} + +.notification-wrapper { + height:8.4em; + border-bottom: 1px solid #eeeeee; + margin: -3px 0; + padding: 0.5em; + color: black; + + &:hover{ + background-color: rgba(2, 182, 163, .05); + } // ToDo: Include media queries for thumbnail .thumbnail-wrapper { width: 7.4em; @@ -46,6 +60,10 @@ $break-medium: 1200px; li a { padding-top: 0; } + border-top: 0; + overflow-y:auto; + overflow-x:hidden; + max-height: 70vh; } }