add the container for the notifications dropdown

This commit is contained in:
Connor Turland 2017-09-19 23:48:46 -04:00
parent 322da431eb
commit 64ffc78f45
8 changed files with 156 additions and 28 deletions

View file

@ -13,6 +13,45 @@ $unread_notifications_dot_size: 8px;
.notificationsIcon {
position: relative;
}
.notificationsBox {
position: absolute;
background: #FFFFFF;
border-radius: 2px;
width: 350px;
right: 0;
top: 50px;
box-shadow: 0 3px 6px rgba(0,0,0,0.16);
.notificationsBoxTriangle {
min-width: 0 !important;
display: block;
position: absolute;
right: 48px;
width: 20px !important;
height: 20px !important;
margin-left: -10px;
top: -10px;
border-top: 1px solid rgba(49, 72, 67, 0.11) !important;
border-left: 1px solid rgba(49, 72, 67, 0.11) !important;
border-bottom: 0 !important;
border-right: 0 !important;
background-color: #fff;
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
}
.notificationsBoxSeeAll {
display: block;
width: 100%;
text-align: center;
padding: 6px 0;
font-family: din-regular, helvetica, sans-serif;
color: #4fb5c0;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
}
}
.controller-notifications {
@ -66,12 +105,12 @@ $unread_notifications_dot_size: 8px;
& > a {
float: left;
width: 85%;
width: 85%;
box-sizing: border-box;
padding-right: 10px;
}
.notification-actor {
.notification-actor {
float: left;
img {
@ -97,7 +136,7 @@ $unread_notifications_dot_size: 8px;
display: inline-block;
margin: 5px 0;
}
}
}
.notification-date {
position: absolute;
@ -129,7 +168,7 @@ $unread_notifications_dot_size: 8px;
}
.notificationPage {
.thirty-two-avatar {
@ -139,14 +178,14 @@ $unread_notifications_dot_size: 8px;
border-radius: 16px;
vertical-align: middle;
}
.button {
line-height: 32px;
img {
margin-top: 8px;
}
&.decline {
background: #DB5D5D;
&:hover {
@ -154,7 +193,7 @@ $unread_notifications_dot_size: 8px;
}
}
}
.notification-body {
p, div {
margin: 1em auto;

View file

@ -10,10 +10,15 @@ class NotificationsController < ApplicationController
respond_to do |format|
format.html
format.json do
render json: @notifications.map do |notification|
notifications = @notifications.map do |notification|
receipt = @receipts.find_by(notification_id: notification.id)
notification.as_json.merge(is_read: receipt.is_read)
notification.as_json.merge(
is_read: receipt.is_read,
notified_object: notification.notified_object,
sender: notification.sender
)
end
render json: notifications
end
end
end

View file

@ -0,0 +1,14 @@
const Notifications = {
notifications: null,
fetch: render => {
$.ajax({
url: '/notifications.json',
success: function(data) {
Notifications.notifications = data
render()
}
})
}
}
export default Notifications

View file

@ -8,6 +8,7 @@ import apply from 'async/apply'
import { notifyUser } from './index.js'
import ImportDialog from './ImportDialog'
import Notifications from './Notifications'
import Active from '../Active'
import DataModel from '../DataModel'
import { ExploreMaps, ChatView, TopicCard, ContextMenu } from '../Views'
@ -107,7 +108,9 @@ const ReactApp = {
mobileTitleWidth: self.mobileTitleWidth,
mobileTitleClick: (e) => Active.Map && InfoBox.toggleBox(e),
openInviteLightbox: () => self.openLightbox('invite'),
serverData: self.serverData
serverData: self.serverData,
notifications: Notifications.notifications,
fetchNotifications: apply(Notifications.fetch, ReactApp.render)
},
self.getMapProps(),
self.getTopicProps(),

View file

@ -0,0 +1,41 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import onClickOutsideAddon from 'react-onclickoutside'
class NotificationBox extends Component {
static propTypes = {
notifications: PropTypes.array,
fetchNotifications: PropTypes.func.isRequired,
toggleNotificationsBox: PropTypes.func.isRequired
}
componentDidMount = () => {
const { notifications, fetchNotifications } = this.props
if (!notifications) {
fetchNotifications()
}
}
handleClickOutside = () => {
this.props.toggleNotificationsBox()
}
render = () => {
const { notifications } = this.props
return <div className='notificationsBox'>
<div className='notificationsBoxTriangle' />
<ul>
{!notifications && <li>loading...</li>}
<li>A notification</li>
<li>A notification</li>
<li>A notification</li>
<li>A notification</li>
</ul>
<a href='/notifications' className='notificationsBoxSeeAll'>See all</a>
</div>
}
}
export default onClickOutsideAddon(NotificationBox)

View file

@ -4,18 +4,14 @@ import PropTypes from 'prop-types'
class NotificationIcon extends Component {
static propTypes = {
unreadNotificationsCount: PropTypes.number
}
constructor(props) {
super(props)
this.state = {
}
unreadNotificationsCount: PropTypes.number,
toggleNotificationsBox: PropTypes.func
}
render = () => {
const { toggleNotificationsBox } = this.props
let linkClasses = 'notificationsIcon upperRightEl upperRightIcon '
linkClasses += 'ignore-react-onclickoutside '
if (this.props.unreadNotificationsCount > 0) {
linkClasses += 'unread'
@ -24,14 +20,14 @@ class NotificationIcon extends Component {
}
return (
<a className={linkClasses} href="/notifications" target="_blank">
<div className={linkClasses} onClick={toggleNotificationsBox}>
<div className="tooltipsUnder">
Notifications
</div>
{this.props.unreadNotificationsCount === 0 ? null : (
<div className="unread-notifications-dot"></div>
)}
</a>
</div>
)
}

View file

@ -4,31 +4,51 @@ import PropTypes from 'prop-types'
import AccountMenu from './AccountMenu'
import LoginForm from './LoginForm'
import NotificationIcon from './NotificationIcon'
import NotificationBox from './NotificationBox'
class UpperRightUI extends Component {
static propTypes = {
currentUser: PropTypes.object,
signInPage: PropTypes.bool,
unreadNotificationsCount: PropTypes.number,
fetchNotifications: PropTypes.func,
notifications: PropTypes.array,
openInviteLightbox: PropTypes.func
}
constructor(props) {
super(props)
this.state = {accountBoxOpen: false}
this.state = {
accountBoxOpen: false,
notificationsBoxOpen: false
}
}
reset = () => {
this.setState({accountBoxOpen: false})
this.setState({
accountBoxOpen: false,
notificationsBoxOpen: false
})
}
toggleAccountBox = () => {
this.setState({accountBoxOpen: !this.state.accountBoxOpen})
this.setState({
accountBoxOpen: !this.state.accountBoxOpen,
notificationsBoxOpen: false
})
}
toggleNotificationsBox = () => {
this.setState({
notificationsBoxOpen: !this.state.notificationsBoxOpen,
accountBoxOpen: false
})
}
render () {
const { currentUser, signInPage, unreadNotificationsCount, openInviteLightbox } = this.props
const { accountBoxOpen } = this.state
const { currentUser, signInPage, unreadNotificationsCount,
notifications, fetchNotifications, openInviteLightbox } = this.props
const { accountBoxOpen, notificationsBoxOpen } = this.state
return <div className="upperRightUI">
{currentUser && <a href="/maps/new" target="_blank" className="addMap upperRightEl upperRightIcon">
<div className="tooltipsUnder">
@ -36,7 +56,13 @@ class UpperRightUI extends Component {
</div>
</a>}
{currentUser && <span id="notification_icon">
<NotificationIcon unreadNotificationsCount={unreadNotificationsCount} />
<NotificationIcon
unreadNotificationsCount={unreadNotificationsCount}
toggleNotificationsBox={this.toggleNotificationsBox}/>
{notificationsBoxOpen && <NotificationBox
notifications={notifications}
fetchNotifications={fetchNotifications}
toggleNotificationsBox={this.toggleNotificationsBox}/>}
</span>}
{!signInPage && <div className="sidebarAccount upperRightEl">
<div className="sidebarAccountIcon ignore-react-onclickoutside" onClick={this.toggleAccountBox}>

View file

@ -11,6 +11,8 @@ class App extends Component {
children: PropTypes.object,
toast: PropTypes.string,
unreadNotificationsCount: PropTypes.number,
notifications: PropTypes.array,
fetchNotifications: PropTypes.func,
location: PropTypes.object,
mobile: PropTypes.bool,
mobileTitle: PropTypes.string,
@ -38,7 +40,7 @@ class App extends Component {
const { children, toast, unreadNotificationsCount, openInviteLightbox,
mobile, mobileTitle, mobileTitleWidth, mobileTitleClick, location,
map, userRequested, requestAnswered, requestApproved, serverData,
onRequestAccess } = this.props
onRequestAccess, notifications, fetchNotifications } = this.props
const { pathname } = location || {}
// this fixes a bug that happens otherwise when you logout
const currentUser = this.props.currentUser && this.props.currentUser.id ? this.props.currentUser : null
@ -58,6 +60,8 @@ class App extends Component {
onRequestClick={onRequestAccess} />}
{!mobile && <UpperRightUI currentUser={currentUser}
unreadNotificationsCount={unreadNotificationsCount}
notifications={notifications}
fetchNotifications={fetchNotifications}
openInviteLightbox={openInviteLightbox}
signInPage={pathname === '/login'} />}
<Toast message={toast} />