mirror of
https://github.com/scratchfoundation/scratch-desktop.git
synced 2024-12-22 21:52:31 -05:00
Merge branch 'develop' into fix-telemetry-modal
This commit is contained in:
commit
3d0963fbd7
10 changed files with 410 additions and 48 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -7,6 +7,7 @@
|
|||
# File types which we know are binary
|
||||
|
||||
# Prefer LF for most file types
|
||||
*.css text eol=lf
|
||||
*.htm text eol=lf
|
||||
*.html text eol=lf
|
||||
*.js text eol=lf
|
||||
|
|
|
@ -90,6 +90,11 @@ class ScratchDesktopTelemetry {
|
|||
// make a singleton so it's easy to share across both Electron processes
|
||||
const scratchDesktopTelemetrySingleton = new ScratchDesktopTelemetry();
|
||||
|
||||
// `handle` works with `invoke`
|
||||
ipcMain.handle('getTelemetryDidOptIn', () =>
|
||||
scratchDesktopTelemetrySingleton.didOptIn
|
||||
);
|
||||
// `on` works with `sendSync` (and `send`)
|
||||
ipcMain.on('getTelemetryDidOptIn', event => {
|
||||
event.returnValue = scratchDesktopTelemetrySingleton.didOptIn;
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {BrowserWindow, Menu, app, dialog, ipcMain, systemPreferences} from 'electron';
|
||||
import {BrowserWindow, Menu, app, dialog, ipcMain, shell, systemPreferences} from 'electron';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import {URL} from 'url';
|
||||
|
@ -193,8 +193,16 @@ const createWindow = ({search = null, url = 'index.html', ...browserWindowOption
|
|||
}
|
||||
});
|
||||
|
||||
webContents.on('new-window', (event, newWindowUrl) => {
|
||||
shell.openExternal(newWindowUrl);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
const fullUrl = makeFullUrl(url, search);
|
||||
window.loadURL(fullUrl);
|
||||
window.once('ready-to-show', () => {
|
||||
webContents.send('ready-to-show');
|
||||
});
|
||||
|
||||
return window;
|
||||
};
|
||||
|
@ -210,6 +218,17 @@ const createAboutWindow = () => {
|
|||
return window;
|
||||
};
|
||||
|
||||
const createPrivacyWindow = () => {
|
||||
const window = createWindow({
|
||||
width: _windows.main.width * 0.8,
|
||||
height: _windows.main.height * 0.8,
|
||||
parent: _windows.main,
|
||||
search: 'route=privacy',
|
||||
title: 'Scratch Desktop Privacy Policy'
|
||||
});
|
||||
return window;
|
||||
};
|
||||
|
||||
const getIsProjectSave = downloadItem => {
|
||||
switch (downloadItem.getMimeType()) {
|
||||
case 'application/x.scratch.sb3':
|
||||
|
@ -371,12 +390,21 @@ app.on('ready', () => {
|
|||
event.preventDefault();
|
||||
_windows.about.hide();
|
||||
});
|
||||
_windows.privacy = createPrivacyWindow();
|
||||
_windows.privacy.on('close', event => {
|
||||
event.preventDefault();
|
||||
_windows.privacy.hide();
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('open-about-window', () => {
|
||||
_windows.about.show();
|
||||
});
|
||||
|
||||
ipcMain.on('open-privacy-policy-window', () => {
|
||||
_windows.privacy.show();
|
||||
});
|
||||
|
||||
// start loading initial project data before the GUI needs it so the load seems faster
|
||||
const initialProjectDataPromise = (async () => {
|
||||
if (argv._.length === 0) {
|
||||
|
|
39
src/renderer/about.css
Normal file
39
src/renderer/about.css
Normal file
|
@ -0,0 +1,39 @@
|
|||
html, body {
|
||||
background-color: #4D97FF;
|
||||
color: white;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
a:active, a:hover, a:link, a:visited {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
a:active, a:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.aboutBox {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.aboutLogo {
|
||||
max-width: 10rem;
|
||||
max-height: 10rem;
|
||||
}
|
||||
|
||||
.aboutText {
|
||||
margin: 1.5rem;
|
||||
}
|
||||
|
||||
.aboutDetails {
|
||||
font-size: x-small;
|
||||
}
|
||||
|
||||
.aboutFooter {
|
||||
font-size: small;
|
||||
}
|
|
@ -1,45 +1,29 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {productName, version} from '../../package.json';
|
||||
|
||||
import logo from '../icon/ScratchDesktop.svg';
|
||||
import styles from './about.css';
|
||||
|
||||
// TODO: localization?
|
||||
const AboutElement = () => (
|
||||
<div
|
||||
style={{
|
||||
color: 'white',
|
||||
fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
|
||||
fontWeight: 'bolder',
|
||||
margin: 0,
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)'
|
||||
}}
|
||||
>
|
||||
<div className={styles.aboutBox}>
|
||||
<div><img
|
||||
alt={`${productName} icon`}
|
||||
src={logo}
|
||||
style={{
|
||||
maxWidth: '10rem',
|
||||
maxHeight: '10rem'
|
||||
}}
|
||||
className={styles.aboutLogo}
|
||||
/></div>
|
||||
<div style={{margin: '1.5rem'}}>
|
||||
<div className={styles.aboutText}>
|
||||
<h2>{productName}</h2>
|
||||
<div>Version {version}</div>
|
||||
<table style={{fontSize: 'x-small'}}>
|
||||
Version {version}
|
||||
<table className={styles.aboutDetails}><tbody>
|
||||
{
|
||||
['Electron', 'Chrome'].map(component => {
|
||||
['Electron', 'Chrome', 'Node'].map(component => {
|
||||
const componentVersion = process.versions[component.toLowerCase()];
|
||||
return <tr key={component}><td>{component}</td><td>{componentVersion}</td></tr>;
|
||||
})
|
||||
}
|
||||
</table>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const appTarget = document.getElementById('app');
|
||||
ReactDOM.render(<AboutElement />, appTarget);
|
||||
export default <AboutElement />;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import {ipcRenderer, remote, shell} from 'electron';
|
||||
import {ipcRenderer, remote} from 'electron';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import omit from 'lodash.omit';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {connect} from 'react-redux';
|
||||
import {compose} from 'redux';
|
||||
import GUI from 'scratch-gui/src/index';
|
||||
|
@ -21,25 +20,17 @@ import {
|
|||
} from 'scratch-gui/src/reducers/project-state';
|
||||
import {
|
||||
openLoadingProject,
|
||||
closeLoadingProject
|
||||
closeLoadingProject,
|
||||
openTelemetryModal
|
||||
} from 'scratch-gui/src/reducers/modals';
|
||||
|
||||
import ElectronStorageHelper from '../common/ElectronStorageHelper';
|
||||
|
||||
import showPrivacyPolicy from './showPrivacyPolicy';
|
||||
import styles from './app.css';
|
||||
|
||||
// override window.open so that it uses the OS's default browser, not an electron browser
|
||||
window.open = function (url, target) {
|
||||
if (target === '_blank') {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
};
|
||||
// Register "base" page view
|
||||
// analytics.pageview('/');
|
||||
|
||||
const appTarget = document.getElementById('app');
|
||||
appTarget.className = styles.app || 'app'; // TODO
|
||||
document.body.appendChild(appTarget);
|
||||
appTarget.className = styles.app || 'app';
|
||||
|
||||
GUI.setAppElement(appTarget);
|
||||
|
||||
|
@ -80,6 +71,10 @@ const ScratchDesktopGUIHOC = function (WrappedComponent) {
|
|||
'handleTelemetryModalOptOut',
|
||||
'handleUpdateProjectTitle'
|
||||
]);
|
||||
this.state = {
|
||||
// use `sendSync` because this should be set before first render
|
||||
telemetryDidOptIn: ipcRenderer.sendSync('getTelemetryDidOptIn')
|
||||
};
|
||||
this.props.onLoadingStarted();
|
||||
ipcRenderer.invoke('get-initial-project-data').then(initialProjectData => {
|
||||
const hasInitialProject = initialProjectData && (initialProjectData.length > 0);
|
||||
|
@ -119,7 +114,7 @@ const ScratchDesktopGUIHOC = function (WrappedComponent) {
|
|||
componentWillUnmount () {
|
||||
ipcRenderer.removeListener('setTitleFromSave', this.handleSetTitleFromSave);
|
||||
}
|
||||
handleClickLogo () {
|
||||
handleClickAbout () {
|
||||
ipcRenderer.send('open-about-window');
|
||||
}
|
||||
handleProjectTelemetryEvent (event, metadata) {
|
||||
|
@ -133,22 +128,47 @@ const ScratchDesktopGUIHOC = function (WrappedComponent) {
|
|||
}
|
||||
handleTelemetryModalOptIn () {
|
||||
ipcRenderer.send('setTelemetryDidOptIn', true);
|
||||
ipcRenderer.invoke('getTelemetryDidOptIn').then(telemetryDidOptIn => {
|
||||
this.setState({telemetryDidOptIn});
|
||||
});
|
||||
}
|
||||
handleTelemetryModalOptOut () {
|
||||
ipcRenderer.send('setTelemetryDidOptIn', false);
|
||||
ipcRenderer.invoke('getTelemetryDidOptIn').then(telemetryDidOptIn => {
|
||||
this.setState({telemetryDidOptIn});
|
||||
});
|
||||
}
|
||||
handleUpdateProjectTitle (newTitle) {
|
||||
this.setState({projectTitle: newTitle});
|
||||
}
|
||||
render () {
|
||||
const shouldShowTelemetryModal = (typeof this.state.telemetryDidOptIn !== 'boolean');
|
||||
|
||||
const childProps = omit(this.props, Object.keys(ScratchDesktopGUIComponent.propTypes));
|
||||
|
||||
return (<WrappedComponent
|
||||
canEditTitle
|
||||
canModifyCloudData={false}
|
||||
canSave={false}
|
||||
isScratchDesktop
|
||||
onClickLogo={this.handleClickLogo}
|
||||
isTelemetryEnabled={this.state.telemetryDidOptIn}
|
||||
showTelemetryModal={shouldShowTelemetryModal}
|
||||
onClickAbout={[
|
||||
{
|
||||
title: 'About',
|
||||
onClick: () => this.handleClickAbout()
|
||||
},
|
||||
{
|
||||
title: 'Privacy Policy',
|
||||
onClick: () => showPrivacyPolicy()
|
||||
},
|
||||
{
|
||||
title: 'Data Settings',
|
||||
onClick: () => this.props.onTelemetrySettingsClicked()
|
||||
}
|
||||
]}
|
||||
onProjectTelemetryEvent={this.handleProjectTelemetryEvent}
|
||||
onShowPrivacyPolicy={showPrivacyPolicy}
|
||||
onStorageInit={this.handleStorageInit}
|
||||
onTelemetryModalOptIn={this.handleTelemetryModalOptIn}
|
||||
onTelemetryModalOptOut={this.handleTelemetryModalOptOut}
|
||||
|
@ -168,6 +188,7 @@ const ScratchDesktopGUIHOC = function (WrappedComponent) {
|
|||
onLoadingCompleted: PropTypes.func,
|
||||
onLoadingStarted: PropTypes.func,
|
||||
onRequestNewProject: PropTypes.func,
|
||||
onTelemetrySettingsClicked: PropTypes.func,
|
||||
// using PropTypes.instanceOf(VM) here will cause prop type warnings due to VM mismatch
|
||||
vm: GUIComponent.WrappedComponent.propTypes.vm
|
||||
};
|
||||
|
@ -197,7 +218,8 @@ const ScratchDesktopGUIHOC = function (WrappedComponent) {
|
|||
const canSaveToServer = false;
|
||||
return dispatch(onLoadedProject(loadingState, canSaveToServer, loadSuccess));
|
||||
},
|
||||
onRequestNewProject: () => dispatch(requestNewProject(false))
|
||||
onRequestNewProject: () => dispatch(requestNewProject(false)),
|
||||
onTelemetrySettingsClicked: () => dispatch(openTelemetryModal())
|
||||
});
|
||||
|
||||
return connect(mapStateToProps, mapDispatchToProps)(ScratchDesktopGUIComponent);
|
||||
|
@ -212,4 +234,4 @@ const WrappedGui = compose(
|
|||
ScratchDesktopGUIHOC
|
||||
)(GUI);
|
||||
|
||||
ReactDOM.render(<WrappedGui />, appTarget);
|
||||
export default <WrappedGui />;
|
||||
|
|
|
@ -1,12 +1,33 @@
|
|||
// this is an async import so that it doesn't block the first render
|
||||
// index.html contains a loading/splash screen which will display while this import loads
|
||||
// This file does async imports of the heavy JSX, especially app.jsx, to avoid blocking the first render.
|
||||
// The main index.html just contains a loading/splash screen which will display while this import loads.
|
||||
|
||||
import {ipcRenderer} from 'electron';
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
ipcRenderer.on('ready-to-show', () => {
|
||||
// Start without any element in focus, otherwise the first link starts with focus and shows an orange box.
|
||||
// We shouldn't disable that box or the focus behavior in case someone wants or needs to navigate that way.
|
||||
// This seems like a hack... maybe there's some better way to do avoid any element starting with focus?
|
||||
document.activeElement.blur();
|
||||
});
|
||||
|
||||
const route = new URLSearchParams(window.location.search).get('route') || 'app';
|
||||
let routeModulePromise;
|
||||
switch (route) {
|
||||
case 'app':
|
||||
import('./app.jsx'); // eslint-disable-line no-unused-expressions
|
||||
routeModulePromise = import('./app.jsx');
|
||||
break;
|
||||
case 'about':
|
||||
import('./about.jsx'); // eslint-disable-line no-unused-expressions
|
||||
routeModulePromise = import('./about.jsx');
|
||||
break;
|
||||
case 'privacy':
|
||||
routeModulePromise = import('./privacy.jsx');
|
||||
break;
|
||||
}
|
||||
|
||||
routeModulePromise.then(routeModule => {
|
||||
const appTarget = document.getElementById('app');
|
||||
const routeElement = routeModule.default;
|
||||
ReactDOM.render(routeElement, appTarget);
|
||||
});
|
||||
|
|
14
src/renderer/privacy.css
Normal file
14
src/renderer/privacy.css
Normal file
|
@ -0,0 +1,14 @@
|
|||
html, body {
|
||||
background-color: #4D97FF;
|
||||
color: white;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: normal;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
.privacyBox {
|
||||
background-color: white;
|
||||
color: #575e75;
|
||||
margin: 3rem;
|
||||
padding: 2rem 3rem;
|
||||
}
|
235
src/renderer/privacy.jsx
Normal file
235
src/renderer/privacy.jsx
Normal file
|
@ -0,0 +1,235 @@
|
|||
import React from 'react';
|
||||
|
||||
import styles from './privacy.css';
|
||||
|
||||
const PrivacyElement = () => (
|
||||
<div className={styles.privacyBox}>
|
||||
<h1>Privacy Policy</h1>
|
||||
<i>The Scratch Privacy Policy was last updated: October 5, 2020</i>
|
||||
<p>
|
||||
The Scratch Foundation (“Scratch”, “we” or “us”) understands how
|
||||
important privacy is to our community. We wrote this Privacy Policy to explain what Personal Information
|
||||
(“Information”) we collect through our offline editor (the “<a
|
||||
href="https://scratch.mit.edu/download"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>Scratch App</a>”), how we use, process, and share it, and what we're doing to keep it safe. It
|
||||
also tells you about your rights and choices with respect to your Personal Information, and how you can <a
|
||||
href="https://scratch.mit.edu/contact-us/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>contact us</a> if you have any questions or concerns.
|
||||
</p>
|
||||
<h2>What Information Does Scratch Collect About Me?</h2>
|
||||
<p>
|
||||
For the purpose of this Privacy Policy, “Information” means any information relating to an
|
||||
identified or identifiable individual. The Scratch App automatically collects and stores locally the
|
||||
following Information through its telemetry system: the title of your project in text form, language
|
||||
setting, time zone and events related to your use of the Scratch App (namely when the Scratch App was
|
||||
opened and closed, if a project file has been loaded or saved, or if a new project is created). If you
|
||||
choose to turn on the telemetry sharing feature, the Scratch App will transmit this information to Scratch.
|
||||
Projects created in the Scratch App are not transmitted to or accessible by Scratch.
|
||||
</p>
|
||||
<h2>How Does Scratch Use My Information?</h2>
|
||||
<p>We use this Information for the following purposes:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Analytics and Improving the Scratch App</b> - We use the Information to analyze use of the Scratch
|
||||
App and to enhance your learning experience on the Scratch App.
|
||||
</li>
|
||||
<li>
|
||||
<b>Academic and Scientific Research</b> - We de-identify and aggregate Information for statistical
|
||||
analysis in the context of scientific and academic research. For example, to help us understand how
|
||||
people learn through the Scratch App and how we can enhance learning tools for young people. The
|
||||
results of such research are shared with educators and researchers through conferences, journals, and
|
||||
other academic or scientific publications. You can find out more on our <a
|
||||
href="https://scratch.mit.edu/research"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>Research page</a>.
|
||||
</li>
|
||||
<li>
|
||||
<b>Legal</b> - We may use your Information to enforce our <a
|
||||
href="https://scratch.mit.edu/terms_of_use"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>Terms of Use</a>, to defend our legal rights, and to comply with our legal obligations and internal
|
||||
policies. We may do this by analyzing your use of the Scratch App.
|
||||
</li>
|
||||
</ul>
|
||||
<h2>What Are The Legal Grounds For Processing Your Information?</h2>
|
||||
<p>
|
||||
If you are located in the European Economic Area, the United Kingdom or Switzerland, we only process your
|
||||
Information based on a valid legal ground. A “legal ground” is a reason that justifies our use
|
||||
of your Information. In this case, we or a third party have a legitimate interest in using your Information
|
||||
(if you choose to allow the Scratch App to send the Scratch team your Information) to create, analyze and
|
||||
share your aggregated or de-identified Information for research purposes, to analyze and enhance your
|
||||
learning experience on the Scratch App and otherwise ensure and improve the safety, security, and
|
||||
performance of the Scratch App. We only rely on our or a third party’s legitimate interests to process your
|
||||
Information when these interests are not overridden by your rights and interests.
|
||||
</p>
|
||||
<h2>How Does Scratch Share My Information?</h2>
|
||||
<p>
|
||||
We disclose information that we collect through the Scratch App to third parties in the following
|
||||
circumstances:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Service Providers</b> - To third parties who provide services such as website hosting, data
|
||||
analysis, Information technology and related infrastructure provisions, customer service, email
|
||||
delivery, and other services.
|
||||
</li>
|
||||
<li>
|
||||
<b>Academic and Scientific Research</b> - To research institutions, such as the Massachusetts Institute
|
||||
of Technology (MIT), to learn about how our users learn through the Scratch App and develop new
|
||||
learning tools. The results of this research or the statistical analysis may be shared through
|
||||
conferences, journals, and other publications.
|
||||
</li>
|
||||
<li>
|
||||
<b>Merger</b> - To a potential or actual acquirer, successor, or assignee as part of any
|
||||
reorganization, merger, sale, joint venture, assignment, transfer, or other disposition of all or any
|
||||
portion of our organization or assets. You will have the opportunity to opt out of any such transfer if
|
||||
the new entity's planned processing of your Information differs materially from that set forth in
|
||||
this Privacy Policy.
|
||||
</li>
|
||||
<li>
|
||||
<b>Legal</b> - If required to do so by law or in the good faith belief that such action is appropriate:
|
||||
(a) under applicable law, including laws outside your country of residence; (b) to comply with legal
|
||||
process; (c) to respond to requests from public and government authorities, such as school, school
|
||||
districts, and law enforcement, including public and government authorities outside your country of
|
||||
residence; (d) to enforce our terms and conditions; (e) to protect our operations or those of any of
|
||||
our affiliates; (f) to protect our rights, privacy, safety, or property, and/or that of our affiliates,
|
||||
you, or others; and (g) to allow us to pursue available remedies or limit the damages that we may
|
||||
sustain.
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Children and Student Privacy</h2>
|
||||
<p>
|
||||
The Scratch Foundation is a 501(c)(3) nonprofit organization. As such, the Children's Online Privacy
|
||||
Protection Act (COPPA) does not apply to Scratch. Nevertheless, Scratch takes children's privacy
|
||||
seriously. Scratch collects only minimal information from its users, and only uses and discloses
|
||||
information to provide the services and for limited other purposes, such as research, as described in this
|
||||
Privacy Policy.
|
||||
</p>
|
||||
<p>
|
||||
Scratch does not collect information from a student's education record, as defined by the Family
|
||||
Educational Rights and Privacy Act (FERPA). Scratch does not disclose information of students to any third
|
||||
parties except as described in this Privacy Policy.
|
||||
</p>
|
||||
<h2>Your Data Protection Rights (EEA)</h2>
|
||||
<p>
|
||||
If you are located in the European Economic Area, the United Kingdom or Switzerland, you have certain
|
||||
rights in relation to your Information:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Access, Correction and Data Portability</b> - You may ask for an overview of the Information we
|
||||
process about you and to receive a copy of your Information. You also have the right to request to
|
||||
correct incomplete, inaccurate or outdated Information. To the extent required by applicable law, you
|
||||
may request us to provide your Information to another company.
|
||||
</li>
|
||||
<li>
|
||||
<b>Objection</b> – You may object to (this means “ask us to stop”) any use of your
|
||||
Information that is not (i) processed to comply with a legal obligation, (ii) necessary to do what is
|
||||
provided in a contract between Scratch and you, or (iii) if we have a compelling reason to do so (such
|
||||
as, to ensure safety and security in our online community). If you do object, we will work with you to
|
||||
find a reasonable solution.
|
||||
</li>
|
||||
<li>
|
||||
<b>Deletion</b> - You may also request the deletion of your Information, as permitted under applicable
|
||||
law. This applies, for instance, where your Information is outdated or the processing is not necessary
|
||||
or is unlawful; where you withdraw your consent to our processing based on such consent; or where you
|
||||
have objected to our processing. In some situations, we may need to retain your Information due to
|
||||
legal obligations or for litigation purposes. If you want to have all of your Information removed from
|
||||
our servers, please contact <a
|
||||
href="mailto:help@scratch.mit.edu"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>help@scratch.mit.edu</a> for assistance.
|
||||
</li>
|
||||
<li>
|
||||
<b>Restriction Of Processing</b> - You may request that we restrict processing of your Information
|
||||
while we are processing a request relating to (i) the accuracy of your Information, (ii) the lawfulness
|
||||
of the processing of your Information, or (iii) our legitimate interests to process this Information.
|
||||
You may also request that we restrict processing of your Information if you wish to use the Information
|
||||
for litigation purposes.
|
||||
</li>
|
||||
<li>
|
||||
<b>Withdrawal Of Consent</b> – Where we rely on consent for the processing of your Information, you
|
||||
have the right to withdraw it at any time and free of charge. When you do so, this will not affect the
|
||||
lawfulness of the processing before your consent withdrawal.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
In addition to the above-mentioned rights, you also have the right to lodge a complaint with a competent
|
||||
supervisory authority subject to applicable law. However, there are exceptions and limitations to each of
|
||||
these rights. We may, for example, refuse to act on a request if the request is manifestly unfounded or
|
||||
excessive, or if the request is likely to adversely affect the rights and freedoms of others, prejudice the
|
||||
execution or enforcement of the law, interfere with pending or future litigation, or infringe applicable
|
||||
law. To submit a request to exercise your rights, please contact <a
|
||||
href="mailto:help@scratch.mit.edu"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>help@scratch.mit.edu</a> for assistance.
|
||||
</p>
|
||||
<h2>Data Retention</h2>
|
||||
<p>
|
||||
We take measures to delete your Information or keep it in a form that does not allow you to be identified
|
||||
when this Information is no longer necessary for the purposes for which we process it, unless we are
|
||||
required by law to keep this Information for a longer period. When determining the retention period, we
|
||||
take into account various criteria, such as the type of services requested by or provided to you, the
|
||||
nature and length of our relationship with you, possible re-enrollment with our services, the impact on the
|
||||
services we provide to you if we delete some Information from or about you, mandatory retention periods
|
||||
provided by law and the statute of limitations.
|
||||
</p>
|
||||
<h2>How Does Scratch Protect My Information?</h2>
|
||||
<p>
|
||||
Scratch has in place administrative, physical, and technical procedures that are intended to protect the
|
||||
Information we collect on the Scratch App against accidental or unlawful destruction, accidental loss,
|
||||
unauthorized alteration, unauthorized disclosure or access, misuse, and any other unlawful form of
|
||||
processing of the Information. However, as effective as these measures are, no security system is
|
||||
impenetrable. We cannot completely guarantee the security of our databases, nor can we guarantee that the
|
||||
Information you supply will not be intercepted while being transmitted to us over the Internet.
|
||||
</p>
|
||||
<h2>International Data Transfer</h2>
|
||||
<p>
|
||||
We may transfer your Information to countries other than the country where you are located, including to
|
||||
the U.S. (where our Scratch servers are located) or any other country in which we or our service providers
|
||||
maintain facilities. If you are located in the European Economic Area, the United Kingdom or Switzerland,
|
||||
or other regions with laws governing data collection and use that may differ from U.S. law, please note
|
||||
that we may transfer your Information to a country and jurisdiction that does not have the same data
|
||||
protection laws as your jurisdiction. We apply appropriate safeguards to the Information processed and
|
||||
transferred on our behalf. Please contact us for more information on the safeguards used.
|
||||
</p>
|
||||
<h2>Notifications Of Changes To The Privacy Policy</h2>
|
||||
<p>
|
||||
We review our Privacy Policy on a periodic basis, and we may modify our policies as appropriate. We will
|
||||
notify you of any material changes. We encourage you to review our Privacy Policy on a regular basis. The
|
||||
“Last Updated” date at the top of this page indicates when this Privacy Policy was last
|
||||
revised. Your continued use of the Scratch App following these changes means that you accept the revised
|
||||
Privacy Policy.
|
||||
</p>
|
||||
<h2>Contact Us</h2>
|
||||
<p>
|
||||
The Scratch Foundation is the entity responsible for the processing of your Information. If you have any
|
||||
questions about this Privacy Policy, or if you would like to exercise your rights to your Information, you
|
||||
may contact us at <a
|
||||
href="mailto:help@scratch.mit.edu"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>help@scratch.mit.edu</a> or via mail at:
|
||||
</p>
|
||||
<div className="vcard">
|
||||
<div className="org">Scratch Foundation</div>
|
||||
<div className="fn">ATTN: Privacy Policy</div>
|
||||
<div className="adr">
|
||||
<div className="street-address">201 South Street</div>
|
||||
<span className="locality">Boston</span>, <span className="region">MA</span> <span
|
||||
className="postal-code"
|
||||
>02111</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default <PrivacyElement />;
|
13
src/renderer/showPrivacyPolicy.js
Normal file
13
src/renderer/showPrivacyPolicy.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import {ipcRenderer} from 'electron';
|
||||
|
||||
const showPrivacyPolicy = event => {
|
||||
if (event) {
|
||||
// Probably a click on a link; don't actually follow the link in the `href` attribute.
|
||||
event.preventDefault();
|
||||
}
|
||||
// tell the main process to open the privacy policy window
|
||||
ipcRenderer.send('open-privacy-policy-window');
|
||||
return false;
|
||||
};
|
||||
|
||||
export default showPrivacyPolicy;
|
Loading…
Reference in a new issue