mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-14 10:35:11 +00:00
add license dialog
This commit is contained in:
@@ -3,12 +3,12 @@
|
||||
|
||||
// The about dialog
|
||||
|
||||
import { AnchorButton, Classes, Dialog } from '@blueprintjs/core';
|
||||
import { AnchorButton, Button, Classes, Dialog } from '@blueprintjs/core';
|
||||
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Action, Dispatch } from '../actions';
|
||||
import { closeAboutDialog } from '../actions/app';
|
||||
import { closeAboutDialog, openLicenseDialog } from '../actions/app';
|
||||
import { RootState } from '../reducers';
|
||||
import {
|
||||
appName,
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
pybricksWebsiteUrl,
|
||||
} from '../settings/ui';
|
||||
import ExternalLinkIcon from './ExternalLinkIcon';
|
||||
import LicenseDialog from './LicenseDialog';
|
||||
import { AboutStringId } from './about-i18n';
|
||||
import en from './about-i18n.en.json';
|
||||
|
||||
@@ -24,13 +25,13 @@ import './about.scss';
|
||||
|
||||
type StateProps = { showAboutDialog: boolean };
|
||||
|
||||
type DispatchProps = { onClose: () => void };
|
||||
type DispatchProps = { onClose: () => void; onLicenseButtonClick: () => void };
|
||||
|
||||
type AboutDialogProps = StateProps & DispatchProps & WithI18nProps;
|
||||
|
||||
class AboutDialog extends React.Component<AboutDialogProps> {
|
||||
render(): JSX.Element {
|
||||
const { i18n, showAboutDialog, onClose } = this.props;
|
||||
const { i18n, showAboutDialog, onClose, onLicenseButtonClick } = this.props;
|
||||
return (
|
||||
<Dialog title={appName} isOpen={showAboutDialog} onClose={() => onClose()}>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
@@ -47,12 +48,16 @@ class AboutDialog extends React.Component<AboutDialogProps> {
|
||||
<small>{legoDisclaimer}</small>
|
||||
</p>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={() => onLicenseButtonClick()}>
|
||||
{i18n.translate(AboutStringId.LicenseButtonLabel)}
|
||||
</Button>
|
||||
<AnchorButton href={pybricksWebsiteUrl} target="blank_">
|
||||
{i18n.translate(AboutStringId.WebsiteButtonLabel)}
|
||||
<ExternalLinkIcon />
|
||||
</AnchorButton>
|
||||
</div>
|
||||
</div>
|
||||
<LicenseDialog />
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -64,6 +69,7 @@ const mapStateToProps = (state: RootState): StateProps => ({
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
|
||||
onClose: (): Action => dispatch(closeAboutDialog()),
|
||||
onLicenseButtonClick: (): Action => dispatch(openLicenseDialog()),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
||||
@@ -5,7 +5,6 @@ import React, { useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import SplitterLayout from 'react-splitter-layout';
|
||||
import { RootState } from '../reducers';
|
||||
import AboutDialog from './AboutDialog';
|
||||
import Editor from './Editor';
|
||||
import SettingsDrawer from './SettingsDrawer';
|
||||
import StatusBar from './StatusBar';
|
||||
@@ -65,7 +64,6 @@ function App(): JSX.Element {
|
||||
</SplitterLayout>
|
||||
<StatusBar />
|
||||
<SettingsDrawer />
|
||||
<AboutDialog key="about" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
|
||||
// The license dialog
|
||||
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Callout,
|
||||
Card,
|
||||
Classes,
|
||||
Dialog,
|
||||
NonIdealState,
|
||||
} from '@blueprintjs/core';
|
||||
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Action, Dispatch } from '../actions';
|
||||
import { closeLicenseDialog } from '../actions/app';
|
||||
import { select } from '../actions/license';
|
||||
import { RootState } from '../reducers';
|
||||
import { LicenseInfo, LicenseList } from '../reducers/license';
|
||||
import { appName } from '../settings/ui';
|
||||
import { LicenseStringId } from './license-i18n';
|
||||
import en from './license-i18n.en.json';
|
||||
|
||||
import './license.scss';
|
||||
|
||||
type StateProps = {
|
||||
showLicenseDialog: boolean;
|
||||
licenseList: LicenseList | null;
|
||||
licenseInfo: LicenseInfo | null;
|
||||
};
|
||||
|
||||
type DispatchProps = {
|
||||
onClose: () => void;
|
||||
onSelectPackage: (info: LicenseInfo) => void;
|
||||
};
|
||||
|
||||
type LicenseDialogProps = StateProps & DispatchProps & WithI18nProps;
|
||||
|
||||
class LicenseDialog extends React.Component<LicenseDialogProps> {
|
||||
render(): JSX.Element {
|
||||
const infoDiv = React.createRef<HTMLDivElement>();
|
||||
|
||||
const {
|
||||
i18n,
|
||||
showLicenseDialog,
|
||||
onClose,
|
||||
licenseList,
|
||||
licenseInfo,
|
||||
onSelectPackage,
|
||||
} = this.props;
|
||||
return (
|
||||
<Dialog
|
||||
title={i18n.translate(LicenseStringId.Title)}
|
||||
isOpen={showLicenseDialog}
|
||||
onClose={() => onClose()}
|
||||
className="pb-license-dialog"
|
||||
>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Callout className={Classes.INTENT_PRIMARY} icon="info-sign">
|
||||
{i18n.translate(LicenseStringId.Description, {
|
||||
name: appName,
|
||||
})}
|
||||
</Callout>
|
||||
<Callout className="pb-license-browser">
|
||||
<div className="pb-license-list">
|
||||
{licenseList === null ? (
|
||||
// TODO: this should be translated and hooked to
|
||||
// state indicating if download is in progress
|
||||
// or there was an actual failure.
|
||||
<NonIdealState>
|
||||
Failed to load license data.
|
||||
</NonIdealState>
|
||||
) : (
|
||||
<ButtonGroup
|
||||
minimal={true}
|
||||
vertical={true}
|
||||
alignText="left"
|
||||
>
|
||||
{licenseList.map((info) => (
|
||||
<Button
|
||||
key={`${info.name}@${info.version}`}
|
||||
onClick={() => {
|
||||
infoDiv.current?.scrollTo(0, 0);
|
||||
onSelectPackage(info);
|
||||
}}
|
||||
>
|
||||
{info.name}
|
||||
</Button>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
)}
|
||||
</div>
|
||||
<div className="pb-license-info" ref={infoDiv}>
|
||||
{licenseInfo == null ? (
|
||||
<NonIdealState>
|
||||
{i18n.translate(LicenseStringId.SelectPackageHelp)}
|
||||
</NonIdealState>
|
||||
) : (
|
||||
<div>
|
||||
<Card>
|
||||
<p>
|
||||
<strong>
|
||||
{i18n.translate(
|
||||
LicenseStringId.PackageLabel,
|
||||
)}
|
||||
</strong>{' '}
|
||||
{licenseInfo.name}{' '}
|
||||
<span className={Classes.TEXT_MUTED}>
|
||||
v{licenseInfo.version}
|
||||
</span>
|
||||
</p>
|
||||
{licenseInfo.author && (
|
||||
<p>
|
||||
<strong>
|
||||
{' '}
|
||||
{i18n.translate(
|
||||
LicenseStringId.AuthorLabel,
|
||||
)}
|
||||
</strong>{' '}
|
||||
{licenseInfo.author}
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
<strong>
|
||||
{' '}
|
||||
{i18n.translate(
|
||||
LicenseStringId.LicenseLabel,
|
||||
)}
|
||||
</strong>{' '}
|
||||
{licenseInfo.license}
|
||||
</p>
|
||||
</Card>
|
||||
<div className="pb-license-text">
|
||||
<pre>{licenseInfo.licenseText}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Callout>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: RootState): StateProps => ({
|
||||
showLicenseDialog: state.app.showLicenseDialog,
|
||||
licenseList: state.license.list,
|
||||
licenseInfo: state.license.selected,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
|
||||
onClose: (): Action => dispatch(closeLicenseDialog()),
|
||||
onSelectPackage: (info): Action => dispatch(select(info)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(withI18n({ id: 'license', fallback: en, translations: { en } })(LicenseDialog));
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
} from '../settings/ui';
|
||||
import { SettingId } from '../settings/user';
|
||||
import { isMacOS } from '../utils/os';
|
||||
import AboutDialog from './AboutDialog';
|
||||
import ExternalLinkIcon from './ExternalLinkIcon';
|
||||
import { SettingsStringId } from './settings-i18n';
|
||||
import en from './settings-i18n.en.json';
|
||||
@@ -192,6 +193,7 @@ class SettingsDrawer extends React.PureComponent<SettingsProps> {
|
||||
>
|
||||
{i18n.translate(SettingsStringId.HelpAboutLabel)}
|
||||
</Button>
|
||||
<AboutDialog />
|
||||
</ButtonGroup>
|
||||
</FormGroup>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"about": {
|
||||
"description": "A simple web app for programming LEGO® Powered Up smart hubs using Pybricks MicroPython.",
|
||||
"licenseButton": { "label": "Open Source Licenses" },
|
||||
"websiteButton": { "label": "Pybricks Website" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
|
||||
export enum AboutStringId {
|
||||
Description = 'about.description',
|
||||
LicenseButtonLabel = 'about.licenseButton.label',
|
||||
WebsiteButtonLabel = 'about.websiteButton.label',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"license": {
|
||||
"title": "Open Source Licenses",
|
||||
"description": "{name} is built on open source software. By using {name} you are agreeing to the terms and conditions of all of the included software licenses.",
|
||||
"packageLabel": "Package:",
|
||||
"authorLabel": "Author:",
|
||||
"licenseLabel": "License:",
|
||||
"help": {
|
||||
"selectPackage": "Select a package to view the license."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
|
||||
import { lookup } from '../../test';
|
||||
import { LicenseStringId } from './license-i18n';
|
||||
import en from './license-i18n.en.json';
|
||||
|
||||
describe('Ensure .json file has matches for LicenseStringId', () => {
|
||||
test.each(Object.values(LicenseStringId))('%s', (id) => {
|
||||
expect(lookup(en, id)).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
|
||||
// License dialog translation keys.
|
||||
|
||||
export enum LicenseStringId {
|
||||
Title = 'license.title',
|
||||
Description = 'license.description',
|
||||
PackageLabel = 'license.packageLabel',
|
||||
AuthorLabel = 'license.authorLabel',
|
||||
LicenseLabel = 'license.licenseLabel',
|
||||
SelectPackageHelp = 'license.help.selectPackage',
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020-2021 The Pybricks Authors
|
||||
|
||||
// Custom styling for the LicenseDialog control.
|
||||
|
||||
@import '../variables.scss';
|
||||
|
||||
.pb-license-dialog {
|
||||
width: 1200px;
|
||||
}
|
||||
|
||||
.pb-license-browser {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.pb-license-list {
|
||||
width: 25%;
|
||||
flex-flow: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.pb-license-info {
|
||||
width: 75%;
|
||||
flex-flow: column;
|
||||
padding: 0px 16px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.pb-license-text {
|
||||
padding: 0px 16px;
|
||||
}
|
||||
Reference in New Issue
Block a user