Added sample react-google-fit (#755)

This commit is contained in:
Nanddeep Nachan 2019-02-09 15:40:15 +05:30 committed by Vesa Juvonen
parent ba7a0e1615
commit 835321c6fd
35 changed files with 18500 additions and 0 deletions

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

32
samples/react-google-fit/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,11 @@
{
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.7.1",
"libraryName": "react-google-fit",
"libraryId": "2469a81b-8fb7-4cb4-a710-50f324465560",
"packageManager": "npm",
"componentType": "webpart"
}
}

View File

@ -0,0 +1,94 @@
## Web part displaying Google Fit information
### Summary
This sample demonstrates integration of Google Fit information with SharePoint Framework. The Google Fitness REST APIs allows developers to extend it further and create their own dashboards. Google Fitness REST APIs are useful if you have fitness app and you want to integrate your data with google fit or if you just want to collect Fitness data and display some information to the users. This web part helps to display the key fitness information (activity time spent, distance travelled, calories burned, step count) from the Google fit data source.
![Web part preview][figure1]
### Generate OAuth 2.0 client ID
In order to use Google REST APIs, we need to generate an OAuth 2.0 client ID. Follow below procedure to generate it.
1. Open Google API Console from [here](https://console.developers.google.com/flows/enableapi?apiid=fitness).
2. Select any existing project or choose to create a new project.
3. Click Continue. <br/>
![Create a new project][figure3]
4. Once the project is created, we will have to generate the credentials.
5. Click "Go to credentials".<br/>
![Generate credentials][figure4]
6. Select the options as highlighted below:<br/>
![Add credentials to your project][figure5]
7. Click "What credentials do I need?".
8. Under Authorized JavaScript origins, add SharePoint Online site url (e.g. https://contoso.sharepoint.com) or https://localhost:4321, if you are using SharePoint local workbench.
9. Under Authorized redirect URI, add https://localhost:4321/auth/google/callback, if you are using SharePoint local workbench.<br/>
![Add authorized origins][figure6]
10. Click "Create OAuth client ID".
11. Set up the OAuth 2.0 consent screen.<br/>
![Setup OAuth consent][figure7]
12. Click Continue.
13. The Client id will be generated. Note it down to use in web part property.<br/>
![OAuth ClientId][figure8]
14. Click Done.
### Configure the Web Part to use
1. Add "Google Fit Activity Viewer" web part on SharePoint page.
2. Edit the web part.
3. Add above generated OAuth 2.0 client ID to "ClientId Field" web part property.
4. Save the changes.
![SharePoint Run][figure2]
### NPM Packages Used
Below NPM package is used to develop this sample.
1. react-google-authorize (https://www.npmjs.com/package/react-google-authorize)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/drop-1.7.1-green.svg)
## Applies to
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
## Solution
Solution|Author(s)
--------|---------
react-google-fit|[Nanddeep Nachan](https://www.linkedin.com/in/nanddeepnachan/) (SharePoint Consultant, [@NanddeepNachan](https://twitter.com/NanddeepNachan))
## Version history
Version|Date|Comments
-------|----|--------
1.0.0|January 14, 2019|Initial release
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
---
## Prerequisites
- SharePoint Online tenant
- Site Collection created under the **/sites/** or **/**
## Minimal Path to Awesome
- Clone this repo
- npm i
- gulp serve --nobrowser
- Open workbench on your tennant, i.e. https://contoso.sharepoint.com/sites/salesteam/_layouts/15/workbench.aspx
- Search and add web part "Google Fit Activity Viewer"
## Features
This sample web part shows how adaptive cards can be used effectively with SharePoint Framework to render an image gallery with data stored in a SharePoint list.
- Integrating Google Fit information
- Consuming Google Fit REST APIs
- Creating extensible services
- Using @react-google-authorize
[figure1]: ./assets/webpart-preview.png
[figure2]: ./assets/sharepoint-run.gif
[figure3]: ./assets/create-new-project.png
[figure4]: ./assets/generate-credentials.png
[figure5]: ./assets/add-credentials-to-your-project.png
[figure6]: ./assets/add-authorized-origins.png
[figure7]: ./assets/setup-oauth-consent.png
[figure8]: ./assets/oauth-clientid.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"google-fit-activity-viewer-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/googleFitActivityViewer/GoogleFitActivityViewerWebPart.js",
"manifest": "./src/webparts/googleFitActivityViewer/GoogleFitActivityViewerWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"GoogleFitActivityViewerWebPartStrings": "lib/webparts/googleFitActivityViewer/loc/{locale}.js"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-google-fit",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,12 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-google-fit-client-side-solution",
"id": "2469a81b-8fb7-4cb4-a710-50f324465560",
"version": "1.0.0.0",
"includeClientSideAssets": true
},
"paths": {
"zippedPackage": "solution/react-google-fit.sppkg"
}
}

View File

@ -0,0 +1,10 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

7
samples/react-google-fit/gulpfile.js vendored Normal file
View File

@ -0,0 +1,7 @@
'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
build.initialize(gulp);

17603
samples/react-google-fit/npm-shrinkwrap.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
{
"name": "react-google-fit",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.7.1",
"@microsoft/sp-lodash-subset": "1.7.1",
"@microsoft/sp-office-ui-fabric-core": "1.7.1",
"@microsoft/sp-webpart-base": "1.7.1",
"@types/es6-promise": "0.0.33",
"@types/react": "16.4.2",
"@types/react-dom": "16.0.5",
"@types/webpack-env": "1.13.1",
"react": "16.3.2",
"react-dom": "16.3.2",
"react-google-authorize": "^1.0.3"
},
"resolutions": {
"@types/react": "16.4.2"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.7.1",
"@microsoft/sp-tslint-rules": "1.7.1",
"@microsoft/sp-module-interfaces": "1.7.1",
"@microsoft/sp-webpart-workbench": "1.7.1",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
}
}

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,130 @@
import { ServiceScope, ServiceKey } from "@microsoft/sp-core-library";
import { IDataService } from './IDataService';
import { HttpClient, HttpClientResponse, IHttpClientOptions } from '@microsoft/sp-http';
import { PageContext } from '@microsoft/sp-page-context';
import { IFitnessActivity, IFitnessPoint, IFitnessPointValue } from './IFitnessActivity';
export class GoogleFitService implements IDataService {
public static readonly serviceKey: ServiceKey<IDataService> = ServiceKey.create<IDataService>('googleFit:data-service', GoogleFitService);
private _httpClient: HttpClient;
private _pageContext: PageContext;
constructor(serviceScope: ServiceScope) {
serviceScope.whenFinished(() => {
// Configure the required dependencies
this._httpClient = serviceScope.consume(HttpClient.serviceKey);
this._pageContext = serviceScope.consume(PageContext.serviceKey);
});
}
// Get step count from Google fit data source
public getStepCount(accessToken: string): Promise<number> {
return new Promise<number>((resolve: (itemId: number) => void, reject: (error: any) => void): void => {
this.getGoogleFitData('derived:com.google.step_count.delta:com.google.android.gms:estimated_steps', accessToken)
.then((fitnessData: IFitnessActivity): void => {
var stepsCount: number = 0;
var i: number = 0;
var j: number = 0;
// Calculate step count of each activity
for (i = 0; i < fitnessData.point.length; i++) {
for (j = 0; j < fitnessData.point[i].value.length; j++) {
stepsCount += fitnessData.point[i].value[j].intVal;
}
}
resolve(stepsCount);
});
});
}
// Get calories burned from Google fit data source
public getCalories(accessToken: string): Promise<number> {
return new Promise<number>((resolve: (itemId: number) => void, reject: (error: any) => void): void => {
this.getGoogleFitData('derived:com.google.calories.expended:com.google.android.gms:merge_calories_expended', accessToken)
.then((fitnessData: IFitnessActivity): void => {
var calories: number = 0;
var i: number = 0;
var j: number = 0;
// Calculate calories burned during each activity
for (i = 0; i < fitnessData.point.length; i++) {
for (j = 0; j < fitnessData.point[i].value.length; j++) {
calories += fitnessData.point[i].value[j].fpVal;
}
}
resolve(calories);
});
});
}
// Get distance travelled from Google fit data source
public getDistance(accessToken: string): Promise<number> {
return new Promise<number>((resolve: (itemId: number) => void, reject: (error: any) => void): void => {
this.getGoogleFitData('derived:com.google.distance.delta:com.google.android.gms:merge_distance_delta', accessToken)
.then((fitnessData: IFitnessActivity): void => {
var distance: number = 0;
var i: number = 0;
var j: number = 0;
// Calculate distance travelled during each activity
for (i = 0; i < fitnessData.point.length; i++) {
for (j = 0; j < fitnessData.point[i].value.length; j++) {
distance += fitnessData.point[i].value[j].fpVal;
}
}
resolve(distance / 1000);
});
});
}
// Get activity time from Google fit data source
public getActivityTime(accessToken: string): Promise<number> {
return new Promise<number>((resolve: (itemId: number) => void, reject: (error: any) => void): void => {
this.getGoogleFitData('derived:com.google.activity.segment:com.google.android.gms:merge_activity_segments', accessToken)
.then((fitnessData: IFitnessActivity): void => {
var activityTime: number = 0;
var i: number = 0;
var j: number = 0;
// Calculate activity time spent for each activity
for (i = 0; i < fitnessData.point.length; i++) {
for (j = 0; j < fitnessData.point[i].value.length; j++) {
activityTime += fitnessData.point[i].value[j].intVal;
}
}
resolve(activityTime);
});
});
}
// Get Google fit data by calling the REST API
private getGoogleFitData(activityScope: string, accessToken: string): Promise<IFitnessActivity> {
// Calculate start date, end date
var startTime: number = new Date().getTime();
var todayMidnight: Date = new Date();
todayMidnight.setHours(0, 0, 0, 0);
var endTime: number = todayMidnight.getTime();
const requestHeaders: Headers = new Headers();
requestHeaders.append("Content-type", "application/json");
requestHeaders.append("Cache-Control", "no-cache");
const postOptions: IHttpClientOptions = {
headers: requestHeaders
};
let sessionUrl: string = `https://www.googleapis.com/fitness/v1/users/me/dataSources/` + activityScope + `/datasets/` + startTime + `000000-` + endTime + `000000?access_token=` + accessToken;
return new Promise<IFitnessActivity>((resolve: (itemId: IFitnessActivity) => void, reject: (error: any) => void): void => {
this._httpClient.get(sessionUrl, HttpClient.configurations.v1, postOptions)
.then((response: HttpClientResponse) => {
response.json().then((responseJSON: IFitnessActivity) => {
resolve(responseJSON);
});
});
});
}
}

View File

@ -0,0 +1,6 @@
export interface IDataService {
getStepCount: (accessToken: string) => Promise<any>;
getCalories: (accessToken: string) => Promise<any>;
getDistance: (accessToken: string) => Promise<any>;
getActivityTime: (accessToken: string) => Promise<any>;
}

View File

@ -0,0 +1,18 @@
export interface IFitnessActivity {
dataSourceId: string;
maxEndTimeNs: string;
minStartTimeNs: string;
point: IFitnessPoint[];
}
export interface IFitnessPoint {
dataTypeName: string;
endTimeNanos: string;
modifiedTimeMillis: string;
value: IFitnessPointValue[];
}
export interface IFitnessPointValue {
intVal: number;
fpVal: number;
}

View File

@ -0,0 +1,31 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "5626ea8d-ef33-451a-9a96-2a5120228e70",
"alias": "GoogleFitActivityViewerWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"preconfiguredEntries": [
{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": {
"default": "Other"
},
"title": {
"default": "Google Fit Activity Viewer"
},
"description": {
"default": "Display Google Fit Activities"
},
"officeFabricIconFontName": "Health",
"properties": {
"description": "Google Fit Activity Viewer"
}
}
]
}

