1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-25 18:56:28 +02:00

notification app

notification refactor in onion - split by piece/edition
This commit is contained in:
diminator 2015-09-03 14:15:00 +02:00
parent 7f39c62130
commit edcd8e57a4
9 changed files with 231 additions and 43 deletions

View 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);

View File

@ -61,7 +61,6 @@ let EditionContainer = React.createClass({
},
render() {
console.log(this.state);
if('title' in this.state.edition) {
return (
<Edition

View File

@ -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() {
let account;
let signup;
@ -103,9 +128,15 @@ let Header = React.createClass({
if (this.state.currentUser.username){
account = (
<DropdownButton
ref='dropdownbutton'
eventKey="1"
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 />
<MenuItemLink eventKey="3" to="logout">{getLangText('Log out')}</MenuItemLink>
</DropdownButton>

View File

@ -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 (
<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() {
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 (
<Nav navbar right>
<DropdownButton
ref='dropdownbutton'
eventKey="1"
title={
<span>
<Glyphicon glyph='envelope' color="green"/>
<span className="notification-amount">({this.state.requestActions.length})</span>
<span className="notification-amount">({numNotifications})</span>
</span>
}
className="notification-menu"
onSelect={this.onSelected}>
{this.state.requestActions.map((pieceOrEdition, i) => {
return (
<MenuItem eventKey={i + 2}>
<NotificationListItem
ref={i}
pieceOrEdition={pieceOrEdition}/>
</MenuItem>);
}
)}
className="notification-menu">
{this.getPieceNotifications()}
{this.getEditionNotifications()}
</DropdownButton>
</Nav>
);
@ -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 (
<Link {...this.getLinkData()}>
<Link {...this.getLinkData()} onClick={this.onClick}>
<div className="row notification-wrapper">
<div className="col-xs-4 clear-paddings">
<div className="thumbnail-wrapper">
@ -127,11 +194,7 @@ let NotificationListItem = React.createClass({
<h1>{this.props.pieceOrEdition.title}</h1>
<div className="sub-header">by {this.props.pieceOrEdition.artist_name}</div>
<div className="notification-action">
{
this.props.pieceOrEdition.request_action.map((requestAction) => {
return 'Pending ' + requestAction.action + ' request';
})
}
{'Pending ' + this.props.notification[0].action + ' request'}
</div>
</div>
</div>

View File

@ -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);
}
},

View File

@ -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/',

View 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;

View 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');

View File

@ -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;
}
}