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 (
);
-};
\ No newline at end of file
+};
diff --git a/src/components/UI/Modal/Modal.js b/src/components/UI/Modal/Modal.js
index 8fa7253..24646d8 100644
--- a/src/components/UI/Modal/Modal.js
+++ b/src/components/UI/Modal/Modal.js
@@ -1,5 +1,7 @@
import React from 'react';
+
import './Modal.css'
+import FocusTrap from 'focus-trap-react';
export default props => {
let modalStyles = [];
@@ -12,11 +14,14 @@ export default props => {
}
return (
-
-
);
-};
\ No newline at end of file
+
+ );
+};
diff --git a/src/containers/Configuration/Configuration.js b/src/containers/Configuration/Configuration.js
index 2136f49..66799a0 100644
--- a/src/containers/Configuration/Configuration.js
+++ b/src/containers/Configuration/Configuration.js
@@ -163,6 +163,8 @@ class Configuration extends IPCContainer {
ObjectLookup: objectLookup,
OriginalItemList: itemListCopy,
OriginalObjectLookup: objectLookupCopy,
+ }, () => {
+
});
} else {
this.props.notifyError(arg.data.Error);
@@ -262,20 +264,7 @@ class Configuration extends IPCContainer {
);
}
- const configurationItems = this.state.ItemList
- .map((k, i) => {
- return (
- ((!this.state.IsRemoteMount || !k.hide_remote) && (!k.advanced || (this.state.ShowAdvanced && k.advanced))) ?
-
this.handleItemChanged(e, i)}
- grouping={'Settings'}
- items={this.state.Template[k.label].items}
- key={i}
- label={k.label}
- template={this.state.Template[k.label]}
- value={k.value}/> :
- null)
- });
+ let autoFocus = true;
let objectItems = [];
for (const key of Object.keys(this.state.ObjectLookup)) {
@@ -285,9 +274,12 @@ class Configuration extends IPCContainer {
{
this.state.ObjectLookup[key].map((k, i) => {
+ const shouldFocus = autoFocus;
+ autoFocus = false;
return (
(!k.advanced || (this.state.ShowAdvanced && k.advanced && !k.remote) || this.showRemoteConfigItem(k, this.state.ObjectLookup[key])) ?
this.handleObjectItemChanged(e, key, i)}
grouping={key}
items={this.state.Template[key].template[k.label].items}
@@ -304,13 +296,33 @@ class Configuration extends IPCContainer {
));
}
+ const configurationItems = this.state.ItemList
+ .map((k, i) => {
+ const shouldFocus = autoFocus;
+ autoFocus = false;
+ return (
+ ((!this.state.IsRemoteMount || !k.hide_remote) && (!k.advanced || (this.state.ShowAdvanced && k.advanced))) ?
+ this.handleItemChanged(e, i)}
+ grouping={'Settings'}
+ items={this.state.Template[k.label].items}
+ key={i}
+ label={k.label}
+ template={this.state.Template[k.label]}
+ value={k.value}/> :
+ null
+ );
+ });
+
return (
{confirmSave}
{(
this.props.DisplayRemoteConfiguration ?
diff --git a/src/containers/Configuration/ConfigurationItem/ConfigurationItem.js b/src/containers/Configuration/ConfigurationItem/ConfigurationItem.js
index 5a4d08a..d46e9e5 100644
--- a/src/containers/Configuration/ConfigurationItem/ConfigurationItem.js
+++ b/src/containers/Configuration/ConfigurationItem/ConfigurationItem.js
@@ -18,7 +18,7 @@ export default connect(null, mapDispatchToProps)(props => {
const handleChanged = (e) => {
const target = e.target;
if (target.type === 'checkbox') {
- target.value = e.target.checked ? "true" : "false";
+ target.value = e.target.checked ? 'true' : 'false';
}
props.changed(target);
};
@@ -30,49 +30,54 @@ export default connect(null, mapDispatchToProps)(props => {
props.notifyInfo(props.label, description);
};
- infoDisplay = {displayInfo(); return false;}}>;
+ infoDisplay = {displayInfo(); return false;}}>;
}
-
+
let data;
switch (props.template.type) {
- case "bool":
+ case 'bool':
data = ;
+ disabled={props.readOnly}
+ autoFocus={props.autoFocus}/>;
break;
- case "double":
+ case 'double':
data = 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' :