mirror of
https://github.com/ascribe/onion.git
synced 2025-01-03 10:25:08 +01:00
notification app
notification refactor in onion - split by piece/edition
This commit is contained in:
parent
7f39c62130
commit
edcd8e57a4
34
js/actions/notification_actions.js
Normal file
34
js/actions/notification_actions.js
Normal file
@ -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);
|
@ -61,7 +61,6 @@ let EditionContainer = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log(this.state);
|
|
||||||
if('title' in this.state.edition) {
|
if('title' in this.state.edition) {
|
||||||
return (
|
return (
|
||||||
<Edition
|
<Edition
|
||||||
|
@ -96,6 +96,31 @@ let Header = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onMenuItemClick(event) {
|
||||||
|
/*
|
||||||
|
This is a hack to make the dropdown close after clicking on an item
|
||||||
|
The function just need to be defined
|
||||||
|
|
||||||
|
from https://github.com/react-bootstrap/react-bootstrap/issues/368:
|
||||||
|
|
||||||
|
@jvillasante - Have you tried to use onSelect with the DropdownButton?
|
||||||
|
I don't have a working example that is exactly like yours,
|
||||||
|
but I just noticed that the Dropdown closes when I've attached an event handler to OnSelect:
|
||||||
|
|
||||||
|
<DropdownButton eventKey={3} title="Admin" onSelect={ this.OnSelected } >
|
||||||
|
|
||||||
|
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() {
|
render() {
|
||||||
let account;
|
let account;
|
||||||
let signup;
|
let signup;
|
||||||
@ -103,9 +128,15 @@ let Header = React.createClass({
|
|||||||
if (this.state.currentUser.username){
|
if (this.state.currentUser.username){
|
||||||
account = (
|
account = (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
ref='dropdownbutton'
|
||||||
eventKey="1"
|
eventKey="1"
|
||||||
title={this.state.currentUser.username}>
|
title={this.state.currentUser.username}>
|
||||||
<MenuItemLink eventKey="2" to="settings">{getLangText('Account Settings')}</MenuItemLink>
|
<MenuItemLink
|
||||||
|
eventKey="2"
|
||||||
|
to="settings"
|
||||||
|
onClick={this.onMenuItemClick}>
|
||||||
|
{getLangText('Account Settings')}
|
||||||
|
</MenuItemLink>
|
||||||
<MenuItem divider />
|
<MenuItem divider />
|
||||||
<MenuItemLink eventKey="3" to="logout">{getLangText('Log out')}</MenuItemLink>
|
<MenuItemLink eventKey="3" to="logout">{getLangText('Log out')}</MenuItemLink>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
|
@ -8,7 +8,8 @@ import MenuItem from 'react-bootstrap/lib/MenuItem';
|
|||||||
|
|
||||||
import Nav from 'react-bootstrap/lib/Nav';
|
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 { mergeOptions } from '../utils/general_utils';
|
||||||
import { getLangText } from '../utils/lang_utils';
|
import { getLangText } from '../utils/lang_utils';
|
||||||
@ -20,23 +21,25 @@ let HeaderNotifications = React.createClass({
|
|||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return mergeOptions(
|
return mergeOptions(
|
||||||
PieceListStore.getState()
|
NotificationStore.getState()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
PieceListStore.listen(this.onChange);
|
NotificationStore.listen(this.onChange);
|
||||||
|
NotificationActions.fetchPieceListNotifications();
|
||||||
|
NotificationActions.fetchEditionListNotifications();
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
PieceListStore.unlisten(this.onChange);
|
NotificationStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
onChange(state) {
|
onChange(state) {
|
||||||
this.setState(state);
|
this.setState(state);
|
||||||
},
|
},
|
||||||
|
|
||||||
onSelected(event) {
|
onMenuItemClick(event) {
|
||||||
/*
|
/*
|
||||||
This is a hack to make the dropdown close after clicking on an item
|
This is a hack to make the dropdown close after clicking on an item
|
||||||
The function just need to be defined
|
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.
|
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.
|
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 (
|
||||||
|
<div>
|
||||||
|
<div className="notification-header">
|
||||||
|
Artworks ({this.state.pieceListNotifications.length})
|
||||||
|
</div>
|
||||||
|
{this.state.pieceListNotifications.map((pieceNotification, i) => {
|
||||||
|
return (
|
||||||
|
<MenuItem eventKey={i + 2}>
|
||||||
|
<NotificationListItem
|
||||||
|
ref={i}
|
||||||
|
notification={pieceNotification.notification}
|
||||||
|
pieceOrEdition={pieceNotification.piece}
|
||||||
|
onClick={this.onMenuItemClick}/>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEditionNotifications(){
|
||||||
|
if (this.state.editionListNotifications && this.state.editionListNotifications.length > 0) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="notification-header">
|
||||||
|
Editions ({this.state.editionListNotifications.length})
|
||||||
|
</div>
|
||||||
|
{this.state.editionListNotifications.map((editionNotification, i) => {
|
||||||
|
return (
|
||||||
|
<MenuItem eventKey={i + 2}>
|
||||||
|
<NotificationListItem
|
||||||
|
ref={'edition' + i}
|
||||||
|
notification={editionNotification.notification}
|
||||||
|
pieceOrEdition={editionNotification.edition}
|
||||||
|
onClick={this.onMenuItemClick}/>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
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 (
|
return (
|
||||||
<Nav navbar right>
|
<Nav navbar right>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
ref='dropdownbutton'
|
||||||
eventKey="1"
|
eventKey="1"
|
||||||
title={
|
title={
|
||||||
<span>
|
<span>
|
||||||
<Glyphicon glyph='envelope' color="green"/>
|
<Glyphicon glyph='envelope' color="green"/>
|
||||||
<span className="notification-amount">({this.state.requestActions.length})</span>
|
<span className="notification-amount">({numNotifications})</span>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
className="notification-menu"
|
className="notification-menu">
|
||||||
onSelect={this.onSelected}>
|
{this.getPieceNotifications()}
|
||||||
{this.state.requestActions.map((pieceOrEdition, i) => {
|
{this.getEditionNotifications()}
|
||||||
return (
|
|
||||||
<MenuItem eventKey={i + 2}>
|
|
||||||
<NotificationListItem
|
|
||||||
ref={i}
|
|
||||||
pieceOrEdition={pieceOrEdition}/>
|
|
||||||
</MenuItem>);
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
</Nav>
|
</Nav>
|
||||||
);
|
);
|
||||||
@ -90,33 +148,42 @@ let HeaderNotifications = React.createClass({
|
|||||||
|
|
||||||
let NotificationListItem = React.createClass({
|
let NotificationListItem = React.createClass({
|
||||||
propTypes: {
|
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() {
|
getLinkData() {
|
||||||
|
|
||||||
if(this.props.pieceOrEdition && this.props.pieceOrEdition.parent) {
|
if (this.isPiece()) {
|
||||||
return {
|
|
||||||
to: 'edition',
|
|
||||||
params: {
|
|
||||||
editionId: this.props.pieceOrEdition.bitcoin_id
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
return {
|
||||||
to: 'piece',
|
to: 'piece',
|
||||||
params: {
|
params: {
|
||||||
pieceId: this.props.pieceOrEdition.id
|
pieceId: this.props.pieceOrEdition.id
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
to: 'edition',
|
||||||
|
params: {
|
||||||
|
editionId: this.props.pieceOrEdition.bitcoin_id
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onClick(event){
|
||||||
|
this.props.onClick(event);
|
||||||
|
},
|
||||||
render() {
|
render() {
|
||||||
if (this.props.pieceOrEdition) {
|
if (this.props.pieceOrEdition) {
|
||||||
return (
|
return (
|
||||||
<Link {...this.getLinkData()}>
|
<Link {...this.getLinkData()} onClick={this.onClick}>
|
||||||
<div className="row notification-wrapper">
|
<div className="row notification-wrapper">
|
||||||
<div className="col-xs-4 clear-paddings">
|
<div className="col-xs-4 clear-paddings">
|
||||||
<div className="thumbnail-wrapper">
|
<div className="thumbnail-wrapper">
|
||||||
@ -127,11 +194,7 @@ let NotificationListItem = React.createClass({
|
|||||||
<h1>{this.props.pieceOrEdition.title}</h1>
|
<h1>{this.props.pieceOrEdition.title}</h1>
|
||||||
<div className="sub-header">by {this.props.pieceOrEdition.artist_name}</div>
|
<div className="sub-header">by {this.props.pieceOrEdition.artist_name}</div>
|
||||||
<div className="notification-action">
|
<div className="notification-action">
|
||||||
{
|
{'Pending ' + this.props.notification[0].action + ' request'}
|
||||||
this.props.pieceOrEdition.request_action.map((requestAction) => {
|
|
||||||
return 'Pending ' + requestAction.action + ' request';
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,8 +65,7 @@ let PieceList = React.createClass({
|
|||||||
let orderBy = this.props.orderBy ? this.props.orderBy : this.state.orderBy;
|
let orderBy = this.props.orderBy ? this.props.orderBy : this.state.orderBy;
|
||||||
if (this.state.pieceList.length === 0 || this.state.page !== page){
|
if (this.state.pieceList.length === 0 || this.state.page !== page){
|
||||||
PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search,
|
PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search,
|
||||||
orderBy, this.state.orderAsc, this.state.filterBy)
|
orderBy, this.state.orderAsc, this.state.filterBy);
|
||||||
.then(() => PieceListActions.fetchPieceRequestActions());
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ let ApiUrls = {
|
|||||||
'note_private_piece': AppConstants.apiEndpoint + 'note/private/pieces/',
|
'note_private_piece': AppConstants.apiEndpoint + 'note/private/pieces/',
|
||||||
'note_public_edition': AppConstants.apiEndpoint + 'note/public/editions/',
|
'note_public_edition': AppConstants.apiEndpoint + 'note/public/editions/',
|
||||||
'note_public_piece': AppConstants.apiEndpoint + 'note/public/pieces/',
|
'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_contract_agreements': AppConstants.apiEndpoint + 'ownership/contract_agreements/',
|
||||||
'ownership_consigns': AppConstants.apiEndpoint + 'ownership/consigns/',
|
'ownership_consigns': AppConstants.apiEndpoint + 'ownership/consigns/',
|
||||||
'ownership_consigns_confirm': AppConstants.apiEndpoint + 'ownership/consigns/confirm/',
|
'ownership_consigns_confirm': AppConstants.apiEndpoint + 'ownership/consigns/confirm/',
|
||||||
@ -52,7 +54,6 @@ let ApiUrls = {
|
|||||||
'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/',
|
'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/',
|
||||||
'piece_first_edition_id': AppConstants.apiEndpoint + 'pieces/${piece_id}/edition_index/',
|
'piece_first_edition_id': AppConstants.apiEndpoint + 'pieces/${piece_id}/edition_index/',
|
||||||
'pieces_list': AppConstants.apiEndpoint + 'pieces/',
|
'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}/',
|
'piece_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/pieces/${piece_id}/',
|
||||||
'user': AppConstants.apiEndpoint + 'users/',
|
'user': AppConstants.apiEndpoint + 'users/',
|
||||||
'users_login': AppConstants.apiEndpoint + 'users/login/',
|
'users_login': AppConstants.apiEndpoint + 'users/login/',
|
||||||
|
17
js/fetchers/notification_fetcher.js
Normal file
17
js/fetchers/notification_fetcher.js
Normal file
@ -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;
|
26
js/stores/notification_store.js
Normal file
26
js/stores/notification_store.js
Normal file
@ -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');
|
@ -2,13 +2,27 @@ $break-small: 764px;
|
|||||||
$break-medium: 991px;
|
$break-medium: 991px;
|
||||||
$break-medium: 1200px;
|
$break-medium: 1200px;
|
||||||
|
|
||||||
.notification-wrapper {
|
.notification-header,.notification-wrapper {
|
||||||
width: 350px;
|
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
|
// ToDo: Include media queries for thumbnail
|
||||||
.thumbnail-wrapper {
|
.thumbnail-wrapper {
|
||||||
width: 7.4em;
|
width: 7.4em;
|
||||||
@ -46,6 +60,10 @@ $break-medium: 1200px;
|
|||||||
li a {
|
li a {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
border-top: 0;
|
||||||
|
overflow-y:auto;
|
||||||
|
overflow-x:hidden;
|
||||||
|
max-height: 70vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user