From 994c3d27df5db1ee11e7b01ca8bc4cbf7af5a994 Mon Sep 17 00:00:00 2001 From: louis Date: Sat, 4 Jun 2022 23:10:52 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Apply=20prettier=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .babelrc | 9 +- .eslintrc | 9 +- .github/dependabot.yml | 24 +- .github/workflows/integration.yml | 2 +- .github/workflows/quality.yaml | 31 ++ .prettierrc.json | 6 + leaflet.config.js | 22 +- package.json | 1 + postcss.config.js | 21 +- public/index.html | 11 +- src/api/Api.js | 2 +- src/api/Message.js | 30 +- src/api/Settings.js | 26 +- src/api/Ticker.js | 62 ++-- src/api/Upload.js | 14 +- src/api/User.js | 30 +- src/components/AuthService.js | 341 +++++++++---------- src/components/Clock.js | 61 ++-- src/components/EditMapModal.js | 186 +++++------ src/components/InactiveSettings.js | 399 ++++++++++++---------- src/components/LocationSearch.js | 108 +++--- src/components/Message.js | 328 +++++++++--------- src/components/MessageForm.js | 431 +++++++++++++----------- src/components/RefreshInterval.js | 215 ++++++------ src/components/Ticker.js | 308 +++++++++-------- src/components/TickerForm.js | 497 +++++++++++++++------------- src/components/TickerList.js | 366 ++++++++++---------- src/components/TickerResetButton.js | 119 ++++--- src/components/TickerUserList.js | 409 ++++++++++++----------- src/components/TickersDropdown.js | 86 ++--- src/components/TwitterCard.js | 211 ++++++------ src/components/UserList.js | 487 +++++++++++++++------------ src/components/withAuth.js | 88 ++--- src/index.css | 12 +- src/index.js | 79 +++-- src/models/Ticker.js | 165 +++++---- src/utils/common.js | 26 +- src/views/HomeView.js | 71 ++-- src/views/LoginView.js | 191 +++++------ src/views/Navigation.js | 150 +++++---- src/views/SettingsView.js | 70 ++-- src/views/TickerView.js | 287 ++++++++-------- src/views/UsersView.js | 54 +-- webpack.config.js | 134 ++++---- webpack.dev.js | 22 +- webpack.prod.js | 28 +- yarn.lock | 5 + 47 files changed, 3350 insertions(+), 2884 deletions(-) create mode 100644 .github/workflows/quality.yaml create mode 100644 .prettierrc.json diff --git a/.babelrc b/.babelrc index d93420f1..faadf6a9 100644 --- a/.babelrc +++ b/.babelrc @@ -1,9 +1,4 @@ { - "plugins": [ - "@babel/plugin-proposal-object-rest-spread" - ], - "presets": [ - "@babel/preset-env", - "@babel/preset-react" - ] + "plugins": ["@babel/plugin-proposal-object-rest-spread"], + "presets": ["@babel/preset-env", "@babel/preset-react"] } diff --git a/.eslintrc b/.eslintrc index 489dbd39..5dc4ffe5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,11 +5,6 @@ "es6": true, "node": true }, - "plugins": [ - "react" - ], - "extends": [ - "eslint:recommended", - "plugin:react/recommended" - ] + "plugins": ["react"], + "extends": ["eslint:recommended", "plugin:react/recommended"] } diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 60123264..4ba9126d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,18 +1,18 @@ --- version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: 'github-actions' + directory: '/' schedule: - interval: "weekly" - day: "friday" - time: "09:00" - timezone: "Europe/Berlin" + interval: 'weekly' + day: 'friday' + time: '09:00' + timezone: 'Europe/Berlin' - - package-ecosystem: "npm" - directory: "/" + - package-ecosystem: 'npm' + directory: '/' schedule: - interval: "weekly" - day: "friday" - time: "09:00" - timezone: "Europe/Berlin" + interval: 'weekly' + day: 'friday' + time: '09:00' + timezone: 'Europe/Berlin' diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index d38476da..832ed8d7 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - node-version: [ '14', '16' ] + node-version: ['14', '16'] steps: - name: Checkout diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml new file mode 100644 index 00000000..579f8a1b --- /dev/null +++ b/.github/workflows/quality.yaml @@ -0,0 +1,31 @@ +name: Quality + +on: + push: + branches: + - main + pull_request: + +jobs: + prettier: + name: Prettier + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: '16' + cache: 'yarn' + + - name: Install Dependencies + run: yarn install --frozen-lockfile + + - name: Run Prettier + uses: wearerequired/lint-action@v1 + with: + check_name: Prettier Results + prettier: true + prettier_dir: src diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..e803c16b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "arrowParens": "avoid", + "semi": false, + "singleQuote": true, + "tabWidth": 2 +} diff --git a/leaflet.config.js b/leaflet.config.js index 83d3223a..e056519d 100644 --- a/leaflet.config.js +++ b/leaflet.config.js @@ -1,17 +1,17 @@ -import L from 'leaflet'; +import L from 'leaflet' -import 'leaflet/dist/leaflet.css'; -import 'leaflet-draw/dist/leaflet.draw.css'; +import 'leaflet/dist/leaflet.css' +import 'leaflet-draw/dist/leaflet.draw.css' // stupid hack so that leaflet's images work after going through webpack -import marker from 'leaflet/dist/images/marker-icon.png'; -import marker2x from 'leaflet/dist/images/marker-icon-2x.png'; -import markerShadow from 'leaflet/dist/images/marker-shadow.png'; +import marker from 'leaflet/dist/images/marker-icon.png' +import marker2x from 'leaflet/dist/images/marker-icon-2x.png' +import markerShadow from 'leaflet/dist/images/marker-shadow.png' -delete L.Icon.Default.prototype._getIconUrl; +delete L.Icon.Default.prototype._getIconUrl L.Icon.Default.mergeOptions({ - iconRetinaUrl: marker2x, - iconUrl: marker, - shadowUrl: markerShadow -}); + iconRetinaUrl: marker2x, + iconUrl: marker, + shadowUrl: markerShadow, +}) diff --git a/package.json b/package.json index 1bde8de7..f814b681 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "postcss": "^7.0.27", "postcss-loader": "^3.0.0", "postcss-remove-google-fonts": "^1.1.2", + "prettier": "^2.6.2", "promise": "^8.1.0", "prop-types": "^15.7.2", "react": "^16.13.1", diff --git a/postcss.config.js b/postcss.config.js index 5515f277..417b2e79 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,12 +1,15 @@ module.exports = { - plugins: { - 'cssnano': { - preset: ['default', { - discardComments: { - removeAll: true, - }, - }] + plugins: { + cssnano: { + preset: [ + 'default', + { + discardComments: { + removeAll: true, + }, }, - 'postcss-remove-google-fonts': {}, + ], }, -}; + 'postcss-remove-google-fonts': {}, + }, +} diff --git a/public/index.html b/public/index.html index 8b88509f..313c2c1b 100644 --- a/public/index.html +++ b/public/index.html @@ -1,10 +1,13 @@ - - - - + + + + Ticker Admin diff --git a/src/api/Api.js b/src/api/Api.js index 3265f877..d005bbf7 100644 --- a/src/api/Api.js +++ b/src/api/Api.js @@ -1 +1 @@ -export let ApiUrl = process.env.REACT_APP_API_URL || 'http://localhost:8080/v1'; +export let ApiUrl = process.env.REACT_APP_API_URL || 'http://localhost:8080/v1' diff --git a/src/api/Message.js b/src/api/Message.js index 553e8b6f..837007de 100644 --- a/src/api/Message.js +++ b/src/api/Message.js @@ -1,14 +1,14 @@ -import {ApiUrl} from "./Api"; -import AuthSingleton from "../components/AuthService"; +import { ApiUrl } from './Api' +import AuthSingleton from '../components/AuthService' -const Auth = AuthSingleton.getInstance(); +const Auth = AuthSingleton.getInstance() /** * @param {string} ticker * @returns {Promise} */ export function getMessages(ticker) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${ticker}/messages`); + return Auth.fetch(`${ApiUrl}/admin/tickers/${ticker}/messages`) } /** @@ -20,14 +20,14 @@ export function getMessages(ticker) { * @returns {Promise} */ export function postMessage(ticker, text, geoInformation, attachments) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${ticker}/messages`, { - body: JSON.stringify({ - text: text, - geo_information: geoInformation, - attachments: attachments, - }), - method: 'POST' - }); + return Auth.fetch(`${ApiUrl}/admin/tickers/${ticker}/messages`, { + body: JSON.stringify({ + text: text, + geo_information: geoInformation, + attachments: attachments, + }), + method: 'POST', + }) } /** @@ -37,7 +37,7 @@ export function postMessage(ticker, text, geoInformation, attachments) { * @returns {Promise} */ export function deleteMessage(ticker, message) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${ticker}/messages/${message}`, { - method: 'DELETE' - }); + return Auth.fetch(`${ApiUrl}/admin/tickers/${ticker}/messages/${message}`, { + method: 'DELETE', + }) } diff --git a/src/api/Settings.js b/src/api/Settings.js index 1c39da43..d88f35e4 100644 --- a/src/api/Settings.js +++ b/src/api/Settings.js @@ -1,20 +1,20 @@ -import {ApiUrl} from "./Api"; -import AuthSingleton from "../components/AuthService"; +import { ApiUrl } from './Api' +import AuthSingleton from '../components/AuthService' -const Auth = AuthSingleton.getInstance(); +const Auth = AuthSingleton.getInstance() /** * @returns {Promise} */ export function getInactiveSettings() { - return Auth.fetch(`${ApiUrl}/admin/settings/inactive_settings`); + return Auth.fetch(`${ApiUrl}/admin/settings/inactive_settings`) } /** * @returns {Promise} */ export function getRefreshInterval() { - return Auth.fetch(`${ApiUrl}/admin/settings/refresh_interval`); + return Auth.fetch(`${ApiUrl}/admin/settings/refresh_interval`) } /** @@ -22,10 +22,10 @@ export function getRefreshInterval() { * @returns {Promise} */ export function putInactiveSettings(data) { - return Auth.fetch(`${ApiUrl}/admin/settings/inactive_settings`, { - body: JSON.stringify(data), - method: 'PUT' - }); + return Auth.fetch(`${ApiUrl}/admin/settings/inactive_settings`, { + body: JSON.stringify(data), + method: 'PUT', + }) } /** @@ -33,8 +33,8 @@ export function putInactiveSettings(data) { * @returns {Promise} */ export function putRefreshInterval(data) { - return Auth.fetch(`${ApiUrl}/admin/settings/refresh_interval`, { - body: JSON.stringify(data), - method: 'PUT' - }); + return Auth.fetch(`${ApiUrl}/admin/settings/refresh_interval`, { + body: JSON.stringify(data), + method: 'PUT', + }) } diff --git a/src/api/Ticker.js b/src/api/Ticker.js index 16d118b7..ce644a9f 100644 --- a/src/api/Ticker.js +++ b/src/api/Ticker.js @@ -1,13 +1,13 @@ -import {ApiUrl} from "./Api"; -import AuthSingleton from "../components/AuthService"; +import { ApiUrl } from './Api' +import AuthSingleton from '../components/AuthService' -const Auth = AuthSingleton.getInstance(); +const Auth = AuthSingleton.getInstance() /** * @returns {Promise} */ export function getTickers() { - return Auth.fetch(`${ApiUrl}/admin/tickers`); + return Auth.fetch(`${ApiUrl}/admin/tickers`) } /** @@ -16,7 +16,7 @@ export function getTickers() { * @returns {Promise} */ export function getTicker(id) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${id}`); + return Auth.fetch(`${ApiUrl}/admin/tickers/${id}`) } /** @@ -25,10 +25,10 @@ export function getTicker(id) { * @returns {Promise} */ export function postTicker(data) { - return Auth.fetch(`${ApiUrl}/admin/tickers`, { - body: JSON.stringify(data), - method: 'POST' - }); + return Auth.fetch(`${ApiUrl}/admin/tickers`, { + body: JSON.stringify(data), + method: 'POST', + }) } /** @@ -38,10 +38,10 @@ export function postTicker(data) { * @returns {Promise} */ export function putTicker(data, id) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${id}`, { - body: JSON.stringify(data), - method: 'PUT' - }); + return Auth.fetch(`${ApiUrl}/admin/tickers/${id}`, { + body: JSON.stringify(data), + method: 'PUT', + }) } /** @@ -51,10 +51,10 @@ export function putTicker(data, id) { * @returns {Promise} */ export function putTickerTwitter(data, id) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/twitter`, { - body: JSON.stringify(data), - method: 'PUT' - }); + return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/twitter`, { + body: JSON.stringify(data), + method: 'PUT', + }) } /** @@ -62,9 +62,9 @@ export function putTickerTwitter(data, id) { * @param id */ export function deleteTicker(id) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${id}`, { - method: 'DELETE' - }); + return Auth.fetch(`${ApiUrl}/admin/tickers/${id}`, { + method: 'DELETE', + }) } /** @@ -73,7 +73,7 @@ export function deleteTicker(id) { * @returns {Promise} */ export function getTickerUsers(id) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/users`); + return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/users`) } /** @@ -82,10 +82,10 @@ export function getTickerUsers(id) { * @returns {Promise} */ export function putTickerUser(id, ...users) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/users`, { - body: JSON.stringify({"users": users}), - method: 'PUT' - }) + return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/users`, { + body: JSON.stringify({ users: users }), + method: 'PUT', + }) } /** @@ -94,9 +94,9 @@ export function putTickerUser(id, ...users) { * @returns {Promise} */ export function deleteTickerUser(id, userId) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/users/${userId}`, { - method: 'DELETE' - }) + return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/users/${userId}`, { + method: 'DELETE', + }) } /** @@ -104,7 +104,7 @@ export function deleteTickerUser(id, userId) { * @returns {Promise} */ export function putTickerReset(id) { - return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/reset`, { - method: 'PUT' - }) + return Auth.fetch(`${ApiUrl}/admin/tickers/${id}/reset`, { + method: 'PUT', + }) } diff --git a/src/api/Upload.js b/src/api/Upload.js index 9d2142b3..70450358 100644 --- a/src/api/Upload.js +++ b/src/api/Upload.js @@ -1,7 +1,7 @@ -import {ApiUrl} from "./Api"; -import AuthSingleton from "../components/AuthService"; +import { ApiUrl } from './Api' +import AuthSingleton from '../components/AuthService' -const Auth = AuthSingleton.getInstance(); +const Auth = AuthSingleton.getInstance() /** * @@ -9,8 +9,8 @@ const Auth = AuthSingleton.getInstance(); * @returns {Promise} */ export function postUpload(formData) { - return Auth.fetch(`${ApiUrl}/admin/upload`, { - body: formData, - method: 'POST' - }); + return Auth.fetch(`${ApiUrl}/admin/upload`, { + body: formData, + method: 'POST', + }) } diff --git a/src/api/User.js b/src/api/User.js index 145429a3..37342bf4 100644 --- a/src/api/User.js +++ b/src/api/User.js @@ -1,13 +1,13 @@ -import {ApiUrl} from "./Api"; -import AuthSingleton from "../components/AuthService"; +import { ApiUrl } from './Api' +import AuthSingleton from '../components/AuthService' -const Auth = AuthSingleton.getInstance(); +const Auth = AuthSingleton.getInstance() /** * @returns {Promise} */ export function getUsers() { - return Auth.fetch(`${ApiUrl}/admin/users`); + return Auth.fetch(`${ApiUrl}/admin/users`) } /** @@ -15,10 +15,10 @@ export function getUsers() { * @returns {Promise} */ export function postUser(data) { - return Auth.fetch(`${ApiUrl}/admin/users`, { - body: JSON.stringify(data), - method: 'POST' - }); + return Auth.fetch(`${ApiUrl}/admin/users`, { + body: JSON.stringify(data), + method: 'POST', + }) } /** @@ -27,10 +27,10 @@ export function postUser(data) { * @returns {Promise} */ export function putUser(data, id) { - return Auth.fetch(`${ApiUrl}/admin/users/${id}`, { - body: JSON.stringify(data), - method: 'PUT' - }); + return Auth.fetch(`${ApiUrl}/admin/users/${id}`, { + body: JSON.stringify(data), + method: 'PUT', + }) } /** @@ -38,7 +38,7 @@ export function putUser(data, id) { * @returns {Promise} */ export function deleteUser(id) { - return Auth.fetch(`${ApiUrl}/admin/users/${id}`, { - method: 'DELETE' - }); + return Auth.fetch(`${ApiUrl}/admin/users/${id}`, { + method: 'DELETE', + }) } diff --git a/src/components/AuthService.js b/src/components/AuthService.js index b8a33d31..4e55ac2f 100644 --- a/src/components/AuthService.js +++ b/src/components/AuthService.js @@ -1,201 +1,202 @@ -import decode from 'jwt-decode'; -import {ApiUrl} from "../api/Api"; +import decode from 'jwt-decode' +import { ApiUrl } from '../api/Api' /** * @type {{getInstance}} */ let AuthSingleton = (function () { - let instance; + let instance + /** + * @returns {AuthService} + */ + function createInstance() { + return new AuthService() + } + + return { /** * @returns {AuthService} */ - function createInstance() { - return new AuthService(); - } + getInstance: function () { + if (!instance) { + instance = createInstance() + } - return { - /** - * @returns {AuthService} - */ - getInstance: function () { - if (!instance) { - instance = createInstance(); - } - - return instance; - } - } -})(); + return instance + }, + } +})() class AuthService { - constructor() { - this.fetch = this.fetch.bind(this); - this.login = this.login.bind(this); - this.loggedIn = this.loggedIn.bind(this); - this.getProfile = this.getProfile.bind(this); - this.checkResponse = this.checkResponse.bind(this); - this.refreshToken = this.refreshToken.bind(this); - - setInterval(() => this.refreshToken(), 60000); + constructor() { + this.fetch = this.fetch.bind(this) + this.login = this.login.bind(this) + this.loggedIn = this.loggedIn.bind(this) + this.getProfile = this.getProfile.bind(this) + this.checkResponse = this.checkResponse.bind(this) + this.refreshToken = this.refreshToken.bind(this) + + setInterval(() => this.refreshToken(), 60000) + } + + /** + * @param {string} username + * @param {string} password + * @returns {Promise} + */ + login(username, password) { + return this.fetch(`${ApiUrl}/admin/login`, { + method: 'POST', + body: JSON.stringify({ + username, + password, + }), + }).then(response => { + this.setToken(response.token) + + return Promise.resolve(response) + }) + } + + /** + * @returns {boolean} + */ + loggedIn() { + const token = this.getToken() + + return !!token && !this.isTokenExpired(token) + } + + /** + * @param {string} token + * @returns {boolean} + */ + isTokenExpired(token) { + try { + const decoded = decode(token) + + return decoded.exp < Date.now() / 1000 + } catch (err) { + return false } - - /** - * @param {string} username - * @param {string} password - * @returns {Promise} - */ - login(username, password) { - return this.fetch(`${ApiUrl}/admin/login`, { - method: 'POST', - body: JSON.stringify({ - username, - password - }) - }).then((response) => { - this.setToken(response.token); - - return Promise.resolve(response); - }); + } + + /** + * @param {string} token + */ + setToken(token) { + localStorage.setItem('id_token', token) + } + + /** + * @returns {string | null} + */ + getToken() { + return localStorage.getItem('id_token') + } + + /** + * @param {string} token + * @returns {number} + */ + getTokenExpiration(token) { + if (null !== token) { + const decoded = decode(token) + + return parseInt(decoded.exp, 10) } - /** - * @returns {boolean} - */ - loggedIn() { - const token = this.getToken(); + return 0 + } - return !!token && !this.isTokenExpired(token); + /** + * @returns {Promise|void} + */ + refreshToken() { + if (!this.loggedIn()) { + return } - /** - * @param {string} token - * @returns {boolean} - */ - isTokenExpired(token) { - try { - const decoded = decode(token); + let now = Date.now() / 1000 + let expire = this.getTokenExpiration(this.getToken()) + let limit = 600 - return decoded.exp < Date.now() / 1000; - } catch (err) { - return false; + if (now >= expire - limit) { + return this.fetch(`${ApiUrl}/admin/refresh_token`).then(response => { + if (response.token !== undefined) { + this.setToken(response.token) } - } - /** - * @param {string} token - */ - setToken(token) { - localStorage.setItem('id_token', token); + return Promise.resolve(response) + }) } - - /** - * @returns {string | null} - */ - getToken() { - return localStorage.getItem('id_token'); + } + + /** + * @returns {void} + */ + removeToken() { + localStorage.removeItem('id_token') + localStorage.removeItem('user') + } + + /** + * @returns {object} + */ + getProfile() { + return decode(this.getToken()) + } + + /** + * @param {Request|string} url + * @param {object} options + * @returns {Promise} + */ + fetch(url, options) { + let headers = { + Accept: 'application/json', + 'Content-Type': 'application/json', } - /** - * @param {string} token - * @returns {number} - */ - getTokenExpiration(token) { - if (null !== token) { - const decoded = decode(token); - - return parseInt(decoded.exp, 10); - } - - return 0; + //With formData the Content-Type will be set automatically + if (options !== undefined && options.body instanceof FormData) { + headers = {} } - /** - * @returns {Promise|void} - */ - refreshToken() { - if (!this.loggedIn()) { - return; - } - - let now = Date.now() / 1000; - let expire = this.getTokenExpiration(this.getToken()); - let limit = 600; - - if (now >= (expire - limit)) { - return this.fetch(`${ApiUrl}/admin/refresh_token`) - .then(response => { - if (response.token !== undefined) { - this.setToken(response.token); - } - - return Promise.resolve(response); - }); - } - } - - /** - * @returns {void} - */ - removeToken() { - localStorage.removeItem('id_token'); - localStorage.removeItem('user'); - } - - /** - * @returns {object} - */ - getProfile() { - return decode(this.getToken()); - } - - /** - * @param {Request|string} url - * @param {object} options - * @returns {Promise} - */ - fetch(url, options) { - let headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }; - - //With formData the Content-Type will be set automatically - if (options !== undefined && options.body instanceof FormData) { - headers = {}; - } - - if (this.loggedIn()) { - headers['Authorization'] = 'Bearer ' + this.getToken(); - } - - return fetch(url, {headers, ...options}) - .then(this.checkResponse) - .then((response) => response.json()); + if (this.loggedIn()) { + headers['Authorization'] = 'Bearer ' + this.getToken() } - /** - * @param {Response} response - * @returns {Response} - * @throws Error - */ - checkResponse(response) { - if (response.status >= 200 && response.status < 300) { - return response; - } else { - return response.json().then(data => { - console.log(response.status); - console.log(data); - let error = new Error(); - - error.message = data.error.message ? data.error.message : "Unknown error"; - error.code = data.error.code; - error.response = response; - - throw error; - }); - } + return fetch(url, { headers, ...options }) + .then(this.checkResponse) + .then(response => response.json()) + } + + /** + * @param {Response} response + * @returns {Response} + * @throws Error + */ + checkResponse(response) { + if (response.status >= 200 && response.status < 300) { + return response + } else { + return response.json().then(data => { + console.log(response.status) + console.log(data) + let error = new Error() + + error.message = data.error.message + ? data.error.message + : 'Unknown error' + error.code = data.error.code + error.response = response + + throw error + }) } + } } -export default AuthSingleton; +export default AuthSingleton diff --git a/src/components/Clock.js b/src/components/Clock.js index 6f8c94c0..6c62c9b7 100644 --- a/src/components/Clock.js +++ b/src/components/Clock.js @@ -1,42 +1,39 @@ -import React from "react"; -import Moment from "react-moment"; -import PropTypes from 'prop-types'; +import React from 'react' +import Moment from 'react-moment' +import PropTypes from 'prop-types' export default class Clock extends React.Component { - constructor(props) { - super(props); - this.state = { - date: new Date(), - format: props.format || null, - }; + constructor(props) { + super(props) + this.state = { + date: new Date(), + format: props.format || null, } + } - componentDidMount() { - this.timerID = setInterval( - () => this.tick(), - 1000 - ); - } + componentDidMount() { + this.timerID = setInterval(() => this.tick(), 1000) + } - componentWillUnmount() { - clearInterval(this.timerID); - } + componentWillUnmount() { + clearInterval(this.timerID) + } - tick() { - this.setState({ - date: new Date() - }); - } + tick() { + this.setState({ + date: new Date(), + }) + } - render() { - return ( - - {this.state.date} - - ); - } + render() { + return ( + + {this.state.date} + + ) + } } Clock.propTypes = { - format: PropTypes.string -}; + format: PropTypes.string, +} diff --git a/src/components/EditMapModal.js b/src/components/EditMapModal.js index eafd94d2..b6462cbb 100644 --- a/src/components/EditMapModal.js +++ b/src/components/EditMapModal.js @@ -1,115 +1,113 @@ -import React from "react"; -import PropTypes from 'prop-types'; -import {Map, TileLayer, FeatureGroup} from 'react-leaflet'; -import L from "leaflet"; -import {EditControl} from 'react-leaflet-draw'; -import {Button, Modal} from "semantic-ui-react"; +import React from 'react' +import PropTypes from 'prop-types' +import { Map, TileLayer, FeatureGroup } from 'react-leaflet' +import L from 'leaflet' +import { EditControl } from 'react-leaflet-draw' +import { Button, Modal } from 'semantic-ui-react' export default class EditMapModal extends React.Component { - constructor(props) { - super(props); - } + constructor(props) { + super(props) + } - onClose() { - this.props.onClose(); - } + onClose() { + this.props.onClose() + } - onSubmit() { - this.props.onSubmit(this.toGeoJSON()) - } + onSubmit() { + this.props.onSubmit(this.toGeoJSON()) + } - onFeatureGroupUpdated(ref) { - if (ref === null) { - return; - } + onFeatureGroupUpdated(ref) { + if (ref === null) { + return + } - this._editableFeatureGroup = ref; + this._editableFeatureGroup = ref - if (this.props.geoInformation.type === 'FeatureCollection') { - let geoJSON = new L.GeoJSON(this.props.geoInformation); - let featureGroup = this._editableFeatureGroup.leafletElement; + if (this.props.geoInformation.type === 'FeatureCollection') { + let geoJSON = new L.GeoJSON(this.props.geoInformation) + let featureGroup = this._editableFeatureGroup.leafletElement - geoJSON.eachLayer(layer => featureGroup.addLayer(layer)); - } + geoJSON.eachLayer(layer => featureGroup.addLayer(layer)) } + } - toGeoJSON() { - let geojson = this._editableFeatureGroup.leafletElement.toGeoJSON(); - // Clean up to submit an empty object instead of an empty feature collection - if (geojson.features.length === 0) { - geojson = {}; - } - - return geojson; + toGeoJSON() { + let geojson = this._editableFeatureGroup.leafletElement.toGeoJSON() + // Clean up to submit an empty object instead of an empty feature collection + if (geojson.features.length === 0) { + geojson = {} } - renderHeadline() { - if (this.props.geoInformation.type === 'FeatureCollection') { - return 'Change Map'; - } + return geojson + } - return 'Add Map'; + renderHeadline() { + if (this.props.geoInformation.type === 'FeatureCollection') { + return 'Change Map' } - render() { - let position = [51, 12]; - let zoom = 6; + return 'Add Map' + } - if (this.props.position[0] !== 0 && this.props.position[1] !== 0) { - position = this.props.position; - zoom = 12; - } + render() { + let position = [51, 12] + let zoom = 6 - return ( - - {this.renderHeadline()} - - - - { - this.onFeatureGroupUpdated(ref) - }}> - - - - - - - - - - - - {marker} - - - ); + resetLocation(e) { + this.setState({ location: { lat: 0.0, lon: 0.0 } }) + + e.preventDefault() + } + + handleLocationResult(result) { + this.setState({ + location: { lat: parseFloat(result.lat), lon: parseFloat(result.lon) }, + }) + } + + renderLocation() { + let position = [52, 12] + let zoom = 6 + let marker = null + let resetDisabled = true + + if (this.state.location.lat !== 0.0 && this.state.location.lon !== 0.0) { + position = [this.state.location.lat, this.state.location.lon] + zoom = 10 + resetDisabled = false + marker = ( + + + Location +
+ Latitude: {this.state.location.lat} +
+ Longitude: {this.state.location.lon} +
+
+
+ ) } - render() { - return ( - +
Location
+ + You can add a default location to the ticker. This will help you to + have a pre selected location when you add a map to a message. + + + + + + + + + + + + {marker} + + + ) + } + + render() { + return ( + +
+ {null === this.props.ticker.id + ? 'Create Ticker' + : 'Edit ' + this.props.ticker.title} +
+ +
+ + (this.form.title = input.value)} + required + /> + (this.form.domain = input.value)} + required + /> + + (this.form.active = input.checked)} + /> + (this.form.description = input.value)} + required + /> +
Information
+ + + + (this.form.information.author = input.value) + } + > + + + + + + + (this.form.information.url = input.value) + } + > + + + + + + + + + (this.form.information.email = input.value) + } + > + + + + + + + (this.form.information.twitter = input.value) + } + > + + + + + + + (this.form.information.facebook = input.value) + } + > + + + + + + {this.renderLocation()} + +
+ + + - - Reset Ticker - -

Are you sure you want to reset the ticker?

-

This will remove all messages, descriptions, the connection to twitter and disable the - ticker.

-
- - - + + Reset Ticker + +

+ Are you sure you want to reset the ticker? +

+

+ This will remove all messages, descriptions, the connection to + twitter and disable the ticker. +

+
+ + + - + - - - - {user.email} - - - ); - } - )} - - ); - } + renderDeleteButton(user) { + return ( + + + + {user.email} + + ) + })} + + ) + } + + render() { + return ( + + + + List of all granted users to this ticker. Only Admins can manage + this list. + + {this.renderUserList()} + {this.renderAddButton()} + + {this.renderDeleteModal()} + + ) + } } -export default withAuth(TickerUserList); +export default withAuth(TickerUserList) TickerUserList.propTypes = { - id: PropTypes.number, -}; + id: PropTypes.number, +} diff --git a/src/components/TickersDropdown.js b/src/components/TickersDropdown.js index e6ef9460..f0e1a3cb 100644 --- a/src/components/TickersDropdown.js +++ b/src/components/TickersDropdown.js @@ -1,48 +1,54 @@ -import React from "react"; -import {Dropdown} from "semantic-ui-react"; -import withAuth from "./withAuth"; -import {getTickers} from "../api/Ticker"; -import PropTypes from 'prop-types'; +import React from 'react' +import { Dropdown } from 'semantic-ui-react' +import withAuth from './withAuth' +import { getTickers } from '../api/Ticker' +import PropTypes from 'prop-types' class TickersDropdown extends React.Component { - constructor(props) { - super(props); + constructor(props) { + super(props) - this.state = { - tickers: [], - }; - } - - componentDidMount() { - getTickers().then(response => { - let options = []; - response.data.tickers.map(ticker => { - return options.push({key: ticker.id, text: ticker.title, value: ticker.id}); - }); - - this.setState({tickers: options}); - }); - } - - render() { - const options = this.state.tickers; - - return ( - - ); + this.state = { + tickers: [], } + } + + componentDidMount() { + getTickers().then(response => { + let options = [] + response.data.tickers.map(ticker => { + return options.push({ + key: ticker.id, + text: ticker.title, + value: ticker.id, + }) + }) + + this.setState({ tickers: options }) + }) + } + + render() { + const options = this.state.tickers + + return ( + + ) + } } -export default withAuth(TickersDropdown); +export default withAuth(TickersDropdown) TickersDropdown.propTypes = { - placeholder: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - defaultValue: PropTypes.array, -}; + placeholder: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + defaultValue: PropTypes.array, +} diff --git a/src/components/TwitterCard.js b/src/components/TwitterCard.js index 7bc0bcb9..b0811a5c 100644 --- a/src/components/TwitterCard.js +++ b/src/components/TwitterCard.js @@ -1,112 +1,127 @@ -import React from "react"; -import PropTypes from "prop-types"; -import {ApiUrl} from "../api/Api"; -import {Button, Card, Container, Icon} from "semantic-ui-react"; -import TwitterLogin from "react-twitter-auth"; -import {putTickerTwitter} from "../api/Ticker"; +import React from 'react' +import PropTypes from 'prop-types' +import { ApiUrl } from '../api/Api' +import { Button, Card, Container, Icon } from 'semantic-ui-react' +import TwitterLogin from 'react-twitter-auth' +import { putTickerTwitter } from '../api/Ticker' export default class TwitterCard extends React.Component { - constructor(props) { - super(props); - } + constructor(props) { + super(props) + } - disconnect() { - this.update(false, '', '', true); - } + disconnect() { + this.update(false, '', '', true) + } - connect(response) { - response.json().then((data) => { - this.update(true, data.access_token, data.access_secret); - }); - } + connect(response) { + response.json().then(data => { + this.update(true, data.access_token, data.access_secret) + }) + } - toggle() { - const twitter = this.props.ticker.twitter; - this.update(!twitter.active); - } + toggle() { + const twitter = this.props.ticker.twitter + this.update(!twitter.active) + } - update(active, token, secret, disconnect) { - let formData = { - "active": active, - "disconnect": disconnect || false, - "token": token || "", - "secret": secret || "", - }; - - putTickerTwitter(formData, this.props.ticker.id).then((response) => { - this.props.callback(response.data.ticker); - }); + update(active, token, secret, disconnect) { + let formData = { + active: active, + disconnect: disconnect || false, + token: token || '', + secret: secret || '', } - render() { - const loginUrl = `${ApiUrl}/admin/auth/twitter`; - const requestTokenUrl = `${ApiUrl}/admin/auth/twitter/request_token?callback=${encodeURI(window.location.origin)}`; - const twitter = this.props.ticker.twitter || {}; + putTickerTwitter(formData, this.props.ticker.id).then(response => { + this.props.callback(response.data.ticker) + }) + } - let toggleButton; - if (twitter.active) { - toggleButton = ; - } else { - toggleButton = ; - } + render() { + const loginUrl = `${ApiUrl}/admin/auth/twitter` + const requestTokenUrl = `${ApiUrl}/admin/auth/twitter/request_token?callback=${encodeURI( + window.location.origin + )}` + const twitter = this.props.ticker.twitter || {} - return (twitter.connected) ? ( - - - - - - {twitter.name} - - - @{twitter.screen_name} - - - {twitter.description} - - - - - {toggleButton} - - - - - - ) : ( - - - - You're currently not connected with Twitter. New messages will not be published to your account. - - - { - alert(error); - }} - onSuccess={this.connect.bind(this)} - >Connect - - - - ); + let toggleButton + if (twitter.active) { + toggleButton = ( + + ) + } else { + toggleButton = ( + + ) } + + return twitter.connected ? ( + + + + + + {twitter.name} + + + + @{twitter.screen_name} + + + {twitter.description} + + + + {toggleButton} + + + + + + ) : ( + + + + You're currently not connected with Twitter. New messages will not + be published to your account. + + + { + alert(error) + }} + onSuccess={this.connect.bind(this)} + > + + Connect + + + + + ) + } } TwitterCard.propTypes = { - ticker: PropTypes.object.isRequired, - callback: PropTypes.func.isRequired, -}; + ticker: PropTypes.object.isRequired, + callback: PropTypes.func.isRequired, +} diff --git a/src/components/UserList.js b/src/components/UserList.js index 3aedbdf2..c0afa39c 100644 --- a/src/components/UserList.js +++ b/src/components/UserList.js @@ -1,241 +1,288 @@ -import React from "react"; -import {Button, Confirm, Container, Form, Header, Icon, Label, Message, Modal, Table} from "semantic-ui-react"; -import {deleteUser, getUsers, postUser, putUser} from "../api/User"; -import Moment from "react-moment"; -import TickersDropdown from "./TickersDropdown"; +import React from 'react' +import { + Button, + Confirm, + Container, + Form, + Header, + Icon, + Label, + Message, + Modal, + Table, +} from 'semantic-ui-react' +import { deleteUser, getUsers, postUser, putUser } from '../api/User' +import Moment from 'react-moment' +import TickersDropdown from './TickersDropdown' export default class UserList extends React.Component { - constructor(props) { - super(props); - - this.form = {}; - - this.state = { - users: [], - deleteUser: null, - editUser: null, - showModal: false, - showDeleteConfirm: false, - }; - - this.openModal = this.openModal.bind(this); - this.renderModal = this.renderModal.bind(this); - this.handleForm = this.handleForm.bind(this); - this.handleDelete = this.handleDelete.bind(this); - this.renderForm = this.renderForm.bind(this); - this.reloadUsers = this.reloadUsers.bind(this); - } + constructor(props) { + super(props) - componentDidMount() { - this.reloadUsers(); - } + this.form = {} - reloadUsers() { - getUsers().then(response => { - this.setState({users: response.data.users}); - }); + this.state = { + users: [], + deleteUser: null, + editUser: null, + showModal: false, + showDeleteConfirm: false, } - openModal(user) { - this.setState({showModal: true, editUser: user}); - } + this.openModal = this.openModal.bind(this) + this.renderModal = this.renderModal.bind(this) + this.handleForm = this.handleForm.bind(this) + this.handleDelete = this.handleDelete.bind(this) + this.renderForm = this.renderForm.bind(this) + this.reloadUsers = this.reloadUsers.bind(this) + } - handleForm() { - let call; + componentDidMount() { + this.reloadUsers() + } - if (this.state.editUser) { - call = putUser(this.form, this.state.editUser.id); - } else { - call = postUser(this.form); - } + reloadUsers() { + getUsers().then(response => { + this.setState({ users: response.data.users }) + }) + } - call.then(() => { - this.setState({editUser: null, showModal: false}); - this.reloadUsers(); - }); - } + openModal(user) { + this.setState({ showModal: true, editUser: user }) + } - handleDelete() { - if (this.state.deleteUser) { - deleteUser(this.state.deleteUser.id).then(() => { - this.setState({showDeleteConfirm: false, deleteUser: null}); + handleForm() { + let call - this.reloadUsers(); - }); - } + if (this.state.editUser) { + call = putUser(this.form, this.state.editUser.id) + } else { + call = postUser(this.form) } - renderForm() { - return ( -
- - this.form.email = input.value} - /> - - this.form.is_super_admin = input.checked} - /> - - this.form.password = input.value} - /> - - - {this.renderTickerDropdown()} - - ); - } + call.then(() => { + this.setState({ editUser: null, showModal: false }) + this.reloadUsers() + }) + } - renderTickerDropdown() { - if ((this.state.editUser && this.state.editUser.is_super_admin) || this.form.is_super_admin) { - return ( - -
Permissions
- Admin Users don't need special permissions for Tickers. -
- ); - } - - return ( - -
Permissions
- this.form.tickers = input.value} - /> -
- ); + handleDelete() { + if (this.state.deleteUser) { + deleteUser(this.state.deleteUser.id).then(() => { + this.setState({ showDeleteConfirm: false, deleteUser: null }) + + this.reloadUsers() + }) } + } - renderModal(user) { - let isNew = this.state.editUser === undefined; - - return ( - this.setState({showModal: false, editUser: undefined})} - size='small' - > -
{isNew ? 'Create User' : 'Edit User'}
- - {this.renderForm(user)} - - - - - - - - - - - ); - } + render() { + return ( + + + + + +
+ + Login +
+
+ +
+ {this.renderError()} + + + + + + + +
+
+
+
+
+
+ ) + } } LoginView.propTypes = { - history: PropTypes.any.isRequired, -}; + history: PropTypes.any.isRequired, +} diff --git a/src/views/Navigation.js b/src/views/Navigation.js index 7062bec2..426f1f91 100644 --- a/src/views/Navigation.js +++ b/src/views/Navigation.js @@ -1,75 +1,99 @@ -import React from 'react'; -import {Container, Dropdown, Image, Menu} from 'semantic-ui-react'; -import Clock from "../components/Clock"; -import AuthSingleton from "../components/AuthService"; -import logo from "../assets/logo.png"; -import PropTypes from "prop-types"; +import React from 'react' +import { Container, Dropdown, Image, Menu } from 'semantic-ui-react' +import Clock from '../components/Clock' +import AuthSingleton from '../components/AuthService' +import logo from '../assets/logo.png' +import PropTypes from 'prop-types' -const Auth = AuthSingleton.getInstance(); +const Auth = AuthSingleton.getInstance() export default class Navigation extends React.Component { - renderUserItem() { - if (Auth.loggedIn()) { - return ( - - - { - Auth.removeToken(); - this.props.history.push("/login"); - }}> - Logout - - - - ); - } + renderUserItem() { + if (Auth.loggedIn()) { + return ( + + + { + Auth.removeToken() + this.props.history.push('/login') + }} + > + Logout + + + + ) } + } - renderUsersItem() { - if (this.props.user.is_super_admin) { - return ( - { - this.props.history.push("/users") - }}>Users - ); - } + renderUsersItem() { + if (this.props.user.is_super_admin) { + return ( + { + this.props.history.push('/users') + }} + > + Users + + ) } + } - renderSettingsItem() { - if (this.props.user.is_super_admin) { - return ( - { - this.props.history.push("/settings") - }}>Settings - ); - } + renderSettingsItem() { + if (this.props.user.is_super_admin) { + return ( + { + this.props.history.push('/settings') + }} + > + Settings + + ) } + } - render() { - return ( - - - - { - this.props.history.push("/") - }}>Home - {this.renderUsersItem()} - {this.renderSettingsItem()} - - - {this.renderUserItem()} - - - - ); - } + render() { + return ( + + + + + + { + this.props.history.push('/') + }} + > + Home + + {this.renderUsersItem()} + {this.renderSettingsItem()} + + + + + {this.renderUserItem()} + + + + ) + } } Navigation.propTypes = { - history: PropTypes.object.isRequired, - user: PropTypes.shape({ - email: PropTypes.string, - is_super_admin: PropTypes.bool.isRequired - }), -}; + history: PropTypes.object.isRequired, + user: PropTypes.shape({ + email: PropTypes.string, + is_super_admin: PropTypes.bool.isRequired, + }), +} diff --git a/src/views/SettingsView.js b/src/views/SettingsView.js index f3698ba4..ef5d14d3 100644 --- a/src/views/SettingsView.js +++ b/src/views/SettingsView.js @@ -1,41 +1,41 @@ -import React from "react"; -import {Container, Grid, Header} from "semantic-ui-react"; -import Navigation from "./Navigation"; -import InactiveSettings from "../components/InactiveSettings"; -import RefreshInterval from "../components/RefreshInterval"; -import PropTypes from "prop-types"; -import withAuth from "../components/withAuth"; +import React from 'react' +import { Container, Grid, Header } from 'semantic-ui-react' +import Navigation from './Navigation' +import InactiveSettings from '../components/InactiveSettings' +import RefreshInterval from '../components/RefreshInterval' +import PropTypes from 'prop-types' +import withAuth from '../components/withAuth' class SettingsView extends React.Component { - render() { - return ( - - - - - - -
Settings
-
-
- - - - - - - - -
-
-
- ); - } + render() { + return ( + + + + + + +
Settings
+
+
+ + + + + + + + +
+
+
+ ) + } } -export default withAuth(SettingsView); +export default withAuth(SettingsView) SettingsView.propTypes = { - history: PropTypes.object.isRequired, - user: PropTypes.object.isRequired, -}; + history: PropTypes.object.isRequired, + user: PropTypes.object.isRequired, +} diff --git a/src/views/TickerView.js b/src/views/TickerView.js index 5315f0c7..fe6ee332 100644 --- a/src/views/TickerView.js +++ b/src/views/TickerView.js @@ -1,146 +1,169 @@ -import React from "react"; -import { - Container, Feed, Grid, - Header, Loader -} from "semantic-ui-react"; -import {getTicker} from "../api/Ticker"; -import Ticker from "../components/Ticker"; -import {getMessages} from "../api/Message"; -import Message from "../components/Message"; -import withAuth from "../components/withAuth"; -import Navigation from "./Navigation"; -import PropTypes from 'prop-types'; -import TickerUserList from "../components/TickerUserList"; -import TickerResetButton from "../components/TickerResetButton"; -import MessageForm from "../components/MessageForm"; -import TwitterCard from "../components/TwitterCard"; +import React from 'react' +import { Container, Feed, Grid, Header, Loader } from 'semantic-ui-react' +import { getTicker } from '../api/Ticker' +import Ticker from '../components/Ticker' +import { getMessages } from '../api/Message' +import Message from '../components/Message' +import withAuth from '../components/withAuth' +import Navigation from './Navigation' +import PropTypes from 'prop-types' +import TickerUserList from '../components/TickerUserList' +import TickerResetButton from '../components/TickerResetButton' +import MessageForm from '../components/MessageForm' +import TwitterCard from '../components/TwitterCard' class TickerView extends React.Component { - constructor(props) { - super(props); - - this.state = { - id: props.id, - isConfigurationLoading: true, - isMessagesLoading: true, - messages: [], - ticker: {}, - }; - - this.loadMessages = this.loadMessages.bind(this); - this.updateTicker = this.updateTicker.bind(this); + constructor(props) { + super(props) + + this.state = { + id: props.id, + isConfigurationLoading: true, + isMessagesLoading: true, + messages: [], + ticker: {}, } - componentDidMount() { - this._loadTicker(); + this.loadMessages = this.loadMessages.bind(this) + this.updateTicker = this.updateTicker.bind(this) + } + + componentDidMount() { + this._loadTicker() + } + + _loadTicker() { + getTicker(this.state.id).then(response => { + if (response.data !== undefined && response.data.ticker !== undefined) { + this.setState({ + ticker: response.data.ticker, + isConfigurationLoading: false, + }) + } + }) + + this.loadMessages() + } + + updateTicker(ticker) { + if (ticker !== undefined) { + this.setState({ ticker: ticker }) } - - _loadTicker() { - getTicker(this.state.id).then((response) => { - if (response.data !== undefined && response.data.ticker !== undefined) { - this.setState({ - ticker: response.data.ticker, - isConfigurationLoading: false, - }); - } - }); - - this.loadMessages(); + } + + _renderTicker() { + if (this.state.ticker.id !== undefined) { + return ( + + ) } - - updateTicker(ticker) { - if (ticker !== undefined) { - this.setState({ticker: ticker}); - } + } + + _renderMessages() { + if (this.state.messages.length > 0) { + return ( + + {this.state.messages.map(message => ( + + ))} + + ) } - - _renderTicker() { - if (this.state.ticker.id !== undefined) { - return ( - - ); - } + } + + loadMessages() { + getMessages(this.state.id).then(response => { + if (response.data !== undefined && response.data.messages !== undefined) { + this.setState({ + messages: response.data.messages, + isMessagesLoading: false, + }) + } + }) + } + + renderUsers() { + if (null !== this.props.user && this.props.user.is_super_admin) { + return ( + +
Users
+ +
+ ) } - - _renderMessages() { - if (this.state.messages.length > 0) { - return ( - - {this.state.messages.map(message => )} - - ); - } + } + + renderDangerZone() { + if (null !== this.props.user && this.props.user.is_super_admin) { + return ( + +
Danger Zone
+ +
+ ) } - - loadMessages() { - getMessages(this.state.id).then((response) => { - if (response.data !== undefined && response.data.messages !== undefined) { - this.setState({messages: response.data.messages, isMessagesLoading: false}); + } + + reset(ticker) { + this.setState({ ticker: ticker, messages: [] }) + } + + render() { + return ( + + + + -
Users
- - - ); - } - } - - renderDangerZone() { - if (null !== this.props.user && this.props.user.is_super_admin) { - return ( - -
Danger Zone
- -
- ) - } - } - - reset(ticker) { - this.setState({ticker: ticker, messages: []}); - } - - render() { - return ( - - - - - - - -
Messages
- - {this._renderMessages()} -
- -
Configuration
- {this._renderTicker()} -
Twitter
- this.setState({ticker: ticker})}/> - {this.renderUsers()} - {this.renderDangerZone()} -
-
-
-
-
- ); - } + size="large" + /> + + + +
Messages
+ + {this._renderMessages()} +
+ +
Configuration
+ {this._renderTicker()} +
Twitter
+ this.setState({ ticker: ticker })} + /> + {this.renderUsers()} + {this.renderDangerZone()} +
+
+
+
+
+ ) + } } -export default withAuth(TickerView); +export default withAuth(TickerView) TickerView.propTypes = { - id: PropTypes.number.isRequired, - history: PropTypes.object.isRequired, - user: PropTypes.object.isRequired, -}; + id: PropTypes.number.isRequired, + history: PropTypes.object.isRequired, + user: PropTypes.object.isRequired, +} diff --git a/src/views/UsersView.js b/src/views/UsersView.js index 1aff054c..f4d282b0 100644 --- a/src/views/UsersView.js +++ b/src/views/UsersView.js @@ -1,31 +1,31 @@ -import React from "react"; -import withAuth from "../components/withAuth"; -import {Container, Grid, Header} from "semantic-ui-react"; -import Navigation from "./Navigation"; -import UserList from "../components/UserList"; +import React from 'react' +import withAuth from '../components/withAuth' +import { Container, Grid, Header } from 'semantic-ui-react' +import Navigation from './Navigation' +import UserList from '../components/UserList' class UsersView extends React.Component { - render() { - return ( - - - - - - -
Users
-
-
- - - - - -
-
-
- ); - } + render() { + return ( + + + + + + +
Users
+
+
+ + + + + +
+
+
+ ) + } } -export default withAuth(UsersView); +export default withAuth(UsersView) diff --git a/webpack.config.js b/webpack.config.js index 60d7e8fc..dacaa86c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,72 +1,68 @@ -const Dotenv = require('dotenv-webpack'); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); -const ManifestPlugin = require('webpack-manifest-plugin'); +const Dotenv = require('dotenv-webpack') +const { CleanWebpackPlugin } = require('clean-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') +const ManifestPlugin = require('webpack-manifest-plugin') module.exports = { - entry: { - app: './src/index.js' - }, - optimization: { - minimizer: [ - new UglifyJsPlugin({ - cache: true, - parallel: true, - sourceMap: true // set to true if you want JS source maps - }) - ] - }, - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - loader: 'babel-loader' - }, - { - test: /\.css$/, - use: [ - MiniCssExtractPlugin.loader, - "css-loader", - "postcss-loader" - ] - }, - { - test: /\.(png|jpg|gif|ico|svg)$/, - loader: 'file-loader', - options: { - outputPath: './images/', - name: '[name].[ext]?[hash:8]', - } - }, - { - test: /\.(woff|woff2|eot|ttf|otf)$/, - loader: 'file-loader', - options: { - outputPath: './fonts/', - name: '[name].[ext]?[hash:8]', - } - } - ] - }, - plugins: [ - new Dotenv(), - new CleanWebpackPlugin(), - new HtmlWebpackPlugin({ - template: __dirname + '/public/index.html', - favicon: __dirname + '/public/favicon.ico', - }), - new MiniCssExtractPlugin({ - filename: "[name].css?[hash:8]", - chunkFilename: "[id].css?[hash:8]" - }), - new ManifestPlugin(), + entry: { + app: './src/index.js', + }, + optimization: { + minimizer: [ + new UglifyJsPlugin({ + cache: true, + parallel: true, + sourceMap: true, // set to true if you want JS source maps + }), ], - output: { - publicPath: '/', - filename: '[name].js?[hash:8]', - path: __dirname + '/dist', - } -}; + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader', + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'], + }, + { + test: /\.(png|jpg|gif|ico|svg)$/, + loader: 'file-loader', + options: { + outputPath: './images/', + name: '[name].[ext]?[hash:8]', + }, + }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/, + loader: 'file-loader', + options: { + outputPath: './fonts/', + name: '[name].[ext]?[hash:8]', + }, + }, + ], + }, + plugins: [ + new Dotenv(), + new CleanWebpackPlugin(), + new HtmlWebpackPlugin({ + template: __dirname + '/public/index.html', + favicon: __dirname + '/public/favicon.ico', + }), + new MiniCssExtractPlugin({ + filename: '[name].css?[hash:8]', + chunkFilename: '[id].css?[hash:8]', + }), + new ManifestPlugin(), + ], + output: { + publicPath: '/', + filename: '[name].js?[hash:8]', + path: __dirname + '/dist', + }, +} diff --git a/webpack.dev.js b/webpack.dev.js index 9d5bb015..f577fb52 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -1,13 +1,13 @@ -const merge = require('webpack-merge'); -const config = require('./webpack.config.js'); +const merge = require('webpack-merge') +const config = require('./webpack.config.js') module.exports = merge(config, { - mode: 'development', - devtool: 'inline-source-map', - devServer: { - contentBase: './dist', - historyApiFallback: true, - watchContentBase: true, - port: 3000 - } -}); + mode: 'development', + devtool: 'inline-source-map', + devServer: { + contentBase: './dist', + historyApiFallback: true, + watchContentBase: true, + port: 3000, + }, +}) diff --git a/webpack.prod.js b/webpack.prod.js index c8a0c65d..95d57b8b 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -1,16 +1,16 @@ -const merge = require('webpack-merge'); -const config = require('./webpack.config.js'); -const OfflinePlugin = require('offline-plugin'); +const merge = require('webpack-merge') +const config = require('./webpack.config.js') +const OfflinePlugin = require('offline-plugin') module.exports = merge(config, { - mode: 'production', - devtool: 'none', - plugins: [ - new OfflinePlugin({ - autoUpdate: true, - ServiceWorker: { - events: true - } - }), - ] -}); + mode: 'production', + devtool: 'none', + plugins: [ + new OfflinePlugin({ + autoUpdate: true, + ServiceWorker: { + events: true, + }, + }), + ], +}) diff --git a/yarn.lock b/yarn.lock index 0eb8a7af..3280cd0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6156,6 +6156,11 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +prettier@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== + pretty-error@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"