View File

@ -0,0 +1,65 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import * as strings from 'GoogleFitActivityViewerWebPartStrings';
import GoogleFitActivityViewer from './components/GoogleFitActivityViewer';
import { IGoogleFitActivityViewerProps } from './components/IGoogleFitActivityViewerProps';
export interface IGoogleFitActivityViewerWebPartProps {
clientId: string;
}
export default class GoogleFitActivityViewerWebPart extends BaseClientSideWebPart<IGoogleFitActivityViewerWebPartProps> {
public render(): void {
const element: React.ReactElement<IGoogleFitActivityViewerProps> = React.createElement(
GoogleFitActivityViewer,
{
clientId: this.properties.clientId,
serviceScope: this.context.serviceScope
}
);
ReactDom.render(element, this.domElement);
}
protected get disableReactivePropertyChanges(): boolean {
return true;
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('clientId', {
label: strings.ClientIdFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,116 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.googleFitActivityViewer {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
// Office UI Fabric (included from: https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-js/master/src/components/Table/Table.scss)
// --------------------------------------------------
// Data table styles
.msTable {
display: table;
border-collapse: collapse;
}
.msTable tr,
.msTableRow {
display: table-row;
line-height: 35px;
font-weight: $ms-font-weight-semilight;
font-size: $ms-font-size-xl;
color: $ms-color-neutralPrimary;
}
.msTableRowHeader {
line-height: 35px;
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-xl;
color: $ms-color-white;
background-color: $ms-color-themeDark;
padding: 10px;
}
.msTable th,
.msTable td,
.msTableCell {
display: table-cell;
padding: 5px 10px;
}
// Style the first row as a header.
.msTable thead th,
.msTableHead {
font-weight: $ms-font-weight-semilight;
font-size: $ms-font-size-xs;
color: $ms-color-neutralSecondary;
}
}

View File

@ -0,0 +1,172 @@
import * as React from 'react';
import styles from './GoogleFitActivityViewer.module.scss';
import { IGoogleFitActivityViewerProps } from './IGoogleFitActivityViewerProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { HttpClient, SPHttpClient, HttpClientConfiguration, HttpClientResponse, ODataVersion, IHttpClientConfiguration, IHttpClientOptions, ISPHttpClientOptions } from '@microsoft/sp-http';
import { GoogleAuthorize } from 'react-google-authorize';
import { IGoogleFitActivityViewerState } from './IGoogleFitActivityViewerState';
import { Icon } from 'office-ui-fabric-react/lib/Icon';
import { ServiceScope, Environment, EnvironmentType } from '@microsoft/sp-core-library';
import { GoogleFitService } from '../../../services/GoogleFitService';
import { IDataService } from '../../../services/IDataService';
export default class GoogleFitActivityViewer extends React.Component<IGoogleFitActivityViewerProps, IGoogleFitActivityViewerState> {
private dataCenterServiceInstance: IDataService;
constructor(props) {
super(props);
this.state = {
isGoogleAuthenticated: false,
accessToken: "",
stepCount: 0,
calories: 0,
distance: 0,
activityTime: 0
};
}
public render(): React.ReactElement<IGoogleFitActivityViewerProps> {
const responseGoogle = (response) => {
this.setState(() => {
return {
...this.state,
isGoogleAuthenticated: true,
accessToken: response.access_token
};
});
this.readStepCount(this.state.accessToken);
this.readCalories(this.state.accessToken);
this.readDistance(this.state.accessToken);
this.readActivityTime(this.state.accessToken);
};
const formatNumber = (num) => parseFloat(num.toFixed(2)).toLocaleString().replace(/\.([0-9])$/, ".$10");
return (
<div className={styles.googleFitActivityViewer}>
<div className={styles.container}>
{
!this.state.isGoogleAuthenticated && this.state.accessToken == "" &&
<GoogleAuthorize
scope={'https://www.googleapis.com/auth/fitness.activity.read https://www.googleapis.com/auth/fitness.location.read'}
clientId={this.props.clientId}
onSuccess={responseGoogle}
onFailure={responseGoogle}
>
<span>Login with Google</span>
</GoogleAuthorize>
}
{
this.state.isGoogleAuthenticated &&
<div>
<div className={styles.msTable}>
<div className={styles.msTableRowHeader}>
<span className={styles.msTableCell}>
Today, {new Date().toDateString()}
</span>
</div>
</div>
<div className={styles.msTable}>
<div className={styles.msTableRow}>
<span className={styles.msTableCell}>
<Icon iconName="Clock" className="ms-IconExample" />
</span>
<span className={styles.msTableCell}>
<b>{formatNumber(this.state.activityTime)}</b> min
</span>
</div>
<div className={styles.msTableRow}>
<span className={styles.msTableCell}>
<Icon iconName="POI" className="ms-IconExample" />
</span>
<span className={styles.msTableCell}>
<b>{formatNumber(this.state.distance)}</b> km
</span>
</div>
<div className={styles.msTableRow}>
<span className={styles.msTableCell}>
<Icon iconName="CaloriesAdd" className="ms-IconExample" />
</span>
<span className={styles.msTableCell}>
<b>{formatNumber(this.state.calories)}</b> calories
</span>
</div>
<div className={styles.msTableRow}>
<span className={styles.msTableCell}>
<Icon iconName="Running" className="ms-IconExample" />
</span>
<span className={styles.msTableCell}>
<b>{formatNumber(this.state.stepCount)}</b> steps
</span>
</div>
</div>
</div>
}
</div>
</div>
);
}
private readStepCount(accessToken: string): void {
let serviceScope: ServiceScope = this.props.serviceScope;
this.dataCenterServiceInstance = serviceScope.consume(GoogleFitService.serviceKey);
this.dataCenterServiceInstance.getStepCount(accessToken).then((stepCount: number) => {
this.setState(() => {
return {
...this.state,
stepCount: stepCount
};
});
});
}
private readCalories(accessToken: string): void {
let serviceScope: ServiceScope = this.props.serviceScope;
this.dataCenterServiceInstance = serviceScope.consume(GoogleFitService.serviceKey);
this.dataCenterServiceInstance.getCalories(accessToken).then((calories: number) => {
this.setState(() => {
return {
...this.state,
calories: calories
};
});
});
}
private readDistance(accessToken: string): void {
let serviceScope: ServiceScope = this.props.serviceScope;
this.dataCenterServiceInstance = serviceScope.consume(GoogleFitService.serviceKey);
this.dataCenterServiceInstance.getDistance(accessToken).then((distance: number) => {
this.setState(() => {
return {
...this.state,
distance: distance
};
});
});
}
private readActivityTime(accessToken: string): void {
let serviceScope: ServiceScope = this.props.serviceScope;
this.dataCenterServiceInstance = serviceScope.consume(GoogleFitService.serviceKey);
this.dataCenterServiceInstance.getActivityTime(accessToken).then((activityTime: number) => {
this.setState(() => {
return {
...this.state,
activityTime: activityTime
};
});
});
}
}

View File

@ -0,0 +1,6 @@
import { ServiceScope } from '@microsoft/sp-core-library';
export interface IGoogleFitActivityViewerProps {
clientId: string;
serviceScope: ServiceScope;
}

View File

@ -0,0 +1,8 @@
export interface IGoogleFitActivityViewerState {
isGoogleAuthenticated: boolean;
accessToken: string;
stepCount: number;
calories: number;
distance: number;
activityTime: number;
}

View File

@ -0,0 +1,7 @@
define([], function () {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"ClientIdFieldLabel": "ClientId Field"
}
});

View File

@ -0,0 +1,10 @@
declare interface IGoogleFitActivityViewerWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
ClientIdFieldLabel: string;
}
declare module 'GoogleFitActivityViewerWebPartStrings' {
const strings: IGoogleFitActivityViewerWebPartStrings;
export = strings;
}

View File

@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -0,0 +1,30 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}