From a8c0a272e5b0da1fec0f01bc50d9c76587338005 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 20 Feb 2020 13:20:17 -0600 Subject: [PATCH] [#38: Enhance new repertory release available notification - partial] [Added `FocusTrap` to modals] --- CHANGELOG.md | 4 ++ package.json | 1 + src/App.js | 20 ++++++++- .../DependencyList/Dependency/Dependency.js | 8 ++-- .../NewReleases/NewRelease/NewRelease.css | 0 .../NewReleases/NewRelease/NewRelease.js | 28 ++++++++++++ src/components/NewReleases/NewReleases.css | 11 +++++ src/components/NewReleases/NewReleases.js | 35 +++++++++++++++ src/components/UI/Button/Button.js | 1 + src/components/UI/CheckBox/CheckBox.js | 3 +- src/components/UI/DropDown/DropDown.js | 3 +- src/components/UI/Modal/Modal.js | 19 +++++--- src/containers/Configuration/Configuration.js | 44 ++++++++++++------- .../ConfigurationItem/ConfigurationItem.js | 40 ++++++++++------- .../MountItems/MountItem/MountItem.js | 4 +- src/index.css | 3 ++ src/redux/actions/release_version_actions.js | 18 +++++++- src/redux/reducers/release_version_reducer.js | 15 +++++++ src/utils.js | 35 ++++++++++++++- 19 files changed, 241 insertions(+), 51 deletions(-) create mode 100644 src/components/NewReleases/NewRelease/NewRelease.css create mode 100644 src/components/NewReleases/NewRelease/NewRelease.js create mode 100644 src/components/NewReleases/NewReleases.css create mode 100644 src/components/NewReleases/NewReleases.js diff --git a/CHANGELOG.md b/CHANGELOG.md index b9baac7..737e80f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Changelog +## 1.1.5 +* \#38: Enhance new repertory release available notification +* Added `FocusTrap` to modals + ## 1.1.4 * \#39: Cleanup old releases and UI upgrades diff --git a/package.json b/package.json index 7bc616c..ce081f2 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "devtron": "^1.4.0", "electron-debug": "^3.0.1", "electron-log": "^4.0.6", + "focus-trap-react": "^6.0.0", "font-awesome": "^4.7.0", "node-schedule": "^1.3.2", "randomstring": "^1.1.5", diff --git a/src/App.js b/src/App.js index e2cdd81..c34ca6e 100644 --- a/src/App.js +++ b/src/App.js @@ -11,6 +11,7 @@ import InfoDetails from './components/InfoDetails/InfoDetails'; import IPCContainer from './containers/IPCContainer/IPCContainer'; import Loading from './components/UI/Loading/Loading'; import MountItems from './containers/MountItems/MountItems'; +import NewReleases from './components/NewReleases/NewReleases'; import {notifyError} from './redux/actions/error_actions'; import Reboot from './components/Reboot/Reboot'; import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay'; @@ -111,12 +112,26 @@ class App extends IPCContainer { !this.props.DismissDependencies && this.props.AllowMount; + const showNewReleases = !showConfig && + !this.props.DisplayConfirmYesNo && + !showDependencies && + !this.props.DownloadActive && + !this.props.DisplayError && + !this.props.DisplayInfo && + !this.props.InstallActive && + !this.props.RebootRequired && + !this.props.DisplaySelectAppPlatform && + !showUpgrade && + !this.props.DismissNewReleasesAvailable && + (this.props.NewReleasesAvailable.length > 0); + const configDisplay = createModalConditionally(showConfig, ); const confirmDisplay = createModalConditionally(this.props.DisplayConfirmYesNo, ); const dependencyDisplay = createModalConditionally(showDependencies, ); - const downloadDisplay = createModalConditionally(this.props.DownloadActive, ); + const downloadDisplay = createModalConditionally(this.props.DownloadActive, , false, true); const errorDisplay = createModalConditionally(this.props.DisplayError, , true); const infoDisplay = createModalConditionally(this.props.DisplayInfo, , true); + const newReleasesDisplay = createModalConditionally(showNewReleases, ); const rebootDisplay = createModalConditionally(this.props.RebootRequired, ); const selectAppPlatformDisplay = createModalConditionally(this.props.DisplaySelectAppPlatform, ); const upgradeDisplay = createModalConditionally(showUpgrade, ); @@ -175,6 +190,7 @@ class App extends IPCContainer { {mainContent} + {newReleasesDisplay} {selectAppPlatformDisplay} {dependencyDisplay} {upgradeDisplay} @@ -201,12 +217,14 @@ const mapStateToProps = state => { DisplayError: state.error.DisplayError, DisplayInfo: state.error.DisplayInfo, DisplaySelectAppPlatform: state.common.DisplaySelectAppPlatform, + DismissNewReleasesAvailable: state.relver.DismissNewReleasesAvailable, DownloadActive: state.download.DownloadActive, InstallActive: state.install.InstallActive, InstalledVersion: state.relver.InstalledVersion, LocationsLookup: state.relver.LocationsLookup, MissingDependencies: state.install.MissingDependencies, MountsBusy: state.mounts.MountsBusy, + NewReleasesAvailable: state.relver.NewReleasesAvailable, Platform: state.common.Platform, ProviderState: state.mounts.ProviderState, RebootRequired: state.common.RebootRequired, diff --git a/src/components/DependencyList/Dependency/Dependency.js b/src/components/DependencyList/Dependency/Dependency.js index f869874..71bc437 100644 --- a/src/components/DependencyList/Dependency/Dependency.js +++ b/src/components/DependencyList/Dependency/Dependency.js @@ -22,9 +22,9 @@ export default connect(mapStateToProps)(props => { {props.AllowDownload ? - {props.onDownload(); return false;}}>Install : + {props.onDownload(); return false;}}>Install : 'Installing...'} @@ -32,4 +32,4 @@ export default connect(mapStateToProps)(props => { ); -}); \ No newline at end of file +}); diff --git a/src/components/NewReleases/NewRelease/NewRelease.css b/src/components/NewReleases/NewRelease/NewRelease.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/NewReleases/NewRelease/NewRelease.js b/src/components/NewReleases/NewRelease/NewRelease.js new file mode 100644 index 0000000..f7765b0 --- /dev/null +++ b/src/components/NewReleases/NewRelease/NewRelease.js @@ -0,0 +1,28 @@ +import React from 'react'; +import * as Constants from '../../../constants'; +import Button from '../../UI/Button/Button'; + +export default ({release}) => { + return ( +
+

{'[' + Constants.RELEASE_TYPES[release.Release].toUpperCase() + '] ' + release.Display }

+ + + + + + + + + + +
+ + +
+
+ +
+
+ ); +}; diff --git a/src/components/NewReleases/NewReleases.css b/src/components/NewReleases/NewReleases.css new file mode 100644 index 0000000..71777c2 --- /dev/null +++ b/src/components/NewReleases/NewReleases.css @@ -0,0 +1,11 @@ +.NewReleasesHeading { + text-align: center; + margin-bottom: 4px; +} + +.NewReleasesContent { + max-height: 60vh; + min-width: 50vw; + overflow-y: auto; + margin-bottom: var(--default_spacing); +} diff --git a/src/components/NewReleases/NewReleases.js b/src/components/NewReleases/NewReleases.js new file mode 100644 index 0000000..74a2343 --- /dev/null +++ b/src/components/NewReleases/NewReleases.js @@ -0,0 +1,35 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import Box from '../UI/Box/Box'; +import Button from '../UI/Button/Button'; +import NewRelease from './NewRelease/NewRelease'; +import './NewReleases.css'; +import {setDismissNewReleasesAvailable} from '../../redux/actions/release_version_actions'; + +const mapStateToProps = state => { + return { + NewReleasesAvailable: state.relver.NewReleasesAvailable, + }; +}; + +const mapDispatchToProps = dispatch => { + return { + dismissNewReleasesAvailable: () => dispatch(setDismissNewReleasesAvailable(true)), + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(props => { + const newReleases = props.NewReleasesAvailable.map(i => { + return ; + }); + + return ( + +

New Repertory Versions Available

+
+ {newReleases} +
+ +
+ ); +}); diff --git a/src/components/UI/Button/Button.js b/src/components/UI/Button/Button.js index 5435032..ae1b462 100644 --- a/src/components/UI/Button/Button.js +++ b/src/components/UI/Button/Button.js @@ -4,6 +4,7 @@ import './Button.css'; export default props => { return ( diff --git a/src/components/UI/CheckBox/CheckBox.js b/src/components/UI/CheckBox/CheckBox.js index adc0e04..b195cf3 100644 --- a/src/components/UI/CheckBox/CheckBox.js +++ b/src/components/UI/CheckBox/CheckBox.js @@ -6,6 +6,7 @@ export default props => {
); -}; \ No newline at end of file +}; diff --git a/src/components/UI/DropDown/DropDown.js b/src/components/UI/DropDown/DropDown.js index f19cc1d..485465c 100644 --- a/src/components/UI/DropDown/DropDown.js +++ b/src/components/UI/DropDown/DropDown.js @@ -11,6 +11,7 @@ export default props => { return (
handleChanged(e)} - step={"0.01"} + step={'0.01'} className={'ConfigurationItemInput'} type={'number'} value={parseFloat(props.value).toFixed(2)}/>; break; - case "list": + case 'list': data = ; break; - case "string": + case 'string': data = handleChanged(e)} + autoFocus={props.autoFocus} className={'ConfigurationItemInput'} disabled={props.readOnly} type={'text'} value={props.value}/>; break; - case "uint8": + case 'uint8': data = handleChanged(e)} className={'ConfigurationItemInput'} @@ -80,9 +85,10 @@ export default connect(null, mapDispatchToProps)(props => { value={props.value}/>; break; - case "uint16": + case 'uint16': data = handleChanged(e)} className={'ConfigurationItemInput'} @@ -90,9 +96,10 @@ export default connect(null, mapDispatchToProps)(props => { value={props.value}/>; break; - case "uint32": + case 'uint32': data = handleChanged(e)} className={'ConfigurationItemInput'} @@ -100,9 +107,10 @@ export default connect(null, mapDispatchToProps)(props => { value={props.value}/>; break; - case "uint64": + case 'uint64': data = handleChanged(e)} className={'ConfigurationItemInput'} @@ -129,4 +137,4 @@ export default connect(null, mapDispatchToProps)(props => {
); -}); \ No newline at end of file +}); diff --git a/src/containers/MountItems/MountItem/MountItem.js b/src/containers/MountItems/MountItem/MountItem.js index 3572b47..267c221 100644 --- a/src/containers/MountItems/MountItem/MountItem.js +++ b/src/containers/MountItems/MountItem/MountItem.js @@ -152,8 +152,8 @@ export default connect(mapStateToProps, mapDispatchToProps)(props => { } }; removeControl = ( - dimensions.columns - 6} - href={void(0)} + dimensions.columns - 6} onClick={handleRemoveMount} row={secondRow + 3} style={removeStyle}> diff --git a/src/index.css b/src/index.css index ce1f473..18e202b 100644 --- a/src/index.css +++ b/src/index.css @@ -30,6 +30,9 @@ a { outline: 0; + color: var(--text_color); + text-decoration: none; + font-weight: bold; } html, body { diff --git a/src/redux/actions/release_version_actions.js b/src/redux/actions/release_version_actions.js index 7ce8b5c..aaf47f1 100644 --- a/src/redux/actions/release_version_actions.js +++ b/src/redux/actions/release_version_actions.js @@ -13,7 +13,10 @@ import { setDismissDependencies } from './install_actions'; import {unmountAll} from './mount_actions'; -import {getIPCRenderer} from '../../utils'; +import { + getIPCRenderer, + getNewReleases +} from '../../utils'; export const CLEAR_UI_UPGRADE = 'relver/clearUIUpgrade'; export const clearUIUpgrade = () => { @@ -123,11 +126,22 @@ export const loadReleases = () => { ...response.data.Locations[appPlatform], }; + const storedReleases = localStorage.getItem('releases'); + let newReleases = []; + if (storedReleases && (storedReleases.length > 0)) { + newReleases = getNewReleases(JSON.parse(storedReleases).VersionLookup, versionLookup); + } + localStorage.setItem('releases', JSON.stringify({ LocationsLookup: locationsLookup, VersionLookup: versionLookup })); dispatchActions(locationsLookup, versionLookup); + + dispatch(setNewReleasesAvailable(newReleases)); + if (getState().relver.NewReleasesAvailable.length > 0) { + dispatch(showWindow()); + } }).catch(error => { const releases = localStorage.getItem('releases'); if (releases && (releases.length > 0)) { @@ -174,8 +188,10 @@ export const setActiveRelease = (release, version) => { }; export const setAllowDismissDependencies = createAction('relver/setAllowDismissDependencies'); +export const setDismissNewReleasesAvailable = createAction('relver/setDismissNewReleasesAvailable'); export const setDismissUIUpgrade = createAction('relver/setDismissUIUpgrade'); export const setInstalledVersion = createAction('relver/setInstalledVersion'); +export const setNewReleasesAvailable = createAction('relver/setNewReleasesAvailable'); export const SET_RELEASE_DATA = 'relver/setReleaseData'; export const setReleaseData = (locationsLookup, versionLookup)=> { diff --git a/src/redux/reducers/release_version_reducer.js b/src/redux/reducers/release_version_reducer.js index 56278ac..52066ec 100644 --- a/src/redux/reducers/release_version_reducer.js +++ b/src/redux/reducers/release_version_reducer.js @@ -15,8 +15,10 @@ const versionLookup = Constants.RELEASE_TYPES.map(k=> { export const releaseVersionReducer = createReducer({ AllowDismissDependencies: false, + DismissNewReleasesAvailable: true, InstalledVersion: 'none', LocationsLookup: {}, + NewReleasesAvailable: [], Release: 0, ReleaseDefault: 0, ReleaseUpgradeAvailable: false, @@ -49,6 +51,12 @@ export const releaseVersionReducer = createReducer({ AllowDismissDependencies: action.payload, }; }, + [Actions.setDismissNewReleasesAvailable]: (state, action) => { + return { + ...state, + DismissNewReleasesAvailable: action.payload, + }; + }, [Actions.setDismissUIUpgrade]: (state, action) => { return { ...state, @@ -61,6 +69,13 @@ export const releaseVersionReducer = createReducer({ InstalledVersion: action.payload, } }, + [Actions.setNewReleasesAvailable]: (state, action) => { + return { + ...state, + DismissNewReleasesAvailable: false, + NewReleasesAvailable: action.payload, + }; + }, [Actions.SET_RELEASE_DATA]: (state, action) => { return { ...state, diff --git a/src/utils.js b/src/utils.js index b263338..b821063 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,8 +6,8 @@ const ipcRenderer = (!process.versions.hasOwnProperty('electron') && window && w window.require('electron').ipcRenderer : null; -export const createModalConditionally = (condition, jsx, critical) => { - const modalProps = {critical: critical}; +export const createModalConditionally = (condition, jsx, critical, disableFocusTrap) => { + const modalProps = {critical: critical, disableFocusTrap: disableFocusTrap}; return condition ? ({jsx}) : null; }; @@ -20,6 +20,37 @@ export const getIPCRenderer = () => { return ipcRenderer; }; +export const getNewReleases = (existingReleases, newReleases) => { + const ret = []; + + existingReleases = Constants.RELEASE_TYPES.reduce((map, release) => { + map[release] = []; + return map; + }, {}); + + if (existingReleases && newReleases) { + Constants.RELEASE_TYPES.forEach(release => { + newReleases[release] + .filter(version => !existingReleases[release].includes(version) && (version !== 'unavailable')) + .forEach(version => { + ret.splice(0, 0, { + Display: version, + Release: Constants.RELEASE_TYPES.indexOf(release), + Version: newReleases[release].indexOf(version), + }); + }); + }); + } + + ret.splice(0, 0, { + Display: '1.1.1', + Release: 0, + Version: 2, + }); + + return ret; +}; + export const getSelectedVersionFromState = state => { return (state.relver.Version === -1) ? 'unavailable' :