improve styling, fix index notifs page
This commit is contained in:
parent
fc8ac6eef1
commit
277644f59d
10 changed files with 121 additions and 22 deletions
|
@ -1,4 +1,6 @@
|
||||||
|
$notifications-border-color: #DDDDDD;
|
||||||
$unread_notifications_dot_size: 8px;
|
$unread_notifications_dot_size: 8px;
|
||||||
|
|
||||||
.unread-notifications-dot {
|
.unread-notifications-dot {
|
||||||
width: $unread_notifications_dot_size;
|
width: $unread_notifications_dot_size;
|
||||||
height: $unread_notifications_dot_size;
|
height: $unread_notifications_dot_size;
|
||||||
|
@ -22,6 +24,7 @@ $unread_notifications_dot_size: 8px;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
box-shadow: 0 3px 6px rgba(0,0,0,0.16);
|
box-shadow: 0 3px 6px rgba(0,0,0,0.16);
|
||||||
|
border: 1px solid $notifications-border-color;
|
||||||
|
|
||||||
.notificationsBoxTriangle {
|
.notificationsBoxTriangle {
|
||||||
min-width: 0 !important;
|
min-width: 0 !important;
|
||||||
|
@ -31,9 +34,9 @@ $unread_notifications_dot_size: 8px;
|
||||||
width: 20px !important;
|
width: 20px !important;
|
||||||
height: 20px !important;
|
height: 20px !important;
|
||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
top: -10px;
|
top: -11px;
|
||||||
border-top: 1px solid rgba(49, 72, 67, 0.11) !important;
|
border-left: 1px solid $notifications-border-color;
|
||||||
border-left: 1px solid rgba(49, 72, 67, 0.11) !important;
|
border-top: 1px solid $notifications-border-color;
|
||||||
border-bottom: 0 !important;
|
border-bottom: 0 !important;
|
||||||
border-right: 0 !important;
|
border-right: 0 !important;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
@ -42,6 +45,11 @@ $unread_notifications_dot_size: 8px;
|
||||||
-ms-transform: rotate(45deg);
|
-ms-transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.notifications {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.notificationsBoxSeeAll {
|
.notificationsBoxSeeAll {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -51,6 +59,14 @@ $unread_notifications_dot_size: 8px;
|
||||||
color: #4fb5c0;
|
color: #4fb5c0;
|
||||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
.notification-body {
|
||||||
|
border-bottom: 1px solid $notifications-border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,11 +137,18 @@ $unread_notifications_dot_size: 8px;
|
||||||
|
|
||||||
ul.notifications {
|
ul.notifications {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
|
li:last-child {
|
||||||
|
.notification-body {
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
padding: 10px;
|
padding: 10px 10px 0 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
font-family: 'din-regular', Sans-Serif;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #F6F6F6;
|
background: #F6F6F6;
|
||||||
|
@ -157,9 +180,9 @@ ul.notifications {
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-body {
|
.notification-body {
|
||||||
font-family: 'din-regular', Sans-Serif;
|
|
||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
.in-bold {
|
.in-bold {
|
||||||
font-family: 'din-medium', Sans-Serif;
|
font-family: 'din-medium', Sans-Serif;
|
||||||
|
@ -180,8 +203,6 @@ ul.notifications {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
color: #607d8b;
|
color: #607d8b;
|
||||||
font-size: 13px;
|
|
||||||
line-height: 13px;
|
|
||||||
margin-top: -6px;
|
margin-top: -6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,11 +211,12 @@ ul.notifications {
|
||||||
float: left;
|
float: left;
|
||||||
width: 15%;
|
width: 15%;
|
||||||
|
|
||||||
div {
|
a, div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ class NotificationsController < ApplicationController
|
||||||
def mark_read
|
def mark_read
|
||||||
@receipt.update(is_read: true)
|
@receipt.update(is_read: true)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
format.js
|
||||||
format.json do
|
format.json do
|
||||||
render json: NotificationDecorator.decorate(@notification, @receipt)
|
render json: NotificationDecorator.decorate(@notification, @receipt)
|
||||||
end
|
end
|
||||||
|
@ -52,6 +53,7 @@ class NotificationsController < ApplicationController
|
||||||
def mark_unread
|
def mark_unread
|
||||||
@receipt.update(is_read: false)
|
@receipt.update(is_read: false)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
format.js
|
||||||
format.json do
|
format.json do
|
||||||
render json: NotificationDecorator.decorate(@notification, @receipt)
|
render json: NotificationDecorator.decorate(@notification, @receipt)
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,7 +38,7 @@ class NotificationDecorator
|
||||||
id: topic1&.id,
|
id: topic1&.id,
|
||||||
name: topic1&.name
|
name: topic1&.name
|
||||||
}
|
}
|
||||||
resul[:data][:topic2] = {
|
result[:data][:topic2] = {
|
||||||
id: topic2&.id,
|
id: topic2&.id,
|
||||||
name: topic2&.name
|
name: topic2&.name
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</header>
|
</header>
|
||||||
<ul class="notifications">
|
<ul class="notifications">
|
||||||
<% blacklist = [MAP_ACCESS_REQUEST, MAP_ACCESS_APPROVED, MAP_INVITE_TO_EDIT] %>
|
<% blacklist = [MAP_ACCESS_REQUEST, MAP_ACCESS_APPROVED, MAP_INVITE_TO_EDIT] %>
|
||||||
<% notifications = @notifications.to_a.delete_if{|n| blacklist.include?(n.notification_code) && (n.notified_object.nil? || n.notified_object.map.nil?) }%>
|
<% notifications = @notifications.to_a.delete_if{|n| blacklist.include?(n.notification_code) && (n.notified_object.nil? || n.notified_object.map.nil?) }%>
|
||||||
<% notifications.each do |notification| %>
|
<% notifications.each do |notification| %>
|
||||||
<% receipt = @receipts.find_by(notification_id: notification.id) %>
|
<% receipt = @receipts.find_by(notification_id: notification.id) %>
|
||||||
<li class="notification <%= receipt.is_read? ? 'read' : 'unread' %>" id="notification-<%= notification.id %>">
|
<li class="notification <%= receipt.is_read? ? 'read' : 'unread' %>" id="notification-<%= notification.id %>">
|
||||||
|
|
7
app/views/notifications/mark_read.js.erb
Normal file
7
app/views/notifications/mark_read.js.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
$('#notification-<%= @notification.id %> .notification-read-unread > a')
|
||||||
|
.text('mark as unread')
|
||||||
|
.attr('href', '<%= mark_unread_notification_path(@notification.id) %>')
|
||||||
|
$('#notification-<%= @notification.id %>')
|
||||||
|
.removeClass('unread')
|
||||||
|
.addClass('read')
|
||||||
|
Metamaps.GlobalUI.Notifications.decrementUnread(Metamaps.GlobalUI.ReactApp.render)
|
7
app/views/notifications/mark_unread.js.erb
Normal file
7
app/views/notifications/mark_unread.js.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
$('#notification-<%= @notification.id %> .notification-read-unread > a')
|
||||||
|
.text('mark as read')
|
||||||
|
.attr('href', '<%= mark_read_notification_path(@notification.id) %>')
|
||||||
|
$('#notification-<%= @notification.id %>')
|
||||||
|
.removeClass('read')
|
||||||
|
.addClass('unread')
|
||||||
|
Metamaps.GlobalUI.Notifications.incrementUnread(Metamaps.GlobalUI.ReactApp.render)
|
|
@ -15,14 +15,22 @@ const Notifications = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
incrementUnread: (render) => {
|
||||||
|
Notifications.unreadNotificationsCount++
|
||||||
|
render()
|
||||||
|
},
|
||||||
|
decrementUnread: (render) => {
|
||||||
|
Notifications.unreadNotificationsCount--
|
||||||
|
render()
|
||||||
|
},
|
||||||
markAsRead: (render, id) => {
|
markAsRead: (render, id) => {
|
||||||
const n = Notifications.notifications.find(n => n.id === id)
|
const n = Notifications.notifications.find(n => n.id === id)
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: `/notifications/${id}/mark_read`,
|
url: `/notifications/${id}/mark_read.json`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
success: function(r) {
|
success: function(r) {
|
||||||
if (n) {
|
if (n) {
|
||||||
Notifications.unreadNotificationsCount--
|
|
||||||
n.is_read = true
|
n.is_read = true
|
||||||
render()
|
render()
|
||||||
}
|
}
|
||||||
|
@ -35,7 +43,7 @@ const Notifications = {
|
||||||
markAsUnread: (render, id) => {
|
markAsUnread: (render, id) => {
|
||||||
const n = Notifications.notifications.find(n => n.id === id)
|
const n = Notifications.notifications.find(n => n.id === id)
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: `/notifications/${id}/mark_unread`,
|
url: `/notifications/${id}/mark_unread.json`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
success: function() {
|
success: function() {
|
||||||
if (n) {
|
if (n) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ const GlobalUI = {
|
||||||
init: function(serverData) {
|
init: function(serverData) {
|
||||||
const self = GlobalUI
|
const self = GlobalUI
|
||||||
|
|
||||||
|
self.Notifications.init(serverData)
|
||||||
self.ReactApp.init(serverData, self.openLightbox)
|
self.ReactApp.init(serverData, self.openLightbox)
|
||||||
self.CreateMap.init(serverData)
|
self.CreateMap.init(serverData)
|
||||||
self.ImportDialog.init(serverData, self.openLightbox, self.closeLightbox)
|
self.ImportDialog.init(serverData, self.openLightbox, self.closeLightbox)
|
||||||
|
|
|
@ -31,7 +31,7 @@ class NotificationBox extends Component {
|
||||||
<div className='notificationsBoxTriangle' />
|
<div className='notificationsBoxTriangle' />
|
||||||
<ul className='notifications'>
|
<ul className='notifications'>
|
||||||
{!notifications && <li>loading...</li>}
|
{!notifications && <li>loading...</li>}
|
||||||
{notifications && notifications.map(n => {
|
{notifications && notifications.slice(0, 10).map(n => {
|
||||||
return <Notification notification={n}
|
return <Notification notification={n}
|
||||||
markAsRead={markAsRead}
|
markAsRead={markAsRead}
|
||||||
markAsUnread={markAsUnread}
|
markAsUnread={markAsUnread}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import outdent from 'outdent'
|
||||||
|
|
||||||
class Notification extends Component {
|
class Notification extends Component {
|
||||||
|
|
||||||
|
@ -26,10 +27,58 @@ class Notification extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notificationTextHtml = () => {
|
||||||
|
const { notification } = this.props
|
||||||
|
let map, topic, topic1, topic2
|
||||||
|
let result = `<div class='in-bold'>${notification.actor.name}</div>`
|
||||||
|
|
||||||
|
switch(notification.type) {
|
||||||
|
case 'ACCESS_APPROVED':
|
||||||
|
map = notification.data.map
|
||||||
|
result += outdent`granted your request to edit map
|
||||||
|
<span class='in-bold'>${map.name}</span>`
|
||||||
|
break
|
||||||
|
case 'ACCESS_REQUEST':
|
||||||
|
map = notification.data.map
|
||||||
|
result += outdent`wants permission to map with you on
|
||||||
|
<span class='in-bold'>${map.name}</span>`
|
||||||
|
if (!notification.data.object.answered) {
|
||||||
|
result += '<br /><div class="action">Offer a response</div>'
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'INVITE_TO_EDIT':
|
||||||
|
map = notification.data.map
|
||||||
|
result += outdent`gave you edit access to map
|
||||||
|
<span class='in-bold'>${map.name}</span>`
|
||||||
|
break
|
||||||
|
case 'TOPIC_ADDED_TO_MAP':
|
||||||
|
map = notification.data.map
|
||||||
|
topic = notification.data.topic
|
||||||
|
result += outdent`added topic <span class='in-bold'>${topic.name}</span>
|
||||||
|
to map <span class='in-bold'>${map.name}</span>`
|
||||||
|
break
|
||||||
|
case 'TOPIC_CONNECTED_1':
|
||||||
|
topic1 = notification.data.topic1
|
||||||
|
topic2 = notification.data.topic2
|
||||||
|
result += outdent`connected <span class='in-bold'>${topic1.name}</span>
|
||||||
|
to <span class='in-bold'>${topic2.name}</span>`
|
||||||
|
break
|
||||||
|
case 'TOPIC_CONNECTED_2':
|
||||||
|
topic1 = notification.data.topic1
|
||||||
|
topic2 = notification.data.topic2
|
||||||
|
result += outdent`connected <span class='in-bold'>${topic2.name}</span>
|
||||||
|
to <span class='in-bold'>${topic1.name}</span>`
|
||||||
|
break
|
||||||
|
case 'MESSAGE_FROM_DEVS':
|
||||||
|
result += notification.subject
|
||||||
|
}
|
||||||
|
return {__html: result}
|
||||||
|
}
|
||||||
|
|
||||||
getDate = () => {
|
getDate = () => {
|
||||||
const { notification: {created_at} } = this.props
|
const { notification: {created_at} } = this.props
|
||||||
const months = ['Jan','Feb','Mar','Apr','May','Jun',
|
const months = ['Jan','Feb','Mar','Apr','May','Jun',
|
||||||
'Jul','Aug','Sep','Oct','Nov','Dec']
|
'Jul','Aug','Sep','Oct','Nov','Dec']
|
||||||
const created = new Date(created_at)
|
const created = new Date(created_at)
|
||||||
return `${months[created.getMonth()]} ${created.getDate()}`
|
return `${months[created.getMonth()]} ${created.getDate()}`
|
||||||
}
|
}
|
||||||
|
@ -47,17 +96,20 @@ class Notification extends Component {
|
||||||
render = () => {
|
render = () => {
|
||||||
const { notification } = this.props
|
const { notification } = this.props
|
||||||
const classes = `notification ${notification.is_read ? 'read' : 'unread'}`
|
const classes = `notification ${notification.is_read ? 'read' : 'unread'}`
|
||||||
|
|
||||||
|
if (!notification.data.object) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return <li className={classes}>
|
return <li className={classes}>
|
||||||
<a href={`/notifications/${notification.id}`}>
|
<a href={`/notifications/${notification.id}`}>
|
||||||
<div className='notification-actor'>
|
<div className='notification-actor'>
|
||||||
<img src={notification.actor.image} />
|
<img src={notification.actor.image} />
|
||||||
</div>
|
</div>
|
||||||
<div className="notification-body">
|
<div className='notification-body'
|
||||||
<div className="in-bold">{notification.actor.name}</div>
|
dangerouslySetInnerHTML={this.notificationTextHtml()} />
|
||||||
Other content
|
|
||||||
</div>
|
|
||||||
</a>
|
</a>
|
||||||
<div className="notification-read-unread">
|
<div className='notification-read-unread'>
|
||||||
{!notification.is_read && <div onClick={this.markAsRead}>
|
{!notification.is_read && <div onClick={this.markAsRead}>
|
||||||
mark read
|
mark read
|
||||||
</div>}
|
</div>}
|
||||||
|
@ -65,10 +117,10 @@ class Notification extends Component {
|
||||||
mark unread
|
mark unread
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className="notification-date">
|
<div className='notification-date'>
|
||||||
{this.getDate()}
|
{this.getDate()}
|
||||||
</div>
|
</div>
|
||||||
<div className="clearfloat"></div>
|
<div className='clearfloat'></div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue