• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / gui / 1081664682

22 Nov 2023 02:11PM UTC coverage: 82.798% (-17.2%) from 99.964%
1081664682

Pull #4214

gitlab-ci

tranchitella
fix: Fixed the infinite page redirects when the back button is pressed

Remove the location and navigate from the useLocationParams.setValue callback
dependencies as they change the set function that is presented in other
useEffect dependencies. This happens when the back button is clicked, which
leads to the location changing infinitely.

Changelog: Title
Ticket: MEN-6847
Ticket: MEN-6796

Signed-off-by: Ihor Aleksandrychiev <ihor.aleksandrychiev@northern.tech>
Signed-off-by: Fabio Tranchitella <fabio.tranchitella@northern.tech>
Pull Request #4214: fix: Fixed the infinite page redirects when the back button is pressed

4319 of 6292 branches covered (0.0%)

8332 of 10063 relevant lines covered (82.8%)

191.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.96
/src/js/components/common/dialogs/deviceconnectiondialog.js
1
// Copyright 2019 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import React, { useEffect, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16
import { useNavigate } from 'react-router-dom';
17

18
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
19
import { makeStyles } from 'tss-react/mui';
20

21
import docker from '../../../../assets/img/docker.png';
22
import raspberryPi4 from '../../../../assets/img/raspberrypi4.png';
23
import raspberryPi from '../../../../assets/img/raspberrypi.png';
24
import { setDeviceListState } from '../../../actions/deviceActions';
25
import { advanceOnboarding } from '../../../actions/onboardingActions';
26
import { saveUserSettings } from '../../../actions/userActions';
27
import { TIMEOUTS } from '../../../constants/appConstants';
28
import { DEVICE_STATES } from '../../../constants/deviceConstants';
29
import { onboardingSteps } from '../../../constants/onboardingConstants';
30
import { getDeviceCountsByStatus, getOnboardingState, getTenantCapabilities } from '../../../selectors';
31
import InfoText from '../../common/infotext';
32
import { HELPTOOLTIPS, MenderHelpTooltip } from '../../helptips/helptooltips';
33
import DocsLink from '../docslink';
34
import PhysicalDeviceOnboarding from './physicaldeviceonboarding';
35
import VirtualDeviceOnboarding from './virtualdeviceonboarding';
36

37
const useStyles = makeStyles()(theme => ({
3✔
38
  rpiQuickstart: {
39
    backgroundColor: theme.palette.background.lightgrey
40
  },
41
  virtualLogo: { height: 40, marginLeft: theme.spacing(2) }
42
}));
43

44
const DeviceConnectionExplainer = ({ hasMonitor, setOnDevice, setVirtualDevice }) => {
2✔
45
  const { classes } = useStyles();
3✔
46
  return (
3✔
47
    <>
48
      <p>
49
        You can connect almost any device and Linux OS with Mender, but to make things simple during evaluation we recommend you use a Raspberry Pi as a test
50
        device.
51
      </p>
52
      <div className={`padding-small padding-top-none rpi-quickstart ${classes.rpiQuickstart}`}>
53
        <h3>Raspberry Pi quick start</h3>
54
        <p>We&apos;ll walk you through the steps to connect a Raspberry Pi and deploy your first update with Mender.</p>
55
        <div className="flexbox column centered">
56
          <div className="flexbox centered os-list">
57
            {[raspberryPi, raspberryPi4].map((tile, index) => (
58
              <img key={`tile-${index}`} src={tile} />
6✔
59
            ))}
60
          </div>
61
          <Button variant="contained" color="secondary" onClick={() => setOnDevice(true)}>
1✔
62
            Get Started
63
          </Button>
64
        </div>
65
      </div>
66
      <div className="two-columns margin-top">
67
        <div className="padding-small padding-top-none">
68
          <div className="flexbox center-aligned">
69
            <h3>Use a virtual device</h3>
70
            <img src={docker} className={classes.virtualLogo} />
71
          </div>
72
          <p className="margin-top-none">Don&apos;t have a Raspberry Pi?</p>
73
          <p>You can use our Docker-run virtual device to go through the same tutorial.</p>
74
          {hasMonitor && (
3!
75
            <InfoText className="slightly-smaller">
76
              If you want to evaluate our commercial components such as mender-monitor, please use a physical device instead as the virtual client does not
77
              support these components at this time.
78
            </InfoText>
79
          )}
80
          <a onClick={() => setVirtualDevice(true)}>Try a virtual device</a>
1✔
81
        </div>
82
        <div className="padding-small padding-top-none">
83
          <h3>Other devices</h3>
84
          <div>See the documentation to integrate the following with Mender:</div>
85
          <ul>
86
            {[
87
              { key: 'debian', target: 'operating-system-updates-debian-family', title: 'Debian family' },
88
              { key: 'yocto', target: 'operating-system-updates-yocto-project', title: 'Yocto OSes' }
89
            ].map(item => (
90
              <li key={item.key}>
6✔
91
                <DocsLink path={item.target} title={item.title} />
92
              </li>
93
            ))}
94
          </ul>
95
          Or visit{' '}
96
          <a href="https://hub.mender.io/c/board-integrations" target="_blank" rel="noopener noreferrer">
97
            Mender Hub
98
          </a>{' '}
99
          and search integrations for your device and OS.
100
        </div>
101
      </div>
102
      <MenderHelpTooltip id={HELPTOOLTIPS.deviceSupportTip.id} style={{ position: 'absolute', bottom: '2.5%', left: '88%' }} />
103
    </>
104
  );
105
};
106

