diff --git a/README.md b/README.md
index 4906a5f1..c84c65d3 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,9 @@ Checklist
- [x] Fix images referenced in the JS
- [x] Figure out how authentication of requests from the frontend to the API works
- [x] Figure out how to combine the nodejs realtime server into server.js
+- [ ] Notifications: make sure loading states are working for popup and page
+- [ ] Notifications: make sure notifications either look nice, or redirect
+- [ ] Notifications: pagination
- [ ] Get actioncable working
- [ ] Request unreadNotificationCount
- [ ] Request invite code
@@ -34,8 +37,8 @@ Checklist
- [ ] Fix Request An Invite page
- [ ] Make 'new map' action work
- [ ] Modify the remaining rails templates into JSX templates
- - [ ] notifications list
- - [ ] notification page
+ - [x] notifications list
+ - [x] notification page
- [ ] list metacodes
- [ ] new metacode
- [ ] edit metacode
diff --git a/src/Metamaps/GlobalUI/Notifications.js b/src/Metamaps/GlobalUI/Notifications.js
index 29908695..b907d00c 100644
--- a/src/Metamaps/GlobalUI/Notifications.js
+++ b/src/Metamaps/GlobalUI/Notifications.js
@@ -1,13 +1,14 @@
/* global $ */
+import { findIndex } from 'lodash'
import GlobalUI from './index'
const Notifications = {
- notifications: null,
+ notifications: [],
unreadNotificationsCount: 0,
init: serverData => {
Notifications.unreadNotificationsCount = serverData.unreadNotificationsCount
},
- fetch: render => {
+ fetchNotifications: render => {
$.ajax({
url: '/notifications.json',
success: function(data) {
@@ -16,6 +17,25 @@ const Notifications = {
}
})
},
+ fetchNotification: (render, id) => {
+ $.ajax({
+ url: `/notifications/${id}.json`,
+ success: function(data) {
+ const index = findIndex(Notifications.notifications, n => n.id === data.id)
+ if (index === -1) {
+ // notification not loaded yet, insert it at the start
+ Notifications.notifications.unshift(data)
+ } else {
+ // notification there, replace it
+ Notifications.notifications[index] = data
+ }
+ render()
+ },
+ error: function() {
+ GlobalUI.notifyUser('There was an error fetching that notification')
+ }
+ })
+ },
incrementUnread: (render) => {
Notifications.unreadNotificationsCount++
render()
@@ -54,7 +74,7 @@ const Notifications = {
}
},
error: function() {
- GlobalUI.notifyUser('There was an error marking that notification as read')
+ GlobalUI.notifyUser('There was an error marking that notification as unread')
}
})
}
diff --git a/src/Metamaps/GlobalUI/ReactApp.js b/src/Metamaps/GlobalUI/ReactApp.js
index 1b3d858f..b3dc3697 100644
--- a/src/Metamaps/GlobalUI/ReactApp.js
+++ b/src/Metamaps/GlobalUI/ReactApp.js
@@ -108,7 +108,8 @@ const ReactApp = {
openInviteLightbox: () => self.openLightbox('invite'),
serverData: self.serverData,
notifications: Notifications.notifications,
- fetchNotifications: apply(Notifications.fetch, ReactApp.render),
+ fetchNotifications: apply(Notifications.fetchNotifications, ReactApp.render),
+ fetchNotification: apply(Notifications.fetchNotification, ReactApp.render),
markAsRead: apply(Notifications.markAsRead, ReactApp.render),
markAsUnread: apply(Notifications.markAsUnread, ReactApp.render)
},
diff --git a/src/components/Notification.js b/src/components/Notification.js
index 22237aaa..a41bd307 100644
--- a/src/components/Notification.js
+++ b/src/components/Notification.js
@@ -1,6 +1,9 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
-import outdent from 'outdent'
+import { Link } from 'react-router'
+
+import NotificationDate from './NotificationDate'
+import NotificationBody from './NotificationBody'
class Notification extends Component {
static propTypes = {
@@ -26,62 +29,6 @@ class Notification extends Component {
})
}
- notificationTextHtml = () => {
- const { notification } = this.props
- let map, topic, topic1, topic2
- let result = `
${notification.actor.name}
`
-
- switch (notification.type) {
- case 'ACCESS_APPROVED':
- map = notification.data.map
- result += outdent`granted your request to edit map
- ${map.name}`
- break
- case 'ACCESS_REQUEST':
- map = notification.data.map
- result += outdent`wants permission to map with you on
- ${map.name}`
- if (!notification.data.object.answered) {
- result += '
mark read
@@ -117,7 +64,7 @@ class Notification extends Component {
}
- {this.getDate()}
+
diff --git a/src/components/NotificationBody.js b/src/components/NotificationBody.js
new file mode 100644
index 00000000..0fb17735
--- /dev/null
+++ b/src/components/NotificationBody.js
@@ -0,0 +1,70 @@
+import React, { Component } from 'react'
+import outdent from 'outdent'
+
+import {
+ MAP_ACCESS_REQUEST,
+ MAP_ACCESS_APPROVED,
+ MAP_INVITE_TO_EDIT,
+ TOPIC_ADDED_TO_MAP,
+ TOPIC_CONNECTED_1,
+ TOPIC_CONNECTED_2,
+ MESSAGE_FROM_DEVS
+} from '../constants'
+
+class NotificationBody extends Component {
+ notificationTextHtml = () => {
+ const { notification } = this.props
+ let map, topic, topic1, topic2
+ let result = `
${notification.actor.name}
`
+
+ switch (notification.type) {
+ case MAP_ACCESS_APPROVED:
+ map = notification.data.map
+ result += outdent`granted your request to edit map
+ ${map.name}`
+ break
+ case MAP_ACCESS_REQUEST:
+ map = notification.data.map
+ result += outdent`wants permission to map with you on
+ ${map.name}`
+ if (!notification.data.object.answered) {
+ result += '
Offer a response
'
+ }
+ break
+ case MAP_INVITE_TO_EDIT:
+ map = notification.data.map
+ result += outdent`gave you edit access to map
+ ${map.name}`
+ break
+ case TOPIC_ADDED_TO_MAP:
+ map = notification.data.map
+ topic = notification.data.topic
+ result += outdent`added topic ${topic.name}
+ to map ${map.name}`
+ break
+ case TOPIC_CONNECTED_1:
+ topic1 = notification.data.topic1
+ topic2 = notification.data.topic2
+ result += outdent`connected ${topic1.name}
+ to ${topic2.name}`
+ break
+ case TOPIC_CONNECTED_2:
+ topic1 = notification.data.topic1
+ topic2 = notification.data.topic2
+ result += outdent`connected ${topic2.name}
+ to ${topic1.name}`
+ break
+ case MESSAGE_FROM_DEVS:
+ result += notification.subject
+ }
+ return {__html: result}
+ }
+
+ render = () => {
+ return (
+
+ )
+ }
+}
+
+export default NotificationBody
\ No newline at end of file
diff --git a/src/components/NotificationBox.js b/src/components/NotificationBox.js
index 0c144b4e..b2315cee 100644
--- a/src/components/NotificationBox.js
+++ b/src/components/NotificationBox.js
@@ -16,10 +16,7 @@ class NotificationBox extends Component {
}
componentDidMount = () => {
- const { notifications, fetchNotifications } = this.props
- if (!notifications) {
- fetchNotifications()
- }
+ this.props.fetchNotifications()
}
handleClickOutside = () => {
@@ -28,7 +25,7 @@ class NotificationBox extends Component {
hasSomeNotifications = () => {
const { notifications } = this.props
- return notifications && notifications.length > 0
+ return notifications.length > 0
}
showLoading = () => {
@@ -51,10 +48,11 @@ class NotificationBox extends Component {
n =>
+ key={`notification-${n.id}`}
+ onClick={() => this.props.toggleNotificationsBox()} />
).concat([
-
+ this.props.toggleNotificationsBox()}>
See all
diff --git a/src/components/NotificationDate.js b/src/components/NotificationDate.js
new file mode 100644
index 00000000..b84adf7a
--- /dev/null
+++ b/src/components/NotificationDate.js
@@ -0,0 +1,13 @@
+import React, { Component } from 'react'
+
+class NotificationDate extends Component {
+ render = () => {
+ const { date } = this.props
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+ const created = new Date(date)
+ return {months[created.getMonth()]} {created.getDate()}
+ }
+}
+
+export default NotificationDate
\ No newline at end of file
diff --git a/src/constants.js b/src/constants.js
new file mode 100644
index 00000000..ead8b055
--- /dev/null
+++ b/src/constants.js
@@ -0,0 +1,7 @@
+export const MAP_ACCESS_REQUEST = 'ACCESS_REQUEST'
+export const MAP_ACCESS_APPROVED = 'ACCESS_APPROVED'
+export const MAP_INVITE_TO_EDIT = 'INVITE_TO_EDIT'
+export const TOPIC_ADDED_TO_MAP = 'TOPIC_ADDED_TO_MAP'
+export const TOPIC_CONNECTED_1 = 'TOPIC_CONNECTED_1'
+export const TOPIC_CONNECTED_2 = 'TOPIC_CONNECTED_2'
+export const MESSAGE_FROM_DEVS = 'MESSAGE_FROM_DEVS'
\ No newline at end of file
diff --git a/src/routes/Notifications/NotificationPage.js b/src/routes/Notifications/NotificationPage.js
index f55aed3f..d35c6417 100644
--- a/src/routes/Notifications/NotificationPage.js
+++ b/src/routes/Notifications/NotificationPage.js
@@ -1,13 +1,89 @@
import React, { Component } from 'react'
import { Link } from 'react-router'
+import { MAP_ACCESS_REQUEST } from '../../constants'
import NotificationsHeader from './NotificationsHeader'
+import Loading from '../../components/Loading'
+import NotificationBody from '../../components/NotificationBody'
+
+/* TODO:
+ allow / decline access loading states
+ make backend serve HTML for raw body too
+*/
class NotificationPage extends Component {
+ componentDidMount() {
+ // the notification id
+ const id = parseInt(this.props.params.id, 10)
+ if (!this.props.notifications.find(n => n.id === id)) {
+ this.props.fetchNotification(id)
+ }
+ }
render = () => {
- return
+ const id = parseInt(this.props.params.id, 10)
+ const notification = this.props.notifications.find(n => n.id === id)
+ if (!notification) {
+ return (
+
+ {request.answered &&
+ {request.approved && You already responded to this access request, and allowed access.}
+ {!request.approved && You already responded to this access request, and declined access. If you changed your mind, you can still grant
+ them access by going to the map and adding them as a collaborator.}
+ }
+ {!request.answered &&
+
+ Allow
+ Decline
+ }
+
- { link_to 'Back to notifications', notifications_path }
-
-
-
- { case @notification.notification_code
- when MAP_ACCESS_REQUEST
- request = @notification.notified_object
- map = request.map }
- { image_tag @notification.sender.image(:thirtytwo), className: 'thirty-two-avatar' } { request.user.name } wants to collaborate on map { map.name }
- { else }
- { @notification.subject }
- { end }
-
- { case @notification.notification_code
- when MAP_ACCESS_REQUEST }
-
-
- { if false && request.answered }
- { if request.approved }
- You already responded to this access request, and allowed access.
- { elsif !request.approved }
- You already responded to this access request, and declined access. If you changed your mind, you can still grant
- them access by going to the map and adding them as a collaborator.
- { end }
- { else }
- { image_tag asset_path('ellipsis.gif'), className: 'hidden' }
- { link_to 'Allow', approve_access_post_map_path(id: map.id, request_id: request.id), remote: true, method: :post, className: 'button allow' }
- { link_to 'Decline', deny_access_post_map_path(id: map.id, request_id: request.id), remote: true, method: :post, className: 'button decline' }
-
- { end }
-