mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 19:26:33 +00:00
Add informational notice about how to disable analytics collection
Change-type: minor
This commit is contained in:
parent
ff852c029e
commit
aac092fd4d
@ -308,6 +308,7 @@ const FlowSelector = styled(
|
|||||||
|
|
||||||
interface SourceSelectorProps {
|
interface SourceSelectorProps {
|
||||||
flashing: boolean;
|
flashing: boolean;
|
||||||
|
hideAnalyticsAlert: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SourceSelectorState {
|
interface SourceSelectorState {
|
||||||
@ -359,6 +360,20 @@ export class SourceSelector extends React.Component<
|
|||||||
ipcRenderer.removeListener('select-image', this.onSelectImage);
|
ipcRenderer.removeListener('select-image', this.onSelectImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate(
|
||||||
|
_prevProps: Readonly<SourceSelectorProps>,
|
||||||
|
prevState: Readonly<SourceSelectorState>,
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
(!prevState.showDriveSelector && this.state.showDriveSelector) ||
|
||||||
|
(!prevState.showURLSelector && this.state.showURLSelector) ||
|
||||||
|
(!prevState.showImageDetails && this.state.showImageDetails) ||
|
||||||
|
(!prevState.imageSelectorOpen && this.state.imageSelectorOpen)
|
||||||
|
) {
|
||||||
|
this.props.hideAnalyticsAlert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async onSelectImage(_event: IpcRendererEvent, imagePath: string) {
|
private async onSelectImage(_event: IpcRendererEvent, imagePath: string) {
|
||||||
this.setState({ imageLoading: true });
|
this.setState({ imageLoading: true });
|
||||||
await this.selectSource(
|
await this.selectSource(
|
||||||
@ -382,6 +397,7 @@ export class SourceSelector extends React.Component<
|
|||||||
});
|
});
|
||||||
|
|
||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
|
this.props.hideAnalyticsAlert();
|
||||||
}
|
}
|
||||||
|
|
||||||
private selectSource(
|
private selectSource(
|
||||||
|
@ -100,12 +100,14 @@ interface TargetSelectorProps {
|
|||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
hasDrive: boolean;
|
hasDrive: boolean;
|
||||||
flashing: boolean;
|
flashing: boolean;
|
||||||
|
hideAnalyticsAlert: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TargetSelector = ({
|
export const TargetSelector = ({
|
||||||
disabled,
|
disabled,
|
||||||
hasDrive,
|
hasDrive,
|
||||||
flashing,
|
flashing,
|
||||||
|
hideAnalyticsAlert,
|
||||||
}: TargetSelectorProps) => {
|
}: TargetSelectorProps) => {
|
||||||
// TODO: inject these from redux-connector
|
// TODO: inject these from redux-connector
|
||||||
const [{ driveListLabel, targets }, setStateSlice] = React.useState(
|
const [{ driveListLabel, targets }, setStateSlice] = React.useState(
|
||||||
@ -137,6 +139,7 @@ export const TargetSelector = ({
|
|||||||
tooltip={driveListLabel}
|
tooltip={driveListLabel}
|
||||||
openDriveSelector={() => {
|
openDriveSelector={() => {
|
||||||
setShowTargetSelectorModal(true);
|
setShowTargetSelectorModal(true);
|
||||||
|
hideAnalyticsAlert();
|
||||||
}}
|
}}
|
||||||
reselectDrive={() => {
|
reselectDrive={() => {
|
||||||
analytics.logEvent('Reselect drive');
|
analytics.logEvent('Reselect drive');
|
||||||
|
@ -15,12 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import CogSvg from '@fortawesome/fontawesome-free/svgs/solid/gear.svg';
|
import CogSvg from '@fortawesome/fontawesome-free/svgs/solid/gear.svg';
|
||||||
|
import CloseSvg from '@fortawesome/fontawesome-free/svgs/solid/x.svg';
|
||||||
import QuestionCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle-question.svg';
|
import QuestionCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle-question.svg';
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import prettyBytes from 'pretty-bytes';
|
import prettyBytes from 'pretty-bytes';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Flex } from 'rendition';
|
import { Alert, Flex, Link } from 'rendition';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import FinishPage from '../../components/finish/finish';
|
import FinishPage from '../../components/finish/finish';
|
||||||
@ -35,6 +36,7 @@ import { observe } from '../../models/store';
|
|||||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
import { open as openExternal } from '../../os/open-external/services/open-external';
|
||||||
import {
|
import {
|
||||||
IconButton as BaseIcon,
|
IconButton as BaseIcon,
|
||||||
|
IconButton,
|
||||||
ThemedProvider,
|
ThemedProvider,
|
||||||
} from '../../styled-components';
|
} from '../../styled-components';
|
||||||
|
|
||||||
@ -46,6 +48,7 @@ import { FlashStep } from './Flash';
|
|||||||
|
|
||||||
import EtcherSvg from '../../../assets/etcher.svg';
|
import EtcherSvg from '../../../assets/etcher.svg';
|
||||||
import { SafeWebview } from '../../components/safe-webview/safe-webview';
|
import { SafeWebview } from '../../components/safe-webview/safe-webview';
|
||||||
|
import { theme } from '../../theme';
|
||||||
|
|
||||||
const Icon = styled(BaseIcon)`
|
const Icon = styled(BaseIcon)`
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
@ -97,6 +100,8 @@ const StepBorder = styled.div<{
|
|||||||
margin-left: ${(props) => (props.right ? '-120px' : undefined)};
|
margin-left: ${(props) => (props.right ? '-120px' : undefined)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ANALYTICS_ALERT_VISIBILITY_KEY = 'analytics_alert_visible';
|
||||||
|
|
||||||
interface MainPageStateFromStore {
|
interface MainPageStateFromStore {
|
||||||
isFlashing: boolean;
|
isFlashing: boolean;
|
||||||
hasImage: boolean;
|
hasImage: boolean;
|
||||||
@ -113,6 +118,7 @@ interface MainPageState {
|
|||||||
isWebviewShowing: boolean;
|
isWebviewShowing: boolean;
|
||||||
hideSettings: boolean;
|
hideSettings: boolean;
|
||||||
featuredProjectURL?: string;
|
featuredProjectURL?: string;
|
||||||
|
analyticsAlertIsVisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MainPage extends React.Component<
|
export class MainPage extends React.Component<
|
||||||
@ -125,6 +131,8 @@ export class MainPage extends React.Component<
|
|||||||
current: 'main',
|
current: 'main',
|
||||||
isWebviewShowing: false,
|
isWebviewShowing: false,
|
||||||
hideSettings: true,
|
hideSettings: true,
|
||||||
|
analyticsAlertIsVisible:
|
||||||
|
localStorage.getItem(ANALYTICS_ALERT_VISIBILITY_KEY) !== 'false',
|
||||||
...this.stateHelper(),
|
...this.stateHelper(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -153,6 +161,13 @@ export class MainPage extends React.Component<
|
|||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hideAnalyticsAlert = () => {
|
||||||
|
if (this.state.analyticsAlertIsVisible) {
|
||||||
|
localStorage.setItem(ANALYTICS_ALERT_VISIBILITY_KEY, 'false');
|
||||||
|
this.setState({ analyticsAlertIsVisible: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public async componentDidMount() {
|
public async componentDidMount() {
|
||||||
observe(() => {
|
observe(() => {
|
||||||
this.setState(this.stateHelper());
|
this.setState(this.stateHelper());
|
||||||
@ -160,6 +175,17 @@ export class MainPage extends React.Component<
|
|||||||
this.setState({ featuredProjectURL: await this.getFeaturedProjectURL() });
|
this.setState({ featuredProjectURL: await this.getFeaturedProjectURL() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate(
|
||||||
|
_prevProps: object,
|
||||||
|
prevState: Readonly<MainPageState & MainPageStateFromStore>,
|
||||||
|
) {
|
||||||
|
if (this.state.analyticsAlertIsVisible) {
|
||||||
|
if (prevState.hideSettings !== this.state.hideSettings) {
|
||||||
|
this.setState({ analyticsAlertIsVisible: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private renderMain() {
|
private renderMain() {
|
||||||
const state = flashState.getFlashState();
|
const state = flashState.getFlashState();
|
||||||
const shouldDriveStepBeDisabled = !this.state.hasImage;
|
const shouldDriveStepBeDisabled = !this.state.hasImage;
|
||||||
@ -169,86 +195,127 @@ export class MainPage extends React.Component<
|
|||||||
!this.state.isFlashing || !this.state.isWebviewShowing;
|
!this.state.isFlashing || !this.state.isWebviewShowing;
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
m={`110px ${this.state.isWebviewShowing ? 35 : 55}px`}
|
m={`110px ${this.state.isWebviewShowing ? 35 : 55}px 18px ${this.state.isWebviewShowing ? 35 : 55}px`}
|
||||||
justifyContent="space-between"
|
flexDirection="column"
|
||||||
>
|
>
|
||||||
{notFlashingOrSplitView && (
|
<Flex
|
||||||
<>
|
justifyContent="space-between"
|
||||||
<SourceSelector flashing={this.state.isFlashing} />
|
mb={this.state.analyticsAlertIsVisible ? '0px' : '92px'}
|
||||||
<Flex>
|
>
|
||||||
<StepBorder disabled={shouldDriveStepBeDisabled} left />
|
{notFlashingOrSplitView && (
|
||||||
</Flex>
|
<>
|
||||||
<TargetSelector
|
<SourceSelector
|
||||||
disabled={shouldDriveStepBeDisabled}
|
flashing={this.state.isFlashing}
|
||||||
hasDrive={this.state.hasDrive}
|
hideAnalyticsAlert={this.hideAnalyticsAlert}
|
||||||
flashing={this.state.isFlashing}
|
/>
|
||||||
/>
|
<Flex>
|
||||||
<Flex>
|
<StepBorder disabled={shouldDriveStepBeDisabled} left />
|
||||||
<StepBorder disabled={shouldFlashStepBeDisabled} right />
|
</Flex>
|
||||||
</Flex>
|
<TargetSelector
|
||||||
</>
|
disabled={shouldDriveStepBeDisabled}
|
||||||
)}
|
hasDrive={this.state.hasDrive}
|
||||||
|
flashing={this.state.isFlashing}
|
||||||
|
hideAnalyticsAlert={this.hideAnalyticsAlert}
|
||||||
|
/>
|
||||||
|
<Flex>
|
||||||
|
<StepBorder disabled={shouldFlashStepBeDisabled} right />
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{this.state.isFlashing && this.state.isWebviewShowing && (
|
{this.state.isFlashing && this.state.isWebviewShowing && (
|
||||||
<Flex
|
<Flex
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '36.2vw',
|
|
||||||
height: '100vh',
|
|
||||||
zIndex: 1,
|
|
||||||
boxShadow: '0 2px 15px 0 rgba(0, 0, 0, 0.2)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ReducedFlashingInfos
|
|
||||||
imageLogo={this.state.imageLogo}
|
|
||||||
imageName={this.state.imageName}
|
|
||||||
imageSize={
|
|
||||||
typeof this.state.imageSize === 'number'
|
|
||||||
? prettyBytes(this.state.imageSize)
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
driveTitle={this.state.driveTitle}
|
|
||||||
driveLabel={this.state.driveLabel}
|
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
color: '#fff',
|
top: 0,
|
||||||
left: 35,
|
left: 0,
|
||||||
top: 72,
|
width: '36.2vw',
|
||||||
|
height: '100vh',
|
||||||
|
zIndex: 1,
|
||||||
|
boxShadow: '0 2px 15px 0 rgba(0, 0, 0, 0.2)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ReducedFlashingInfos
|
||||||
|
imageLogo={this.state.imageLogo}
|
||||||
|
imageName={this.state.imageName}
|
||||||
|
imageSize={
|
||||||
|
typeof this.state.imageSize === 'number'
|
||||||
|
? prettyBytes(this.state.imageSize)
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
driveTitle={this.state.driveTitle}
|
||||||
|
driveLabel={this.state.driveLabel}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
color: '#fff',
|
||||||
|
left: 35,
|
||||||
|
top: 72,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
{this.state.isFlashing && this.state.featuredProjectURL && (
|
||||||
|
<SafeWebview
|
||||||
|
src={this.state.featuredProjectURL}
|
||||||
|
onWebviewShow={(isWebviewShowing: boolean) => {
|
||||||
|
this.setState({ isWebviewShowing });
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
width: '63.8vw',
|
||||||
|
height: '100vh',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
)}
|
||||||
)}
|
|
||||||
{this.state.isFlashing && this.state.featuredProjectURL && (
|
|
||||||
<SafeWebview
|
|
||||||
src={this.state.featuredProjectURL}
|
|
||||||
onWebviewShow={(isWebviewShowing: boolean) => {
|
|
||||||
this.setState({ isWebviewShowing });
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
width: '63.8vw',
|
|
||||||
height: '100vh',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FlashStep
|
<FlashStep
|
||||||
width={this.state.isWebviewShowing ? '220px' : '200px'}
|
width={this.state.isWebviewShowing ? '220px' : '200px'}
|
||||||
goToSuccess={() => this.setState({ current: 'success' })}
|
goToSuccess={() => this.setState({ current: 'success' })}
|
||||||
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
|
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
|
||||||
isFlashing={this.state.isFlashing}
|
isFlashing={this.state.isFlashing}
|
||||||
step={state.type}
|
step={state.type}
|
||||||
percentage={state.percentage}
|
percentage={state.percentage}
|
||||||
position={state.position}
|
position={state.position}
|
||||||
failed={state.failed}
|
failed={state.failed}
|
||||||
speed={state.speed}
|
speed={state.speed}
|
||||||
eta={state.eta}
|
eta={state.eta}
|
||||||
style={{ zIndex: 1 }}
|
style={{ zIndex: 1 }}
|
||||||
/>
|
/>
|
||||||
|
</Flex>
|
||||||
|
{this.state.analyticsAlertIsVisible && (
|
||||||
|
<Alert mt="18px" style={{ boxShadow: 'none', fontSize: '12px' }}>
|
||||||
|
<Flex alignItems="center" justifyContent="space-between">
|
||||||
|
<Flex flexDirection="column">
|
||||||
|
<div>
|
||||||
|
Etcher collects a limited amount of anonymous data to help us
|
||||||
|
improve user experience. You can opt out in the{' '}
|
||||||
|
<Link onClick={() => this.setState({ hideSettings: false })}>
|
||||||
|
settings
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
For more information about how we use this data, see our{' '}
|
||||||
|
<Link
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openExternal('https://www.balena.io/privacy-policy');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
privacy policy
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
{/* TODO: can we use onDismiss instead? */}
|
||||||
|
<IconButton onClick={this.hideAnalyticsAlert}>
|
||||||
|
<CloseSvg height="0.75rem" fill={theme.colors.text.main} />
|
||||||
|
</IconButton>
|
||||||
|
</Flex>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user