Skip to content

Commit

Permalink
Add the QR code generating bits for mobile app
Browse files Browse the repository at this point in the history
  • Loading branch information
montgomp committed Nov 4, 2021
1 parent 54e351b commit b9ef12f
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 14 deletions.
Binary file added images/qrCodes/appstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/qrCodes/playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"react-dom": "16.13.1",
"react-i18next": "11.11.1",
"react-jsonschema-form": "1.7.0",
"react-qr-code": "^2.0.1",
"react-router-dom": "5.2.0",
"react-smooth-dnd": "0.11.0",
"react-toastify": "4.4.0",
Expand Down
13 changes: 12 additions & 1 deletion src/app/css/_deviceContent.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,28 @@ $nav-width: 20%;

.device-content-detail {
transition: margin-left 0.5s;
margin-left: $nav-width;
margin: 0;
margin-left: 300px;
}

.device-content-detail.collapsed {
margin-left: $nav-collapsed-width;
}
}
}
@media screen and (min-width: $screenSize) and (max-width: 1500px) {
.device-content{
.device-content-detail{
margin-left: $nav-width;
}
}
}

@media screen and (max-width: $screenSize) {
.device-content {
.device-content-detail{
margin:0;
}
.device-content-nav-bar.collapsed {
.nav-links {
display: none;
Expand Down
15 changes: 15 additions & 0 deletions src/app/css/_qrGeneration.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.qrCodeGeneration {
text-align: center;
.storeLinks {
padding-top: 20px;
}
.qrCode {
padding:5px;
background-color: white;
width: 200px;
height: 200px;
border-radius: 8px;
margin-left: auto;
margin-right: auto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DeviceModules matches snapshot 1`] = `
<Component
expanded={false}
label="deviceIdentity.qrCode.label"
tooltipText="deviceIdentity.qrCode.toolTip"
>
<div
className="qrCodeGeneration"
>
<section
className="storeLinks"
>
<StyledLinkBase
href="https://play.google.com/store/apps/details?id=com.iot_pnp"
target="_blank"
>
<img
src="images/qrCodes/playstore.png"
/>
</StyledLinkBase>
<StyledLinkBase
href="https://apps.apple.com/app/iot-plug-and-play/id1563783687"
target="_blank"
>
<img
src="images/qrCodes/appstore.png"
/>
</StyledLinkBase>
</section>
<section>
<div
className="qrCode"
>
<Memo(QRCode)
size={200}
value="eyJkZXZpY2VJZCI6InNvbWVfZGV2aWNlIiwiZGV2aWNlS2V5IjoiaW52YWxpZF9kZXZpY2Vfa2V5IiwiaG9zdE5hbWUiOiJpbnZhbGlkX2hvc3QifQ=="
/>
</div>
</section>
</div>
</Component>
`;
36 changes: 23 additions & 13 deletions src/app/devices/deviceIdentity/components/deviceIdentity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { MultiLineShimmer } from '../../../shared/components/multiLineShimmer';
import { HeaderView } from '../../../shared/components/headerView';
import { SasTokenGenerationView } from '../../shared/components/sasTokenGenerationView';
import { useIotHubContext } from '../../../iotHub/hooks/useIotHubContext';
import { QrGenerationView } from './qrGenerationView';
import '../../../css/_deviceDetail.scss';

export interface DeviceIdentityDispatchProps {
Expand All @@ -34,7 +35,7 @@ export const DeviceIdentityInformation: React.FC<DeviceIdentityDataProps & Devic
const { hostName } = useIotHubContext();

const { deviceIdentity, synchronizationStatus } = props;
const [ state, setState ] = React.useState({
const [state, setState] = React.useState({
identity: props.deviceIdentity,
isDirty: false,
});
Expand All @@ -56,9 +57,9 @@ export const DeviceIdentityInformation: React.FC<DeviceIdentityDataProps & Devic

if (props.deviceIdentity &&
props.deviceIdentity.authentication.type === DeviceAuthenticationType.SymmetricKey) {
onSwapKeys = swapKeys;
onGeneratePrimaryKey = generatePrimaryKey;
onGenerateSecondaryKey = generateSecondaryKey;
onSwapKeys = swapKeys;
onGeneratePrimaryKey = generatePrimaryKey;
onGenerateSecondaryKey = generateSecondaryKey;
}

return (
Expand All @@ -82,7 +83,7 @@ export const DeviceIdentityInformation: React.FC<DeviceIdentityDataProps & Devic
return (
<>
{props.synchronizationStatus === SynchronizationStatus.working || props.synchronizationStatus === SynchronizationStatus.updating ?
<MultiLineShimmer/> :
<MultiLineShimmer /> :
<>
<MaskedCopyableTextField
ariaLabel={t(ResourceKeys.deviceIdentity.deviceID)}
Expand All @@ -93,7 +94,7 @@ export const DeviceIdentityInformation: React.FC<DeviceIdentityDataProps & Devic
labelCallout={t(ResourceKeys.deviceIdentity.deviceIDTooltip)}
/>
{renderDeviceAuthProperties()}
<br/>
<br />
{authType === DeviceAuthenticationType.SymmetricKey && renderSasTokenSection()}
{renderHubRelatedInformation()}
</>
Expand All @@ -104,10 +105,18 @@ export const DeviceIdentityInformation: React.FC<DeviceIdentityDataProps & Devic

const renderSasTokenSection = () => {
return (
<SasTokenGenerationView
activeAzureResourceHostName={hostName}
deviceIdentity={state.identity}
/>
<>
<SasTokenGenerationView
activeAzureResourceHostName={hostName}
deviceIdentity={state.identity}
/>
<br />
<QrGenerationView
hostName={hostName}
deviceId={state.identity.deviceId}
deviceKey={state.identity.authentication.symmetricKey.primaryKey}
/>
</>
);
};

Expand Down Expand Up @@ -261,8 +270,8 @@ export const DeviceIdentityInformation: React.FC<DeviceIdentityDataProps & Devic

const swapKeys = () => {
const identityDeepCopy: DeviceIdentity = JSON.parse(JSON.stringify(state.identity));
const originalPrimaryKey = identityDeepCopy.authentication.symmetricKey.primaryKey;
const originalSecondaryKey = identityDeepCopy.authentication.symmetricKey.secondaryKey;
const originalPrimaryKey = identityDeepCopy.authentication.symmetricKey.primaryKey;
const originalSecondaryKey = identityDeepCopy.authentication.symmetricKey.secondaryKey;

identityDeepCopy.authentication.symmetricKey.primaryKey = originalSecondaryKey;
identityDeepCopy.authentication.symmetricKey.secondaryKey = originalPrimaryKey;
Expand All @@ -277,7 +286,8 @@ export const DeviceIdentityInformation: React.FC<DeviceIdentityDataProps & Devic
const changeToggle = (event: React.MouseEvent<HTMLElement>, checked?: boolean) => {
const identity = {
...state.identity,
status: checked ? DeviceStatus.Enabled.toString() : DeviceStatus.Disabled.toString()};
status: checked ? DeviceStatus.Enabled.toString() : DeviceStatus.Disabled.toString()
};
setState({
...state,
identity,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/***********************************************************
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License
**********************************************************/
import * as React from 'react';
import { shallow } from 'enzyme';
import { QrGenerationView } from './qrGenerationView';

describe('DeviceModules', () => {
it('matches snapshot', () => {
expect(shallow(
<QrGenerationView
deviceId="some_device"
deviceKey="invalid_device_key"
hostName="invalid_host"
/>
)).toMatchSnapshot();
});
});
58 changes: 58 additions & 0 deletions src/app/devices/deviceIdentity/components/qrGenerationView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/***********************************************************
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License
**********************************************************/
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from '@fluentui/react';
import QRCode from 'react-qr-code';
import { CollapsibleSection } from '../../../shared/components/collapsibleSection';
import '../../../css/_qrGeneration.scss';
import { ResourceKeys } from '../../../../localization/resourceKeys';

const QR_SIZE = 200;

export interface QrGenerationDataProps {
hostName: string;
deviceId: string;
deviceKey: string;
}
export const QrGenerationView: React.FC<QrGenerationDataProps> = (props: QrGenerationDataProps) => {
const { t } = useTranslation();
const { deviceId, deviceKey, hostName } = props;
const connection = Buffer.from(JSON.stringify({
deviceId,
deviceKey,
hostName
})).toString('base64');

return (
<CollapsibleSection
expanded={false}
label={t(ResourceKeys.deviceIdentity.qrCode.label)}
tooltipText={t(ResourceKeys.deviceIdentity.qrCode.toolTip)}
>
<div className="qrCodeGeneration">
<section className="storeLinks">
<Link
href="https://play.google.com/store/apps/details?id=com.iot_pnp"
target="_blank"
>
<img src="images/qrCodes/playstore.png" />
</Link>
<Link
href="https://apps.apple.com/app/iot-plug-and-play/id1563783687"
target="_blank"
>
<img src="images/qrCodes/appstore.png" />
</Link>
</section>
<section>
<div className="qrCode">
<QRCode value={connection} size={QR_SIZE} />
</div>
</section>
</div>
</CollapsibleSection>
);
};
4 changes: 4 additions & 0 deletions src/localization/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@
"invalidDeviceId": "Invalid characters in device ID",
"invalidKey": "Invalid key",
"invalidThumbprint": "Invalid thumbprint"
},
"qrCode": {
"label": "Connect your phone as an IoT device",
"toolTip": "Use the IoT mobile app and a QR code to turn your mobile device into a Plug and Play IoT device"
}
},
"deviceLists": {
Expand Down
4 changes: 4 additions & 0 deletions src/localization/resourceKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,10 @@ export class ResourceKeys {
label : "deviceIdentity.hubConnectivity.label",
tooltip : "deviceIdentity.hubConnectivity.tooltip",
},
qrCode : {
label : "deviceIdentity.qrCode.label",
toolTip : "deviceIdentity.qrCode.toolTip",
},
validation : {
invalidDeviceId : "deviceIdentity.validation.invalidDeviceId",
invalidKey : "deviceIdentity.validation.invalidKey",
Expand Down

0 comments on commit b9ef12f

Please sign in to comment.