Added the Trending in the sites I follow sample (#22)
This commit is contained in:
parent
ee4e6f4e8f
commit
74be206b38
|
@ -22,6 +22,12 @@ Sample SharePoint Framework Client-Side Web Part built using React showing docum
|
|||
|
||||
![Working with Web Part in the SharePoint Workbench](./assets/my-recent-documents-preview.png)
|
||||
|
||||
### Trending in the sites I follow
|
||||
|
||||
Sample SharePoint Framework Client-Side Web Part built using React showing documents trending in the sites followed by the current user.
|
||||
|
||||
![Working with Web Part in the SharePoint Workbench](./assets/trending-in-sites-i-follow-preview.png)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
|
||||
|
@ -37,6 +43,7 @@ react-officegraph|Waldek Mastykarz (MVP, Rencore, @waldekm)
|
|||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.3.0|September 20, 2016|Added the Trending in the sites I follow sample
|
||||
1.2.0|September 20, 2016|Added the My recent documents sample
|
||||
1.1.0|September 19, 2016|Added the Working with sample
|
||||
1.0.0|September 9, 2016|Initial release
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 421 KiB |
|
@ -14,6 +14,11 @@
|
|||
"entry": "./lib/webparts/myRecentDocuments/MyRecentDocumentsWebPart.js",
|
||||
"manifest": "./src/webparts/myRecentDocuments/MyRecentDocumentsWebPart.manifest.json",
|
||||
"outputPath": "./dist/my-recent-documents.bundle.js"
|
||||
},
|
||||
{
|
||||
"entry": "./lib/webparts/trendingInTheSitesIFollow/TrendingInTheSitesIFollowWebPart.js",
|
||||
"manifest": "./src/webparts/trendingInTheSitesIFollow/TrendingInTheSitesIFollowWebPart.manifest.json",
|
||||
"outputPath": "./dist/trending-in-the-sites-i-follow.bundle.js"
|
||||
}
|
||||
],
|
||||
"externals": {
|
||||
|
@ -28,6 +33,7 @@
|
|||
"localizedResources": {
|
||||
"trendingInThisSiteStrings": "webparts/trendingInThisSite/loc/{locale}.js",
|
||||
"workingWithStrings": "webparts/workingWith/loc/{locale}.js",
|
||||
"myRecentDocumentsStrings": "webparts/myRecentDocuments/loc/{locale}.js"
|
||||
"myRecentDocumentsStrings": "webparts/myRecentDocuments/loc/{locale}.js",
|
||||
"trendingInTheSitesIFollowStrings": "webparts/trendingInTheSitesIFollow/loc/{locale}.js"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-officegraph",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
|
|
@ -4,6 +4,8 @@ export interface ITrendingDocument {
|
|||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
webUrl?: string;
|
||||
webTitle?: string;
|
||||
previewImageUrl: string;
|
||||
extension: string;
|
||||
activity: IActivity;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export interface ITrendingInTheSitesIFollowWebPartProps {
|
||||
title: string;
|
||||
numberOfDocuments: number;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
.trendingInTheSitesIFollow {
|
||||
.webPartTitle {
|
||||
margin-bottom: 0.7em;
|
||||
margin-left: 0.38em;
|
||||
}
|
||||
|
||||
:global .ms-DocumentCard {
|
||||
float: left;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
:global .ms-Spinner {
|
||||
width: 7em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
|
||||
|
||||
"id": "90a04bb9-fbdf-43d1-bfe1-0491d34941be",
|
||||
"componentType": "WebPart",
|
||||
"version": "0.0.1",
|
||||
"manifestVersion": 2,
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "90a04bb9-fbdf-43d1-bfe1-0491d34941be",
|
||||
"group": { "default": "Content rollup" },
|
||||
"title": { "default": "Trending in the sites I follow" },
|
||||
"description": { "default": "Shows documents trending in the sites followed by the current user" },
|
||||
"officeFabricIconFontName": "Chart",
|
||||
"properties": {
|
||||
"title": "Trending in the sites I follow",
|
||||
"numberOfDocuments": 5
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneSettings,
|
||||
IWebPartContext,
|
||||
PropertyPaneTextField,
|
||||
PropertyPaneSlider
|
||||
} from '@microsoft/sp-client-preview';
|
||||
|
||||
import * as strings from 'trendingInTheSitesIFollowStrings';
|
||||
import TrendingInTheSitesIFollow, { ITrendingInTheSitesIFollowProps } from './components/TrendingInTheSitesIFollow';
|
||||
import { ITrendingInTheSitesIFollowWebPartProps } from './ITrendingInTheSitesIFollowWebPartProps';
|
||||
|
||||
export default class TrendingInTheSitesIFollowWebPart extends BaseClientSideWebPart<ITrendingInTheSitesIFollowWebPartProps> {
|
||||
|
||||
public constructor(context: IWebPartContext) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<ITrendingInTheSitesIFollowProps> = React.createElement(TrendingInTheSitesIFollow, {
|
||||
title: this.properties.title,
|
||||
numberOfDocuments: this.properties.numberOfDocuments,
|
||||
httpClient: this.context.httpClient,
|
||||
siteUrl: this.context.pageContext.web.absoluteUrl
|
||||
});
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected get propertyPaneSettings(): IPropertyPaneSettings {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.ViewGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('title', {
|
||||
label: strings.TitleFieldLabel
|
||||
}),
|
||||
PropertyPaneSlider('numberOfDocuments', {
|
||||
label: strings.NumberOfDocumentsFieldLabel,
|
||||
min: 1,
|
||||
max: 10,
|
||||
step: 1
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
import * as React from 'react';
|
||||
import {
|
||||
css,
|
||||
DocumentCard,
|
||||
DocumentCardLocation,
|
||||
DocumentCardPreview,
|
||||
DocumentCardTitle,
|
||||
DocumentCardActivity,
|
||||
Spinner
|
||||
} from 'office-ui-fabric-react';
|
||||
|
||||
import styles from '../TrendingInTheSitesIFollow.module.scss';
|
||||
import { ITrendingInTheSitesIFollowWebPartProps } from '../ITrendingInTheSitesIFollowWebPartProps';
|
||||
import { HttpClient } from '@microsoft/sp-client-base';
|
||||
import { ITrendingDocument } from '../../ITrendingDocument';
|
||||
import { IActorInformation } from '../../IActorInformation';
|
||||
import { SearchUtils, ISearchQueryResponse, IRow, ICell, IEdge } from '../../SearchUtils';
|
||||
import { Utils } from '../../Utils';
|
||||
|
||||
export interface ITrendingInTheSitesIFollowProps extends ITrendingInTheSitesIFollowWebPartProps {
|
||||
httpClient: HttpClient;
|
||||
siteUrl: string;
|
||||
}
|
||||
|
||||
export interface ITrendingInTheSitesIFollowState {
|
||||
trendingDocuments: ITrendingDocument[];
|
||||
loading: boolean;
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface ISiteInfo {
|
||||
Uri: string;
|
||||
}
|
||||
|
||||
export default class TrendingInTheSitesIFollow extends React.Component<ITrendingInTheSitesIFollowProps, ITrendingInTheSitesIFollowState> {
|
||||
constructor(props: ITrendingInTheSitesIFollowProps, state: ITrendingInTheSitesIFollowState) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
trendingDocuments: [] as ITrendingDocument[],
|
||||
loading: true,
|
||||
error: null
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.loadDocuments(this.props.siteUrl, this.props.numberOfDocuments);
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: ITrendingInTheSitesIFollowProps, prevState: ITrendingInTheSitesIFollowState, prevContext: any): void {
|
||||
if (this.props.numberOfDocuments !== prevProps.numberOfDocuments ||
|
||||
this.props.siteUrl !== prevProps.siteUrl && (
|
||||
this.props.numberOfDocuments && this.props.siteUrl
|
||||
)) {
|
||||
this.loadDocuments(this.props.siteUrl, this.props.numberOfDocuments);
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const loading: JSX.Element = this.state.loading ? <div style={{ margin: '0 auto' }}><Spinner label={'Loading...'} /></div> : <div/>;
|
||||
const error: JSX.Element = this.state.error ? <div><strong>Error: </strong> {this.state.error}</div> : <div/>;
|
||||
const documents: JSX.Element[] = this.state.trendingDocuments.map((doc: ITrendingDocument, i: number) => {
|
||||
const iconUrl: string = `https://spoprod-a.akamaihd.net/files/odsp-next-prod_ship-2016-08-15_20160815.002/odsp-media/images/filetypes/32/${doc.extension}.png`;
|
||||
return (
|
||||
<DocumentCard onClickHref={doc.url} key={doc.id}>
|
||||
<DocumentCardPreview
|
||||
previewImages={[
|
||||
{
|
||||
previewImageSrc: doc.previewImageUrl,
|
||||
iconSrc: iconUrl,
|
||||
width: 318,
|
||||
height: 196,
|
||||
accentColor: '#ce4b1f'
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<DocumentCardTitle title={doc.title}/>
|
||||
<DocumentCardLocation location={doc.webTitle} locationHref={doc.webUrl} />
|
||||
<DocumentCardActivity
|
||||
activity={`${doc.activity.name} ${doc.activity.date}`}
|
||||
people={
|
||||
[
|
||||
{ name: doc.activity.actorName, profileImageSrc: doc.activity.actorPhotoUrl }
|
||||
]
|
||||
}
|
||||
/>
|
||||
</DocumentCard>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div className={styles.trendingInTheSitesIFollow}>
|
||||
<div className={css('ms-font-xl', styles.webPartTitle) }>{this.props.title}</div>
|
||||
{loading}
|
||||
{error}
|
||||
{documents}
|
||||
<div style={{ clear: 'both' }}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private loadDocuments(siteUrl: string, numberOfDocuments: number): void {
|
||||
this.setState({
|
||||
loading: true,
|
||||
error: undefined,
|
||||
trendingDocuments: []
|
||||
});
|
||||
const trendingDocuments: ITrendingDocument[] = [];
|
||||
this.getSitesIFollow(siteUrl)
|
||||
.then((sitesIFollow: string[]): Promise<ITrendingDocument[]> => {
|
||||
return this.getTrendingDocuments(sitesIFollow, siteUrl, numberOfDocuments);
|
||||
})
|
||||
.then((trendingDocuments: ITrendingDocument[]): void => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: undefined,
|
||||
trendingDocuments: trendingDocuments
|
||||
});
|
||||
}, (error: any): void => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: error,
|
||||
trendingDocuments: []
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private getSitesIFollow(siteUrl: string): Promise<string[]> {
|
||||
return new Promise((resolve: (sitesIFollow: string[]) => void, reject: (error: any) => void): void => {
|
||||
this.props.httpClient.get(`${siteUrl}/_api/social.following/my/followed(types=4)`, {
|
||||
headers: {
|
||||
'Accept': 'application/json;odata=nometadata',
|
||||
'odata-version': ''
|
||||
}
|
||||
})
|
||||
.then((response: Response): Promise<{ value: ISiteInfo[] }> => {
|
||||
return response.json();
|
||||
})
|
||||
.then((sitesIFollowInfo: { value: ISiteInfo[] }): void => {
|
||||
const sitesIFollow: string[] = [];
|
||||
|
||||
for (let i: number = 0; i < sitesIFollowInfo.value.length; i++) {
|
||||
sitesIFollow.push(sitesIFollowInfo.value[i].Uri);
|
||||
}
|
||||
|
||||
resolve(sitesIFollow);
|
||||
}, (error: any): void => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getTrendingDocuments(sitesIFollow: string[], siteUrl: string, numberOfDocuments: number): Promise<ITrendingDocument[]> {
|
||||
return new Promise((resolve: (trendingDocuments: ITrendingDocument[]) => void, reject: (error: any) => void): void => {
|
||||
if (sitesIFollow.length === 0) {
|
||||
return resolve([]);
|
||||
}
|
||||
|
||||
let query: string = '(';
|
||||
for (let i: number = 0; i < sitesIFollow.length; i++) {
|
||||
if (query.length > 1) {
|
||||
query += ' OR ';
|
||||
}
|
||||
|
||||
query += `Path:"${sitesIFollow[i]}"`;
|
||||
}
|
||||
query += ') AND (IsDocument:1)';
|
||||
|
||||
const postData: string = JSON.stringify({
|
||||
'request': {
|
||||
'__metadata': {
|
||||
'type': 'Microsoft.Office.Server.Search.REST.SearchRequest'
|
||||
},
|
||||
'Querytext': query,
|
||||
'SelectProperties': {
|
||||
'results': ['Author', 'AuthorOwsUser', 'DocId', 'DocumentPreviewMetadata', 'Edges', 'EditorOwsUser', 'FileExtension', 'FileType', 'HitHighlightedProperties', 'HitHighlightedSummary', 'LastModifiedTime', 'LikeCountLifetime', 'ListID', 'ListItemID', 'OriginalPath', 'Path', 'Rank', 'SPWebUrl', 'SecondaryFileExtension', 'ServerRedirectedURL', 'SiteTitle', 'Title', 'ViewCountLifetime', 'siteID', 'uniqueID', 'webID']
|
||||
},
|
||||
'ClientType': 'TrendingInTheSitesIFollow',
|
||||
'BypassResultTypes': 'true',
|
||||
'RowLimit': numberOfDocuments,
|
||||
'StartRow': '0',
|
||||
'RankingModelId': '0c77ded8-c3ef-466d-929d-905670ea1d72',
|
||||
'Properties': {
|
||||
'results': [{
|
||||
'Name': 'IncludeExternalContent',
|
||||
'Value': {
|
||||
'BoolVal': 'True',
|
||||
'QueryPropertyValueTypeIndex': 3
|
||||
}
|
||||
}, {
|
||||
'Name': 'GraphQuery',
|
||||
'Value': {
|
||||
'StrVal': 'actor(ME,action:1021)',
|
||||
'QueryPropertyValueTypeIndex': 1
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
this.props.httpClient.post(`${siteUrl}/_api/search/postquery`, {
|
||||
headers: {
|
||||
'Accept': 'application/json;odata=nometadata',
|
||||
'Content-type': 'application/json;odata=verbose',
|
||||
'odata-version': ''
|
||||
},
|
||||
body: postData
|
||||
})
|
||||
.then((response: Response): Promise<ISearchQueryResponse> => {
|
||||
return response.json();
|
||||
})
|
||||
.then((response: ISearchQueryResponse): void => {
|
||||
if (!response ||
|
||||
!response.PrimaryQueryResult ||
|
||||
!response.PrimaryQueryResult.RelevantResults ||
|
||||
response.PrimaryQueryResult.RelevantResults.RowCount === 0) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const trendingDocuments: ITrendingDocument[] = [];
|
||||
for (let i: number = 0; i < response.PrimaryQueryResult.RelevantResults.Table.Rows.length; i++) {
|
||||
const row: IRow = response.PrimaryQueryResult.RelevantResults.Table.Rows[i];
|
||||
const cells: ICell[] = row.Cells;
|
||||
const editorInfo: string[] = SearchUtils.getValueFromResults('EditorOwsUser', cells).split('|');
|
||||
const modifiedDate: Date = new Date(SearchUtils.getValueFromResults('LastModifiedTime', cells).replace('.0000000', ''));
|
||||
const dateString: string = (modifiedDate.getMonth() + 1) + '/' + modifiedDate.getDate() + '/' + modifiedDate.getFullYear();
|
||||
trendingDocuments.push({
|
||||
id: SearchUtils.getValueFromResults('DocId', cells),
|
||||
url: SearchUtils.getValueFromResults('ServerRedirectedURL', cells),
|
||||
webUrl: SearchUtils.getValueFromResults('SPWebUrl', cells),
|
||||
webTitle: SearchUtils.getValueFromResults('SiteTitle', cells),
|
||||
title: SearchUtils.getValueFromResults('Title', cells),
|
||||
previewImageUrl: SearchUtils.getPreviewImageUrl(cells, siteUrl),
|
||||
extension: SearchUtils.getValueFromResults('FileType', cells),
|
||||
activity: {
|
||||
actorId: -1,
|
||||
actorName: Utils.trim(editorInfo[1]),
|
||||
actorPhotoUrl: Utils.getUserPhotoUrl(Utils.trim(editorInfo[0]), siteUrl),
|
||||
date: dateString,
|
||||
name: 'Modified'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resolve(trendingDocuments);
|
||||
}, (error: any): void => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
8
samples/react-officegraph/src/webparts/trendingInTheSitesIFollow/loc/en-us.js
vendored
Normal file
8
samples/react-officegraph/src/webparts/trendingInTheSitesIFollow/loc/en-us.js
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Manage the settings of this Web Part",
|
||||
"ViewGroupName": "View",
|
||||
"NumberOfDocumentsFieldLabel": "Number of documents to show",
|
||||
"TitleFieldLabel": "Web Part Title (displayed in the body)"
|
||||
}
|
||||
});
|
11
samples/react-officegraph/src/webparts/trendingInTheSitesIFollow/loc/mystrings.d.ts
vendored
Normal file
11
samples/react-officegraph/src/webparts/trendingInTheSitesIFollow/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
declare interface ITrendingInTheSitesIFollowStrings {
|
||||
PropertyPaneDescription: string;
|
||||
ViewGroupName: string;
|
||||
NumberOfDocumentsFieldLabel: string;
|
||||
TitleFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'trendingInTheSitesIFollowStrings' {
|
||||
const strings: ITrendingInTheSitesIFollowStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import * as assert from 'assert';
|
||||
|
||||
describe('TrendingInTheSitesIFollowWebPart', () => {
|
||||
it('should do something', () => {
|
||||
assert.ok(true);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue