From 705cd9da2d67b1c686f8d51d181df1a997a34bb6 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 4 Feb 2021 08:03:40 +0100 Subject: [PATCH 01/46] Added Safari registration --- .env.development | 6 +++--- package.json | 2 +- src/components/devices/AddDevice.js | 19 ++++++++++++++++--- src/utils/user-agent.js | 14 ++++++++++++-- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.env.development b/.env.development index b22fa355..5bfa60f8 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,4 @@ -REACT_APP_BASE_URL=https://localhost:8080 -REACT_APP_OAUTH_REDIRECT_URL=https://localhost:3000/redirect -REACT_APP_OAUTH_LOGOUT_URL=https://localhost:3000/logout +REACT_APP_BASE_URL=https://192.168.0.168:8080 +REACT_APP_OAUTH_REDIRECT_URL=https://192.168.0.168:3000/redirect +REACT_APP_OAUTH_LOGOUT_URL=https://192.168.0.168:3000/logout REACT_APP_NODE_TLS_REJECT_UNAUTHORIZED=0 diff --git a/package.json b/package.json index da99fe68..ef99f575 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "scripts": { "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/", "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive", - "start-js": "react-scripts start", + "start-js": "HOST=0.0.0.0 react-scripts start", "start": "HTTPS=true npm-run-all -p watch-css start-js", "build-js": "react-scripts build", "build": "npm-run-all build-css build-js", diff --git a/src/components/devices/AddDevice.js b/src/components/devices/AddDevice.js index 07253f2f..5107cd26 100644 --- a/src/components/devices/AddDevice.js +++ b/src/components/devices/AddDevice.js @@ -6,7 +6,8 @@ import {Form, Modal, Button, Label, Message, Segment} from 'semantic-ui-react'; import * as deviceActions from 'actions/devices'; import * as showSnackBarActionCreators from '../../common/actions/Snackbar'; import ServiceWorkerRegistration from './ServiceWorkerRegistration'; -import getClientInformation from '../../utils/user-agent'; +import SafariRegistration from './SafariRegistration'; +import {getClientInformation, isSafari} from '../../utils/user-agent'; import './AddDevice.scss'; const clientInformation = getClientInformation(); @@ -14,12 +15,23 @@ const clientInformation = getClientInformation(); // Full list // const deviceTypesEnum = ['BROWSER', 'ANDROID', 'IOS', 'WINDOWS', 'LINUX', 'MAC', 'MAIL']; // Current supported list -const deviceTypesEnum = ['BROWSER', 'WINDOWS', 'MAIL']; +const deviceTypesEnum = ['BROWSER', 'APP', 'MAIL']; +const deviceSubTypesEnum = [ + 'SAFARI', + 'OTHER', + 'IOS', + 'ANDROID', + 'WINDOWS', + 'LINUX', + 'MAC', + 'PRIMARY', +]; const AddDevice = ({createDevice, showSnackbar, loading}) => { const [deviceName, setDeviceName] = useState(clientInformation); const [deviceInfo, setDeviceInfo] = useState(navigator.userAgent); const [deviceType, setDeviceType] = useState('BROWSER'); + const [deviceSubType, setDeviceSubType] = useState(isSafari() ? 'SAFARI' : 'OTHER'); const [deviceToken, setDeviceToken] = useState(''); const [modalOpen, setModalOpen] = useState(false); @@ -29,7 +41,8 @@ const AddDevice = ({createDevice, showSnackbar, loading}) => { const resetFormValues = () => { setDeviceName(clientInformation); // initialize with platform name, let user edit for easy remembering setDeviceInfo(navigator.userAgent); - setDeviceType('BROWSER'); // Browser only for now. + setDeviceType('BROWSER'); + setDeviceSubType(isSafari() ? 'SAFARI' : 'OTHER'); setDeviceToken(''); setModalOpen(false); }; diff --git a/src/utils/user-agent.js b/src/utils/user-agent.js index 5ec02a08..7e19ce3f 100644 --- a/src/utils/user-agent.js +++ b/src/utils/user-agent.js @@ -1,7 +1,7 @@ const os = ['Win', 'Linux', 'Mac', 'Android', 'iPhone']; // add OS values const br = ['Chrome', 'Firefox', 'MSIE', 'Edge', 'Safari', 'Opera']; // add browser values -function getClientInformation() { +export function getClientInformation() { return ( (os.find(v => navigator.platform.indexOf(v) >= 0) || 'Other') + ' ' + @@ -9,4 +9,14 @@ function getClientInformation() { ); } -export default getClientInformation; +export function isSafari() { + return ( + navigator.vendor && + navigator.vendor.indexOf('Apple') > -1 && + navigator.userAgent && + navigator.userAgent.indexOf('CriOS') === -1 && + navigator.userAgent.indexOf('FxiOS') === -1 + ); +} + +//export default getClientInformation; -- GitLab From fe0636ed272c5a24d237d8032a0cc8df82cb80d5 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 4 Feb 2021 08:04:14 +0100 Subject: [PATCH 02/46] Added Safari registration --- src/components/devices/SafariRegistration.js | 103 +++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/components/devices/SafariRegistration.js diff --git a/src/components/devices/SafariRegistration.js b/src/components/devices/SafariRegistration.js new file mode 100644 index 00000000..8a310b47 --- /dev/null +++ b/src/components/devices/SafariRegistration.js @@ -0,0 +1,103 @@ +import React, {useState, useEffect} from 'react'; +import {Form, Message, Checkbox, Popup} from 'semantic-ui-react'; + +function SafariRegistration({onSubscription, onLoadStatus}) { + const [status, setStatus] = useState([]); + const [errormsg, setErrormsg] = useState([]); + const [isEnabled, setIsEnabled] = useState(false); + + useEffect(() => { + // Ensure that the user can receive Safari Push Notifications. + if ('safari' in window && 'pushNotification' in window.safari) { + var permissionData = window.safari.pushNotification.permission( + process.env.REACT_APP_APPLE_WEBSITEPUSHID + ); + if (permissionData.permission === 'granted') { + setIsEnabled(true); + onLoadStatus(true); + onSubscription(permissionData.deviceToken); + setStatus(prevState => [...prevState, 'Registration is already active.']); + } else if (permissionData.permission === 'denied') { + onLoadStatus(false); + setErrormsg(prevState => [ + ...prevState, + 'Permission for notifications on this site is denied. Check your preferences.', + ]); + } else onLoadStatus(false); + } else { + setErrormsg(prevState => [...prevState, 'Not Safari or no push support in this version !']); + onLoadStatus(false); + } + }, [onLoadStatus, onSubscription]); + + function checkRemotePermission(permissionData) { + if (permissionData.permission === 'default') { + window.safari.pushNotification.requestPermission( + process.env.REACT_APP_APPLE_WEBSERVICEURL, // The web service URL. + process.env.REACT_APP_APPLE_WEBSITEPUSHID, // The Website Push ID. + {anonid: 'anonid-123456-anonid'}, // Data that you choose to send to your server to help you identify the user. + checkRemotePermission // The callback function. + ); + } else if (permissionData.permission === 'denied') { + // The user said no. + setErrormsg(prevState => [ + ...prevState, + 'Permission for notifications on this site is denied. Check your preferences.', + ]); + } else if (permissionData.permission === 'granted') { + // The web service URL is a valid push provider, and the user said yes. + // permissionData.deviceToken is available to use. + setIsEnabled(true); + setStatus(prevState => [...prevState, 'Registration completed.']); + onSubscription(permissionData.deviceToken); + } + } + + function registerSafari() { + // console.log('Service worker is active.'); + // setStatus(prevState => [...prevState, 'Service worker is active.']); + // setErrormsg(prevState => [...prevState, 'No notification permission granted!']); + checkRemotePermission(window.safari.pushNotification.permission('web.ch.cern.notifications')); + } + + function unRegisterSafari() { + setErrormsg(prevState => [ + ...prevState, + 'To remove registration, open Safari Preferences / Notifications, and Remove CERN Notification.', + ]); + } + + function toggleRegistration() { + if (isEnabled) unRegisterSafari(); + else registerSafari(); + } + + return ( + <Form.Field> + <label>Register for Push Notifications</label> + <Popup + trigger={<Checkbox checked={isEnabled} onChange={toggleRegistration} toggle />} + content="Enable Push Notifications" + position="right center" + /> + {status && status.length > 0 && ( + <Message size="tiny"> + <Message.Header>Status</Message.Header> + {status.map((str, i) => ( + <div key={i}>{str} </div> + ))} + </Message> + )} + {errormsg && errormsg.length > 0 && ( + <Message negative size="tiny"> + <Message.Header>Error</Message.Header> + {errormsg.map((str, i) => ( + <div key={i}>{str} </div> + ))} + </Message> + )} + </Form.Field> + ); +} + +export default SafariRegistration; -- GitLab From fa6e4bb6cb009839474aa17454246c2ddc2c0b53 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 4 Feb 2021 16:47:34 +0100 Subject: [PATCH 03/46] Added uuid for device and safari ref --- .env.development | 6 ++--- package-lock.json | 28 +++++++++++++++++--- package.json | 3 ++- src/components/devices/AddDevice.js | 2 ++ src/components/devices/SafariRegistration.js | 5 ++-- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/.env.development b/.env.development index 5bfa60f8..b22fa355 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,4 @@ -REACT_APP_BASE_URL=https://192.168.0.168:8080 -REACT_APP_OAUTH_REDIRECT_URL=https://192.168.0.168:3000/redirect -REACT_APP_OAUTH_LOGOUT_URL=https://192.168.0.168:3000/logout +REACT_APP_BASE_URL=https://localhost:8080 +REACT_APP_OAUTH_REDIRECT_URL=https://localhost:3000/redirect +REACT_APP_OAUTH_LOGOUT_URL=https://localhost:3000/logout REACT_APP_NODE_TLS_REJECT_UNAUTHORIZED=0 diff --git a/package-lock.json b/package-lock.json index a2cf62c1..27e10c50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12723,7 +12723,8 @@ "prettier": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==" + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true }, "prettier-linter-helpers": { "version": "1.0.0", @@ -13921,6 +13922,11 @@ "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -14975,6 +14981,13 @@ "faye-websocket": "^0.10.0", "uuid": "^3.4.0", "websocket-driver": "0.6.5" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } } }, "sockjs-client": { @@ -16393,9 +16406,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.1.1", @@ -17039,6 +17052,13 @@ "requires": { "ansi-colors": "^3.0.0", "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } } }, "webpack-manifest-plugin": { diff --git a/package.json b/package.json index ef99f575..01d4134d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "redux-thunk": "^2.3.0", "register-service-worker": "^1.7.2", "semantic-ui-react": "^2.0.0", - "serve": "^11.3.0" + "serve": "^11.3.0", + "uuid": "^8.3.2" }, "scripts": { "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/", diff --git a/src/components/devices/AddDevice.js b/src/components/devices/AddDevice.js index 5107cd26..5c88e73e 100644 --- a/src/components/devices/AddDevice.js +++ b/src/components/devices/AddDevice.js @@ -32,6 +32,7 @@ const AddDevice = ({createDevice, showSnackbar, loading}) => { const [deviceInfo, setDeviceInfo] = useState(navigator.userAgent); const [deviceType, setDeviceType] = useState('BROWSER'); const [deviceSubType, setDeviceSubType] = useState(isSafari() ? 'SAFARI' : 'OTHER'); + const [deviceUuid, setdeviceUuid] = useState(uuidv4()); const [deviceToken, setDeviceToken] = useState(''); const [modalOpen, setModalOpen] = useState(false); @@ -43,6 +44,7 @@ const AddDevice = ({createDevice, showSnackbar, loading}) => { setDeviceInfo(navigator.userAgent); setDeviceType('BROWSER'); setDeviceSubType(isSafari() ? 'SAFARI' : 'OTHER'); + setdeviceUuid(uuidv4()); setDeviceToken(''); setModalOpen(false); }; diff --git a/src/components/devices/SafariRegistration.js b/src/components/devices/SafariRegistration.js index 8a310b47..6de7b8e6 100644 --- a/src/components/devices/SafariRegistration.js +++ b/src/components/devices/SafariRegistration.js @@ -1,7 +1,7 @@ import React, {useState, useEffect} from 'react'; import {Form, Message, Checkbox, Popup} from 'semantic-ui-react'; -function SafariRegistration({onSubscription, onLoadStatus}) { +function SafariRegistration({onSubscription, onLoadStatus, deviceUuid}) { const [status, setStatus] = useState([]); const [errormsg, setErrormsg] = useState([]); const [isEnabled, setIsEnabled] = useState(false); @@ -35,7 +35,8 @@ function SafariRegistration({onSubscription, onLoadStatus}) { window.safari.pushNotification.requestPermission( process.env.REACT_APP_APPLE_WEBSERVICEURL, // The web service URL. process.env.REACT_APP_APPLE_WEBSITEPUSHID, // The Website Push ID. - {anonid: 'anonid-123456-anonid'}, // Data that you choose to send to your server to help you identify the user. + // TODO: Encrypt uuid + {anonid: deviceUuid}, // Data that you choose to send to your server to help you identify the user. checkRemotePermission // The callback function. ); } else if (permissionData.permission === 'denied') { -- GitLab From 5b86d3d63e562fc01b6101d3b371aec115c67710 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Fri, 5 Feb 2021 16:49:36 +0100 Subject: [PATCH 04/46] Changed notification list and notiifcation display --- package.json | 1 + src/common/pages/MainPage/MainPage.js | 7 ++ src/notifications/actions/GetNotifications.js | 15 +++ .../NotificationsList/NotificationList.js | 107 +++++++++++------- .../reducers/NotificationsList.js | 10 ++ 5 files changed, 98 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 01d4134d..a7fdedb3 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "connected-react-router": "^6.7.0", "cross-env": "^7.0.0", "env-cmd": "^10.1.0", + "he": "^1.2.0", "history": "^4.7.2", "js-cookie": "^2.2.0", "jwt-decode": "^2.2.0", diff --git a/src/common/pages/MainPage/MainPage.js b/src/common/pages/MainPage/MainPage.js index e64bdbb7..cd1b2f62 100644 --- a/src/common/pages/MainPage/MainPage.js +++ b/src/common/pages/MainPage/MainPage.js @@ -12,6 +12,7 @@ import CERNToolBar from '../../components/CERNToolBar/CERNToolBar'; import EditChannelPage from '../../../channels/pages/EditChannelPage/EditChannelPage'; import ChannelGlobalPreferences from '../../../channels/pages/ChannelGlobalPreferences/ChannelGlobalPreferences'; import DevicesGlobal from 'common/pages/Devices/DevicesGlobal'; +import NotificationPage from 'common/pages/Notification/NotificationPage'; const MainPage = props => { const {snackbars, isAuthenticated} = props; @@ -48,6 +49,12 @@ const MainPage = props => { <Route path="/main/channels/:channelId" component={EditChannelPage} key={4} /> <Route path="/main/preferences" component={ChannelGlobalPreferences} key={5} /> <Route path="/main/devices" component={DevicesGlobal} key={6} /> + + <Route + path="/main/notification/:notificationId" + component={NotificationPage} + key={7} + /> </Switch> </div> </div> diff --git a/src/notifications/actions/GetNotifications.js b/src/notifications/actions/GetNotifications.js index cb36ca60..9ce6b8e0 100644 --- a/src/notifications/actions/GetNotifications.js +++ b/src/notifications/actions/GetNotifications.js @@ -7,6 +7,11 @@ export const GET_NOTIFICATIONS = 'GET_NOTIFICATIONS'; export const GET_NOTIFICATIONS_SUCCESS = 'GET_NOTIFICATIONS_SUCCESS'; export const GET_NOTIFICATIONS_FAILURE = 'GET_NOTIFICATIONS_FAILURE'; +// Get notification (from ID) +export const GET_NOTIFICATION = 'GET_NOTIFICATION'; +export const GET_NOTIFICATION_SUCCESS = 'GET_NOTIFICATION_SUCCESS'; +export const GET_NOTIFICATION_FAILURE = 'GET_NOTIFICATION_FAILURE'; + export const SET_GET_NOTIFICATIONS_QUERY = 'SET_GET_NOTIFICATIONS_QUERY'; export const getNotifications = (channelId, query) => ({ @@ -32,3 +37,13 @@ export const setGetNotificationsQuery = query => { payload: query, }; }; + +export const getNotification = notificationId => ({ + [RSAA]: { + endpoint: `${process.env.REACT_APP_BASE_URL}/notifications/notification/${notificationId}`, + method: 'GET', + credentials: 'include', + headers: withAuth({'Content-Type': 'application/json'}), + types: [GET_NOTIFICATION, GET_NOTIFICATION_SUCCESS, GET_NOTIFICATION_FAILURE], + }, +}); diff --git a/src/notifications/components/NotificationsList/NotificationList.js b/src/notifications/components/NotificationsList/NotificationList.js index fd8ddd05..ad2fb2b7 100644 --- a/src/notifications/components/NotificationsList/NotificationList.js +++ b/src/notifications/components/NotificationsList/NotificationList.js @@ -1,10 +1,12 @@ -import React, {useState, Fragment, useEffect} from 'react'; -import {Accordion, Icon, Visibility} from 'semantic-ui-react'; +import React, {useEffect} from 'react'; +import {Icon, Feed} from 'semantic-ui-react'; import {connect} from 'react-redux'; import * as getNotificationsActionCreators from 'notifications/actions/GetNotifications'; import * as getPublicNotificationsActionCreators from 'notifications/actions/GetPublicNotifications'; import {bindActionCreators} from 'redux'; import {useParams} from 'react-router-dom'; +import * as he from 'he'; +import './NotificationList.scss'; const NotificationsList = props => { const { @@ -15,7 +17,7 @@ const NotificationsList = props => { isAuthenticated, getPublicNotifications, } = props; - const [activeIndex, setActiveIndex] = useState(-1); + //const [activeIndex, setActiveIndex] = useState(-1); const {channelId} = useParams(); useEffect(() => { @@ -23,54 +25,75 @@ const NotificationsList = props => { else getPublicNotifications(channelId, getNotificationsQuery); }, [getNotifications, channelId, getNotificationsQuery, getPublicNotifications, isAuthenticated]); - const handleClick = (e, titleProps) => { - const {index} = titleProps; - const newIndex = activeIndex === index ? -1 : index; + // const handleClick = (e, titleProps) => { + // const { index } = titleProps; + // const newIndex = activeIndex === index ? -1 : index; + // setActiveIndex(newIndex); + // }; - setActiveIndex(newIndex); + const bodyPreview = body => { + const preview = body.replace(/(<([^>]+)>)/gi, ''); + // Using he.decode to decode html special chars + return preview && he.decode(preview).substring(0, 100) + '...'; }; + + const feedIcon = priority => { + if (priority === 'IMPORTANT') return 'warning circle'; + else return 'info circle'; + }; + if (notifications.length === 0) { return <p>The are no notifications in this channel</p>; } return ( - <Accordion style={{width: '100%'}} styled> - <Visibility - onBottomVisible={() => { - setGetNotificationsQuery({ - ...getNotificationsQuery, - skip: getNotificationsQuery.skip + getNotificationsQuery.take, - }); - }} - > + <> + <Feed> {notifications.map((notification, index) => ( - // eslint-disable-next-line react/jsx-fragments - <Fragment> - <Accordion.Title active={activeIndex === index} index={index} onClick={handleClick}> - <Icon name="dropdown" /> - {notification.subject} - <div style={{float: 'right'}}>{new Date(notification.sentAt).toLocaleString()}</div> - {activeIndex !== index && ( - <p - style={{ - overflow: 'hidden', - textOverflow: 'ellipsis', - display: '-webkit-box', - WebkitLineClamp: 3, - WebkitBoxOrient: 'vertical', - maxHeight: '150px', - }} - dangerouslySetInnerHTML={{__html: notification.body}} - /> - )} - </Accordion.Title> - <Accordion.Content active={activeIndex === index}> - <div dangerouslySetInnerHTML={{__html: notification.body}} /> - </Accordion.Content> - </Fragment> + <Feed.Event key={index}> + <Feed.Label> + <Icon name={feedIcon(notification.priority)} /> + </Feed.Label> + <Feed.Content> + <Feed.Summary> + <a href="" target="_blank" rel="noopener noreferrer"> + {notification.summary} + </a> + <Feed.Date>{new Date(notification.sentAt).toLocaleString()}</Feed.Date> + </Feed.Summary> + <Feed.Extra text>{bodyPreview(notification.body)}</Feed.Extra> + </Feed.Content> + </Feed.Event> ))} - </Visibility> - </Accordion> + </Feed> + + {/* <Accordion style={{ width: '100%' }} styled> + <Visibility + onBottomVisible={() => { + setGetNotificationsQuery({ + ...getNotificationsQuery, + skip: getNotificationsQuery.skip + getNotificationsQuery.take, + }); + }} + > + {notifications.map((notification, index) => ( + // eslint-disable-next-line react/jsx-fragments + <Fragment> + <Accordion.Title active={activeIndex === index} index={index} onClick={handleClick}> + <Icon name="dropdown" /> + {notification.summary} + <div style={{ float: 'right' }}>{new Date(notification.sentAt).toLocaleString()}</div> + </Accordion.Title> + <Accordion.Content active={activeIndex === index}> + <div dangerouslySetInnerHTML={{ __html: notification.body }} /> + <div><img src={notification.imgUrl} border="0" alt="" /></div> + <div><a href={notification.link} target="_blank" rel="noopener noreferrer">{notification.link}</a></div> + </Accordion.Content> + </Fragment> + ))} + </Visibility> + </Accordion> */} + </> ); }; diff --git a/src/notifications/reducers/NotificationsList.js b/src/notifications/reducers/NotificationsList.js index dbe1c4ad..8dbb93ef 100644 --- a/src/notifications/reducers/NotificationsList.js +++ b/src/notifications/reducers/NotificationsList.js @@ -3,12 +3,17 @@ import { GET_NOTIFICATIONS_SUCCESS, GET_NOTIFICATIONS_FAILURE, SET_GET_NOTIFICATIONS_QUERY, + // GET_NOTIFICATION, + GET_NOTIFICATION_SUCCESS, + // GET_NOTIFICATION_SUCCESS, + // GET_NOTIFICATION_FAILURE, } from 'notifications/actions/GetNotifications'; import {CREATE_NOTIFICATION_SUCCESS} from 'notifications/actions/CreateNotification'; const INITIAL_STATE = { notifications: [], + notification: null, getNotificationsQuery: { searchText: '', skip: 0, @@ -84,6 +89,7 @@ function processSetGetNotificationsQuery(state, query) { export default function (state = INITIAL_STATE, action) { let error; + switch (action.type) { case GET_NOTIFICATIONS: return processGetNotifications(state, action.payload); @@ -99,6 +105,10 @@ export default function (state = INITIAL_STATE, action) { case SET_GET_NOTIFICATIONS_QUERY: return processSetGetNotificationsQuery(state, action.payload); + case GET_NOTIFICATION_SUCCESS: { + return {...state, notification: action.payload}; + } + default: return state; } -- GitLab From b6acd02341c02a018077981316a680356371ce4b Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Fri, 5 Feb 2021 16:50:37 +0100 Subject: [PATCH 05/46] Changed notification list and notiifcation display --- .../pages/Notification/NotificationPage.js | 36 ++++++++++++++ .../NotificationDisplay.js | 48 +++++++++++++++++++ .../NotificationsList/NotificationList.scss | 13 +++++ 3 files changed, 97 insertions(+) create mode 100644 src/common/pages/Notification/NotificationPage.js create mode 100644 src/notifications/components/NotificationDisplay/NotificationDisplay.js create mode 100644 src/notifications/components/NotificationsList/NotificationList.scss diff --git a/src/common/pages/Notification/NotificationPage.js b/src/common/pages/Notification/NotificationPage.js new file mode 100644 index 00000000..38fd95f7 --- /dev/null +++ b/src/common/pages/Notification/NotificationPage.js @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {bindActionCreators} from 'redux'; +import {connect} from 'react-redux'; +import {useParams} from 'react-router-dom'; +import NotificationDisplay from '../../../notifications/components/NotificationDisplay/NotificationDisplay'; + +function NotificationPage({isAuthenticated}) { + const {notificationId} = useParams(); + + return ( + <div> + NotificationId = {notificationId} + <br /> + isAuthenticated = {isAuthenticated} + {isAuthenticated && <NotificationDisplay notificationId={notificationId} />} + </div> + ); +} + +NotificationPage.propTypes = { + isAuthenticated: PropTypes.bool.isRequired, +}; + +const mapStateToProps = state => ({ + isAuthenticated: state.auth.loggedIn, +}); + +export default connect(mapStateToProps, dispatch => + bindActionCreators( + { + // getPreferences: preferencesActions.getPreferences, + }, + dispatch + ) +)(NotificationPage); diff --git a/src/notifications/components/NotificationDisplay/NotificationDisplay.js b/src/notifications/components/NotificationDisplay/NotificationDisplay.js new file mode 100644 index 00000000..26abb20a --- /dev/null +++ b/src/notifications/components/NotificationDisplay/NotificationDisplay.js @@ -0,0 +1,48 @@ +import React, {useState, useEffect} from 'react'; +import {bindActionCreators} from 'redux'; +import {connect} from 'react-redux'; +import {Modal, Button} from 'semantic-ui-react'; +import {useParams} from 'react-router-dom'; + +import * as showSnackBarActionCreators from '../../../common/actions/Snackbar'; +import * as notificationActions from '../../actions/GetNotifications'; + +const NotificationDisplay = ({notification, getNotification}) => { + const {notificationId} = useParams(); + + const [modalOpen, setModalOpen] = useState(true); + + useEffect(() => { + getNotification(notificationId); + }, [getNotification, notificationId]); + + return ( + <Modal open={modalOpen}> + <Modal.Header>Notification</Modal.Header> + <Modal.Content> + <Modal.Description> + displaying id={notificationId} + <br /> + content = {notification} + </Modal.Description> + </Modal.Content> + <Modal.Actions> + <Button onClick={() => setModalOpen(false)}>Close</Button> + </Modal.Actions> + </Modal> + ); +}; + +export default connect( + state => ({ + notification: state.notification, + }), + dispatch => + bindActionCreators( + { + getNotification: notificationActions.getNotification, + ...showSnackBarActionCreators, + }, + dispatch + ) +)(NotificationDisplay); diff --git a/src/notifications/components/NotificationsList/NotificationList.scss b/src/notifications/components/NotificationsList/NotificationList.scss new file mode 100644 index 00000000..63c8e591 --- /dev/null +++ b/src/notifications/components/NotificationsList/NotificationList.scss @@ -0,0 +1,13 @@ +.ui.feed .event { + .label { + margin-top: 10px; + } + + .content .extra { + margin: 0; + } + + .content .extra.text { + font-size: small; + } +} -- GitLab From ac2759042b0b650cf3baae8a2464c9609df2f638 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Mon, 15 Feb 2021 13:58:02 +0100 Subject: [PATCH 06/46] Notification Display v0 --- .../NotificationDisplay.js | 48 ++++++++++++++++--- .../NotificationsList/NotificationList.js | 16 ++++--- .../reducers/NotificationsList.js | 2 +- src/utils/notification-type-icon.js | 5 ++ 4 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 src/utils/notification-type-icon.js diff --git a/src/notifications/components/NotificationDisplay/NotificationDisplay.js b/src/notifications/components/NotificationDisplay/NotificationDisplay.js index 26abb20a..617e4fb2 100644 --- a/src/notifications/components/NotificationDisplay/NotificationDisplay.js +++ b/src/notifications/components/NotificationDisplay/NotificationDisplay.js @@ -1,10 +1,11 @@ import React, {useState, useEffect} from 'react'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {Modal, Button} from 'semantic-ui-react'; +import {Modal, Button, Item, Image, Icon} from 'semantic-ui-react'; import {useParams} from 'react-router-dom'; -import * as showSnackBarActionCreators from '../../../common/actions/Snackbar'; +import {notificationIcon} from '../../../utils/notification-type-icon'; +//import * as showSnackBarActionCreators from '../../../common/actions/Snackbar'; import * as notificationActions from '../../actions/GetNotifications'; const NotificationDisplay = ({notification, getNotification}) => { @@ -21,9 +22,42 @@ const NotificationDisplay = ({notification, getNotification}) => { <Modal.Header>Notification</Modal.Header> <Modal.Content> <Modal.Description> - displaying id={notificationId} - <br /> - content = {notification} + <Item.Group> + <Item> + <Item.Image + size="tiny" + src={ + notification && notification.imgUrl + ? notification.imgUrl + : 'https://react.semantic-ui.com/images/wireframe/image.png' + } + /> + + <Item.Content> + <Item.Header>{notification.summary}</Item.Header> + <Item.Meta>{notification.sentAt}</Item.Meta> + <Item.Meta> + <Icon name={notificationIcon(notification.priority)} /> {notification.priority} + </Item.Meta> + + <Item.Description> + <div dangerouslySetInnerHTML={{__html: notification.body}} /> + </Item.Description> + {notification && notification.imgUrl && ( + <Item.Extra> + <Image src={notification.imgUrl} /> + </Item.Extra> + )} + {notification && notification.link && ( + <Item.Extra> + <a href={notification.link} target="_blank" rel="noopener noreferrer"> + {notification.link} + </a> + </Item.Extra> + )} + </Item.Content> + </Item> + </Item.Group> </Modal.Description> </Modal.Content> <Modal.Actions> @@ -35,13 +69,13 @@ const NotificationDisplay = ({notification, getNotification}) => { export default connect( state => ({ - notification: state.notification, + notification: state.notifications.notificationsList.notification, }), dispatch => bindActionCreators( { getNotification: notificationActions.getNotification, - ...showSnackBarActionCreators, + // ...showSnackBarActionCreators, }, dispatch ) diff --git a/src/notifications/components/NotificationsList/NotificationList.js b/src/notifications/components/NotificationsList/NotificationList.js index ad2fb2b7..eace3c16 100644 --- a/src/notifications/components/NotificationsList/NotificationList.js +++ b/src/notifications/components/NotificationsList/NotificationList.js @@ -1,11 +1,13 @@ import React, {useEffect} from 'react'; import {Icon, Feed} from 'semantic-ui-react'; import {connect} from 'react-redux'; -import * as getNotificationsActionCreators from 'notifications/actions/GetNotifications'; -import * as getPublicNotificationsActionCreators from 'notifications/actions/GetPublicNotifications'; import {bindActionCreators} from 'redux'; import {useParams} from 'react-router-dom'; import * as he from 'he'; + +import {notificationIcon} from '../../../utils/notification-type-icon'; +import * as getNotificationsActionCreators from 'notifications/actions/GetNotifications'; +import * as getPublicNotificationsActionCreators from 'notifications/actions/GetPublicNotifications'; import './NotificationList.scss'; const NotificationsList = props => { @@ -37,10 +39,10 @@ const NotificationsList = props => { return preview && he.decode(preview).substring(0, 100) + '...'; }; - const feedIcon = priority => { - if (priority === 'IMPORTANT') return 'warning circle'; - else return 'info circle'; - }; + // const feedIcon = priority => { + // if (priority === 'IMPORTANT') return 'warning circle'; + // else return 'info circle'; + // }; if (notifications.length === 0) { return <p>The are no notifications in this channel</p>; @@ -52,7 +54,7 @@ const NotificationsList = props => { {notifications.map((notification, index) => ( <Feed.Event key={index}> <Feed.Label> - <Icon name={feedIcon(notification.priority)} /> + <Icon name={notificationIcon(notification.priority)} /> </Feed.Label> <Feed.Content> <Feed.Summary> diff --git a/src/notifications/reducers/NotificationsList.js b/src/notifications/reducers/NotificationsList.js index 8dbb93ef..56d4ff43 100644 --- a/src/notifications/reducers/NotificationsList.js +++ b/src/notifications/reducers/NotificationsList.js @@ -13,7 +13,7 @@ import {CREATE_NOTIFICATION_SUCCESS} from 'notifications/actions/CreateNotificat const INITIAL_STATE = { notifications: [], - notification: null, + notification: '', getNotificationsQuery: { searchText: '', skip: 0, diff --git a/src/utils/notification-type-icon.js b/src/utils/notification-type-icon.js new file mode 100644 index 00000000..44e43891 --- /dev/null +++ b/src/utils/notification-type-icon.js @@ -0,0 +1,5 @@ +export function notificationIcon(priority) { + if (priority === 'CRITICAL') return 'exclamation triangle'; + else if (priority === 'IMPORTANT') return 'warning circle'; + else return 'info circle'; +} -- GitLab From 5256e15ff08114299c288d530973f0b610ce455d Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 16 Feb 2021 09:00:01 +0100 Subject: [PATCH 07/46] Notification Display v0.1 --- public/images/empty.png | Bin 0 -> 7175 bytes src/common/colors/colors.scss | 1 + .../pages/Notification/NotificationPage.js | 54 ++++++++----- .../NotificationDisplay.js | 72 ++++++++---------- .../NotificationDisplay.scss | 22 ++++++ .../NotificationsList/NotificationList.scss | 13 ---- ...tificationList.js => NotificationsList.js} | 20 ++--- .../NotificationsList/NotificationsList.scss | 24 ++++++ .../SearchNotificationComponent.js | 2 +- .../NotificationsPage/NotificationsPage.js | 2 +- src/utils/notification-type-icon.js | 24 ++++-- 11 files changed, 146 insertions(+), 88 deletions(-) create mode 100644 public/images/empty.png create mode 100644 src/notifications/components/NotificationDisplay/NotificationDisplay.scss delete mode 100644 src/notifications/components/NotificationsList/NotificationList.scss rename src/notifications/components/NotificationsList/{NotificationList.js => NotificationsList.js} (86%) create mode 100644 src/notifications/components/NotificationsList/NotificationsList.scss diff --git a/public/images/empty.png b/public/images/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..11ce432e9f182ee39e756ea68065ee0d28e4c0c6 GIT binary patch literal 7175 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 literal 0 HcmV?d00001 diff --git a/src/common/colors/colors.scss b/src/common/colors/colors.scss index e0476456..0cc9da9d 100644 --- a/src/common/colors/colors.scss +++ b/src/common/colors/colors.scss @@ -1,3 +1,4 @@ $light-gray: #666; $dark-gray: #333; $background-lightgrey: #eee; +$separator-lightgrey: #ccc; diff --git a/src/common/pages/Notification/NotificationPage.js b/src/common/pages/Notification/NotificationPage.js index 38fd95f7..6cef5cd1 100644 --- a/src/common/pages/Notification/NotificationPage.js +++ b/src/common/pages/Notification/NotificationPage.js @@ -1,20 +1,35 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; import {useParams} from 'react-router-dom'; + +import * as notificationActions from '../../../notifications/actions/GetNotifications'; import NotificationDisplay from '../../../notifications/components/NotificationDisplay/NotificationDisplay'; -function NotificationPage({isAuthenticated}) { +function NotificationPage({isAuthenticated, notification, getNotification}) { const {notificationId} = useParams(); + useEffect(() => { + getNotification(notificationId); + }, [getNotification, notificationId]); + + const onCloseModal = () => { + console.log('closed modal, redirecting to channel notifications list'); + console.log(notification); + //history.push(`/main/channels/${channel.id}`); + }; + + // TODO2: onclose modal -> redirect to channel notification list return ( - <div> - NotificationId = {notificationId} - <br /> - isAuthenticated = {isAuthenticated} - {isAuthenticated && <NotificationDisplay notificationId={notificationId} />} - </div> + isAuthenticated && + notification && ( + <NotificationDisplay + notification={notification} + modalinitialopenstate={true} + onCloseModal={onCloseModal} + /> + ) ); } @@ -22,15 +37,16 @@ NotificationPage.propTypes = { isAuthenticated: PropTypes.bool.isRequired, }; -const mapStateToProps = state => ({ - isAuthenticated: state.auth.loggedIn, -}); - -export default connect(mapStateToProps, dispatch => - bindActionCreators( - { - // getPreferences: preferencesActions.getPreferences, - }, - dispatch - ) +export default connect( + state => ({ + isAuthenticated: state.auth.loggedIn, + notification: state.notifications.notificationsList.notification, + }), + dispatch => + bindActionCreators( + { + getNotification: notificationActions.getNotification, + }, + dispatch + ) )(NotificationPage); diff --git a/src/notifications/components/NotificationDisplay/NotificationDisplay.js b/src/notifications/components/NotificationDisplay/NotificationDisplay.js index 617e4fb2..1ebfeebf 100644 --- a/src/notifications/components/NotificationDisplay/NotificationDisplay.js +++ b/src/notifications/components/NotificationDisplay/NotificationDisplay.js @@ -1,24 +1,26 @@ -import React, {useState, useEffect} from 'react'; -import {bindActionCreators} from 'redux'; -import {connect} from 'react-redux'; -import {Modal, Button, Item, Image, Icon} from 'semantic-ui-react'; -import {useParams} from 'react-router-dom'; +import React, {useState} from 'react'; +import {Modal, Button, Item, Image} from 'semantic-ui-react'; -import {notificationIcon} from '../../../utils/notification-type-icon'; -//import * as showSnackBarActionCreators from '../../../common/actions/Snackbar'; -import * as notificationActions from '../../actions/GetNotifications'; +import NotificationIcon from '../../../utils/notification-type-icon'; +import './NotificationDisplay.scss'; -const NotificationDisplay = ({notification, getNotification}) => { - const {notificationId} = useParams(); +const NotificationDisplay = props => { + const notification = props.notification; - const [modalOpen, setModalOpen] = useState(true); - - useEffect(() => { - getNotification(notificationId); - }, [getNotification, notificationId]); + const [modalOpen, setModalOpen] = useState( + props.modalinitialopenstate ? props.modalinitialopenstate : false + ); return ( - <Modal open={modalOpen}> + <Modal + trigger={ + <Button className="tertiarynohover" onClick={() => setModalOpen(true)}> + {notification.summary} + </Button> + } + open={modalOpen} + onClose={() => setModalOpen(false)} + > <Modal.Header>Notification</Modal.Header> <Modal.Content> <Modal.Description> @@ -27,19 +29,15 @@ const NotificationDisplay = ({notification, getNotification}) => { <Item.Image size="tiny" src={ - notification && notification.imgUrl - ? notification.imgUrl - : 'https://react.semantic-ui.com/images/wireframe/image.png' + notification && notification.imgUrl ? notification.imgUrl : '/images/empty.png' } /> <Item.Content> - <Item.Header>{notification.summary}</Item.Header> - <Item.Meta>{notification.sentAt}</Item.Meta> - <Item.Meta> - <Icon name={notificationIcon(notification.priority)} /> {notification.priority} - </Item.Meta> - + <Item.Header> + <NotificationIcon priority={notification.priority} /> {notification.summary} + </Item.Header> + <Item.Meta>{new Date(notification.sentAt).toLocaleString()}</Item.Meta> <Item.Description> <div dangerouslySetInnerHTML={{__html: notification.body}} /> </Item.Description> @@ -50,6 +48,7 @@ const NotificationDisplay = ({notification, getNotification}) => { )} {notification && notification.link && ( <Item.Extra> + Link:{' '} <a href={notification.link} target="_blank" rel="noopener noreferrer"> {notification.link} </a> @@ -61,22 +60,17 @@ const NotificationDisplay = ({notification, getNotification}) => { </Modal.Description> </Modal.Content> <Modal.Actions> - <Button onClick={() => setModalOpen(false)}>Close</Button> + <Button + onClick={() => { + setModalOpen(false); + if (props.onCloseModal) props.onCloseModal(); + }} + > + Close + </Button> </Modal.Actions> </Modal> ); }; -export default connect( - state => ({ - notification: state.notifications.notificationsList.notification, - }), - dispatch => - bindActionCreators( - { - getNotification: notificationActions.getNotification, - // ...showSnackBarActionCreators, - }, - dispatch - ) -)(NotificationDisplay); +export default NotificationDisplay; diff --git a/src/notifications/components/NotificationDisplay/NotificationDisplay.scss b/src/notifications/components/NotificationDisplay/NotificationDisplay.scss new file mode 100644 index 00000000..efe6b68b --- /dev/null +++ b/src/notifications/components/NotificationDisplay/NotificationDisplay.scss @@ -0,0 +1,22 @@ +.ui.button { + &.tertiary { + &:not(:hover) { + box-shadow: none !important; + background: none !important; + } + } +} + +.ui.button { + &.tertiarynohover { + box-shadow: none !important; + background: none !important; + padding-left: 0px; + } +} + +.ui.button { + &.tertiarynohover:hover { + text-decoration: underline; + } +} diff --git a/src/notifications/components/NotificationsList/NotificationList.scss b/src/notifications/components/NotificationsList/NotificationList.scss deleted file mode 100644 index 63c8e591..00000000 --- a/src/notifications/components/NotificationsList/NotificationList.scss +++ /dev/null @@ -1,13 +0,0 @@ -.ui.feed .event { - .label { - margin-top: 10px; - } - - .content .extra { - margin: 0; - } - - .content .extra.text { - font-size: small; - } -} diff --git a/src/notifications/components/NotificationsList/NotificationList.js b/src/notifications/components/NotificationsList/NotificationsList.js similarity index 86% rename from src/notifications/components/NotificationsList/NotificationList.js rename to src/notifications/components/NotificationsList/NotificationsList.js index eace3c16..573bf3a9 100644 --- a/src/notifications/components/NotificationsList/NotificationList.js +++ b/src/notifications/components/NotificationsList/NotificationsList.js @@ -1,21 +1,22 @@ import React, {useEffect} from 'react'; -import {Icon, Feed} from 'semantic-ui-react'; +import {Feed} from 'semantic-ui-react'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {useParams} from 'react-router-dom'; import * as he from 'he'; -import {notificationIcon} from '../../../utils/notification-type-icon'; +import NotificationDisplay from '../NotificationDisplay/NotificationDisplay'; +import NotificationIcon from '../../../utils/notification-type-icon'; import * as getNotificationsActionCreators from 'notifications/actions/GetNotifications'; import * as getPublicNotificationsActionCreators from 'notifications/actions/GetPublicNotifications'; -import './NotificationList.scss'; +import './NotificationsList.scss'; const NotificationsList = props => { const { notifications, getNotifications, getNotificationsQuery, - setGetNotificationsQuery, + // setGetNotificationsQuery, isAuthenticated, getPublicNotifications, } = props; @@ -52,16 +53,15 @@ const NotificationsList = props => { <> <Feed> {notifications.map((notification, index) => ( - <Feed.Event key={index}> + <Feed.Event key={index} className="sep"> <Feed.Label> - <Icon name={notificationIcon(notification.priority)} /> + <NotificationIcon priority={notification.priority} /> </Feed.Label> <Feed.Content> + <Feed.Date>{new Date(notification.sentAt).toLocaleString()}</Feed.Date> <Feed.Summary> - <a href="" target="_blank" rel="noopener noreferrer"> - {notification.summary} - </a> - <Feed.Date>{new Date(notification.sentAt).toLocaleString()}</Feed.Date> + {/* {notification.summary} */} + <NotificationDisplay notification={notification} /> </Feed.Summary> <Feed.Extra text>{bodyPreview(notification.body)}</Feed.Extra> </Feed.Content> diff --git a/src/notifications/components/NotificationsList/NotificationsList.scss b/src/notifications/components/NotificationsList/NotificationsList.scss new file mode 100644 index 00000000..3670ff66 --- /dev/null +++ b/src/notifications/components/NotificationsList/NotificationsList.scss @@ -0,0 +1,24 @@ +@import '../../../common/colors/colors.scss'; + +.ui.feed .event { + .label { + margin-top: 20px; + } + + .content .date { + margin: 0 0 -10px 0; + font-size: 0.8em; + } + + .content .extra { + margin: 0; + } + + .content .extra.text { + font-size: small; + } +} + +.ui.feed .sep { + border-bottom: 1px solid $separator-lightgrey; +} diff --git a/src/notifications/components/SearchNotificationComponent/SearchNotificationComponent.js b/src/notifications/components/SearchNotificationComponent/SearchNotificationComponent.js index 0ae4e6d7..67c9722e 100644 --- a/src/notifications/components/SearchNotificationComponent/SearchNotificationComponent.js +++ b/src/notifications/components/SearchNotificationComponent/SearchNotificationComponent.js @@ -4,7 +4,7 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; import * as getNotificationsActionCreators from '../../actions/GetNotifications'; -const WAIT_INTERVAL = 250; +const WAIT_INTERVAL = 500; let timer = null; const SearchNotificationComponent = ({getNotificationsQuery, setGetNotificationsQuery}) => { diff --git a/src/notifications/pages/NotificationsPage/NotificationsPage.js b/src/notifications/pages/NotificationsPage/NotificationsPage.js index 3b5053b0..b8db4c37 100644 --- a/src/notifications/pages/NotificationsPage/NotificationsPage.js +++ b/src/notifications/pages/NotificationsPage/NotificationsPage.js @@ -7,7 +7,7 @@ import {Container, Divider, Header, Menu, Segment} from 'semantic-ui-react'; import * as preferencesActions from 'actions/preferences'; import PreferencesList from 'components/preferences/PreferencesList'; -import NotificationsList from '../../components/NotificationsList/NotificationList'; +import NotificationsList from '../../components/NotificationsList/NotificationsList'; import SearchNotificationComponent from '../../components/SearchNotificationComponent/SearchNotificationComponent'; import './NotificationsPage.scss'; diff --git a/src/utils/notification-type-icon.js b/src/utils/notification-type-icon.js index 44e43891..8d523bd3 100644 --- a/src/utils/notification-type-icon.js +++ b/src/utils/notification-type-icon.js @@ -1,5 +1,19 @@ -export function notificationIcon(priority) { - if (priority === 'CRITICAL') return 'exclamation triangle'; - else if (priority === 'IMPORTANT') return 'warning circle'; - else return 'info circle'; -} +import React from 'react'; +import {Icon, Popup} from 'semantic-ui-react'; + +const NotificationIcon = props => { + if (!props.priority || props.priority === 'LOW') return null; + + let icon = <Icon name="info circle" color="green" />; + if (props.priority === 'CRITICAL') icon = <Icon name="exclamation triangle" color="red" />; + else if (props.priority === 'IMPORTANT') icon = <Icon name="warning circle" color="orange" />; + + return ( + <Popup trigger={icon} position="top center"> + Priority:{' '} + {props.priority ? props.priority.charAt(0) + props.priority.substring(1).toLowerCase() : ''} + </Popup> + ); +}; + +export default NotificationIcon; -- GitLab From 9267963b496942277ca575ac2c5e97916395adc8 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 16 Feb 2021 09:03:51 +0100 Subject: [PATCH 08/46] Notification Display v0.1 --- src/common/pages/Notification/NotificationPage.js | 12 ++++++------ .../NotificationDisplay/NotificationDisplay.js | 6 +++--- .../NotificationsList/NotificationsList.js | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/common/pages/Notification/NotificationPage.js b/src/common/pages/Notification/NotificationPage.js index 6cef5cd1..b6ee6ccd 100644 --- a/src/common/pages/Notification/NotificationPage.js +++ b/src/common/pages/Notification/NotificationPage.js @@ -1,14 +1,14 @@ -import React, {useEffect} from 'react'; +import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; -import {bindActionCreators} from 'redux'; -import {connect} from 'react-redux'; -import {useParams} from 'react-router-dom'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { useParams } from 'react-router-dom'; import * as notificationActions from '../../../notifications/actions/GetNotifications'; import NotificationDisplay from '../../../notifications/components/NotificationDisplay/NotificationDisplay'; -function NotificationPage({isAuthenticated, notification, getNotification}) { - const {notificationId} = useParams(); +function NotificationPage({ isAuthenticated, notification, getNotification }) { + const { notificationId } = useParams(); useEffect(() => { getNotification(notificationId); diff --git a/src/notifications/components/NotificationDisplay/NotificationDisplay.js b/src/notifications/components/NotificationDisplay/NotificationDisplay.js index 1ebfeebf..a6224502 100644 --- a/src/notifications/components/NotificationDisplay/NotificationDisplay.js +++ b/src/notifications/components/NotificationDisplay/NotificationDisplay.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import {Modal, Button, Item, Image} from 'semantic-ui-react'; +import React, { useState } from 'react'; +import { Modal, Button, Item, Image } from 'semantic-ui-react'; import NotificationIcon from '../../../utils/notification-type-icon'; import './NotificationDisplay.scss'; @@ -39,7 +39,7 @@ const NotificationDisplay = props => { </Item.Header> <Item.Meta>{new Date(notification.sentAt).toLocaleString()}</Item.Meta> <Item.Description> - <div dangerouslySetInnerHTML={{__html: notification.body}} /> + <div dangerouslySetInnerHTML={{ __html: notification.body }} /> </Item.Description> {notification && notification.imgUrl && ( <Item.Extra> diff --git a/src/notifications/components/NotificationsList/NotificationsList.js b/src/notifications/components/NotificationsList/NotificationsList.js index 573bf3a9..967ca285 100644 --- a/src/notifications/components/NotificationsList/NotificationsList.js +++ b/src/notifications/components/NotificationsList/NotificationsList.js @@ -1,8 +1,8 @@ -import React, {useEffect} from 'react'; -import {Feed} from 'semantic-ui-react'; -import {connect} from 'react-redux'; -import {bindActionCreators} from 'redux'; -import {useParams} from 'react-router-dom'; +import React, { useEffect } from 'react'; +import { Feed } from 'semantic-ui-react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { useParams } from 'react-router-dom'; import * as he from 'he'; import NotificationDisplay from '../NotificationDisplay/NotificationDisplay'; @@ -21,7 +21,7 @@ const NotificationsList = props => { getPublicNotifications, } = props; //const [activeIndex, setActiveIndex] = useState(-1); - const {channelId} = useParams(); + const { channelId } = useParams(); useEffect(() => { if (isAuthenticated) getNotifications(channelId, getNotificationsQuery); -- GitLab From 9fad28686f3090c8c2a2afa5bdcdfe2aed032c22 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 5 Jan 2021 13:53:11 +0100 Subject: [PATCH 09/46] [#28] Added device management (browser only) --- public/service.js | 110 ++++++++++++++++++ src/components/preferences/AddPreferences.css | 17 +++ 2 files changed, 127 insertions(+) create mode 100644 public/service.js create mode 100644 src/components/preferences/AddPreferences.css diff --git a/public/service.js b/public/service.js new file mode 100644 index 00000000..663e7afc --- /dev/null +++ b/public/service.js @@ -0,0 +1,110 @@ +/* + +// urlB64ToUint8Array is a magic function that will encode the base64 public key +// to Array buffer which is needed by the subscription option +const urlB64ToUint8Array = base64String => { + const padding = '='.repeat((4 - (base64String.length % 4)) % 4) + const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/') + const rawData = atob(base64) + const outputArray = new Uint8Array(rawData.length) + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i) + } + return outputArray + } + + + const saveSubscription = async subscription => { + const SERVER_URL = 'https://send-push-test.app.cern.ch/save-subscription' + const response = await fetch(SERVER_URL, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(subscription), + }) + return response.json() + } + + self.addEventListener('activate', async () => { + // This will be called only once when the service worker is installed for first time. + try { + const applicationServerKey = urlB64ToUint8Array( + 'BDC_Z4KiJzs0II7qLJT3DvlVJ0qcWkCMA6u7Q8B1QnpJeax5kShz6-PjoTM7HlTJua1qlXVGLbiZRT3SBM7ZzaY' + ) + const options = { applicationServerKey, userVisibleOnly: true } + const subscription = await self.registration.pushManager.subscribe(options) + const response = await saveSubscription(subscription) + console.log(response) + } catch (err) { + console.log('Error', err) + } + })*/ + + self.addEventListener('push', async function(event) { + if (event.data) { + console.log('Push event text: ', event.data.text()); + console.log(event.data); + + var options = { + //body: str, + icon: '/images/Logo-Outline-web-Blue100.png', + badge: '/images/Logo-Outline-web-Blue100.png', + // image: '/images/pic.jpg', + actions: [ + { + action: 'yes-action', + title: 'Yes', + //icon: '/images/action-1-128x128.png' + }, + { + action: 'no-action', + title: 'No', + //icon: '/images/action-2-128x128.png' + } + ] + }; + + try { + const blob = JSON.parse(event.data.text()); + options.body = blob.message; + options.image = blob.image; + if (blob.url) + options.data = { url: blob.url}; + } + catch {} + + // fallback is no json blob in message + if (!options.body) + options.body = event.data.text(); + + // 2 Actions Max + //return showLocalNotification('Yolo', event.data.text(), self.registration) + event.waitUntil( + self.registration.showNotification('Yola', options) + ); + } else { + console.log('Push event but no data'); + } + }) + + self.addEventListener('notificationclick', function(event) { + if (event.notification.data && event.notification.data.url) + { + event.notification.close(); + event.waitUntil( + clients.openWindow(event.notification.data.url) + ); + } else + console.log('Url undefined.'); + }) + + const showLocalNotification = (title, body, swRegistration) => { + const options = { + body, + // here you can add more properties like icon, image, vibrate, etc. + } + return swRegistration.showNotification(title, options) + } + + \ No newline at end of file diff --git a/src/components/preferences/AddPreferences.css b/src/components/preferences/AddPreferences.css new file mode 100644 index 00000000..dd79445f --- /dev/null +++ b/src/components/preferences/AddPreferences.css @@ -0,0 +1,17 @@ +.ui.form .add-preferences-time { + display: flex; + font-weight: bold; + flex-wrap: wrap; + vertical-align: middle; +} +.ui.form .add-preferences-time-label { + margin-right: 1em; + font-weight: bold; +} +.ui.form .add-preferences-time-clock > input[type='text'] { + border: 2px solid #666; + width: 60px !important; + padding: 5px 8px; + color: #333; + border-radius: 3px; +} -- GitLab From 5840895f6c4f2c1a50e74e6fdcfb4f3556487585 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 5 Jan 2021 16:16:03 +0100 Subject: [PATCH 10/46] [#28] Added try push functionnality --- public/service.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/public/service.js b/public/service.js index 663e7afc..b1d58847 100644 --- a/public/service.js +++ b/public/service.js @@ -51,6 +51,7 @@ const urlB64ToUint8Array = base64String => { icon: '/images/Logo-Outline-web-Blue100.png', badge: '/images/Logo-Outline-web-Blue100.png', // image: '/images/pic.jpg', + /* actions: [ { action: 'yes-action', @@ -63,12 +64,17 @@ const urlB64ToUint8Array = base64String => { //icon: '/images/action-2-128x128.png' } ] + */ }; + let title = "CERN Notification"; try { const blob = JSON.parse(event.data.text()); options.body = blob.message; - options.image = blob.image; + if (blob.title) + title = blob.title; + if (blob.image) + options.image = blob.image; if (blob.url) options.data = { url: blob.url}; } @@ -81,7 +87,7 @@ const urlB64ToUint8Array = base64String => { // 2 Actions Max //return showLocalNotification('Yolo', event.data.text(), self.registration) event.waitUntil( - self.registration.showNotification('Yola', options) + self.registration.showNotification(title, options) ); } else { console.log('Push event but no data'); -- GitLab From 8f82b3d256cd496b4a55e367f42c14bc5641f214 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Wed, 6 Jan 2021 15:43:16 +0100 Subject: [PATCH 11/46] [#28] - --- src/components/preferences/AddPreferences.css | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/components/preferences/AddPreferences.css b/src/components/preferences/AddPreferences.css index dd79445f..f2486b1b 100644 --- a/src/components/preferences/AddPreferences.css +++ b/src/components/preferences/AddPreferences.css @@ -2,16 +2,13 @@ display: flex; font-weight: bold; flex-wrap: wrap; - vertical-align: middle; -} -.ui.form .add-preferences-time-label { - margin-right: 1em; - font-weight: bold; -} -.ui.form .add-preferences-time-clock > input[type='text'] { - border: 2px solid #666; - width: 60px !important; - padding: 5px 8px; - color: #333; - border-radius: 3px; -} + vertical-align: middle; } + .ui.form .add-preferences-time-label { + margin-right: 1em; + font-weight: bold; } + .ui.form .add-preferences-time-clock > input[type='text'] { + border: 2px solid #666; + width: 60px !important; + padding: 5px 8px; + color: #333; + border-radius: 3px; } -- GitLab From 553ba815f140a0d5d2a93c7b042cccd195645b4b Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 14 Jan 2021 09:31:55 +0100 Subject: [PATCH 12/46] list devices on prefernce pages --- src/actions/preferences.js | 28 +++++++++++++++++++++ src/components/preferences/AddPreference.js | 1 + 2 files changed, 29 insertions(+) diff --git a/src/actions/preferences.js b/src/actions/preferences.js index cfe21426..490b6249 100644 --- a/src/actions/preferences.js +++ b/src/actions/preferences.js @@ -17,6 +17,14 @@ export const TOGGLE_GLOBAL_PREFERENCE = 'TOGGLE_GLOBAL_PREFERENCE'; export const TOGGLE_GLOBAL_PREFERENCE_SUCCESS = 'TOGGLE_GLOBAL_PREFERENCE_SUCCESS'; export const TOGGLE_GLOBAL_PREFERENCE_FAILURE = 'TOGGLE_GLOBAL_PREFERENCE_FAILURE'; +export const ADD_TARGET_DEVICE = 'ADD_TARGET_DEVICE'; +export const ADD_TARGET_DEVICE_SUCCESS = 'ADD_TARGET_DEVICE_SUCCESS'; +export const ADD_TARGET_DEVICE_FAILURE = 'ADD_TARGET_DEVICE_FAILURE'; + +export const DEL_TARGET_DEVICE = 'DEL_TARGET_DEVICE'; +export const DEL_TARGET_DEVICE_SUCCESS = 'DEL_TARGET_DEVICE_SUCCESS'; +export const DEL_TARGET_DEVICE_FAILURE = 'DEL_TARGET_DEVICE_FAILURE'; + export const getPreferences = channelId => ({ [RSAA]: { endpoint: `${process.env.REACT_APP_BASE_URL}/preferences/${channelId || ''}`, @@ -61,3 +69,23 @@ export const toggleGlobalPreferenceForChannel = (preferenceId, channelId, isEnab ], }, }); + +export const addTargetDevice = (preferenceId, deviceId, type) => ({ + [RSAA]: { + endpoint: `${process.env.REACT_APP_BASE_URL}/preferences/${preferenceId}/target-devices/${type}/${deviceId}`, + method: 'POST', + credentials: 'include', + headers: withAuth({'Content-Type': 'application/json'}), + types: [ADD_TARGET_DEVICE, ADD_TARGET_DEVICE_SUCCESS, ADD_TARGET_DEVICE_FAILURE], + }, +}); + +export const delTargetDevice = (preferenceId, deviceId, type) => ({ + [RSAA]: { + endpoint: `${process.env.REACT_APP_BASE_URL}/preferences/${preferenceId}/target-devices/${type}/${deviceId}`, + method: 'DELETE', + credentials: 'include', + headers: withAuth({'Content-Type': 'application/json'}), + types: [DEL_TARGET_DEVICE, DEL_TARGET_DEVICE_SUCCESS, DEL_TARGET_DEVICE_FAILURE], + }, +}); diff --git a/src/components/preferences/AddPreference.js b/src/components/preferences/AddPreference.js index 462a965f..28fe3d48 100644 --- a/src/components/preferences/AddPreference.js +++ b/src/components/preferences/AddPreference.js @@ -9,6 +9,7 @@ import * as preferenceActions from 'actions/preferences'; import * as devicesActions from 'actions/devices'; import DeviceTypeIcon from 'utils/device-type-icon'; import * as showSnackBarActionCreators from '../../common/actions/Snackbar'; +import * as devicesActions from 'actions/devices'; import './AddPreferences.scss'; import {notificationPriorityTypes} from '../../common/types/NotificationPriorityTypes'; -- GitLab From de0fd885f467dd80d3c805d80ffaa79086761571 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Fri, 15 Jan 2021 15:34:10 +0100 Subject: [PATCH 13/46] Management of target devices in preferences --- public/service.js | 116 -------------------- src/components/preferences/AddPreference.js | 1 + 2 files changed, 1 insertion(+), 116 deletions(-) delete mode 100644 public/service.js diff --git a/public/service.js b/public/service.js deleted file mode 100644 index b1d58847..00000000 --- a/public/service.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - -// urlB64ToUint8Array is a magic function that will encode the base64 public key -// to Array buffer which is needed by the subscription option -const urlB64ToUint8Array = base64String => { - const padding = '='.repeat((4 - (base64String.length % 4)) % 4) - const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/') - const rawData = atob(base64) - const outputArray = new Uint8Array(rawData.length) - for (let i = 0; i < rawData.length; ++i) { - outputArray[i] = rawData.charCodeAt(i) - } - return outputArray - } - - - const saveSubscription = async subscription => { - const SERVER_URL = 'https://send-push-test.app.cern.ch/save-subscription' - const response = await fetch(SERVER_URL, { - method: 'post', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(subscription), - }) - return response.json() - } - - self.addEventListener('activate', async () => { - // This will be called only once when the service worker is installed for first time. - try { - const applicationServerKey = urlB64ToUint8Array( - 'BDC_Z4KiJzs0II7qLJT3DvlVJ0qcWkCMA6u7Q8B1QnpJeax5kShz6-PjoTM7HlTJua1qlXVGLbiZRT3SBM7ZzaY' - ) - const options = { applicationServerKey, userVisibleOnly: true } - const subscription = await self.registration.pushManager.subscribe(options) - const response = await saveSubscription(subscription) - console.log(response) - } catch (err) { - console.log('Error', err) - } - })*/ - - self.addEventListener('push', async function(event) { - if (event.data) { - console.log('Push event text: ', event.data.text()); - console.log(event.data); - - var options = { - //body: str, - icon: '/images/Logo-Outline-web-Blue100.png', - badge: '/images/Logo-Outline-web-Blue100.png', - // image: '/images/pic.jpg', - /* - actions: [ - { - action: 'yes-action', - title: 'Yes', - //icon: '/images/action-1-128x128.png' - }, - { - action: 'no-action', - title: 'No', - //icon: '/images/action-2-128x128.png' - } - ] - */ - }; - - let title = "CERN Notification"; - try { - const blob = JSON.parse(event.data.text()); - options.body = blob.message; - if (blob.title) - title = blob.title; - if (blob.image) - options.image = blob.image; - if (blob.url) - options.data = { url: blob.url}; - } - catch {} - - // fallback is no json blob in message - if (!options.body) - options.body = event.data.text(); - - // 2 Actions Max - //return showLocalNotification('Yolo', event.data.text(), self.registration) - event.waitUntil( - self.registration.showNotification(title, options) - ); - } else { - console.log('Push event but no data'); - } - }) - - self.addEventListener('notificationclick', function(event) { - if (event.notification.data && event.notification.data.url) - { - event.notification.close(); - event.waitUntil( - clients.openWindow(event.notification.data.url) - ); - } else - console.log('Url undefined.'); - }) - - const showLocalNotification = (title, body, swRegistration) => { - const options = { - body, - // here you can add more properties like icon, image, vibrate, etc. - } - return swRegistration.showNotification(title, options) - } - - \ No newline at end of file diff --git a/src/components/preferences/AddPreference.js b/src/components/preferences/AddPreference.js index 28fe3d48..f2e9bdf4 100644 --- a/src/components/preferences/AddPreference.js +++ b/src/components/preferences/AddPreference.js @@ -10,6 +10,7 @@ import * as devicesActions from 'actions/devices'; import DeviceTypeIcon from 'utils/device-type-icon'; import * as showSnackBarActionCreators from '../../common/actions/Snackbar'; import * as devicesActions from 'actions/devices'; +import DeviceTypeIcon from 'utils/device-type-icon'; import './AddPreferences.scss'; import {notificationPriorityTypes} from '../../common/types/NotificationPriorityTypes'; -- GitLab From 54c305ac9f810613628c41b07a689857694f8b46 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Fri, 15 Jan 2021 15:36:54 +0100 Subject: [PATCH 14/46] [#28] new css for add device, new component for deviceicon --- src/components/devices/AddDevice.css | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/components/devices/AddDevice.css diff --git a/src/components/devices/AddDevice.css b/src/components/devices/AddDevice.css new file mode 100644 index 00000000..d0ae1581 --- /dev/null +++ b/src/components/devices/AddDevice.css @@ -0,0 +1,8 @@ +.ui.form .add-device { + padding: 15px; +} + +.ui.form .add-device-advanced { + background-color: #eee; + padding: 15px; +} -- GitLab From 48c4fa0b816000f633ef856586d40c93e07fc01b Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 19 Jan 2021 14:57:55 +0100 Subject: [PATCH 15/46] Fixed target devices --- src/components/devices/AddDevice.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/devices/AddDevice.css b/src/components/devices/AddDevice.css index d0ae1581..b6cc7510 100644 --- a/src/components/devices/AddDevice.css +++ b/src/components/devices/AddDevice.css @@ -1,5 +1,6 @@ .ui.form .add-device { - padding: 15px; + padding-left: 15px; + padding-right: 15px; } .ui.form .add-device-advanced { -- GitLab From 2aef17d0b35fb309925b36206fc55e03e74fdecc Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Wed, 20 Jan 2021 13:27:29 +0000 Subject: [PATCH 16/46] Delete AddDevice.css --- src/components/devices/AddDevice.css | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/components/devices/AddDevice.css diff --git a/src/components/devices/AddDevice.css b/src/components/devices/AddDevice.css deleted file mode 100644 index b6cc7510..00000000 --- a/src/components/devices/AddDevice.css +++ /dev/null @@ -1,9 +0,0 @@ -.ui.form .add-device { - padding-left: 15px; - padding-right: 15px; -} - -.ui.form .add-device-advanced { - background-color: #eee; - padding: 15px; -} -- GitLab From 5572a2fbcb6a80747b830db6e82994645dab027b Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Wed, 20 Jan 2021 13:27:46 +0000 Subject: [PATCH 17/46] Delete AddPreferences.css --- src/components/preferences/AddPreferences.css | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/components/preferences/AddPreferences.css diff --git a/src/components/preferences/AddPreferences.css b/src/components/preferences/AddPreferences.css deleted file mode 100644 index f2486b1b..00000000 --- a/src/components/preferences/AddPreferences.css +++ /dev/null @@ -1,14 +0,0 @@ -.ui.form .add-preferences-time { - display: flex; - font-weight: bold; - flex-wrap: wrap; - vertical-align: middle; } - .ui.form .add-preferences-time-label { - margin-right: 1em; - font-weight: bold; } - .ui.form .add-preferences-time-clock > input[type='text'] { - border: 2px solid #666; - width: 60px !important; - padding: 5px 8px; - color: #333; - border-radius: 3px; } -- GitLab From d22e11e552bc9dc93f13aca250e995487f3af501 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 26 Jan 2021 08:00:25 +0100 Subject: [PATCH 18/46] fixed radio boxes --- src/components/preferences/AddPreference.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/preferences/AddPreference.js b/src/components/preferences/AddPreference.js index f2e9bdf4..b2429b22 100644 --- a/src/components/preferences/AddPreference.js +++ b/src/components/preferences/AddPreference.js @@ -109,6 +109,7 @@ const AddPreference = ({ </label> </Form.Field> {filter.map(oneDevice => { + console.log(devices); return ( <Form.Field key={oneDevice.id} -- GitLab From 2ad96db8d96d80c75f9bc0da0cabe6a7d215c385 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 26 Jan 2021 09:39:05 +0100 Subject: [PATCH 19/46] checked default device and fixed target device selection --- src/components/preferences/AddPreference.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/preferences/AddPreference.js b/src/components/preferences/AddPreference.js index b2429b22..f2e9bdf4 100644 --- a/src/components/preferences/AddPreference.js +++ b/src/components/preferences/AddPreference.js @@ -109,7 +109,6 @@ const AddPreference = ({ </label> </Form.Field> {filter.map(oneDevice => { - console.log(devices); return ( <Form.Field key={oneDevice.id} -- GitLab From 2663eb14f42baa9a03a84d99424c0f728ab84b67 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 28 Jan 2021 15:45:37 +0100 Subject: [PATCH 20/46] Typo fixed on channel page --- .../CreateChannelPage/CreateChannelPage.js | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 src/channels/pages/CreateChannelPage/CreateChannelPage.js diff --git a/src/channels/pages/CreateChannelPage/CreateChannelPage.js b/src/channels/pages/CreateChannelPage/CreateChannelPage.js new file mode 100644 index 00000000..2adbded4 --- /dev/null +++ b/src/channels/pages/CreateChannelPage/CreateChannelPage.js @@ -0,0 +1,234 @@ +import React, {useState} from 'react'; + +import {connect} from 'react-redux'; +import './CreateChannelPage.css'; +import {bindActionCreators} from 'redux'; +import {Form, Radio, Popup, Segment} from 'semantic-ui-react'; + +import * as showSnackBarActionCreator from 'common/actions/Snackbar'; +import * as createChannelActionCreator from '../../actions/CreateChannel'; + +const CreateChannelPage = ({createChannel, showSnackbar, history}) => { + const [channel, setChannel] = useState({ + id: '', + name: '', + description: '', + adminGroup: { + groupIdentifier: '', + }, + visibility: 'RESTRICTED', + subscriptionPolicy: 'SELF_SUBSCRIPTION', + archive: false, + }); + + const updateField = (e, d) => { + setChannel({ + ...channel, + [d.name]: d.value || d.checked, + }); + }; + + return ( + <Segment + style={{ + width: '933px', + marginTop: '100px', + marginLeft: 'auto', + marginRight: 'auto', + }} + > + <Form + style={{margin: 100}} + onSubmit={() => + createChannel(channel).then(({payload, error}) => { + if (error) { + showSnackbar(`Error: ${payload.response.message}`, 'error'); + } else { + showSnackbar('The channel has been created successfully', 'success'); + history.push('/main/channels'); + } + }) + } + > + <Form.Input + label="ID" + name="id" + placeholder="ID" + value={channel.id} + onChange={updateField} + /> + <Form.Input + label="Name" + name="name" + placeholder="Name" + value={channel.name} + onChange={updateField} + /> + <Form.TextArea + label="Description" + placeholder="Description" + name="description" + value={channel.description} + onChange={updateField} + /> + <Form.Input + label="Admin group" + name="adminGroup" + placeholder="Admin group" + value={channel.adminGroup.groupIdentifier} + onChange={(e, d) => { + setChannel({ + ...channel, + adminGroup: { + ...channel.adminGroup, + groupIdentifier: d.value, + }, + }); + }} + /> + <Popup + content="Who can send notifications via e-mail to this channel?" + trigger={ + <Form.Input + label="Incoming e-mail address" + type="email" + name="incomingEmail" + placeholder="Incoming e-mail address" + value={channel.incomingEmail} + onChange={updateField} + /> + } + /> + <Form.Group inline> + <label>Visibility</label> + <Popup + content="Authenticated and not authenticated users can see the channel and its notifications" + trigger={ + <Form.Field + control={Radio} + label="Public" + value="PUBLIC" + checked={channel.visibility === 'PUBLIC'} + onChange={() => setChannel({...channel, visibility: 'PUBLIC'})} + /> + } + /> + <Popup + content="Only authenticated users can see the channel and its notifications" + trigger={ + <Form.Field + control={Radio} + label="Internal" + value="INTERNAL" + checked={channel.visibility === 'INTERNAL'} + onChange={() => setChannel({...channel, visibility: 'INTERNAL'})} + /> + } + /> + <Popup + content="Only channel members can see the channel and its notifictions" + trigger={ + <Form.Field + control={Radio} + label="Restricted" + value="RESTRICTED" + checked={channel.visibility === 'RESTRICTED'} + onChange={() => setChannel({...channel, visibility: 'RESTRICTED'})} + /> + } + /> + </Form.Group> + {channel.visibility !== 'PRIVATE' && ( + <Form.Group inline> + <label>Subscription Policy</label> + <Popup + content="All authenticated users can subscribe to the channel" + trigger={ + <Form.Field + control={Radio} + label="Self subscription" + value="SELF_SUBSCRIPTION" + checked={channel.subscriptionPolicy === 'SELF_SUBSCRIPTION'} + onChange={() => + setChannel({ + ...channel, + subscriptionPolicy: 'SELF_SUBSCRIPTION', + }) + } + /> + } + /> + + <Popup + content="All authenticated users can subscribe to the channel" + trigger={ + <Form.Field + control={Radio} + label="Self subscription with approval" + value="SELF_SUBSCRIPTION_APPROVAL" + checked={channel.subscriptionPolicy === 'SELF_SUBSCRIPTION_APPROVAL'} + onChange={() => + setChannel({ + ...channel, + subscriptionPolicy: 'SELF_SUBSCRIPTION_APPROVAL', + }) + } + /> + } + /> + </Form.Group> + )} + + <Form.Group inline> + <label>Archive</label> + <Popup + content={`Channel content will be automatically archived in ${process.env.REACT_APP_ARCHIVE_URL}`} + trigger={ + <Form.Field + control={Radio} + label="Enabled" + value="true" + checked={channel.archive === true} + onChange={() => setChannel({...channel, archive: true})} + /> + } + /> + <Popup + content="Channel content will not be archived." + trigger={ + <Form.Field + control={Radio} + label="Disabled" + value="false" + checked={channel.archive === false} + onChange={() => setChannel({...channel, archive: false})} + /> + } + /> + <span> + (archive content to{' '} + <a href={process.env.REACT_APP_ARCHIVE_URL} target="_blank" rel="noopener noreferrer"> + {process.env.REACT_APP_ARCHIVE_URL} + </a> + ) + </span> + </Form.Group> + + <Form.Button>Submit</Form.Button> + </Form> + </Segment> + ); +}; + +const mapStateToProps = state => { + return {}; +}; + +const mapDispatchToProps = dispatch => { + return { + ...bindActionCreators(createChannelActionCreator, dispatch), + ...bindActionCreators(showSnackBarActionCreator, dispatch), + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(CreateChannelPage); -- GitLab From d2cbb60d564668d4415bed087ab758a770fce990 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 5 Jan 2021 13:53:11 +0100 Subject: [PATCH 21/46] [#28] Added device management (browser only) --- public/service.js | 110 ++++++++++++++++++ src/components/preferences/AddPreferences.css | 17 +++ 2 files changed, 127 insertions(+) create mode 100644 public/service.js create mode 100644 src/components/preferences/AddPreferences.css diff --git a/public/service.js b/public/service.js new file mode 100644 index 00000000..663e7afc --- /dev/null +++ b/public/service.js @@ -0,0 +1,110 @@ +/* + +// urlB64ToUint8Array is a magic function that will encode the base64 public key +// to Array buffer which is needed by the subscription option +const urlB64ToUint8Array = base64String => { + const padding = '='.repeat((4 - (base64String.length % 4)) % 4) + const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/') + const rawData = atob(base64) + const outputArray = new Uint8Array(rawData.length) + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i) + } + return outputArray + } + + + const saveSubscription = async subscription => { + const SERVER_URL = 'https://send-push-test.app.cern.ch/save-subscription' + const response = await fetch(SERVER_URL, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(subscription), + }) + return response.json() + } + + self.addEventListener('activate', async () => { + // This will be called only once when the service worker is installed for first time. + try { + const applicationServerKey = urlB64ToUint8Array( + 'BDC_Z4KiJzs0II7qLJT3DvlVJ0qcWkCMA6u7Q8B1QnpJeax5kShz6-PjoTM7HlTJua1qlXVGLbiZRT3SBM7ZzaY' + ) + const options = { applicationServerKey, userVisibleOnly: true } + const subscription = await self.registration.pushManager.subscribe(options) + const response = await saveSubscription(subscription) + console.log(response) + } catch (err) { + console.log('Error', err) + } + })*/ + + self.addEventListener('push', async function(event) { + if (event.data) { + console.log('Push event text: ', event.data.text()); + console.log(event.data); + + var options = { + //body: str, + icon: '/images/Logo-Outline-web-Blue100.png', + badge: '/images/Logo-Outline-web-Blue100.png', + // image: '/images/pic.jpg', + actions: [ + { + action: 'yes-action', + title: 'Yes', + //icon: '/images/action-1-128x128.png' + }, + { + action: 'no-action', + title: 'No', + //icon: '/images/action-2-128x128.png' + } + ] + }; + + try { + const blob = JSON.parse(event.data.text()); + options.body = blob.message; + options.image = blob.image; + if (blob.url) + options.data = { url: blob.url}; + } + catch {} + + // fallback is no json blob in message + if (!options.body) + options.body = event.data.text(); + + // 2 Actions Max + //return showLocalNotification('Yolo', event.data.text(), self.registration) + event.waitUntil( + self.registration.showNotification('Yola', options) + ); + } else { + console.log('Push event but no data'); + } + }) + + self.addEventListener('notificationclick', function(event) { + if (event.notification.data && event.notification.data.url) + { + event.notification.close(); + event.waitUntil( + clients.openWindow(event.notification.data.url) + ); + } else + console.log('Url undefined.'); + }) + + const showLocalNotification = (title, body, swRegistration) => { + const options = { + body, + // here you can add more properties like icon, image, vibrate, etc. + } + return swRegistration.showNotification(title, options) + } + + \ No newline at end of file diff --git a/src/components/preferences/AddPreferences.css b/src/components/preferences/AddPreferences.css new file mode 100644 index 00000000..dd79445f --- /dev/null +++ b/src/components/preferences/AddPreferences.css @@ -0,0 +1,17 @@ +.ui.form .add-preferences-time { + display: flex; + font-weight: bold; + flex-wrap: wrap; + vertical-align: middle; +} +.ui.form .add-preferences-time-label { + margin-right: 1em; + font-weight: bold; +} +.ui.form .add-preferences-time-clock > input[type='text'] { + border: 2px solid #666; + width: 60px !important; + padding: 5px 8px; + color: #333; + border-radius: 3px; +} -- GitLab From b7b0e4b5d25e0e87c2c9688877c6a39a60f0bfef Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 5 Jan 2021 16:16:03 +0100 Subject: [PATCH 22/46] [#28] Added try push functionnality --- public/service.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/public/service.js b/public/service.js index 663e7afc..b1d58847 100644 --- a/public/service.js +++ b/public/service.js @@ -51,6 +51,7 @@ const urlB64ToUint8Array = base64String => { icon: '/images/Logo-Outline-web-Blue100.png', badge: '/images/Logo-Outline-web-Blue100.png', // image: '/images/pic.jpg', + /* actions: [ { action: 'yes-action', @@ -63,12 +64,17 @@ const urlB64ToUint8Array = base64String => { //icon: '/images/action-2-128x128.png' } ] + */ }; + let title = "CERN Notification"; try { const blob = JSON.parse(event.data.text()); options.body = blob.message; - options.image = blob.image; + if (blob.title) + title = blob.title; + if (blob.image) + options.image = blob.image; if (blob.url) options.data = { url: blob.url}; } @@ -81,7 +87,7 @@ const urlB64ToUint8Array = base64String => { // 2 Actions Max //return showLocalNotification('Yolo', event.data.text(), self.registration) event.waitUntil( - self.registration.showNotification('Yola', options) + self.registration.showNotification(title, options) ); } else { console.log('Push event but no data'); -- GitLab From 93202bc712d7b0747c24485338c906ada97f1bdb Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Wed, 6 Jan 2021 15:43:16 +0100 Subject: [PATCH 23/46] [#28] - --- src/components/preferences/AddPreferences.css | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/components/preferences/AddPreferences.css b/src/components/preferences/AddPreferences.css index dd79445f..f2486b1b 100644 --- a/src/components/preferences/AddPreferences.css +++ b/src/components/preferences/AddPreferences.css @@ -2,16 +2,13 @@ display: flex; font-weight: bold; flex-wrap: wrap; - vertical-align: middle; -} -.ui.form .add-preferences-time-label { - margin-right: 1em; - font-weight: bold; -} -.ui.form .add-preferences-time-clock > input[type='text'] { - border: 2px solid #666; - width: 60px !important; - padding: 5px 8px; - color: #333; - border-radius: 3px; -} + vertical-align: middle; } + .ui.form .add-preferences-time-label { + margin-right: 1em; + font-weight: bold; } + .ui.form .add-preferences-time-clock > input[type='text'] { + border: 2px solid #666; + width: 60px !important; + padding: 5px 8px; + color: #333; + border-radius: 3px; } -- GitLab From e496f07a6a9047e8c388f3f068b3ff1e80e19d7b Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Fri, 15 Jan 2021 15:34:10 +0100 Subject: [PATCH 24/46] Management of target devices in preferences --- public/service.js | 116 ---------------------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 public/service.js diff --git a/public/service.js b/public/service.js deleted file mode 100644 index b1d58847..00000000 --- a/public/service.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - -// urlB64ToUint8Array is a magic function that will encode the base64 public key -// to Array buffer which is needed by the subscription option -const urlB64ToUint8Array = base64String => { - const padding = '='.repeat((4 - (base64String.length % 4)) % 4) - const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/') - const rawData = atob(base64) - const outputArray = new Uint8Array(rawData.length) - for (let i = 0; i < rawData.length; ++i) { - outputArray[i] = rawData.charCodeAt(i) - } - return outputArray - } - - - const saveSubscription = async subscription => { - const SERVER_URL = 'https://send-push-test.app.cern.ch/save-subscription' - const response = await fetch(SERVER_URL, { - method: 'post', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(subscription), - }) - return response.json() - } - - self.addEventListener('activate', async () => { - // This will be called only once when the service worker is installed for first time. - try { - const applicationServerKey = urlB64ToUint8Array( - 'BDC_Z4KiJzs0II7qLJT3DvlVJ0qcWkCMA6u7Q8B1QnpJeax5kShz6-PjoTM7HlTJua1qlXVGLbiZRT3SBM7ZzaY' - ) - const options = { applicationServerKey, userVisibleOnly: true } - const subscription = await self.registration.pushManager.subscribe(options) - const response = await saveSubscription(subscription) - console.log(response) - } catch (err) { - console.log('Error', err) - } - })*/ - - self.addEventListener('push', async function(event) { - if (event.data) { - console.log('Push event text: ', event.data.text()); - console.log(event.data); - - var options = { - //body: str, - icon: '/images/Logo-Outline-web-Blue100.png', - badge: '/images/Logo-Outline-web-Blue100.png', - // image: '/images/pic.jpg', - /* - actions: [ - { - action: 'yes-action', - title: 'Yes', - //icon: '/images/action-1-128x128.png' - }, - { - action: 'no-action', - title: 'No', - //icon: '/images/action-2-128x128.png' - } - ] - */ - }; - - let title = "CERN Notification"; - try { - const blob = JSON.parse(event.data.text()); - options.body = blob.message; - if (blob.title) - title = blob.title; - if (blob.image) - options.image = blob.image; - if (blob.url) - options.data = { url: blob.url}; - } - catch {} - - // fallback is no json blob in message - if (!options.body) - options.body = event.data.text(); - - // 2 Actions Max - //return showLocalNotification('Yolo', event.data.text(), self.registration) - event.waitUntil( - self.registration.showNotification(title, options) - ); - } else { - console.log('Push event but no data'); - } - }) - - self.addEventListener('notificationclick', function(event) { - if (event.notification.data && event.notification.data.url) - { - event.notification.close(); - event.waitUntil( - clients.openWindow(event.notification.data.url) - ); - } else - console.log('Url undefined.'); - }) - - const showLocalNotification = (title, body, swRegistration) => { - const options = { - body, - // here you can add more properties like icon, image, vibrate, etc. - } - return swRegistration.showNotification(title, options) - } - - \ No newline at end of file -- GitLab From 1272dc17bd7f7130b2abb3076ee3c3bc28224586 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Fri, 15 Jan 2021 15:36:54 +0100 Subject: [PATCH 25/46] [#28] new css for add device, new component for deviceicon --- src/components/devices/AddDevice.css | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/components/devices/AddDevice.css diff --git a/src/components/devices/AddDevice.css b/src/components/devices/AddDevice.css new file mode 100644 index 00000000..d0ae1581 --- /dev/null +++ b/src/components/devices/AddDevice.css @@ -0,0 +1,8 @@ +.ui.form .add-device { + padding: 15px; +} + +.ui.form .add-device-advanced { + background-color: #eee; + padding: 15px; +} -- GitLab From d13a8abd07cc897a90372deeb74be4cfe61c0c00 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 19 Jan 2021 14:57:55 +0100 Subject: [PATCH 26/46] Fixed target devices --- src/components/devices/AddDevice.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/devices/AddDevice.css b/src/components/devices/AddDevice.css index d0ae1581..b6cc7510 100644 --- a/src/components/devices/AddDevice.css +++ b/src/components/devices/AddDevice.css @@ -1,5 +1,6 @@ .ui.form .add-device { - padding: 15px; + padding-left: 15px; + padding-right: 15px; } .ui.form .add-device-advanced { -- GitLab From 67bfe7c397abd60c3ec7e208540b712687967cc0 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Wed, 20 Jan 2021 14:25:59 +0100 Subject: [PATCH 27/46] gitignore css -- GitLab From 7a3966875b3c99d84c7441ba00e5e9e6a97569e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Wed, 20 Jan 2021 13:27:29 +0000 Subject: [PATCH 28/46] Delete AddDevice.css --- src/components/devices/AddDevice.css | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/components/devices/AddDevice.css diff --git a/src/components/devices/AddDevice.css b/src/components/devices/AddDevice.css deleted file mode 100644 index b6cc7510..00000000 --- a/src/components/devices/AddDevice.css +++ /dev/null @@ -1,9 +0,0 @@ -.ui.form .add-device { - padding-left: 15px; - padding-right: 15px; -} - -.ui.form .add-device-advanced { - background-color: #eee; - padding: 15px; -} -- GitLab From 04582d70c3410771bf6e50b5a648af8a257fe764 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Wed, 20 Jan 2021 13:27:46 +0000 Subject: [PATCH 29/46] Delete AddPreferences.css --- src/components/preferences/AddPreferences.css | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/components/preferences/AddPreferences.css diff --git a/src/components/preferences/AddPreferences.css b/src/components/preferences/AddPreferences.css deleted file mode 100644 index f2486b1b..00000000 --- a/src/components/preferences/AddPreferences.css +++ /dev/null @@ -1,14 +0,0 @@ -.ui.form .add-preferences-time { - display: flex; - font-weight: bold; - flex-wrap: wrap; - vertical-align: middle; } - .ui.form .add-preferences-time-label { - margin-right: 1em; - font-weight: bold; } - .ui.form .add-preferences-time-clock > input[type='text'] { - border: 2px solid #666; - width: 60px !important; - padding: 5px 8px; - color: #333; - border-radius: 3px; } -- GitLab From 3daae0be8ac5f0a90d8c194d1b84b67eaa6f2f60 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 26 Jan 2021 08:00:25 +0100 Subject: [PATCH 30/46] fixed radio boxes --- src/components/preferences/AddPreference.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/preferences/AddPreference.js b/src/components/preferences/AddPreference.js index f2e9bdf4..b2429b22 100644 --- a/src/components/preferences/AddPreference.js +++ b/src/components/preferences/AddPreference.js @@ -109,6 +109,7 @@ const AddPreference = ({ </label> </Form.Field> {filter.map(oneDevice => { + console.log(devices); return ( <Form.Field key={oneDevice.id} -- GitLab From 945e1b78b9ec03d4405c1970d7842828203d0ad0 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 26 Jan 2021 09:39:05 +0100 Subject: [PATCH 31/46] checked default device and fixed target device selection --- src/components/preferences/AddPreference.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/preferences/AddPreference.js b/src/components/preferences/AddPreference.js index b2429b22..f2e9bdf4 100644 --- a/src/components/preferences/AddPreference.js +++ b/src/components/preferences/AddPreference.js @@ -109,7 +109,6 @@ const AddPreference = ({ </label> </Form.Field> {filter.map(oneDevice => { - console.log(devices); return ( <Form.Field key={oneDevice.id} -- GitLab From 8bef1fd3298d01b34aba89f861d36a339917d167 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 28 Jan 2021 15:45:37 +0100 Subject: [PATCH 32/46] Typo fixed on channel page -- GitLab From a7af5833ff8786203c02f7950a358a15acb07287 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Sun, 31 Jan 2021 12:41:02 +0100 Subject: [PATCH 33/46] latest comments 02.02.21 fixed -- GitLab From c2a27440866e367941672655fa35a6a69a94c6e8 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Sun, 31 Jan 2021 13:31:09 +0100 Subject: [PATCH 34/46] Bugfix on frequency change reset target devices --- src/actions/preferences.js | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/actions/preferences.js b/src/actions/preferences.js index 490b6249..e0608ac0 100644 --- a/src/actions/preferences.js +++ b/src/actions/preferences.js @@ -69,23 +69,3 @@ export const toggleGlobalPreferenceForChannel = (preferenceId, channelId, isEnab ], }, }); - -export const addTargetDevice = (preferenceId, deviceId, type) => ({ - [RSAA]: { - endpoint: `${process.env.REACT_APP_BASE_URL}/preferences/${preferenceId}/target-devices/${type}/${deviceId}`, - method: 'POST', - credentials: 'include', - headers: withAuth({'Content-Type': 'application/json'}), - types: [ADD_TARGET_DEVICE, ADD_TARGET_DEVICE_SUCCESS, ADD_TARGET_DEVICE_FAILURE], - }, -}); - -export const delTargetDevice = (preferenceId, deviceId, type) => ({ - [RSAA]: { - endpoint: `${process.env.REACT_APP_BASE_URL}/preferences/${preferenceId}/target-devices/${type}/${deviceId}`, - method: 'DELETE', - credentials: 'include', - headers: withAuth({'Content-Type': 'application/json'}), - types: [DEL_TARGET_DEVICE, DEL_TARGET_DEVICE_SUCCESS, DEL_TARGET_DEVICE_FAILURE], - }, -}); -- GitLab From 675c105b57faa4a4bce3a2fbb3ab299057d7c37b Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 4 Feb 2021 08:03:40 +0100 Subject: [PATCH 35/46] Added Safari registration --- .env.development | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.development b/.env.development index b22fa355..5bfa60f8 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,4 @@ -REACT_APP_BASE_URL=https://localhost:8080 -REACT_APP_OAUTH_REDIRECT_URL=https://localhost:3000/redirect -REACT_APP_OAUTH_LOGOUT_URL=https://localhost:3000/logout +REACT_APP_BASE_URL=https://192.168.0.168:8080 +REACT_APP_OAUTH_REDIRECT_URL=https://192.168.0.168:3000/redirect +REACT_APP_OAUTH_LOGOUT_URL=https://192.168.0.168:3000/logout REACT_APP_NODE_TLS_REJECT_UNAUTHORIZED=0 -- GitLab From bac96eaa189ab9179de444b395885fe9a584bdbd Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 4 Feb 2021 08:04:14 +0100 Subject: [PATCH 36/46] Added Safari registration -- GitLab From 8437ccf84fdee1b79f083095ebfc5d4491405e19 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Thu, 4 Feb 2021 16:47:34 +0100 Subject: [PATCH 37/46] Added uuid for device and safari ref --- .env.development | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.development b/.env.development index 5bfa60f8..b22fa355 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,4 @@ -REACT_APP_BASE_URL=https://192.168.0.168:8080 -REACT_APP_OAUTH_REDIRECT_URL=https://192.168.0.168:3000/redirect -REACT_APP_OAUTH_LOGOUT_URL=https://192.168.0.168:3000/logout +REACT_APP_BASE_URL=https://localhost:8080 +REACT_APP_OAUTH_REDIRECT_URL=https://localhost:3000/redirect +REACT_APP_OAUTH_LOGOUT_URL=https://localhost:3000/logout REACT_APP_NODE_TLS_REJECT_UNAUTHORIZED=0 -- GitLab From afb9621d1752c7dfde9662849acf465e86c327f3 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Fri, 5 Feb 2021 16:49:36 +0100 Subject: [PATCH 38/46] Changed notification list and notiifcation display --- src/channels/pages/CreateChannelPage/CreateChannelPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/channels/pages/CreateChannelPage/CreateChannelPage.js b/src/channels/pages/CreateChannelPage/CreateChannelPage.js index 2adbded4..a9f6de53 100644 --- a/src/channels/pages/CreateChannelPage/CreateChannelPage.js +++ b/src/channels/pages/CreateChannelPage/CreateChannelPage.js @@ -18,7 +18,7 @@ const CreateChannelPage = ({createChannel, showSnackbar, history}) => { }, visibility: 'RESTRICTED', subscriptionPolicy: 'SELF_SUBSCRIPTION', - archive: false, + archive: true, }); const updateField = (e, d) => { -- GitLab From 41852841b26952b00278054e4f04f73fbeaab2f7 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Fri, 5 Feb 2021 16:50:37 +0100 Subject: [PATCH 39/46] Changed notification list and notiifcation display --- .../NotificationsList/NotificationList.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/notifications/components/NotificationsList/NotificationList.scss diff --git a/src/notifications/components/NotificationsList/NotificationList.scss b/src/notifications/components/NotificationsList/NotificationList.scss new file mode 100644 index 00000000..63c8e591 --- /dev/null +++ b/src/notifications/components/NotificationsList/NotificationList.scss @@ -0,0 +1,13 @@ +.ui.feed .event { + .label { + margin-top: 10px; + } + + .content .extra { + margin: 0; + } + + .content .extra.text { + font-size: small; + } +} -- GitLab From ef663edbf08629dcc59f068a04527942531bbfc4 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Mon, 15 Feb 2021 13:58:02 +0100 Subject: [PATCH 40/46] Notification Display v0 -- GitLab From 74a93e594ef2c1be053e4aed46b6bdc99a120300 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 16 Feb 2021 09:00:01 +0100 Subject: [PATCH 41/46] Notification Display v0.1 --- .../NotificationsList/NotificationList.scss | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/notifications/components/NotificationsList/NotificationList.scss diff --git a/src/notifications/components/NotificationsList/NotificationList.scss b/src/notifications/components/NotificationsList/NotificationList.scss deleted file mode 100644 index 63c8e591..00000000 --- a/src/notifications/components/NotificationsList/NotificationList.scss +++ /dev/null @@ -1,13 +0,0 @@ -.ui.feed .event { - .label { - margin-top: 10px; - } - - .content .extra { - margin: 0; - } - - .content .extra.text { - font-size: small; - } -} -- GitLab From 66ebc00c894c5cb907398b47b6e76417ebb726d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 16 Feb 2021 09:03:51 +0100 Subject: [PATCH 42/46] Notification Display v0.1 -- GitLab From cd471cd1fcdc33b85a45b2c98c93bf01c9fe4d28 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 16 Feb 2021 15:27:25 +0100 Subject: [PATCH 43/46] single notification display and nicer list --- src/common/pages/MainPage/MainPage.js | 9 +- .../pages/Notification/NotificationPage.js | 52 ---- src/components/devices/AddDevice.js | 274 ++++++++++-------- src/components/preferences/AddPreference.js | 2 - src/notifications/actions/GetNotifications.js | 15 - .../NotificationsList/NotificationsList.js | 61 ++-- .../NotificationsList/NotificationsList.scss | 22 +- .../NotificationsPage/NotificationsPage.js | 1 + .../reducers/NotificationsList.js | 9 - src/registerServiceWorker.js | 6 + src/utils/notification-type-icon.js | 10 +- 11 files changed, 213 insertions(+), 248 deletions(-) delete mode 100644 src/common/pages/Notification/NotificationPage.js diff --git a/src/common/pages/MainPage/MainPage.js b/src/common/pages/MainPage/MainPage.js index cd1b2f62..044de4e9 100644 --- a/src/common/pages/MainPage/MainPage.js +++ b/src/common/pages/MainPage/MainPage.js @@ -12,7 +12,6 @@ import CERNToolBar from '../../components/CERNToolBar/CERNToolBar'; import EditChannelPage from '../../../channels/pages/EditChannelPage/EditChannelPage'; import ChannelGlobalPreferences from '../../../channels/pages/ChannelGlobalPreferences/ChannelGlobalPreferences'; import DevicesGlobal from 'common/pages/Devices/DevicesGlobal'; -import NotificationPage from 'common/pages/Notification/NotificationPage'; const MainPage = props => { const {snackbars, isAuthenticated} = props; @@ -42,19 +41,13 @@ const MainPage = props => { <Switch> <Route exact path="/main/channels" component={AllChannelsPage} key={1} /> <Route - path="/main/channels/:channelId/notifications" + path="/main/channels/:channelId/notifications/:notificationId?" component={NotificationsPage} key={3} /> <Route path="/main/channels/:channelId" component={EditChannelPage} key={4} /> <Route path="/main/preferences" component={ChannelGlobalPreferences} key={5} /> <Route path="/main/devices" component={DevicesGlobal} key={6} /> - - <Route - path="/main/notification/:notificationId" - component={NotificationPage} - key={7} - /> </Switch> </div> </div> diff --git a/src/common/pages/Notification/NotificationPage.js b/src/common/pages/Notification/NotificationPage.js deleted file mode 100644 index b6ee6ccd..00000000 --- a/src/common/pages/Notification/NotificationPage.js +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { useParams } from 'react-router-dom'; - -import * as notificationActions from '../../../notifications/actions/GetNotifications'; -import NotificationDisplay from '../../../notifications/components/NotificationDisplay/NotificationDisplay'; - -function NotificationPage({ isAuthenticated, notification, getNotification }) { - const { notificationId } = useParams(); - - useEffect(() => { - getNotification(notificationId); - }, [getNotification, notificationId]); - - const onCloseModal = () => { - console.log('closed modal, redirecting to channel notifications list'); - console.log(notification); - //history.push(`/main/channels/${channel.id}`); - }; - - // TODO2: onclose modal -> redirect to channel notification list - return ( - isAuthenticated && - notification && ( - <NotificationDisplay - notification={notification} - modalinitialopenstate={true} - onCloseModal={onCloseModal} - /> - ) - ); -} - -NotificationPage.propTypes = { - isAuthenticated: PropTypes.bool.isRequired, -}; - -export default connect( - state => ({ - isAuthenticated: state.auth.loggedIn, - notification: state.notifications.notificationsList.notification, - }), - dispatch => - bindActionCreators( - { - getNotification: notificationActions.getNotification, - }, - dispatch - ) -)(NotificationPage); diff --git a/src/components/devices/AddDevice.js b/src/components/devices/AddDevice.js index 5c88e73e..f48737b2 100644 --- a/src/components/devices/AddDevice.js +++ b/src/components/devices/AddDevice.js @@ -1,7 +1,8 @@ import React, {useState} from 'react'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {Form, Modal, Button, Label, Message, Segment} from 'semantic-ui-react'; +import {Form, Modal, Button, Label, Radio, Message} from 'semantic-ui-react'; +import {v4 as uuidv4} from 'uuid'; import * as deviceActions from 'actions/devices'; import * as showSnackBarActionCreators from '../../common/actions/Snackbar'; @@ -12,8 +13,6 @@ import './AddDevice.scss'; const clientInformation = getClientInformation(); -// Full list -// const deviceTypesEnum = ['BROWSER', 'ANDROID', 'IOS', 'WINDOWS', 'LINUX', 'MAC', 'MAIL']; // Current supported list const deviceTypesEnum = ['BROWSER', 'APP', 'MAIL']; const deviceSubTypesEnum = [ @@ -27,7 +26,7 @@ const deviceSubTypesEnum = [ 'PRIMARY', ]; -const AddDevice = ({createDevice, showSnackbar, loading}) => { +const AddDevice = ({createDevice, showSnackbar}) => { const [deviceName, setDeviceName] = useState(clientInformation); const [deviceInfo, setDeviceInfo] = useState(navigator.userAgent); const [deviceType, setDeviceType] = useState('BROWSER'); @@ -36,6 +35,7 @@ const AddDevice = ({createDevice, showSnackbar, loading}) => { const [deviceToken, setDeviceToken] = useState(''); const [modalOpen, setModalOpen] = useState(false); + const [submitButtonDisabled, setSubmitButtonDisabled] = useState(false); const [advancedAdd, setAdvancedAdd] = useState(false); const [workerAlreadyRegistered, setWorkerAlreadyRegistered] = useState(false); @@ -46,145 +46,173 @@ const AddDevice = ({createDevice, showSnackbar, loading}) => { setDeviceSubType(isSafari() ? 'SAFARI' : 'OTHER'); setdeviceUuid(uuidv4()); setDeviceToken(''); - setModalOpen(false); }; const saveSubscriptionBlob = subscriptionBlob => { setDeviceToken(JSON.stringify(subscriptionBlob)); }; - function handleClose() { - resetFormValues(); - } - - async function handleSubmit() { - if (!deviceToken) { - showSnackbar( - 'Notification subscription data missing, please try to toggle subscription again.', - 'error' - ); - return; - } - - const response = await createDevice({ - name: deviceName, - info: deviceInfo, - type: deviceType, - token: deviceToken, - }); - - if (response.error) { - showSnackbar('An error occurred while adding your device', 'error'); - } else { - handleClose(); - showSnackbar('The device has been added successfully', 'success'); - } - } + const handleTypeChange = (e, d) => setDeviceType(d.value); + const handleSubTypeChange = (e, d) => setDeviceSubType(d.value); return ( <Modal - trigger={ - <Button primary onClick={() => setModalOpen(true)}> - Add device - </Button> - } + trigger={<Button onClick={() => setModalOpen(true)}>Add device</Button>} open={modalOpen} - onClose={handleClose} + onClose={() => setModalOpen(false)} > <Modal.Header>Add device</Modal.Header> - <Segment basic> - <Form> - <Form.Group grouped className="add-device"> - <Label color="blue" ribbon> - Specify a device or computer name for easy identification in preferences targets - </Label> - <Form.Input - label="Device name" - placeholder="Device name" - value={deviceName} - onChange={(e, d) => setDeviceName(d.value)} - /> - </Form.Group> - - {advancedAdd ? ( - <Form.Group grouped className="add-device-advanced"> - <Label color="blue" ribbon> - Advanced options, use at your own risk + <Modal.Content> + <Modal.Description> + <Form> + <Form.Group grouped className="add-device"> + <Label color="teal" ribbon> + Specify a device or computer name for easy identification in preferences targets </Label> - + <Form.Field> + <Form.Input + label="Device name" + placeholder="Device name" + value={deviceName} + onChange={(e, d) => setDeviceName(d.value)} + /> + </Form.Field> + </Form.Group> + {!advancedAdd && ( <Form.Group grouped> - <label>Device Type</label> - <Form.Group inline> - {deviceTypesEnum.map(type => ( - <Form.Radio - key={type} - label={type.charAt(0) + type.substring(1).toLowerCase()} - name="radioDeviceTypes" - value={type} - checked={deviceType === type} - onChange={(e, d) => setDeviceType(d.value)} - /> - ))} - </Form.Group> + <Button size="mini" onClick={() => setAdvancedAdd(true)} icon="settings" /> Advanced + options, use at your own risk </Form.Group> + )} + {advancedAdd && ( + <Form.Group grouped className="add-device-advanced"> + <Label color="teal" ribbon> + Advanced options, use at your own risk + </Label> + + <Form.Group grouped> + <label>Device Type</label> + <Form.Group inline> + {deviceTypesEnum.map(oneType => ( + <Form.Field key={oneType}> + <Radio + label={oneType.charAt(0) + oneType.substring(1).toLowerCase()} + control="input" + name="radioDeviceTypes" + value={oneType} + checked={deviceType === oneType} + onChange={handleTypeChange} + /> + </Form.Field> + ))} + </Form.Group> + + <label>Device Subtype</label> + <Form.Group inline> + {deviceSubTypesEnum.map(oneSubType => ( + <Form.Field key={oneSubType}> + <Radio + label={oneSubType.charAt(0) + oneSubType.substring(1).toLowerCase()} + control="input" + name="radioDeviceSubTypes" + value={oneSubType} + checked={deviceSubType === oneSubType} + onChange={handleSubTypeChange} + /> + </Form.Field> + ))} + </Form.Group> + </Form.Group> - <Form.Input - label="Information" - placeholder="Information" - value={deviceInfo} - onChange={(e, d) => setDeviceInfo(d.value)} - /> - - <Form.Input - label="Device Token" - placeholder="Device Token" - value={deviceToken} - onChange={(e, d) => setDeviceToken(d.value)} - /> - </Form.Group> - ) : ( - <Form.Group grouped> - <Button size="mini" onClick={() => setAdvancedAdd(true)} icon="settings" /> Advanced - options, use at your own risk - </Form.Group> - )} - - {deviceType === 'BROWSER' && ( - <Form.Group grouped className="add-device"> - <Label color="blue" ribbon> - Toggle to activate push notifications, click authorize if browser asks permission - </Label> - <ServiceWorkerRegistration - onSubscription={saveSubscriptionBlob} - onLoadStatus={setWorkerAlreadyRegistered} - /> - </Form.Group> - )} - - {deviceType === 'BROWSER' && workerAlreadyRegistered && ( - <Message negative> - This browser seems to be already registered. If you want to proceed anyway, make sure - you delete the other device entry if it still exists, and toggle the above - subscription box off and on. - </Message> - )} - - <Form.Button disabled={loading} loading={loading} primary onClick={handleSubmit}> - Submit - </Form.Button> - </Form> - </Segment> + <Form.Field> + <Form.Input + label="Information" + placeholder="Information" + value={deviceInfo} + onChange={(e, d) => setDeviceInfo(d.value)} + /> + </Form.Field> + + <Form.Field> + <Form.Input + label="Device Token" + placeholder="Device Token" + value={deviceToken} + onChange={(e, d) => setDeviceToken(d.value)} + /> + </Form.Field> + </Form.Group> + )} + + {deviceType === 'BROWSER' && ( + <Form.Group grouped className="add-device"> + <Label color="teal" ribbon> + Toggle to activate push notifications, click authorize if browser asks permission + </Label> + {deviceSubType !== 'SAFARI' && ( + <ServiceWorkerRegistration + onSubscription={saveSubscriptionBlob} + onLoadStatus={setWorkerAlreadyRegistered} + /> + )} + {deviceSubType === 'SAFARI' && ( + <SafariRegistration + onSubscription={saveSubscriptionBlob} + onLoadStatus={setWorkerAlreadyRegistered} + deviceUuid={deviceUuid} + /> + )} + </Form.Group> + )} + + {deviceType === 'BROWSER' && deviceSubType !== 'SAFARI' && workerAlreadyRegistered && ( + <Message negative> + This browser seems to be already registered. If you want to proceed anyway, make + sure you delete the other device entry if it still exists, and toggle the above + subscription box off and on. + </Message> + )} + + <Form.Button + disabled={submitButtonDisabled} + onClick={() => { + if (deviceToken) { + setSubmitButtonDisabled(true); + createDevice({ + name: deviceName, + info: deviceInfo, + type: deviceType, + subType: deviceSubType, + uuid: deviceUuid, + token: deviceToken, + }).then(({error}) => { + setSubmitButtonDisabled(false); + if (error) { + showSnackbar('An error occurred while adding your device', 'error'); + } else { + showSnackbar('The device has been added successfully', 'success'); + resetFormValues(); + setModalOpen(false); + } + }); + } else { + showSnackbar( + 'Notification subscription data missing, please try to toggle subscription again.', + 'error' + ); + } + }} + > + Submit + </Form.Button> + </Form> + </Modal.Description> + </Modal.Content> </Modal> ); }; -const mapStateToProps = state => { - return { - loading: state.devices.loadingCreate, - }; -}; - -export default connect(mapStateToProps, dispatch => +export default connect(null, dispatch => bindActionCreators( { createDevice: deviceActions.createDevice, diff --git a/src/components/preferences/AddPreference.js b/src/components/preferences/AddPreference.js index f2e9bdf4..462a965f 100644 --- a/src/components/preferences/AddPreference.js +++ b/src/components/preferences/AddPreference.js @@ -9,8 +9,6 @@ import * as preferenceActions from 'actions/preferences'; import * as devicesActions from 'actions/devices'; import DeviceTypeIcon from 'utils/device-type-icon'; import * as showSnackBarActionCreators from '../../common/actions/Snackbar'; -import * as devicesActions from 'actions/devices'; -import DeviceTypeIcon from 'utils/device-type-icon'; import './AddPreferences.scss'; import {notificationPriorityTypes} from '../../common/types/NotificationPriorityTypes'; diff --git a/src/notifications/actions/GetNotifications.js b/src/notifications/actions/GetNotifications.js index 9ce6b8e0..cb36ca60 100644 --- a/src/notifications/actions/GetNotifications.js +++ b/src/notifications/actions/GetNotifications.js @@ -7,11 +7,6 @@ export const GET_NOTIFICATIONS = 'GET_NOTIFICATIONS'; export const GET_NOTIFICATIONS_SUCCESS = 'GET_NOTIFICATIONS_SUCCESS'; export const GET_NOTIFICATIONS_FAILURE = 'GET_NOTIFICATIONS_FAILURE'; -// Get notification (from ID) -export const GET_NOTIFICATION = 'GET_NOTIFICATION'; -export const GET_NOTIFICATION_SUCCESS = 'GET_NOTIFICATION_SUCCESS'; -export const GET_NOTIFICATION_FAILURE = 'GET_NOTIFICATION_FAILURE'; - export const SET_GET_NOTIFICATIONS_QUERY = 'SET_GET_NOTIFICATIONS_QUERY'; export const getNotifications = (channelId, query) => ({ @@ -37,13 +32,3 @@ export const setGetNotificationsQuery = query => { payload: query, }; }; - -export const getNotification = notificationId => ({ - [RSAA]: { - endpoint: `${process.env.REACT_APP_BASE_URL}/notifications/notification/${notificationId}`, - method: 'GET', - credentials: 'include', - headers: withAuth({'Content-Type': 'application/json'}), - types: [GET_NOTIFICATION, GET_NOTIFICATION_SUCCESS, GET_NOTIFICATION_FAILURE], - }, -}); diff --git a/src/notifications/components/NotificationsList/NotificationsList.js b/src/notifications/components/NotificationsList/NotificationsList.js index 967ca285..50e22b04 100644 --- a/src/notifications/components/NotificationsList/NotificationsList.js +++ b/src/notifications/components/NotificationsList/NotificationsList.js @@ -1,8 +1,8 @@ -import React, { useEffect } from 'react'; -import { Feed } from 'semantic-ui-react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { useParams } from 'react-router-dom'; +import React, {useEffect} from 'react'; +import {Item} from 'semantic-ui-react'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {useParams} from 'react-router-dom'; import * as he from 'he'; import NotificationDisplay from '../NotificationDisplay/NotificationDisplay'; @@ -16,58 +16,51 @@ const NotificationsList = props => { notifications, getNotifications, getNotificationsQuery, - // setGetNotificationsQuery, + // setGetNotificationsQuery, // was used for search, unsure it's still needed. W8 for search fix. isAuthenticated, getPublicNotifications, } = props; - //const [activeIndex, setActiveIndex] = useState(-1); - const { channelId } = useParams(); + const {channelId, notificationId} = useParams(); useEffect(() => { if (isAuthenticated) getNotifications(channelId, getNotificationsQuery); else getPublicNotifications(channelId, getNotificationsQuery); }, [getNotifications, channelId, getNotificationsQuery, getPublicNotifications, isAuthenticated]); - // const handleClick = (e, titleProps) => { - // const { index } = titleProps; - // const newIndex = activeIndex === index ? -1 : index; - // setActiveIndex(newIndex); - // }; - const bodyPreview = body => { const preview = body.replace(/(<([^>]+)>)/gi, ''); // Using he.decode to decode html special chars return preview && he.decode(preview).substring(0, 100) + '...'; }; - // const feedIcon = priority => { - // if (priority === 'IMPORTANT') return 'warning circle'; - // else return 'info circle'; - // }; - if (notifications.length === 0) { return <p>The are no notifications in this channel</p>; } return ( <> - <Feed> + <Item.Group divided relaxed> {notifications.map((notification, index) => ( - <Feed.Event key={index} className="sep"> - <Feed.Label> - <NotificationIcon priority={notification.priority} /> - </Feed.Label> - <Feed.Content> - <Feed.Date>{new Date(notification.sentAt).toLocaleString()}</Feed.Date> - <Feed.Summary> - {/* {notification.summary} */} - <NotificationDisplay notification={notification} /> - </Feed.Summary> - <Feed.Extra text>{bodyPreview(notification.body)}</Feed.Extra> - </Feed.Content> - </Feed.Event> + <Item key={index}> + {notification.imgUrl && <Item.Image size="tiny" src={notification.imgUrl} />} + <Item.Content> + <Item.Header> + <NotificationIcon priority={notification.priority} /> + <NotificationDisplay + notification={notification} + modalinitialopenstate={ + notificationId + ? notification.id.toString() === notificationId.toString() + : false + } + /> + </Item.Header> + <Item.Description>{bodyPreview(notification.body)}</Item.Description> + <Item.Extra>{new Date(notification.sentAt).toLocaleString()}</Item.Extra> + </Item.Content> + </Item> ))} - </Feed> + </Item.Group> {/* <Accordion style={{ width: '100%' }} styled> <Visibility diff --git a/src/notifications/components/NotificationsList/NotificationsList.scss b/src/notifications/components/NotificationsList/NotificationsList.scss index 3670ff66..48b33058 100644 --- a/src/notifications/components/NotificationsList/NotificationsList.scss +++ b/src/notifications/components/NotificationsList/NotificationsList.scss @@ -15,10 +15,30 @@ } .content .extra.text { - font-size: small; + font-size: smaller; } } .ui.feed .sep { border-bottom: 1px solid $separator-lightgrey; } + +.ui.items .item .content { + .header { + // margin-top: -10px !important; + + .button { + font-size: large; + margin-top: -10px; + } + } + + .description { + margin-top: -6px !important; + } + + .extra { + margin-top: -2px; + font-size: small; + } +} diff --git a/src/notifications/pages/NotificationsPage/NotificationsPage.js b/src/notifications/pages/NotificationsPage/NotificationsPage.js index b8db4c37..5156d0bd 100644 --- a/src/notifications/pages/NotificationsPage/NotificationsPage.js +++ b/src/notifications/pages/NotificationsPage/NotificationsPage.js @@ -21,6 +21,7 @@ const NotificationsPage = ({ }) => { const [activeItem, setActiveItem] = useState('Notifications'); const {channelId} = useParams(); + const handleItemClick = (e, {name}) => setActiveItem(name); useEffect(() => { diff --git a/src/notifications/reducers/NotificationsList.js b/src/notifications/reducers/NotificationsList.js index 56d4ff43..e62a5175 100644 --- a/src/notifications/reducers/NotificationsList.js +++ b/src/notifications/reducers/NotificationsList.js @@ -3,17 +3,12 @@ import { GET_NOTIFICATIONS_SUCCESS, GET_NOTIFICATIONS_FAILURE, SET_GET_NOTIFICATIONS_QUERY, - // GET_NOTIFICATION, - GET_NOTIFICATION_SUCCESS, - // GET_NOTIFICATION_SUCCESS, - // GET_NOTIFICATION_FAILURE, } from 'notifications/actions/GetNotifications'; import {CREATE_NOTIFICATION_SUCCESS} from 'notifications/actions/CreateNotification'; const INITIAL_STATE = { notifications: [], - notification: '', getNotificationsQuery: { searchText: '', skip: 0, @@ -105,10 +100,6 @@ export default function (state = INITIAL_STATE, action) { case SET_GET_NOTIFICATIONS_QUERY: return processSetGetNotificationsQuery(state, action.payload); - case GET_NOTIFICATION_SUCCESS: { - return {...state, notification: action.payload}; - } - default: return state; } diff --git a/src/registerServiceWorker.js b/src/registerServiceWorker.js index 37798c60..9c9ff528 100644 --- a/src/registerServiceWorker.js +++ b/src/registerServiceWorker.js @@ -50,11 +50,15 @@ function checkValidServiceWorker(swUrl) { // Check if the service worker can be found. If it can't reload the page. fetch(swUrl) .then(response => { + console.log(swUrl); + // Ensure service worker exists, and that we really are getting a JS file. if ( response.status === 404 || response.headers.get('content-type').indexOf('javascript') === -1 ) { + console.log('404'); + // No service worker found. Probably a different app. Reload the page. navigator.serviceWorker.ready.then(registration => { registration.unregister().then(() => { @@ -62,6 +66,8 @@ function checkValidServiceWorker(swUrl) { }); }); } else { + console.log('register'); + // Service worker found. Proceed as normal. registerValidSW(swUrl); } diff --git a/src/utils/notification-type-icon.js b/src/utils/notification-type-icon.js index 8d523bd3..f2f7286c 100644 --- a/src/utils/notification-type-icon.js +++ b/src/utils/notification-type-icon.js @@ -2,11 +2,13 @@ import React from 'react'; import {Icon, Popup} from 'semantic-ui-react'; const NotificationIcon = props => { - if (!props.priority || props.priority === 'LOW') return null; + if (!props.priority || props.priority === 'LOW' || props.priority === 'NORMAL') return null; - let icon = <Icon name="info circle" color="green" />; - if (props.priority === 'CRITICAL') icon = <Icon name="exclamation triangle" color="red" />; - else if (props.priority === 'IMPORTANT') icon = <Icon name="warning circle" color="orange" />; + let icon = <Icon name="info circle" color="green" size="small" />; + if (props.priority === 'CRITICAL') + icon = <Icon name="exclamation triangle" color="red" size="small" />; + else if (props.priority === 'IMPORTANT') + icon = <Icon name="exclamation" color="orange" size="small" />; return ( <Popup trigger={icon} position="top center"> -- GitLab From 221ebd6fc160bfbb62346f5f2640f9b7f0d560d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 16 Feb 2021 15:29:14 +0100 Subject: [PATCH 44/46] removed default serviceworker registration --- src/registerServiceWorker.js | 120 ----------------------------------- 1 file changed, 120 deletions(-) delete mode 100644 src/registerServiceWorker.js diff --git a/src/registerServiceWorker.js b/src/registerServiceWorker.js deleted file mode 100644 index 9c9ff528..00000000 --- a/src/registerServiceWorker.js +++ /dev/null @@ -1,120 +0,0 @@ -// In production, we register a service worker to serve assets from local cache. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on the "N+1" visit to a page, since previously -// cached resources are updated in the background. - -// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. -// This link also includes instructions on opting out of this behavior. - -const isLocalhost = Boolean( - window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) -); - -function registerValidSW(swUrl) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - // eslint-disable-next-line no-param-reassign - registration.onupdatefound = () => { - const installingWorker = registration.installing; - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the old content will have been purged and - // the fresh content will have been added to the cache. - // It's the perfect time to display a "New content is - // available; please refresh." message in your web app. - console.log('New content is available; please refresh.'); - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); - } - } - }; - }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); -} - -function checkValidServiceWorker(swUrl) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - console.log(swUrl); - - // Ensure service worker exists, and that we really are getting a JS file. - if ( - response.status === 404 || - response.headers.get('content-type').indexOf('javascript') === -1 - ) { - console.log('404'); - - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - console.log('register'); - - // Service worker found. Proceed as normal. - registerValidSW(swUrl); - } - }) - .catch(() => { - console.log('No internet connection found. App is running in offline mode.'); - }); -} - -export default function register() { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 - return; - } - - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - - if (isLocalhost) { - // This is running on localhost. Lets check if a service worker still exists or not. - checkValidServiceWorker(swUrl); - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit https://goo.gl/SC7cgQ' - ); - }); - } else { - // Is not local host. Just register service worker - registerValidSW(swUrl); - } - }); - } -} - -export function unregister() { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then(registration => { - registration.unregister(); - }); - } -} -- GitLab From 987aa2d2197ffc0cfc4ca315e273f8612a2d2b3b Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Tue, 16 Feb 2021 15:32:08 +0100 Subject: [PATCH 45/46] code cleanup --- .../NotificationDisplay.js | 7 +++-- .../NotificationsList/NotificationsList.js | 27 ------------------- .../NotificationsList/NotificationsList.scss | 2 -- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/src/notifications/components/NotificationDisplay/NotificationDisplay.js b/src/notifications/components/NotificationDisplay/NotificationDisplay.js index a6224502..71a4306c 100644 --- a/src/notifications/components/NotificationDisplay/NotificationDisplay.js +++ b/src/notifications/components/NotificationDisplay/NotificationDisplay.js @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; -import { Modal, Button, Item, Image } from 'semantic-ui-react'; +import React, {useState} from 'react'; +import {Modal, Button, Item, Image} from 'semantic-ui-react'; import NotificationIcon from '../../../utils/notification-type-icon'; import './NotificationDisplay.scss'; @@ -32,14 +32,13 @@ const NotificationDisplay = props => { notification && notification.imgUrl ? notification.imgUrl : '/images/empty.png' } /> - <Item.Content> <Item.Header> <NotificationIcon priority={notification.priority} /> {notification.summary} </Item.Header> <Item.Meta>{new Date(notification.sentAt).toLocaleString()}</Item.Meta> <Item.Description> - <div dangerouslySetInnerHTML={{ __html: notification.body }} /> + <div dangerouslySetInnerHTML={{__html: notification.body}} /> </Item.Description> {notification && notification.imgUrl && ( <Item.Extra> diff --git a/src/notifications/components/NotificationsList/NotificationsList.js b/src/notifications/components/NotificationsList/NotificationsList.js index 50e22b04..54fe4c46 100644 --- a/src/notifications/components/NotificationsList/NotificationsList.js +++ b/src/notifications/components/NotificationsList/NotificationsList.js @@ -61,33 +61,6 @@ const NotificationsList = props => { </Item> ))} </Item.Group> - - {/* <Accordion style={{ width: '100%' }} styled> - <Visibility - onBottomVisible={() => { - setGetNotificationsQuery({ - ...getNotificationsQuery, - skip: getNotificationsQuery.skip + getNotificationsQuery.take, - }); - }} - > - {notifications.map((notification, index) => ( - // eslint-disable-next-line react/jsx-fragments - <Fragment> - <Accordion.Title active={activeIndex === index} index={index} onClick={handleClick}> - <Icon name="dropdown" /> - {notification.summary} - <div style={{ float: 'right' }}>{new Date(notification.sentAt).toLocaleString()}</div> - </Accordion.Title> - <Accordion.Content active={activeIndex === index}> - <div dangerouslySetInnerHTML={{ __html: notification.body }} /> - <div><img src={notification.imgUrl} border="0" alt="" /></div> - <div><a href={notification.link} target="_blank" rel="noopener noreferrer">{notification.link}</a></div> - </Accordion.Content> - </Fragment> - ))} - </Visibility> - </Accordion> */} </> ); }; diff --git a/src/notifications/components/NotificationsList/NotificationsList.scss b/src/notifications/components/NotificationsList/NotificationsList.scss index 48b33058..a75da83f 100644 --- a/src/notifications/components/NotificationsList/NotificationsList.scss +++ b/src/notifications/components/NotificationsList/NotificationsList.scss @@ -25,8 +25,6 @@ .ui.items .item .content { .header { - // margin-top: -10px !important; - .button { font-size: large; margin-top: -10px; -- GitLab From 2b68914df5c3082c1cc2acc2046ab7ddf92cda6e Mon Sep 17 00:00:00 2001 From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch> Date: Wed, 17 Feb 2021 09:02:20 +0100 Subject: [PATCH 46/46] fixed serviceworker disabling --- src/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 3b93fbd2..cacb131f 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,8 @@ import {MuiThemeProvider, createMuiTheme} from '@material-ui/core/styles'; import {createBrowserHistory} from 'history'; import configureStore from './store/ConfigureStore'; -import registerServiceWorker from './registerServiceWorker'; +// Disable for now due to conflict with push service worker +//import registerServiceWorker from './registerServiceWorker'; import App from './App'; import './index.scss'; @@ -30,4 +31,5 @@ ReactDOM.render( </MuiThemeProvider>, document.getElementById('root') ); -registerServiceWorker(); +// Disable for now due to conflict with push service worker +//registerServiceWorker(); -- GitLab