From c06dce146b372643179a8b1ebffec3bbd3f35b04 Mon Sep 17 00:00:00 2001 From: Hugo Bernier Date: Wed, 12 Aug 2020 20:37:10 -0400 Subject: [PATCH 1/2] Updated PnP --- .../package-lock.json | 111 +++++++++++++++--- .../package.json | 7 +- .../AnalysisServices/AnalysisService.ts | 1 + .../AnalysisDialog/AnalysisDialogContent.tsx | 58 +++++---- 4 files changed, 132 insertions(+), 45 deletions(-) diff --git a/samples/react-smart-profile-photo-editor/package-lock.json b/samples/react-smart-profile-photo-editor/package-lock.json index 4af55994a..21a3a84ed 100644 --- a/samples/react-smart-profile-photo-editor/package-lock.json +++ b/samples/react-smart-profile-photo-editor/package-lock.json @@ -1,6 +1,6 @@ { "name": "smart-profile-photo-editor", - "version": "0.0.1", + "version": "1.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2488,9 +2488,9 @@ } }, "@microsoft/microsoft-graph-types": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-1.7.0.tgz", - "integrity": "sha512-Mxu5H+69F8T5NzV4+U8FkTvpIYYWHsmRZzfAuOlIO0zJJGlVyRIVqpq4NmOdUXGC00vZ73ONgCuzuaksxqDm/Q==" + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-1.13.0.tgz", + "integrity": "sha512-U55VWJWwVxgGZSSJy+s6UtExK6FNnGxhIqE6MkEOqjRwge40QTqBDhNlrCLJzXXDs5Cl0EnDkcgNDMjL97gd5A==" }, "@microsoft/node-core-library": { "version": "3.13.0", @@ -4265,18 +4265,47 @@ } }, "@pnp/graph": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@pnp/graph/-/graph-1.3.8.tgz", - "integrity": "sha512-uBBDrpWNILGBfTxtHqFrdv3YkJZm+jgXOLBp3KR8ocBrTt+yQmkCEwztawYqUhSys6SoDkcFE/DqLIjhyHiBDA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@pnp/graph/-/graph-2.0.8.tgz", + "integrity": "sha512-kLL0Zvu4jvr88voRgCv//ZpU0k3qx/e1sfI1dXHGuo2yp1BlUpYTcJj1ZW3y6C3Zu2lCfIC5F83zcQ9JgoZ+DQ==", "requires": { - "@microsoft/microsoft-graph-types": "1.7.0", - "tslib": "1.10.0" + "@microsoft/microsoft-graph-types": "1.13.0", + "@pnp/common": "2.0.8", + "@pnp/logging": "2.0.8", + "@pnp/odata": "2.0.8", + "tslib": "2.0.0" }, "dependencies": { + "@pnp/common": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@pnp/common/-/common-2.0.8.tgz", + "integrity": "sha512-z3un/uLnmBHCk6y+OxK9CioI2XqFWw8m6GLdSpkXUcoG7Is41Rqia89Sy6CatAVAhhAcLoW9brI884ju/OXhCA==", + "requires": { + "tslib": "2.0.0" + } + }, + "@pnp/logging": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-2.0.8.tgz", + "integrity": "sha512-76I5j9g/2CgxtscujPvx5vbm3OifuDJgjUYZ3hkuMSmFwK2dN7WcLA+gQqiEkLcSgPx1M9G7vdrZARQSc2pUNA==", + "requires": { + "tslib": "2.0.0" + } + }, + "@pnp/odata": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@pnp/odata/-/odata-2.0.8.tgz", + "integrity": "sha512-keHdsXIiRd6cd5xKQ2hA/RLfYCCm56um5CvWAFts0xdFjsxaLlSkFgvHaw0u2lF7+4Jyr6vvmjCAOlUuPwIZhg==", + "requires": { + "@pnp/common": "2.0.8", + "@pnp/logging": "2.0.8", + "tslib": "2.0.0" + } + }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" } } }, @@ -4311,17 +4340,46 @@ } }, "@pnp/sp": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@pnp/sp/-/sp-1.3.8.tgz", - "integrity": "sha512-x85cQL/L5fBYJWqWvDL3e2sHdYZIqUeWifFndsGRk6iLFHTq2DsxNNzTnCpP7JCKUwK6jHpu0JzIuK98E8Hl9w==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@pnp/sp/-/sp-2.0.8.tgz", + "integrity": "sha512-/+V3IilrOI2ab2LOHOjruQhHPAjMbtWaUNEziDSi2eFUGc+8joCtrudM6MN14z6yMGyAItW2AyyBMqaaaceH2Q==", "requires": { - "tslib": "1.10.0" + "@pnp/common": "2.0.8", + "@pnp/logging": "2.0.8", + "@pnp/odata": "2.0.8", + "tslib": "2.0.0" }, "dependencies": { + "@pnp/common": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@pnp/common/-/common-2.0.8.tgz", + "integrity": "sha512-z3un/uLnmBHCk6y+OxK9CioI2XqFWw8m6GLdSpkXUcoG7Is41Rqia89Sy6CatAVAhhAcLoW9brI884ju/OXhCA==", + "requires": { + "tslib": "2.0.0" + } + }, + "@pnp/logging": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-2.0.8.tgz", + "integrity": "sha512-76I5j9g/2CgxtscujPvx5vbm3OifuDJgjUYZ3hkuMSmFwK2dN7WcLA+gQqiEkLcSgPx1M9G7vdrZARQSc2pUNA==", + "requires": { + "tslib": "2.0.0" + } + }, + "@pnp/odata": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@pnp/odata/-/odata-2.0.8.tgz", + "integrity": "sha512-keHdsXIiRd6cd5xKQ2hA/RLfYCCm56um5CvWAFts0xdFjsxaLlSkFgvHaw0u2lF7+4Jyr6vvmjCAOlUuPwIZhg==", + "requires": { + "@pnp/common": "2.0.8", + "@pnp/logging": "2.0.8", + "tslib": "2.0.0" + } + }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" } } }, @@ -4487,6 +4545,21 @@ "react-ace": "5.8.0" }, "dependencies": { + "@pnp/sp": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@pnp/sp/-/sp-1.3.11.tgz", + "integrity": "sha512-NjdeGe81aukiSPelSPjgAFRC1+SrNPTXvTdEqTH+Q1ZvgNtk8bdZp6K6xf9emfeM2qZDOu9GpKZpg0W/emq++g==", + "requires": { + "tslib": "1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + } + } + }, "@uifabric/icons": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-5.8.0.tgz", diff --git a/samples/react-smart-profile-photo-editor/package.json b/samples/react-smart-profile-photo-editor/package.json index 44724bfc0..cd9371da6 100644 --- a/samples/react-smart-profile-photo-editor/package.json +++ b/samples/react-smart-profile-photo-editor/package.json @@ -20,11 +20,8 @@ "@microsoft/sp-office-ui-fabric-core": "1.11.0", "@microsoft/sp-property-pane": "1.11.0", "@microsoft/sp-webpart-base": "1.11.0", - "@pnp/common": "^1.3.8", - "@pnp/graph": "^1.3.8", - "@pnp/logging": "^1.3.8", - "@pnp/odata": "^1.3.8", - "@pnp/sp": "^1.3.8", + "@pnp/graph": "^2.0.8", + "@pnp/sp": "^2.0.8", "@pnp/spfx-controls-react": "^1.19.0", "@pnp/spfx-property-controls": "^1.20.0-beta.1472053", "cropperjs": "^1.5.6", diff --git a/samples/react-smart-profile-photo-editor/src/services/AnalysisServices/AnalysisService.ts b/samples/react-smart-profile-photo-editor/src/services/AnalysisServices/AnalysisService.ts index 770c6c9d9..6f9bb8b0e 100644 --- a/samples/react-smart-profile-photo-editor/src/services/AnalysisServices/AnalysisService.ts +++ b/samples/react-smart-profile-photo-editor/src/services/AnalysisServices/AnalysisService.ts @@ -26,6 +26,7 @@ export class AnalysisService implements IAnalysisService { visualFeatures: ["Categories", "Adult", "Tags", + "Tags", "Description", "Faces", "Color", diff --git a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/AnalysisDialogContent.tsx b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/AnalysisDialogContent.tsx index f4d67e8f2..584f168d0 100644 --- a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/AnalysisDialogContent.tsx +++ b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/AnalysisDialogContent.tsx @@ -29,8 +29,12 @@ import { IAnalysisService, AnalysisService, MockAnalysisService } from '../../.. import { AnalyzeImageInStreamResponse, ImageTag } from '@azure/cognitiveservices-computervision/esm/models'; import AnalysisChecklist from '../AnalysisChecklist/AnalysisChecklist'; +// This is used if you use the graph client to update pictures +import { MSGraphClient } from '@microsoft/sp-http'; + +// This is used if you use the PnP library to update pictures import { sp } from "@pnp/sp"; -import { MSGraphClient, SPHttpClient } from '@microsoft/sp-http'; +import "@pnp/sp/profiles"; export class AnalysisDialogContent extends React.Component { @@ -215,12 +219,12 @@ export class AnalysisDialogContent extends } {this.state.isSubmitted && - - {strings.SuccessMessage} - + + {strings.SuccessMessage} + } @@ -241,30 +245,42 @@ export class AnalysisDialogContent extends private onUpdateProfilePhoto = async (_ev?: React.SyntheticEvent) => { console.log("Submitting photo"); - const profileBlob: Blob = this.props.blob; // Get image array buffer - this.updateProfilePic(profileBlob); + const profileBlob: Blob = this.props.blob; + + // Submit using the approach you want + //this.updateProfilePicUsingGraph(profileBlob); + this.updateProfilePicUsingPnP(profileBlob); } - private async updateProfilePic(buffer) { - console.log("Update profile pic", buffer); + private async updateProfilePicUsingPnP(blob: Blob) { + console.log("Update profile pic using PnP", blob); + const response = await sp.profiles.setMyProfilePic(blob); + console.log("Profile property Updated", response); + this.setState({ + isSubmitted: true + }); + } - this.props.context.msGraphClientFactory - .getClient().then((client: MSGraphClient) => { - client - .api("me/photo/$value") - .version("v1.0").header("Content-Type", buffer.type).put(buffer, (error, res) => { - if (error) { + private async updateProfilePicUsingGraph(blob: Blob) { + console.log("Update profile pic using Graph", blob); + + this.props.context.msGraphClientFactory + .getClient().then((client: MSGraphClient) => { + client + .api("me/photo/$value") + .version("v1.0").header("Content-Type", blob.type).put(blob, (error, res) => { + if (error) { console.log("Error updating profile", error); - } else { + } else { console.log("Profile property Updated"); this.setState({ isSubmitted: true }); - } - }); - }); + } + }); + }); } private onDismiss = (_ev?: React.SyntheticEvent) => { From d3eb04fd590c8acc9d4fca35e1f98956078a638c Mon Sep 17 00:00:00 2001 From: Hugo Bernier Date: Wed, 12 Aug 2020 23:26:54 -0400 Subject: [PATCH 2/2] Added celebrity detection --- .../package-lock.json | 94 -------------- .../package.json | 2 - .../AnalysisServices/AnalysisService.ts | 1 + .../AnalysisDialog/AnalysisDialogContent.tsx | 118 ++++++++++-------- .../IAnalysisDialogContentState.ts | 3 +- .../components/ProfilePhotoEditor.tsx | 2 - .../components/WebCamDialog/WebCamDialog.tsx | 14 ++- .../webparts/profilePhotoEditor/loc/en-us.js | 1 + .../profilePhotoEditor/loc/mystrings.d.ts | 1 + 9 files changed, 82 insertions(+), 154 deletions(-) diff --git a/samples/react-smart-profile-photo-editor/package-lock.json b/samples/react-smart-profile-photo-editor/package-lock.json index 21a3a84ed..0e7418a0c 100644 --- a/samples/react-smart-profile-photo-editor/package-lock.json +++ b/samples/react-smart-profile-photo-editor/package-lock.json @@ -2487,11 +2487,6 @@ "isomorphic-fetch": "^2.2.1" } }, - "@microsoft/microsoft-graph-types": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-1.13.0.tgz", - "integrity": "sha512-U55VWJWwVxgGZSSJy+s6UtExK6FNnGxhIqE6MkEOqjRwge40QTqBDhNlrCLJzXXDs5Cl0EnDkcgNDMjL97gd5A==" - }, "@microsoft/node-core-library": { "version": "3.13.0", "resolved": "https://registry.npmjs.org/@microsoft/node-core-library/-/node-core-library-3.13.0.tgz", @@ -4264,51 +4259,6 @@ } } }, - "@pnp/graph": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@pnp/graph/-/graph-2.0.8.tgz", - "integrity": "sha512-kLL0Zvu4jvr88voRgCv//ZpU0k3qx/e1sfI1dXHGuo2yp1BlUpYTcJj1ZW3y6C3Zu2lCfIC5F83zcQ9JgoZ+DQ==", - "requires": { - "@microsoft/microsoft-graph-types": "1.13.0", - "@pnp/common": "2.0.8", - "@pnp/logging": "2.0.8", - "@pnp/odata": "2.0.8", - "tslib": "2.0.0" - }, - "dependencies": { - "@pnp/common": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@pnp/common/-/common-2.0.8.tgz", - "integrity": "sha512-z3un/uLnmBHCk6y+OxK9CioI2XqFWw8m6GLdSpkXUcoG7Is41Rqia89Sy6CatAVAhhAcLoW9brI884ju/OXhCA==", - "requires": { - "tslib": "2.0.0" - } - }, - "@pnp/logging": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-2.0.8.tgz", - "integrity": "sha512-76I5j9g/2CgxtscujPvx5vbm3OifuDJgjUYZ3hkuMSmFwK2dN7WcLA+gQqiEkLcSgPx1M9G7vdrZARQSc2pUNA==", - "requires": { - "tslib": "2.0.0" - } - }, - "@pnp/odata": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@pnp/odata/-/odata-2.0.8.tgz", - "integrity": "sha512-keHdsXIiRd6cd5xKQ2hA/RLfYCCm56um5CvWAFts0xdFjsxaLlSkFgvHaw0u2lF7+4Jyr6vvmjCAOlUuPwIZhg==", - "requires": { - "@pnp/common": "2.0.8", - "@pnp/logging": "2.0.8", - "tslib": "2.0.0" - } - }, - "tslib": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", - "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" - } - } - }, "@pnp/logging": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-1.3.8.tgz", @@ -4339,50 +4289,6 @@ } } }, - "@pnp/sp": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@pnp/sp/-/sp-2.0.8.tgz", - "integrity": "sha512-/+V3IilrOI2ab2LOHOjruQhHPAjMbtWaUNEziDSi2eFUGc+8joCtrudM6MN14z6yMGyAItW2AyyBMqaaaceH2Q==", - "requires": { - "@pnp/common": "2.0.8", - "@pnp/logging": "2.0.8", - "@pnp/odata": "2.0.8", - "tslib": "2.0.0" - }, - "dependencies": { - "@pnp/common": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@pnp/common/-/common-2.0.8.tgz", - "integrity": "sha512-z3un/uLnmBHCk6y+OxK9CioI2XqFWw8m6GLdSpkXUcoG7Is41Rqia89Sy6CatAVAhhAcLoW9brI884ju/OXhCA==", - "requires": { - "tslib": "2.0.0" - } - }, - "@pnp/logging": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-2.0.8.tgz", - "integrity": "sha512-76I5j9g/2CgxtscujPvx5vbm3OifuDJgjUYZ3hkuMSmFwK2dN7WcLA+gQqiEkLcSgPx1M9G7vdrZARQSc2pUNA==", - "requires": { - "tslib": "2.0.0" - } - }, - "@pnp/odata": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@pnp/odata/-/odata-2.0.8.tgz", - "integrity": "sha512-keHdsXIiRd6cd5xKQ2hA/RLfYCCm56um5CvWAFts0xdFjsxaLlSkFgvHaw0u2lF7+4Jyr6vvmjCAOlUuPwIZhg==", - "requires": { - "@pnp/common": "2.0.8", - "@pnp/logging": "2.0.8", - "tslib": "2.0.0" - } - }, - "tslib": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", - "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" - } - } - }, "@pnp/sp-clientsvc": { "version": "1.3.11", "resolved": "https://registry.npmjs.org/@pnp/sp-clientsvc/-/sp-clientsvc-1.3.11.tgz", diff --git a/samples/react-smart-profile-photo-editor/package.json b/samples/react-smart-profile-photo-editor/package.json index cd9371da6..4d7ec3eac 100644 --- a/samples/react-smart-profile-photo-editor/package.json +++ b/samples/react-smart-profile-photo-editor/package.json @@ -20,8 +20,6 @@ "@microsoft/sp-office-ui-fabric-core": "1.11.0", "@microsoft/sp-property-pane": "1.11.0", "@microsoft/sp-webpart-base": "1.11.0", - "@pnp/graph": "^2.0.8", - "@pnp/sp": "^2.0.8", "@pnp/spfx-controls-react": "^1.19.0", "@pnp/spfx-property-controls": "^1.20.0-beta.1472053", "cropperjs": "^1.5.6", diff --git a/samples/react-smart-profile-photo-editor/src/services/AnalysisServices/AnalysisService.ts b/samples/react-smart-profile-photo-editor/src/services/AnalysisServices/AnalysisService.ts index 6f9bb8b0e..f208f5977 100644 --- a/samples/react-smart-profile-photo-editor/src/services/AnalysisServices/AnalysisService.ts +++ b/samples/react-smart-profile-photo-editor/src/services/AnalysisServices/AnalysisService.ts @@ -23,6 +23,7 @@ export class AnalysisService implements IAnalysisService { new ApiKeyCredentials({ inHeader: { 'Ocp-Apim-Subscription-Key': this.key } }), this.endpoint); var analysis: AnalyzeImageInStreamResponse = (await computerVisionClient.analyzeImageInStream(buf, { + details: ["Celebrities"], visualFeatures: ["Categories", "Adult", "Tags", diff --git a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/AnalysisDialogContent.tsx b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/AnalysisDialogContent.tsx index 584f168d0..5c0f86749 100644 --- a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/AnalysisDialogContent.tsx +++ b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/AnalysisDialogContent.tsx @@ -3,28 +3,31 @@ import * as React from 'react'; import { IAnalysisDialogContentProps } from './IAnalysisDialogContentProps'; import { IAnalysisDialogContentState } from './IAnalysisDialogContentState'; +import styles from './AnalysisDialogContent.module.scss'; +import { css } from "@uifabric/utilities/lib/css"; + +// Used for localized text +import * as strings from 'ProfilePhotoEditorWebPartStrings'; +import { Text } from '@microsoft/sp-core-library'; + +// Used to determine if we should be making real calls to APIs or just mock calls +import { Environment, EnvironmentType } from '@microsoft/sp-core-library'; + +// Stuff we use for the dialog +import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button'; +import { Icon } from 'office-ui-fabric-react/lib/Icon'; +import { + MessageBar, + MessageBarType +} from 'office-ui-fabric-react'; +import { Image, ImageFit } from 'office-ui-fabric-react/lib/Image'; import { Panel } from 'office-ui-fabric-react/lib/Panel'; import { Label } from 'office-ui-fabric-react/lib/Label'; import { Shimmer } from 'office-ui-fabric-react/lib/Shimmer'; import { ProgressIndicator } from 'office-ui-fabric-react/lib/ProgressIndicator'; -import styles from './AnalysisDialogContent.module.scss'; -// Used for localized text -import * as strings from 'ProfilePhotoEditorWebPartStrings'; - -// Used to determine if we should be making real calls to APIs or just mock calls -import { Environment, EnvironmentType } from '@microsoft/sp-core-library'; - -import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button'; -import { Icon } from 'office-ui-fabric-react/lib/Icon'; -import { css } from "@uifabric/utilities/lib/css"; -import { - MessageBar, - MessageBarType -} from 'office-ui-fabric-react'; - -import { Image, ImageFit } from 'office-ui-fabric-react/lib/Image'; +// Stuff we use for analysis results import { IAnalysisService, AnalysisService, MockAnalysisService } from '../../../../services/AnalysisServices'; import { AnalyzeImageInStreamResponse, ImageTag } from '@azure/cognitiveservices-computervision/esm/models'; import AnalysisChecklist from '../AnalysisChecklist/AnalysisChecklist'; @@ -32,16 +35,9 @@ import AnalysisChecklist from '../AnalysisChecklist/AnalysisChecklist'; // This is used if you use the graph client to update pictures import { MSGraphClient } from '@microsoft/sp-http'; -// This is used if you use the PnP library to update pictures -import { sp } from "@pnp/sp"; -import "@pnp/sp/profiles"; - export class AnalysisDialogContent extends React.Component { - /** - * - */ constructor(props: IAnalysisDialogContentProps) { super(props); this.state = { @@ -74,11 +70,23 @@ export class AnalysisDialogContent extends const analysis: AnalyzeImageInStreamResponse = await service.AnalyzeImage(this.props.imageUrl); // Evaluate analysis against requirements + + // Is this a portrait? const isPortrait: boolean = analysis && analysis.categories && analysis.categories.filter(c => c.name === "people_portrait").length > 0; + + // If the portrait valid? const isPortraitValid: boolean = photoRequirements.requirePortrait ? isPortrait : true; + + // is there only one person in the photo? const onlyOnePersonValid: boolean = analysis.faces.length === 1; + + // Is this a clipart? const isClipartValid: boolean = photoRequirements.allowClipart ? true : analysis.imageType.clipArtType === 0; + + // Is this a line drawing? const isLinedrawingValid: boolean = photoRequirements.allowLinedrawing ? true : analysis.imageType.lineDrawingType === 0; + + // Are we looking at naughty pictures? const isAdultValid: boolean = photoRequirements.allowAdult ? true : !analysis.adult.isAdultContent; const isRacyValid: boolean = photoRequirements.allowRacy ? true : !analysis.adult.isRacyContent; const isGoryValid: boolean = photoRequirements.allowGory ? true : !analysis.adult.isGoryContent; @@ -95,9 +103,16 @@ export class AnalysisDialogContent extends }); } + // Did we find forbidden keywords const keywordsValid: boolean = invalidKeywords.length < 1; - console.log("Invalid keywords", invalidKeywords); + // Look for celebrities + let celebName: string = undefined; + const categories = analysis && analysis.categories && analysis.categories.filter(c => c.detail !== undefined && c.detail.celebrities !== undefined); + if (categories && categories.length > 0) { + // Get the first celebrity + celebName = categories[0] && categories[0].detail && categories[0].detail.celebrities[0] && categories[0].detail.celebrities[0].name; + } // Photo is valid if it meets all requirements const isValid: boolean = isPortraitValid @@ -109,6 +124,7 @@ export class AnalysisDialogContent extends && isGoryValid && keywordsValid; + // Set the state so we can refresh the status this.setState({ isAnalyzing: false, analysis, @@ -122,7 +138,8 @@ export class AnalysisDialogContent extends isRacyValid, isGoryValid, keywordsValid, - invalidKeywords + invalidKeywords, + celebrity: celebName }); } @@ -142,7 +159,8 @@ export class AnalysisDialogContent extends isLinedrawingValid, onlyOnePersonValid, invalidKeywords, - keywordsValid } = this.state; + keywordsValid, + celebrity } = this.state; if (analysis !== undefined) { console.log("Analysis", analysis); @@ -191,6 +209,11 @@ export class AnalysisDialogContent extends
} + {!isAnalyzing && celebrity !== undefined && +

{Text.format(strings.YouLookLikeACelebrity, celebrity)}

+ } + + {!isAnalyzing && isValid &&
{strings.AnalysisGoodLabel}
} @@ -244,33 +267,35 @@ export class AnalysisDialogContent extends } private onUpdateProfilePhoto = async (_ev?: React.SyntheticEvent) => { - console.log("Submitting photo"); - // Get image array buffer const profileBlob: Blob = this.props.blob; // Submit using the approach you want - //this.updateProfilePicUsingGraph(profileBlob); - this.updateProfilePicUsingPnP(profileBlob); + this.updateProfilePicUsingGraph(profileBlob); + //this.updateProfilePicUsingPnP(profileBlob); } - private async updateProfilePicUsingPnP(blob: Blob) { - console.log("Update profile pic using PnP", blob); - const response = await sp.profiles.setMyProfilePic(blob); - console.log("Profile property Updated", response); - this.setState({ - isSubmitted: true - }); - } + // private async updateProfilePicUsingPnP(blob: Blob) { + // pnpSetup({ + // spfxContext: this.props.context + // }); + // console.log("Update profile pic using PnP", blob); + // const response = await sp.profiles.setMyProfilePic(blob); + // console.log("Profile property Updated", response); + // this.setState({ + // isSubmitted: true + // }); + // } + + // Update photo using Graph. + // See https://docs.microsoft.com/en-us/graph/api/profilephoto-update?view=graph-rest-1.0&tabs=http private async updateProfilePicUsingGraph(blob: Blob) { - console.log("Update profile pic using Graph", blob); - this.props.context.msGraphClientFactory .getClient().then((client: MSGraphClient) => { client .api("me/photo/$value") - .version("v1.0").header("Content-Type", blob.type).put(blob, (error, res) => { + .version("v1.0").header("Content-Type", blob.type).put(blob, (error, _res) => { if (error) { console.log("Error updating profile", error); } else { @@ -286,15 +311,4 @@ export class AnalysisDialogContent extends private onDismiss = (_ev?: React.SyntheticEvent) => { this.props.onDismiss(); } - - // private dataURLtoBlob = (dataurl: string): Blob => { - // var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], - // bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); - // while (n--) { - // u8arr[n] = bstr.charCodeAt(n); - // } - // return new Blob([u8arr], { type: mime }); - // } - - } diff --git a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/IAnalysisDialogContentState.ts b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/IAnalysisDialogContentState.ts index 5a1012d23..050e4b006 100644 --- a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/IAnalysisDialogContentState.ts +++ b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/AnalysisDialog/IAnalysisDialogContentState.ts @@ -16,4 +16,5 @@ export interface IAnalysisDialogContentState { keywordsValid?: boolean; invalidKeywords?: string[]; isSubmitted: boolean; -} \ No newline at end of file + celebrity?: string; +} diff --git a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/ProfilePhotoEditor.tsx b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/ProfilePhotoEditor.tsx index 1fc817623..2ea35b61c 100644 --- a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/ProfilePhotoEditor.tsx +++ b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/ProfilePhotoEditor.tsx @@ -289,8 +289,6 @@ export default class ProfilePhotoEditor extends React.Component { - console.log("Blob", blob); - const photoRequirements: IPhotoRequirements = { allowAdult: this.props.allowAdult, allowClipart: this.props.allowClipart, diff --git a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/WebCamDialog/WebCamDialog.tsx b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/WebCamDialog/WebCamDialog.tsx index 0eac0455e..4b228e9fe 100644 --- a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/WebCamDialog/WebCamDialog.tsx +++ b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/components/WebCamDialog/WebCamDialog.tsx @@ -10,6 +10,9 @@ import styles from './WebCamDialog.module.scss'; import Webcam from "react-webcam"; +/** + * Set the video constraints to be a square from the user-facing camera + */ const videoConstraints = { width: 300, height: 300, @@ -41,8 +44,6 @@ export class WebCamDialog extends React.Component console.log("OnUserMedia")} - onUserMediaError={() => console.log("OnUserMediaError")} screenshotQuality={0.92} /> @@ -54,12 +55,19 @@ export class WebCamDialog extends React.Component { const imageSrc = this.webcamRef.getScreenshot(); - console.log("ImageSrc", imageSrc); this.props.onCapture(imageSrc); } + /** + * + * Dismisses the dialog + * @param _ev + */ private onDismiss = (_ev?: React.SyntheticEvent) => { this.props.onDismiss(); } diff --git a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/loc/en-us.js b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/loc/en-us.js index 6350a7007..0f7e79944 100644 --- a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/loc/en-us.js +++ b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/loc/en-us.js @@ -1,5 +1,6 @@ define([], function() { return { + YouLookLikeACelebrity: "Uh... did anyone ever tell you that you look like {0}? It's uncanny!", CaptureButtonLabel: "Capture", WebCamDialogTitle: "Insert photo from camera", NoKeywords: "(none)", diff --git a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/loc/mystrings.d.ts b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/loc/mystrings.d.ts index 5f457aec8..e9a029aa0 100644 --- a/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/loc/mystrings.d.ts +++ b/samples/react-smart-profile-photo-editor/src/webparts/profilePhotoEditor/loc/mystrings.d.ts @@ -1,4 +1,5 @@ declare interface IProfilePhotoEditorWebPartStrings { + YouLookLikeACelebrity: string; CaptureButtonLabel: string; WebCamDialogTitle: string; NoKeywords: string;