107
export const DeviceConnectionDialog = ({ onCancel }) => {
2✔
108
  const [onDevice, setOnDevice] = useState(false);
7✔
109
  const [progress, setProgress] = useState(1);
7✔
110
  const [virtualDevice, setVirtualDevice] = useState(false);
7✔
111
  const { pending: pendingCount } = useSelector(getDeviceCountsByStatus);
7✔
112
  const [pendingDevicesCount] = useState(pendingCount);
7✔
113
  const [hasMoreDevices, setHasMoreDevices] = useState(false);
7✔
114
  const { hasMonitor } = useSelector(getTenantCapabilities);
7✔
115
  const { complete: onboardingComplete, deviceType: onboardingDeviceType } = useSelector(getOnboardingState);
7✔
116
  const dispatch = useDispatch();
7✔
117
  const navigate = useNavigate();
7✔
118

119
  useEffect(() => {
7✔
120
    setHasMoreDevices(pendingCount > pendingDevicesCount);
2✔
121
  }, [pendingDevicesCount, pendingCount]);
122

123
  useEffect(() => {
7✔
124
    if ((virtualDevice || progress >= 2) && hasMoreDevices && !window.location.hash.includes('pending')) {
3!
125
      dispatch(advanceOnboarding(onboardingSteps.DASHBOARD_ONBOARDING_START));
×
126
      dispatch(setDeviceListState({ state: DEVICE_STATES.pending }));
×
127
      navigate('/devices/pending');
×
128
    }
129
    if (virtualDevice || progress >= 2) {
3✔
130
      dispatch(saveUserSettings({ onboarding: { deviceConnection: new Date().toISOString() } }));
1✔
131
    }
132
  }, [dispatch, hasMoreDevices, navigate, progress, virtualDevice]);
133

134
  const onBackClick = () => {
7✔
135
    let updatedProgress = progress - 1;
1✔
136
    if (!updatedProgress) {
1!
137
      updatedProgress = 1;
1✔
138
      setOnDevice(false);
1✔
139
      setVirtualDevice(false);
1✔
140
    }
141
    setProgress(updatedProgress);
1✔
142
  };
143

144
  const onAdvance = () => {
7✔
145
    dispatch(advanceOnboarding(onboardingSteps.DASHBOARD_ONBOARDING_START));
×
146
    setProgress(progress + 1);
×
147
  };
148

149
  let content = <DeviceConnectionExplainer hasMonitor={hasMonitor} setOnDevice={setOnDevice} setVirtualDevice={setVirtualDevice} />;
7✔
150
  if (onDevice) {
7✔
151
    content = <PhysicalDeviceOnboarding progress={progress} />;
2✔
152
  } else if (virtualDevice) {
5✔
153
    content = <VirtualDeviceOnboarding />;
2✔
154
  }
155

156
  if (hasMoreDevices && !onboardingComplete) {
7!
157
    setTimeout(onCancel, TIMEOUTS.twoSeconds);
×
158
  }
159

160
  return (
7✔
161
    <Dialog open={true} PaperProps={{ sx: { maxWidth: '720px' } }}>
162
      <DialogTitle>Connecting a device</DialogTitle>
163
      <DialogContent className="onboard-dialog" style={{ margin: '0 30px' }}>
164
        {content}
165
      </DialogContent>
166
      <DialogActions>
167
        <Button onClick={onCancel}>Cancel</Button>
168
        <div style={{ flexGrow: 1 }} />
169
        {(onDevice || virtualDevice) && (
16✔
170
          <div>
171
            <Button onClick={onBackClick}>Back</Button>
172
            {progress < 2 && (!virtualDevice || progress < 1) ? (
14✔
173
              <Button variant="contained" disabled={!(virtualDevice || (onDevice && onboardingDeviceType))} onClick={onAdvance}>
6✔
174
                Next
175
              </Button>
176
            ) : (
177
              <Button variant="contained" disabled={!onboardingComplete} onClick={onCancel}>
178
                {onboardingComplete ? 'Close' : 'Waiting for device'}
2!
179
              </Button>
180
            )}
181
          </div>
182
        )}
183
      </DialogActions>
184
    </Dialog>
185
  );
186
};
187

188
export default DeviceConnectionDialog;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc